Skip to content

runtime: the keys and actions domains answer 405 METHOD_NOT_ALLOWED with no Allow header (deps.error), where RFC 9110 requires one and the mcp, meta and ai domains send it #24023

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), a signed-in GET, PUT, PATCH or DELETE to /api/v1/keys answers 405 METHOD_NOT_ALLOWED with no Allow header. Before PR #24009 the adapter answered the same 405 with Allow: POST. The createHonoApp host answers without it at base too.

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 4) and that card's seat ruling B (6222005517). ⛔ Not a claim; the grade is triage's.

Reader who acts: the domain:cli seat. Both sites are in packages/runtime.

Where (origin/main fd0e72abc)

  • packages/runtime/src/domains/keys.ts, handleKeysRequest's method check: deps.error('Method not allowed', 405).
  • packages/runtime/src/domains/actions.ts about :446: the same deps.error 405.
  • The other dispatcher domains build their 405 with an Allow header: domains/mcp.ts, domains/meta.ts, domains/ai.ts.
  • RFC 9110 §15.5.6: "The origin server MUST generate an Allow header field in a 405 (Method Not Allowed) response containing a list of the target resource's currently supported methods."

Done when

  • A 405 from the keys and actions domains carries Allow naming the methods each serves, on both hosts.
  • A pin asserts the header for one non-allowed verb on each domain and fails against today's code.
  • The search for the shape covers every deps.error(…, 405) in packages/runtime/src/domains, so no third site is left.

Dedupe

MCP search_issues on this repository, closed included: 405 Method Not Allowed response missing Allow header keys actions domain deps.error gave 1 hit, #23909, the parent and not this gap.

Dedupe words: 405 no Allow header; deps.error 405; keys actions method not allowed Allow.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

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

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

  2. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 2 · 2026-10-17T21:36Z
    Session: session_01QyoWKK9RwUNxBxmevy9Neb
    Account: marchtian (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24023-keys-actions-405-allow
    Worktree: objectstack-issue-24023
    Domain: domain:cli
    Seat: domain:cli#2 (seat post #22648)
    File surface (read on origin/main aa31569b2; line numbers move; stop on a breach and explain it in the report):


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24023,
      "status": "done",
      "branch": "claude/issue-24023-keys-actions-405-allow",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24227",
      "session": "session_01QyoWKK9RwUNxBxmevy9Neb — mode:subagent, the parent PM session id (container CLAUDE_CODE_REMOTE_SESSION_ID, as the relay route resolved it)",
      "premise_still_valid": true,
      "summary": "The keys and actions dispatcher domains built their 405 with deps.error, whose definition (HttpDispatcher.error, which calls apiErrorResponse) returns { status, body } and no headers. Both now hand-roll the 405 in the mcp/meta/ai shape: status 405, headers { Allow: 'POST' }, and the body through buildApiError, so the code stays derived as METHOD_NOT_ALLOWED and the body is byte-identical. The anonymous baseline stays each handler's first statement. Measured on both hosts with the e614b2adb build, in process. Neither host drops domain headers. createHonoApp now answers both domains' signed-in non-POST with 405 + Allow: POST. The standalone server answers that for /keys; for /actions its adapter answers first, because only POST is mounted, and that answer already carried Allow: POST. The sweep at head finds zero deps.error(…, 405) sites in packages/runtime/src/domains, and five status: 405 literals, all with Allow. check:route-envelope required its DISPATCHER_DOMAINS ledger to declare the two new hand-built responses (keys 1 to 2, actions 0 to 1). That file is outside the claim's listed surface and is declared in deviations and in the PR body.",
      "tests": "New pin packages/runtime/src/domains/keys-actions-method-not-allowed-allow.test.ts, 7 cases: dispatch() per domain, signed-in GET gets 405 + headers.Allow 'POST' + code METHOD_NOT_ALLOWED + httpStatus 405 + spec BaseResponseSchema/ApiErrorSchema parse, anonymous GET gets 401 UNAUTHENTICATED with no Allow; plus a real Hono socket (HonoServerPlugin + createDispatcherPlugin) for keys 405 allow POST, keys anonymous 401 no allow, and the actions adapter 405 allow POST. With the 3 neighbour files: 'Test Files 4 passed (4) / Tests 96 passed (96)'. ABLATION (fix committed first, one run, nested node scripts/ablation-replace.mjs, both sites back to today's deps.error line): keys anchor 1 to 0, blob f6d9a5547690 to 0365f3e2c755; actions anchor 1 to 0, blob e15f8b9fff19 to bda747ac32f9; result 'Tests 3 failed | 4 passed (7)', the 3 Allow rows failing with 'expected undefined to be POST' / 'expected null to be POST', and the anonymous rows and the adapter row staying green; restore blob == HEAD and git diff HEAD empty for both. The subject is imported from source (../http-dispatcher.js, ../dispatcher-plugin.js), so no dist of the mutated package is on its path. FULL SUITE: pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 at 3fbad0f81: 'Test Files 410 passed (410) / Tests 6287 passed | 19 skipped (6306)', VERDICT command-exit 0; packages/runtime has no byte change from 3fbad0f81 to head 71613b4ef. pnpm --filter @objectstack/runtime typecheck: exit 0; the test-layer program lists the new file (tsc --listFiles -p tsconfig.test.json: 1 hit, 413 test files), and check:test-typecheck OK with the ledger unchanged. Closure build pnpm --filter '@objectstack/runtime^...' build: exit 0. test:repo was not run locally (diff touches none of its files); it is declared to CI Test Core.",
      "gates": "LOCAL at head 71613b4ef (after merging origin/main b9ac35cbf): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 84 commands; each was run with its exit captured before any pipe; --ran: '84 derived, 83 run, 1 NOT-MEASURED, 0 UNRUN', and all 83 run exit 0. NOT MEASURED: pnpm check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET, because it reads every package's dist (38 absent) and no whole-repo build was done; a declared narrowing, run by CI Lint & Repo Gates. pnpm check:route-envelope was exit 1 on the first run at 3fbad0f81 (handBuilt keys 2 vs 1 and actions 1 vs 0 declared) and exit 0 after the ledger entry, in the final run too. pnpm exec eslint --no-inline-config over keys.ts, actions.ts, the new test and scripts/check-route-envelope.mjs: exit 0. Not run: check-platform-checklist (no export renamed or removed); check:dev-prereqs and check:skill-frame-freshness (spec and skill frame untouched). CI on PR #24227 head 71613b4ef, read once at report time: 23 check-runs; 'No other open PR may claim the same issue' success, 'Packed-tarball smoke (opt-in)' skipped, and the other 21 in_progress (Lint · ESLint, Lint · repo & tooling gates, Lint · PM dispatch-gates, Type Check · workspace, Type Check · source gates, Type Check · debt ledger, Check Changeset, Check PR Size, Governed Surface Queue Guard, and others). The CI conclusion is the seat's to read.",
      "mcp_calls": "0 — none",
      "api_writes": "2 — both through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches as the session; executed as objectstack-fleet[bot]): (1) pr_create, which became POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24227/assignees [marchtian], relay read-back: 10902 bytes sent, 10902 stored, identical; (2) the os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/24023/comments. git push x5 (not REST): the empty-branch probe, the fix, the changeset, the ledger, the merge.",
      "open_questions": [
        {
          "question": "The order says the 405 change must not leak the method set to an anonymous caller. /actions passes the anonymous decision `method`, and shouldDenyAnonymous lets OPTIONS through by contract (CORS preflight). So an anonymous OPTIONS reaches the actions 405, which now carries Allow: POST. Measured: only on a createHonoApp host with CORS off. With CORS on (the default on both hosts) every OPTIONS is answered 204 first. The standalone server with CORS off already answers that same request 405 + Allow: POST, from its adapter. /keys decides anonymity without the method, so its anonymous OPTIONS is 401. Does the rule cover this caller?",
          "options": [
            "A — keep as shipped: every actions 405 carries Allow (card 'Done when' line 1, verbatim), and this caller learns POST, as the standalone adapter already tells it; zero further change",
            "B — withhold Allow when the caller is anonymous: one conditional in actions.ts, and that one 405 stays header-less; this contradicts 'Done when' line 1 for that case and diverges from the standalone host's answer to the same request"
          ],
          "recommendation": "A, because an OPTIONS request is the question 'which methods does this resource allow' (RFC 9110 OPTIONS), every browser preflight to this route is anonymous by construction, the other host already gives this caller the same answer, and B would buy a header-less 405 (the defect this card closes) to hide one method name that the public API surface already documents. No change needed unless the seat rules B."
        }
      ],
      "out_of_scope_findings": [
        "class: b · reach: standalone server composition (HonoServerPlugin + createDispatcherPlugin over LiteKernel, fake auth, real socket, e614b2adb build; a stock showcase boot NOT MEASURED): an anonymous GET or PUT /api/v1/actions/task/close is answered 405 METHOD_NOT_ALLOWED + Allow: POST by the adapter (error.details present, no httpStatus), with CORS default and off; the createHonoApp host answers the same request 401 UNAUTHENTICATED · contract: ADR-0138 D2 'Everything else answers 401 (UNAUTHENTICATED). The decision is taken once per domain, as the domain's first statement, never surface by surface', and actions.ts's own 'Deliberately the FIRST statement, ahead of the 405: an anonymous caller learns the auth baseline and nothing about the route's shape' · Seam: spec: none (ADR-0138 D2 record) → runtime: packages/runtime/src/dispatcher-plugin.ts registerActionRoutes (three POST-only mounts) → plugin-hono-server unmatchedResponse() | consumer: none · the shape PR #24009 removed for /keys and the notifications mounts change removed for /notifications · dedupe words: actions anonymous 405 standalone; registerActionRoutes POST only every verb; actions domain floor unreachable non-POST",
        "class: a · reach: standalone server with CORS off (HonoServerPlugin({ port: 0, cors: false }) + createDispatcherPlugin, real socket, measured): OPTIONS /api/v1/keys is answered 405 with Allow: DELETE, GET, HEAD, PATCH, POST, PUT, while GET/PUT/PATCH/DELETE on that path are refused (401 anonymous, 405 signed-in), so the adapter names the refusal-only mounts as allowed methods (RFC 9110: Allow lists the target resource's currently supported methods). The same refusal-only mount shape exists for the /notifications faces and /mcp/skill, NOT MEASURED · Seam: spec: IHttpServer unmatched-request contract (packages/spec/src/contracts/http-server.ts, 'MUST include an Allow header listing the methods registered for that path') → runtime: dispatcher-plugin.ts mountKeys refusal-only mounts → plugin-hono-server unmatchedResponse() · dedupe words: Allow lists refusal-only mounts; OPTIONS keys 405 Allow every verb; registered vs supported methods",
        "carrier: none (承接者:无) · noted in PR Acceptance notes, not filed · domains/mcp.ts about :174-:179: the comment still says the actions.ts and keys.ts 405s 'go through deps.error', which this PR makes false; a comment-only edit outside the claim's surface",
        "carrier: none (承接者:无) · noted, not filed · error-envelope.conformance.test.ts's source-scan MODULES list does not include domains/keys.ts, domains/actions.ts or domains/meta.ts, so their hand-built 405 bodies are held by the new pin's envelope parse and by check:route-envelope's ledger, not by that scan",
        "carrier: none (承接者:无) · noted, not filed · the data door's exposure refusal 405 OBJECT_API_METHOD_NOT_ALLOWED (thrown in action-execution.ts, answered through deps.errorFromThrown in domains/data.ts; RestServer's twin is the same) carries no Allow, and its 'allowed' field lists API operations, not HTTP methods; a different shape from this card's deps.error(…, 405) search; read from source, not measured",
        "carrier: none (承接者:无) · noted, not filed · handleKeysRequest never reads its path, so on a createHonoApp host /api/v1/keys/ANYTHING reaches the domain (Allow: POST is accurate there), while the standalone server answers 404 for any /keys sub-path; read from source, not measured"
      ],
      "line_budget": "n/a — no skills/** path touched",
      "deviations": [
        "File surface: scripts/check-route-envelope.mjs was edited, which is outside the claim's list. Its DISPATCHER_DOMAINS ledger had to declare the two new hand-built 405s (keys 1 to 2, actions 0 to 1, kind 1, with notes), or pnpm check:route-envelope stays red; the gate's failure text asks for exactly that, and the meta and ai 405s raised the same ledger. The script's own suite (its --self-test inside pnpm check:route-envelope) is exit 0. No other file outside the surface was touched; meta.ts, security/** and packages/spec are untouched.",
        "The keys-every-verb.hono.integration.test.ts and error-envelope.conformance.test.ts files were left unedited; the standalone-host leg lives in the new pin under domains/ instead.",
        "Merged origin/main (b9ac35cbf, CI workflows and scripts only, disjoint from this diff) before the final gate run, because dispatch-gates flagged the tree STALE against workflow files it derives from; no package source moved, so no rebuild or second full suite was run after the merge.",
        "Both hosts were measured in process (LiteKernel + fake auth service; createHonoApp via app.request, the standalone server via a real socket), not on a stock showcase boot: NOT MEASURED on showcase.",
        "Harness reminders asked for a model-named Co-Authored-By trailer and a different PR footer; per AGENTS.md and this order, commits carry the model-free pair and the PR body carries the session-URL footer."
      ],
      "files_changed": [
        "packages/runtime/src/domains/keys.ts",
        "packages/runtime/src/domains/actions.ts",
        "packages/runtime/src/domains/keys-actions-method-not-allowed-allow.test.ts",
        "scripts/check-route-envelope.mjs",
        ".changeset/24023-keys-actions-405-allow.md"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    Patch round addendum. The new head is 761b73cbd. Three exits, each captured before any pipe: pnpm --filter @objectstack/runtime typecheck exit 0; pnpm exec eslint --no-inline-config packages/runtime/src/domains/mcp.ts exit 0; pnpm check:route-envelope exit 0.

    {
      "issue": 24023,
      "status": "done",
      "branch": "claude/issue-24023-keys-actions-405-allow",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24227",
      "head": "761b73cbd",
      "session": "session_01QyoWKK9RwUNxBxmevy9Neb — mode:subagent, the parent PM session id (container CLAUDE_CODE_REMOTE_SESSION_ID, as the relay route resolved it)",
      "premise_still_valid": true,
      "round": "patch round 1 (seat item: the stale domains/mcp.ts 405 comment); supersedes the report at comment 6242659006 for head, gates, files_changed, open_questions and out_of_scope_findings",
      "summary": "Patch round: comment-only edit in packages/runtime/src/domains/mcp.ts at about :174-:177. It no longer says the actions.ts and keys.ts 405s go through deps.error; it now says every 405 the dispatcher's domains answer (actions.ts, ai.ts, keys.ts, meta.ts and the mcp one) carries an Allow header and builds its body through the one builder. The same comment also said the derived code was 'method_not_allowed'; it now says METHOD_NOT_ALLOWED, the code the error-envelope conformance case asserts. No code change in mcp.ts. New head 761b73cbd (commit 'docs(runtime): the mcp 405 comment names every dispatcher 405 as carrying Allow'), pushed to the same branch without force. Everything else is as in the first report: The keys and actions dispatcher domains built their 405 with deps.error, whose definition (HttpDispatcher.error, which calls apiErrorResponse) returns { status, body } and no headers. Both now hand-roll the 405 in the mcp/meta/ai shape: status 405, headers { Allow: 'POST' }, and the body through buildApiError, so the code stays derived as METHOD_NOT_ALLOWED and the body is byte-identical. The anonymous baseline stays each handler's first statement. Measured on both hosts with the e614b2adb build, in process. Neither host drops domain headers. createHonoApp now answers both domains' signed-in non-POST with 405 + Allow: POST. The standalone server answers that for /keys; for /actions its adapter answers first, because only POST is mounted, and that answer already carried Allow: POST. The sweep at head finds zero deps.error(…, 405) sites in packages/runtime/src/domains, and five status: 405 literals, all with Allow. check:route-envelope required its DISPATCHER_DOMAINS ledger to declare the two new hand-built responses (keys 1 to 2, actions 0 to 1). That file is outside the claim's listed surface and is declared in deviations and in the PR body.",
      "tests": "This round (comment-only diff): no test re-run beyond the three named checks below; the earlier evidence stands for unchanged code. packages/runtime source differs from 71613b4ef only in that comment. Earlier: New pin packages/runtime/src/domains/keys-actions-method-not-allowed-allow.test.ts, 7 cases: dispatch() per domain, signed-in GET gets 405 + headers.Allow 'POST' + code METHOD_NOT_ALLOWED + httpStatus 405 + spec BaseResponseSchema/ApiErrorSchema parse, anonymous GET gets 401 UNAUTHENTICATED with no Allow; plus a real Hono socket (HonoServerPlugin + createDispatcherPlugin) for keys 405 allow POST, keys anonymous 401 no allow, and the actions adapter 405 allow POST. With the 3 neighbour files: 'Test Files 4 passed (4) / Tests 96 passed (96)'. ABLATION (fix committed first, one run, nested node scripts/ablation-replace.mjs, both sites back to today's deps.error line): keys anchor 1 to 0, blob f6d9a5547690 to 0365f3e2c755; actions anchor 1 to 0, blob e15f8b9fff19 to bda747ac32f9; result 'Tests 3 failed | 4 passed (7)', the 3 Allow rows failing with 'expected undefined to be POST' / 'expected null to be POST', and the anonymous rows and the adapter row staying green; restore blob == HEAD and git diff HEAD empty for both. The subject is imported from source (../http-dispatcher.js, ../dispatcher-plugin.js), so no dist of the mutated package is on its path. FULL SUITE: pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 at 3fbad0f81: 'Test Files 410 passed (410) / Tests 6287 passed | 19 skipped (6306)', VERDICT command-exit 0; packages/runtime has no byte change from 3fbad0f81 to head 71613b4ef. pnpm --filter @objectstack/runtime typecheck: exit 0; the test-layer program lists the new file (tsc --listFiles -p tsconfig.test.json: 1 hit, 413 test files), and check:test-typecheck OK with the ledger unchanged. Closure build pnpm --filter '@objectstack/runtime^...' build: exit 0. test:repo was not run locally (diff touches none of its files); it is declared to CI Test Core.",
      "gates": "THIS ROUND at head 761b73cbd, each exit captured before any pipe: (1) pnpm --filter @objectstack/runtime typecheck under the verify lock, after rebuilding the closure pnpm --filter '@objectstack/runtime^...' build (exit 0, because the worktree was recreated from origin): exit 0, VERDICT command-exit 0, 'check:test-typecheck: OK … 27 file(s) / 190 error(s) / 68 pinned signature(s)'. (2) pnpm exec eslint --no-inline-config packages/runtime/src/domains/mcp.ts: exit 0, no output. (3) pnpm check:route-envelope: exit 0, '✓ check-route-envelope self-test passed', '✓ Dispatcher domains — 22 audited: 16 helper-only, 6 with declared hand-built responses (0 ratcheted)'. The derived union was not re-run for a comment-only edit, per the round's instruction. EARLIER at 71613b4ef: LOCAL at head 71613b4ef (after merging origin/main b9ac35cbf): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 84 commands; each was run with its exit captured before any pipe; --ran: '84 derived, 83 run, 1 NOT-MEASURED, 0 UNRUN', and all 83 run exit 0. NOT MEASURED: pnpm check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET, because it reads every package's dist (38 absent) and no whole-repo build was done; a declared narrowing, run by CI Lint & Repo Gates. pnpm check:route-envelope was exit 1 on the first run at 3fbad0f81 (handBuilt keys 2 vs 1 and actions 1 vs 0 declared) and exit 0 after the ledger entry, in the final run too. pnpm exec eslint --no-inline-config over keys.ts, actions.ts, the new test and scripts/check-route-envelope.mjs: exit 0. Not run: check-platform-checklist (no export renamed or removed); check:dev-prereqs and check:skill-frame-freshness (spec and skill frame untouched). CI on PR #24227 head 71613b4ef, read once at report time: 23 check-runs; 'No other open PR may claim the same issue' success, 'Packed-tarball smoke (opt-in)' skipped, and the other 21 in_progress (Lint · ESLint, Lint · repo & tooling gates, Lint · PM dispatch-gates, Type Check · workspace, Type Check · source gates, Type Check · debt ledger, Check Changeset, Check PR Size, Governed Surface Queue Guard, and others). The CI conclusion is the seat's to read.",
      "mcp_calls": "0 — none",
      "api_writes": "3 in total — this round 1: this os-dev-report addendum comment, through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/objectstack/issues/24023/comments). First round 2: pr_create (POST /pulls draft + POST /issues/24227/assignees [marchtian]) and the first os-dev-report (POST /issues/24023/comments). git push this round x1 (71613b4ef to 761b73cbd, no force); 6 in total.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: b · reach: standalone server composition (HonoServerPlugin + createDispatcherPlugin over LiteKernel, fake auth, real socket, e614b2adb build; a stock showcase boot NOT MEASURED): an anonymous GET or PUT /api/v1/actions/task/close is answered 405 METHOD_NOT_ALLOWED + Allow: POST by the adapter (error.details present, no httpStatus), with CORS default and off; the createHonoApp host answers the same request 401 UNAUTHENTICATED · contract: ADR-0138 D2 'Everything else answers 401 (UNAUTHENTICATED). The decision is taken once per domain, as the domain's first statement, never surface by surface', and actions.ts's own 'Deliberately the FIRST statement, ahead of the 405: an anonymous caller learns the auth baseline and nothing about the route's shape' · Seam: spec: none (ADR-0138 D2 record) → runtime: packages/runtime/src/dispatcher-plugin.ts registerActionRoutes (three POST-only mounts) → plugin-hono-server unmatchedResponse() | consumer: none · the shape PR #24009 removed for /keys and the notifications mounts change removed for /notifications · dedupe words: actions anonymous 405 standalone; registerActionRoutes POST only every verb; actions domain floor unreachable non-POST",
        "class: a · reach: standalone server with CORS off (HonoServerPlugin({ port: 0, cors: false }) + createDispatcherPlugin, real socket, measured): OPTIONS /api/v1/keys is answered 405 with Allow: DELETE, GET, HEAD, PATCH, POST, PUT, while GET/PUT/PATCH/DELETE on that path are refused (401 anonymous, 405 signed-in), so the adapter names the refusal-only mounts as allowed methods (RFC 9110: Allow lists the target resource's currently supported methods). The same refusal-only mount shape exists for the /notifications faces and /mcp/skill, NOT MEASURED · Seam: spec: IHttpServer unmatched-request contract (packages/spec/src/contracts/http-server.ts, 'MUST include an Allow header listing the methods registered for that path') → runtime: dispatcher-plugin.ts mountKeys refusal-only mounts → plugin-hono-server unmatchedResponse() · dedupe words: Allow lists refusal-only mounts; OPTIONS keys 405 Allow every verb; registered vs supported methods",
        "carrier: none (承接者:无) · noted, not filed · error-envelope.conformance.test.ts's source-scan MODULES list does not include domains/keys.ts, domains/actions.ts or domains/meta.ts, so their hand-built 405 bodies are held by the new pin's envelope parse and by check:route-envelope's ledger, not by that scan",
        "carrier: none (承接者:无) · noted, not filed · the data door's exposure refusal 405 OBJECT_API_METHOD_NOT_ALLOWED (thrown in action-execution.ts, answered through deps.errorFromThrown in domains/data.ts; RestServer's twin is the same) carries no Allow, and its 'allowed' field lists API operations, not HTTP methods; a different shape from this card's deps.error(…, 405) search; read from source, not measured",
        "carrier: none (承接者:无) · noted, not filed · handleKeysRequest never reads its path, so on a createHonoApp host /api/v1/keys/ANYTHING reaches the domain (Allow: POST is accurate there), while the standalone server answers 404 for any /keys sub-path; read from source, not measured"
      ],
      "line_budget": "n/a — no skills/** path touched",
      "deviations": [
        "packages/runtime/src/domains/mcp.ts (comment only) is outside the claimed surface. The seat accepted it in this patch round as fallout of this PR.",
        "The worktree had been removed after the first report, so it was recreated from origin/claude/issue-24023-keys-actions-405-allow at 71613b4ef. The shared checkout was not touched.",
        "The seat ruled A on the open question (keep as shipped); open_questions is therefore empty.",
        "File surface: scripts/check-route-envelope.mjs was edited, which is outside the claim's list. Its DISPATCHER_DOMAINS ledger had to declare the two new hand-built 405s (keys 1 to 2, actions 0 to 1, kind 1, with notes), or pnpm check:route-envelope stays red; the gate's failure text asks for exactly that, and the meta and ai 405s raised the same ledger. The script's own suite (its --self-test inside pnpm check:route-envelope) is exit 0. No other file outside the surface was touched; meta.ts, security/** and packages/spec are untouched.",
        "The keys-every-verb.hono.integration.test.ts and error-envelope.conformance.test.ts files were left unedited; the standalone-host leg lives in the new pin under domains/ instead.",
        "Merged origin/main (b9ac35cbf, CI workflows and scripts only, disjoint from this diff) before the final gate run, because dispatch-gates flagged the tree STALE against workflow files it derives from; no package source moved, so no rebuild or second full suite was run after the merge.",
        "Both hosts were measured in process (LiteKernel + fake auth service; createHonoApp via app.request, the standalone server via a real socket), not on a stock showcase boot: NOT MEASURED on showcase.",
        "Harness reminders asked for a model-named Co-Authored-By trailer and a different PR footer; per AGENTS.md and this order, commits carry the model-free pair and the PR body carries the session-URL footer."
      ],
      "files_changed": [
        "packages/runtime/src/domains/keys.ts",
        "packages/runtime/src/domains/actions.ts",
        "packages/runtime/src/domains/keys-actions-method-not-allowed-allow.test.ts",
        "scripts/check-route-envelope.mjs",
        ".changeset/24023-keys-actions-405-allow.md",
        "packages/runtime/src/domains/mcp.ts (comment only)"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24227 comment 6242843801 · domain:cli seat 2 · session session_01QyoWKK9RwUNxBxmevy9Neb · 2026-10-17T23:07Z · landing waits on every check of head 761b73cbd. Out-of-scope findings 1 and 2 → #24228.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #24227 → 6e43b7bc3 (#24023, Fixes #24023) · domain:cli seat 2 · marchtian · session session_01QyoWKK9RwUNxBxmevy9Neb · 2026-10-17T23:51Z

    Reading Result
    PR #24227 object merged: true, merge commit 6e43b7bc3, merged 2026-10-17T23:50Z through the merge queue (enqueued 2026-10-17T23:28Z)
    origin/main 6e43b7bc3 is an ancestor of origin/main, with a single parent (squash). The 6 files and +270 / −9 match what was reviewed at 761b73cbd
    queue branch no gh-readonly-queue/main/*pr-24227* 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:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions