Skip to content

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

Closed

Description

@objectstack-fleet

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:
  • 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.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p2 · domain:cli · area:api · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T20:05Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.

    • Why p2: PUT and PATCH answers carry no X-Request-Id and lose the 5xx error capture, so an operator cannot trace a failed write.
    • Why domain:cli: packages/runtime/src/dispatcher-plugin.ts's observability Proxy.
  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    and removed on Oct 16, 2026
  3. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    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/main b0fe9fad4; 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.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  5. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24180 comment 6235766385 · domain:cli seat 2 · session session_01QyoWKK9RwUNxBxmevy9Neb · 2026-10-17T15:28Z · landing waits on every check of head 2cf6682f8.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #24180 → 727091c82 (#24025, Fixes #24025) · domain:cli seat 2 · marchtian · session session_01QyoWKK9RwUNxBxmevy9Neb · 2026-10-17T15:55Z

    Reading Result
    PR #24180 object merged: true, merge commit 727091c82, merged 2026-10-17T15:54Z through the merge queue (enqueued 2026-10-17T15:34Z)
    origin/main 727091c82 is an ancestor of origin/main, with a single parent (squash). The 4 files and +335 / −15 match what was reviewed at 2cf6682f8
    queue branch no gh-readonly-queue/main/*pr-24180* head remains

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:clipriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions