You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
runtime(dispatcher): PUT and PATCH routes skip the observability Proxy, so they answer with no X-Request-Id and lose the 5xx error capture; only get/post/delete are wrapped #24025
Filing gate ①: a product defect at a public door, measured. reach: on a stock standalone showcase boot (os dev, PR #24009's head 5e3b39fcd), PUT and PATCH /api/v1/keys answer with no X-Request-Id, while GET and DELETE /api/v1/keys on the same boot carry one.
Filed by the domain:cli seat 2 (seat post #22648) · os-tesla · session session_018t6a5rvqPRvEDMCVVW9kYT, from #23909's dev report (6221942285, out-of-scope finding 1). ⛔ Not a claim; the grade is triage's.
Reader who acts: the domain:cli seat, for packages/runtime/src/dispatcher-plugin.ts.
Where (origin/main fd0e72abc)
The observability Proxy in createDispatcherPlugin (about :1045) wraps registrations through instrumentRouteHandler only for get, post and delete. put and patch pass through unwrapped.
A PUT or PATCH mount therefore loses:
the request-id echo or mint;
req.requestId;
the 5xx errorReporter capture;
the res.__obsRequest coordinates on 5xx log lines.
The counter and the histogram survive on the standalone Hono transport only because it implements afterResponse (plugin-hono-server/src/adapter.ts about :1630).
content/docs/protocol/kernel/error-handling.mdx says the id "is attached by the observability instrumentation wrapper … one that does echoes a valid inbound X-Request-Id and otherwise mints req_hex".
The un-wrapped mounts today:
PATCH /packages/:id, /packages/:id/enable and /packages/:id/disable (mountPackagesRoute);
PUT /automation/:name;
the PUT and PATCH /ai/* wildcards (registerAIRoutes);
service-emitted PATCH routes through mountRouteOnServer.
The /mcp/skill comment (about :1300) chose not to mount PUT/PATCH for exactly this reason.
Done when
Every verb the dispatcher mounts is wrapped. A PUT and a PATCH route echo a valid inbound X-Request-Id, mint one otherwise, and report a 5xx to the error reporter. A pin fails against today's Proxy.
Dedupe
MCP search_issues on this repository, closed included: X-Request-Id missing on PUT PATCH routes observability Proxy instrumentRouteHandler only get post delete gave 0 hits.
Dedupe words: observability Proxy PUT PATCH not instrumented; X-Request-Id missing PUT PATCH; instrumentRouteHandler verb list.
Claim: PM loop round 1 · 2026-10-17T14:17Z
Session: session_01QyoWKK9RwUNxBxmevy9Neb
Account: marchtian (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-24025-put-patch-observability
Worktree: objectstack-issue-24025
Domain: domain:cli
Seat: domain:cli#2 (seat post #22648)
File surface (read on origin/mainb0fe9fad4; line numbers move; stop on a breach and explain it in the report): packages/runtime/src/dispatcher-plugin.ts, only the observability Proxy in createDispatcherPlugin (about :1045-1070, the prop === 'get' || prop === 'post' || prop === 'delete' arm); packages/runtime/src/observability/instrument.ts, only if instrumentRouteHandler must learn a verb; pins under packages/runtime/src/; one .changeset/24025-*.md. ⛔ Hold-offs: every route body and mount helper (mountPackagesRoute, registerAIRoutes, the /keys mounts), packages/plugins/plugin-hono-server, packages/rest.
Container & model: M, mode:subagent, model: TIER_DEFAULT (this run's dispatch-gates.mjs --tier --repo objectstack-ai/objectstack: "Model tier — no path-derived mandate")
Clause-②: no
Responsibility: packages/runtime's dispatcher Proxy wraps only get, post and delete | none for the request id and the 5xx capture; the Hono transport's afterResponse keeps only the counter and the histogram | every client of a dispatcher PUT or PATCH route (/packages/:id*, /automation/:name, /ai/*, /keys) and the operator tracing its failures; measured on a stock showcase boot (card body)
Thread-read: 6223713375
Serial constraints cleared: today's predecessors on dispatcher-plugin.ts, PR #24149 (6dede7a51) and PR #24009 (9e797a897), have landed. None of the 4 open PRs touches the file. #24061 (domain:services, claim 6234889035) declares packages/runtime/src/** for the me/* grant resolution, which lives in domains/auth.ts, so it does not overlap the Proxy. #24143 (seat 1) is packages/rest/src/data-action-routes.ts, which does not overlap.
os-dev-report
{
"issue": 24025,
"status": "done",
"branch": "claude/issue-24025-put-patch-observability",
"pr": "#24180",
"session": "session_01QyoWKK9RwUNxBxmevy9Neb (subagent run, parent's session; the relay route read it from CLAUDE_CODE_REMOTE_SESSION_ID)",
"premise_still_valid": true,
"summary": "H1 held at b0fe9fa: the observability Proxy in createDispatcherPlugin (dispatcher-plugin.ts:1045/1047) trapped only get/post/delete, so every PUT/PATCH mount (/keys, /notifications PUT+PATCH, /packages/:id*, /automation/:name, the /ai/* wildcards, ai:routes PATCH via mountRouteOnServer) answered with no X-Request-Id, no req.requestId, no errorReporter capture on 5xx and no [5xx]-line coordinates. The Proxy now wraps all five IHttpServer route verbs (a typed set, satisfies ReadonlyArray of keyof IHttpServer). H2 held on read and on measurement: instrumentRouteHandler is verb-agnostic (method is only a label) and the emit flags already hand counter+histogram to an afterResponse transport for every verb, so PUT/PATCH stay single-emitter on Hono (pinned on a real socket). H3 held: the /mcp/skill comment was corrected (no route added), and so were the same false sentence in the /keys and /notifications blocks; the #9650 seam-A measurement that copies the old Proxy inline was retitled to the past tense with its assertion unchanged. Patch changeset for @objectstack/runtime with Clause-②: no.",
"tests": "All at head 2cf6682 unless noted. (1) Pins on the fixed tree: pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 src/dispatcher-observability-every-verb.test.ts src/http-metrics-inbound-coverage.hono.integration.test.ts -> 'Test Files 2 passed (2) / Tests 48 passed (48)', lock VERDICT command-exit 0; the 22 new rows listed by --reporter=verbose. (2) Ablation, once, fix committed first (ee2cb0f): scripts/ablation-replace.mjs put back the arm prop === 'get' || prop === 'post' || prop === 'delete' (anchor 1 -> 0, blob 0136bea52b19 -> 1a1311e602b9), same two files -> 'Test Files 2 failed (2) / Tests 10 failed | 38 passed (48)'; red = exactly the 10 PUT/PATCH rows (9 unit + the real-socket row: 'PUT echoes the inbound id: expected null to be probe-put-24025'), all 15 GET/POST/DELETE control rows green; restore proven by the tool: blob after restore 0136bea52b19 == HEAD blob, git diff HEAD empty, git status clean. No rebuild leg: both files import ./dispatcher-plugin.js relatively (source, not dist). Direction observed: turned red (the normal direction). (3) Package suite: pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 -> 'Test Files 408 passed (408) / Tests 6276 passed | 19 skipped (6295)', lock VERDICT command-exit 0. test:repo not run locally (diff does not touch the repo project's 3 files; CI Test Core owns it). (4) pnpm --filter @objectstack/runtime run typecheck -> exit 0 (tsc --noEmit + check:test-typecheck: 27 files / 190 errors / 68 pinned signatures held, the new test file adds no debt). (5) Dependency closure build: turbo run build --filter=@objectstack/runtime^... -> 30/30 (full cache); runtime build -> 31/31, fix present in dist/index.js and dist/index.cjs (instrumentedRouteVerbs x2 each, old arm x0).",
"gates": "Local, at 2cf6682: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 62 commands over 4 paths (merge base cb8756b); each run with its exit code captured before any pipe; --ran reconciliation exit 0: '62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN'. 59 exited 0 on the first pass. 3 stopped on a local prerequisite first (exit 3, not a measurement) and are green on rerun, and the --ran record carries the rerun codes: node scripts/check-plugin-teardown-shape.mjs --self-test (pinned fixture commit 621a487 unreachable on the shallow clone; fetched that one commit with git fetch --depth=1; rerun exit 0, 48 cases); pnpm check:dual-build-cjs-loads (PREREQUISITE NOT MET, no workspace dist; turbo build of the workspace 76/76 under the lock; rerun exit 0: 111 require entry points across 69 packages load); pnpm check:type-check-debt (its inner closure build hit lock queue-timeout 99; rerun wholly under the lock: exit 0, 82/84 packages type-checked, re-measure none above its recorded number). Roster gates the derivation flagged as rostered under a touched directory, also run: check-changeset-fixed 0, check:authz-resolver 0, check:error-code-casing 0, check:filter-alias-parity 0, check:route-ledger-census 0. Lane blind spot: pnpm exec eslint --no-inline-config over the 3 changed .ts files -> exit 0, no output. Changeset gates: check-adr-0087-registration --base origin/main 0 (1 non-breaking changeset), check-changeset-no-major --base origin/main 0. CI on PR head 2cf6682 at report time: 23 check-runs, 21 in_progress (Check Changeset in_progress, Governed Surface Queue Guard in_progress), 1 success, 1 skipped; Lint & Repo Gates, TypeScript Type Check, Test Core and Build Core not yet reported. NOT MEASURED: those required CI families, reason: in_progress at report time (convergence is the PM's).",
"line_budget": "n/a: skills/** not touched",
"files_changed": [
".changeset/24025-put-patch-request-id.md",
"packages/runtime/src/dispatcher-plugin.ts",
"packages/runtime/src/dispatcher-observability-every-verb.test.ts",
"packages/runtime/src/http-metrics-inbound-coverage.hono.integration.test.ts"
],
"deviations": [
"File surface: beyond the Proxy arm, comment-only edits in the /keys and /notifications blocks of dispatcher-plugin.ts (the same false 'PUT/PATCH not wrapped' sentence H3 named for /mcp/skill), and a past-tense retitle plus note on the #9650 section-2 seam-A case in http-metrics-inbound-coverage.hono.integration.test.ts (assertion unchanged). No mount helper, route body, plugin-hono-server or packages/rest line changed.",
"Merged origin/main cb8756b before opening the PR (no conflict). That merge brought an os-dev.md change: the write budget is now three strokes and labels ride pr_create. This order's text still names label-write; no label was due either way (a changeset ships), so zero labels were written.",
"The merge commit 2cf6682 was made with git merge --no-edit and carries no Claude-Session / Co-Authored-By trailer pair; the three authored commits carry the model-free pair. The pre-push hook passed it (no card relation, no model identifier). History not rewritten.",
"Fetched one commit (621a487, depth 1) into the shared object store so a derived self-test could read its pinned fixture, instead of unshallowing the clone.",
"The package test ran as pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 (the test script's project, workers capped), not as the bare pnpm test script."
],
"mcp_calls": "0",
"api_writes": "3 REST writes in 2 relay strokes, both as objectstack-fleet[bot] via scripts/pm/with-fleet.sh --via dispatch (each stroke is one POST /repos/objectstack-ai/objectstack/dispatches): stroke 1 pr_create (run 39044336835, success) = POST /repos/objectstack-ai/objectstack/pulls (draft, #24180) + POST /repos//issues/24180/assignees (marchtian); stroke 2 = POST /repos//issues/24025/comments (this report). Plus git push (not REST), 5 pushes of the branch (empty probe, fix, tests, title fix, merge). No label write.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted in the PR's Acceptance notes, not filed: RouteDefinition.method in dispatcher-plugin.ts is typed GET/POST/PATCH/DELETE and mountRouteOnServer has no put arm, so a PUT route emitted on ai:routes would be skipped, visible only in the boot line's mount count; no named producer emits PUT on that hook (no reach). Dedupe words: ai:routes PUT not mounted; mountRouteOnServer put arm; RouteDefinition method PUT"
]
}
Activity
objectstack-fleet commentedon Oct 16, 2026
Triage: first grade,
bug·priority:p2·domain:cli·area:api·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T20:05ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
PUTandPATCHanswers carry noX-Request-Idand lose the 5xx error capture, so an operator cannot trace a failed write.domain:cli:packages/runtime/src/dispatcher-plugin.ts's observability Proxy.objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 1 · 2026-10-17T14:17Z
Session:
session_01QyoWKK9RwUNxBxmevy9NebAccount:
marchtian(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24025-put-patch-observabilityWorktree:
objectstack-issue-24025Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)File surface (read on
origin/mainb0fe9fad4; line numbers move; stop on a breach and explain it in the report):packages/runtime/src/dispatcher-plugin.ts, only the observabilityProxyincreateDispatcherPlugin(about:1045-1070, theprop === 'get' || prop === 'post' || prop === 'delete'arm);packages/runtime/src/observability/instrument.ts, only ifinstrumentRouteHandlermust learn a verb; pins underpackages/runtime/src/; one.changeset/24025-*.md. ⛔ Hold-offs: every route body and mount helper (mountPackagesRoute,registerAIRoutes, the/keysmounts),packages/plugins/plugin-hono-server,packages/rest.Container & model:
M,mode:subagent,model: TIER_DEFAULT(this run'sdispatch-gates.mjs --tier --repo objectstack-ai/objectstack: "Model tier — no path-derived mandate")Clause-②: no
Responsibility:
packages/runtime's dispatcherProxywraps onlyget,postanddelete| none for the request id and the 5xx capture; the Hono transport'safterResponsekeeps only the counter and the histogram | every client of a dispatcherPUTorPATCHroute (/packages/:id*,/automation/:name,/ai/*,/keys) and the operator tracing its failures; measured on a stock showcase boot (card body)Thread-read: 6223713375
Serial constraints cleared: today's predecessors on
dispatcher-plugin.ts, PR #24149 (6dede7a51) and PR #24009 (9e797a897), have landed. None of the 4 open PRs touches the file. #24061 (domain:services, claim 6234889035) declarespackages/runtime/src/**for theme/*grant resolution, which lives indomains/auth.ts, so it does not overlap theProxy. #24143 (seat 1) ispackages/rest/src/data-action-routes.ts, which does not overlap.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{
"issue": 24025,
"status": "done",
"branch": "claude/issue-24025-put-patch-observability",
"pr": "#24180",
"session": "session_01QyoWKK9RwUNxBxmevy9Neb (subagent run, parent's session; the relay route read it from CLAUDE_CODE_REMOTE_SESSION_ID)",
"premise_still_valid": true,
"summary": "H1 held at b0fe9fa: the observability Proxy in createDispatcherPlugin (dispatcher-plugin.ts:1045/1047) trapped only get/post/delete, so every PUT/PATCH mount (/keys, /notifications PUT+PATCH, /packages/:id*, /automation/:name, the /ai/* wildcards, ai:routes PATCH via mountRouteOnServer) answered with no X-Request-Id, no req.requestId, no errorReporter capture on 5xx and no [5xx]-line coordinates. The Proxy now wraps all five IHttpServer route verbs (a typed set, satisfies ReadonlyArray of keyof IHttpServer). H2 held on read and on measurement: instrumentRouteHandler is verb-agnostic (method is only a label) and the emit flags already hand counter+histogram to an afterResponse transport for every verb, so PUT/PATCH stay single-emitter on Hono (pinned on a real socket). H3 held: the /mcp/skill comment was corrected (no route added), and so were the same false sentence in the /keys and /notifications blocks; the #9650 seam-A measurement that copies the old Proxy inline was retitled to the past tense with its assertion unchanged. Patch changeset for @objectstack/runtime with Clause-②: no.",
"tests": "All at head 2cf6682 unless noted. (1) Pins on the fixed tree: pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 src/dispatcher-observability-every-verb.test.ts src/http-metrics-inbound-coverage.hono.integration.test.ts -> 'Test Files 2 passed (2) / Tests 48 passed (48)', lock VERDICT command-exit 0; the 22 new rows listed by --reporter=verbose. (2) Ablation, once, fix committed first (ee2cb0f): scripts/ablation-replace.mjs put back the arm prop === 'get' || prop === 'post' || prop === 'delete' (anchor 1 -> 0, blob 0136bea52b19 -> 1a1311e602b9), same two files -> 'Test Files 2 failed (2) / Tests 10 failed | 38 passed (48)'; red = exactly the 10 PUT/PATCH rows (9 unit + the real-socket row: 'PUT echoes the inbound id: expected null to be probe-put-24025'), all 15 GET/POST/DELETE control rows green; restore proven by the tool: blob after restore 0136bea52b19 == HEAD blob, git diff HEAD empty, git status clean. No rebuild leg: both files import ./dispatcher-plugin.js relatively (source, not dist). Direction observed: turned red (the normal direction). (3) Package suite: pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 -> 'Test Files 408 passed (408) / Tests 6276 passed | 19 skipped (6295)', lock VERDICT command-exit 0. test:repo not run locally (diff does not touch the repo project's 3 files; CI Test Core owns it). (4) pnpm --filter @objectstack/runtime run typecheck -> exit 0 (tsc --noEmit + check:test-typecheck: 27 files / 190 errors / 68 pinned signatures held, the new test file adds no debt). (5) Dependency closure build: turbo run build --filter=@objectstack/runtime^... -> 30/30 (full cache); runtime build -> 31/31, fix present in dist/index.js and dist/index.cjs (instrumentedRouteVerbs x2 each, old arm x0).",
"gates": "Local, at 2cf6682: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 62 commands over 4 paths (merge base cb8756b); each run with its exit code captured before any pipe; --ran reconciliation exit 0: '62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN'. 59 exited 0 on the first pass. 3 stopped on a local prerequisite first (exit 3, not a measurement) and are green on rerun, and the --ran record carries the rerun codes: node scripts/check-plugin-teardown-shape.mjs --self-test (pinned fixture commit 621a487 unreachable on the shallow clone; fetched that one commit with git fetch --depth=1; rerun exit 0, 48 cases); pnpm check:dual-build-cjs-loads (PREREQUISITE NOT MET, no workspace dist; turbo build of the workspace 76/76 under the lock; rerun exit 0: 111 require entry points across 69 packages load); pnpm check:type-check-debt (its inner closure build hit lock queue-timeout 99; rerun wholly under the lock: exit 0, 82/84 packages type-checked, re-measure none above its recorded number). Roster gates the derivation flagged as rostered under a touched directory, also run: check-changeset-fixed 0, check:authz-resolver 0, check:error-code-casing 0, check:filter-alias-parity 0, check:route-ledger-census 0. Lane blind spot: pnpm exec eslint --no-inline-config over the 3 changed .ts files -> exit 0, no output. Changeset gates: check-adr-0087-registration --base origin/main 0 (1 non-breaking changeset), check-changeset-no-major --base origin/main 0. CI on PR head 2cf6682 at report time: 23 check-runs, 21 in_progress (Check Changeset in_progress, Governed Surface Queue Guard in_progress), 1 success, 1 skipped; Lint & Repo Gates, TypeScript Type Check, Test Core and Build Core not yet reported. NOT MEASURED: those required CI families, reason: in_progress at report time (convergence is the PM's).",
"line_budget": "n/a: skills/** not touched",
"files_changed": [
".changeset/24025-put-patch-request-id.md",
"packages/runtime/src/dispatcher-plugin.ts",
"packages/runtime/src/dispatcher-observability-every-verb.test.ts",
"packages/runtime/src/http-metrics-inbound-coverage.hono.integration.test.ts"
],
"deviations": [
"File surface: beyond the Proxy arm, comment-only edits in the /keys and /notifications blocks of dispatcher-plugin.ts (the same false 'PUT/PATCH not wrapped' sentence H3 named for /mcp/skill), and a past-tense retitle plus note on the #9650 section-2 seam-A case in http-metrics-inbound-coverage.hono.integration.test.ts (assertion unchanged). No mount helper, route body, plugin-hono-server or packages/rest line changed.",
"Merged origin/main cb8756b before opening the PR (no conflict). That merge brought an os-dev.md change: the write budget is now three strokes and labels ride pr_create. This order's text still names label-write; no label was due either way (a changeset ships), so zero labels were written.",
"The merge commit 2cf6682 was made with git merge --no-edit and carries no Claude-Session / Co-Authored-By trailer pair; the three authored commits carry the model-free pair. The pre-push hook passed it (no card relation, no model identifier). History not rewritten.",
"Fetched one commit (621a487, depth 1) into the shared object store so a derived self-test could read its pinned fixture, instead of unshallowing the clone.",
"The package test ran as pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 (the test script's project, workers capped), not as the bare pnpm test script."
],
"mcp_calls": "0",
"api_writes": "3 REST writes in 2 relay strokes, both as objectstack-fleet[bot] via scripts/pm/with-fleet.sh --via dispatch (each stroke is one POST /repos/objectstack-ai/objectstack/dispatches): stroke 1 pr_create (run 39044336835, success) = POST /repos/objectstack-ai/objectstack/pulls (draft, #24180) + POST /repos//issues/24180/assignees (marchtian); stroke 2 = POST /repos//issues/24025/comments (this report). Plus git push (not REST), 5 pushes of the branch (empty probe, fix, tests, title fix, merge). No label write.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted in the PR's Acceptance notes, not filed: RouteDefinition.method in dispatcher-plugin.ts is typed GET/POST/PATCH/DELETE and mountRouteOnServer has no put arm, so a PUT route emitted on ai:routes would be skipped, visible only in the boot line's mount count; no named producer emits PUT on that hook (no reach). Dedupe words: ai:routes PUT not mounted; mountRouteOnServer put arm; RouteDefinition method PUT"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
ACCEPT → PR #24180 comment
6235766385·domain:cliseat 2 · sessionsession_01QyoWKK9RwUNxBxmevy9Neb· 2026-10-17T15:28Z · landing waits on every check of head2cf6682f8.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Landed: PR #24180 →
727091c82(#24025,Fixes #24025) ·domain:cliseat 2 ·marchtian· sessionsession_01QyoWKK9RwUNxBxmevy9Neb· 2026-10-17T15:55Zmerged: true, merge commit727091c82, merged 2026-10-17T15:54Z through the merge queue (enqueued 2026-10-17T15:34Z)origin/main727091c82is an ancestor oforigin/main, with a single parent (squash). The 4 files and +335 / −15 match what was reviewed at2cf6682f8gh-readonly-queue/main/*pr-24180*head remains6234981195→ report6235627499→ ACCEPT6235766385on PR fix(runtime): the dispatcher's observability Proxy wraps PUT and PATCH, so those routes carry X-Request-Id and report their 5xx #24180 → pointer6235784270.completedbyFixes.pm:dispatchedis stripped in this act, anddomain:cli/priority:p2/area:apistay.main's next full battery: nothing specific. The diff adds no route-ledger row, so the dogfood census pins do not move.Generated by Claude Code