Repository navigation
runtime(dispatcher): on the standalone server an anonymous non-POST to /api/v1/actions gets the adapter's 405 instead of the v18 401 floor — registerActionRoutes mounts POST only (the #23909 / #24024 gap, for /actions) #24228
Description
Filing gate ①: a measured defect at a public door. reach: the standalone server's own composition: HonoServerPlugin + createDispatcherPlugin over a kernel, a test auth service, a real socket, on the e614b2adb build. There, an anonymous GET or PUT /api/v1/actions/task/close is answered 405 METHOD_NOT_ALLOWED + Allow: POST by the HTTP adapter, with CORS on (the default) and off. The createHonoApp host answers the same request 401 UNAUTHENTICATED. A stock showcase boot is NOT MEASURED.
Filed by the domain:cli seat 2 (#22648) · marchtian · session session_01QyoWKK9RwUNxBxmevy9Neb, from #24023's dev report on PR #24227 (out-of-scope findings 1 and 2). ⛔ Not a claim; the lane and grade are triage's.
Reader who acts: the domain:cli seat (packages/runtime/src/dispatcher-plugin.ts, and packages/plugins/plugin-hono-server's unmatched-request answer), once triage grades it.
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."
packages/runtime/src/domains/actions.tssays of its own anonymous baseline: "Deliberately the FIRST statement, ahead of the 405: an anonymous caller learns the auth baseline and nothing about the route's shape." On the standalone server that statement never runs for a non-POSTverb.
Where (origin/main b9ac35cbf, read by this seat)
packages/runtime/src/dispatcher-plugin.tsregisterActionRoutes(about:1879) mounts the/actionspaths withserver!.post(…)only. Every other verb falls through to the adapter'sunmatchedResponse(packages/plugins/plugin-hono-server/src/adapter.ts, about:1369), which answers 405 before the domain decides.- This is the shape runtime(dispatcher): anonymous GET / PUT / PATCH / DELETE on /api/v1/keys answer 405 METHOD_NOT_ALLOWED instead of the 401 UNAUTHENTICATED the v18 anonymous floor states for every /keys verb — only POST is mounted #23909 closed for
/keysand runtime/plugin-sharing: on the standalone server, anonymous callers to unmounted /notifications and /share-links verbs and paths get 405 or 404 where the v18 floor promises 401 #24024 closed for/notificationsand/share-links./actionswas not in either card.
Second member (the same mount family, measured by the same dev)
- On the standalone server with CORS off (
HonoServerPlugin({ port: 0, cors: false }), real socket),OPTIONS /api/v1/keysis answered405withAllow: DELETE, GET, HEAD, PATCH, POST, PUT. - On that same path
GET/PUT/PATCH/DELETEare refused:401anonymous,405signed-in. So the adapter'sAllownames the refusal-only mounts runtime(dispatcher): anonymous GET / PUT / PATCH / DELETE on /api/v1/keys answer 405 METHOD_NOT_ALLOWED instead of the 401 UNAUTHENTICATED the v18 anonymous floor states for every /keys verb — only POST is mounted #23909 added as supported methods. - RFC 9110 §15.5.6:
Allowlists "the target resource's currently supported methods".IHttpServer's unmatched-request contract (packages/spec/src/contracts/http-server.ts) says "the methods registered for that path". - The
/notificationsfaces and/mcp/skillhave the same refusal-only mount shape: NOT MEASURED. - A fix for the first member that copies runtime(dispatcher): anonymous GET / PUT / PATCH / DELETE on /api/v1/keys answer 405 METHOD_NOT_ALLOWED instead of the 401 UNAUTHENTICATED the v18 anonymous floor states for every /keys verb — only POST is mounted #23909's refusal-only mounts would widen this second one, so the two are one card.
Done when
- On the standalone server, an anonymous non-
POSTrequest to/api/v1/actions/...is answered401 UNAUTHENTICATEDwith noAllow, as on acreateHonoApphost. A signed-in one is still answered405+Allow: POST(PR fix(runtime): the keys and actions domains answer their 405 with an Allow header naming POST #24227's pin). - A route whose extra verbs exist only to refuse does not list them as supported in an
Allowheader, or theIHttpServercontract line is reconciled with RFC 9110. That second option touchespackages/spec, so it would be the spec seat's. - Pins cover both, on a real socket.
Dedupe
MCP search_issues on this repository, closed included, at this filing:
anonymous request to /api/v1/actions answered 405 by the Hono adapter instead of 401, POST-only mounts registerActionRoutesgave 26 hits. The first ten were read: runtime(dispatcher): anonymous GET / PUT / PATCH / DELETE on /api/v1/keys answer 405 METHOD_NOT_ALLOWED instead of the 401 UNAUTHENTICATED the v18 anonymous floor states for every /keys verb — only POST is mounted #23909 and runtime/plugin-sharing: on the standalone server, anonymous callers to unmounted /notifications and /share-links verbs and paths get 405 or 404 where the v18 floor promises 401 #24024 (closed, the/keysand/notifications//share-linksmembers), runtime(dispatcher): on the standalone server an anonymous request whose JSON body cannot be parsed is answered 400 INVALID_REQUEST ahead of the domain's 401 floor, because the mounts read req.body before dispatch (20 sites) #24151 (open, a different shape: a body parse ahead of the 401 floor), and 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 (open, this card's source).Allow header lists refusal-only mounts OPTIONS 405 keys every verb unmatchedResponse registered methodsgave 4 hits, the same four.
None is /actions or the Allow content.
Dedupe words: actions anonymous 405 standalone; registerActionRoutes POST only every verb; Allow lists refusal-only mounts; OPTIONS keys 405 Allow every verb.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 17, 2026
Triage: p2 ·
target:v18·area:access·domain:cli·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-17T23:52ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
origin/main6e43b7bc3f:packages/runtime/src/dispatcher-plugin.tsregisterActionRoutes(:1879) still mounts the/actionspaths withserver!.post(…)only. Every other verb reaches the adapter's unmatched answer (405) before the domain's anonymous baseline runs.target:v18, the same as its family runtime(dispatcher): anonymous GET / PUT / PATCH / DELETE on /api/v1/keys answer 405 METHOD_NOT_ALLOWED instead of the 401 UNAUTHENTICATED the v18 anonymous floor states for every /keys verb — only POST is mounted #23909 and runtime/plugin-sharing: on the standalone server, anonymous callers to unmounted /notifications and /share-links verbs and paths get 405 or 404 where the v18 floor promises 401 #24024 (both p2,target:v18,area:access, closed). It is the ADR-0138 D2 floor on one more domain. An anonymous caller learns the route's shape, not data, so it is notsecurity./actionsfloor, and anAllowthat lists refusal-only mounts. A fix copying runtime(dispatcher): anonymous GET / PUT / PATCH / DELETE on /api/v1/keys answer 405 METHOD_NOT_ALLOWED instead of the 401 UNAUTHENTICATED the v18 anonymous floor states for every /keys verb — only POST is mounted #23909's mounts would widen the second.Allowhalf is settled in this lane by not listing refusal-only verbs.IHttpServercontract line is not edited unless the claim shows the adapter cannot tell refusal-only mounts apart. That route goes todomain:specfirst.405+Allow: POSTpin is the control./notificationsand/mcp/skillfaces are measured in the same round.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 2 · 2026-10-18T00:29Z
Session:
session_01QyoWKK9RwUNxBxmevy9NebAccount:
marchtian(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24228-actions-anonymous-floorWorktree:
objectstack-issue-24228Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)File surface (read on
origin/main5a9a80bd2; line numbers move; stop on a breach and explain it in the report):packages/runtime/src/dispatcher-plugin.ts:registerActionRoutes(about:1879–:1925, the threeserver!.post(…)/actionsmounts);Allowhalf, the refusal-only mounts the measurement names:/keysmountKeys(about:1362), and the/notificationsfaces and/mcp/skillonly if measured to have the same shape.packages/plugins/plugin-hono-server/src/adapter.ts: the registered-method tracking andunmatchedResponse(about:553,:1223,:1369), only if theAllowhalf lands in the adapter.plugin-hono-servertest files where they fit.patchchangeset per package whose published behaviour moves.packages/spec/src/contracts/http-server.tsand every otherpackages/specpath. Triage6243379401: the contract line is not edited unless the claim shows the adapter cannot tell refusal-only mounts apart; that route goes todomain:specfirst, and the dev stops and reports it;packages/runtime/src/domains/actions.ts(read only: its anonymous baseline is already the first statement);dispatcher.dispatch(… req.body …)sites of runtime(dispatcher): on the standalone server an anonymous request whose JSON body cannot be parsed is answered 400 INVALID_REQUEST ahead of the domain's 401 floor, because the mounts read req.body before dispatch (20 sites) #24151 (serial, below);readGatedSessionUser(about:210, PR feat(spec): the remaining getSession readers read the declared AuthSessionApi result (user.isPlatformAdmin, role, name); the top-level activeOrganizationId fallback is removed #24232).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
Basis for the Clause-② line: every request whose answer changes is refused before and after. An anonymous non-
POSTto/actionsmoves from the adapter's405to the domain's401, and a refusal-only verb leavesAllow. No verb is admitted that was refused, and no accept set or export widens.Responsibility:
packages/runtime's standalone/actionsmounts registerPOSTonly, so theplugin-hono-serveradapter answers an anonymous non-POST405+Allow: POSTbefore the domain's ADR-0138 D2 baseline runs; the adapter'sAllowalso lists/keys' refusal-only verbs | none:createHonoAppanswers the same request401, and the/keysand/notificationsmembers were closed by runtime(dispatcher): anonymous GET / PUT / PATCH / DELETE on /api/v1/keys answer 405 METHOD_NOT_ALLOWED instead of the 401 UNAUTHENTICATED the v18 anonymous floor states for every /keys verb — only POST is mounted #23909 and runtime/plugin-sharing: on the standalone server, anonymous callers to unmounted /notifications and /share-links verbs and paths get 405 or 404 where the v18 floor promises 401 #24024 | every anonymous caller of the standalone server (os dev,os serve) and every client that trustsAllow; measured by 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's dev on PR fix(runtime): the keys and actions domains answer their 405 with an Allow header naming POST #24227 (real socket, CORS on and off)Thread-read: 6243379401
Serial constraints cleared:
getSessionreaders still read the session throughanyor a cast, and two read a top-levelactiveOrganizationIdthatAuthSessionApinever declares (the follow-up to #24196) #24222,domain:spec) touchesdispatcher-plugin.ts, atreadGatedSessionUser(:210), which is disjoint from the mounts. No open PR touchesplugin-hono-server/srcordomains/actions.ts.pm:dispatchedclaims, only contracts: the othergetSessionreaders still read the session throughanyor a cast, and two read a top-levelactiveOrganizationIdthatAuthSessionApinever declares (the follow-up to #24196) #24222's namesdispatcher-plugin.ts(that same PR). Nopm:on-holdRestart-touch:names these files.pm:queue, this lane: 20req.bodysites in the same file, three of them the/actionsmounts this card rewrites), by the five gates: gate ① fails. This card is the mounts' verb coverage and theAllowcontent. runtime(dispatcher): on the standalone server an anonymous request whose JSON body cannot be parsed is answered 400 INVALID_REQUEST ahead of the domain's 401 floor, because the mounts read req.body before dispatch (20 sites) #24151 is the body read ahead of the floor, a different defect shape and a different fix. → serial: runtime(dispatcher): on the standalone server an anonymous request whose JSON body cannot be parsed is answered 400 INVALID_REQUEST ahead of the domain's 401 floor, because the mounts read req.body before dispatch (20 sites) #24151 waits until this card lands and is re-priced then.405+Allow: POSTpin is the control.Scope: p2, inside this seat's p1/p2/p3 scope (the maintainer, in this session's chat: 「暂时只处理 p1 p2 任务」, then 「p3 任务也可以开始处理」).
Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
os-dev-report
{ "issue": 24228, "status": "done", "branch": "claude/issue-24228-actions-anonymous-floor", "pr": "https://github.com/objectstack-ai/objectstack/pull/24244", "session": "session_01QyoWKK9RwUNxBxmevy9Neb", "premise_still_valid": true, "summary": "Both members are fixed in packages/runtime/src/dispatcher-plugin.ts alone, with no spec or adapter change. The dispatcher now declares the paths whose domain decides every verb (the three POST /actions patterns on both bases, POST /keys, the three /notifications faces, GET /mcp/skill), and its existing setFallbackHandler seam hands every request the router cannot route on such a path to dispatch(), the way createHonoApp's catch-all does. Membership is the transport router's verdict through IHttpServer.resolveMountedRoute; there is no second matcher. So an anonymous non-POST to /api/v1/actions/... gets the domain's 401 with no Allow, a signed-in one gets the domain's 405 + Allow: POST, and the adapter never builds an Allow from refusal-only mounts on those paths. The PM's suggested route (refusal-only /actions mounts) was measured and rejected: OPTIONS with CORS off, and TRACE/PROPFIND even with CORS on, still reach the adapter's 405, whose Allow would have widened from POST to all six verbs. H2: the adapter cannot tell a refusal-only mount apart through the (path, handler) registration signature, but it does not need to. Residual outside this claim's surface: plugin-sharing's /share-links/* still lists refusal-only verbs (out_of_scope_findings 1, open_questions 1).", "tests": "All at final head 6c8625968 unless noted. The implementation blob 24648a251fc8 is the same at d60989609, 194b25dc2 and 6c8625968. (1) Full suite: pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 gave Test Files 413 passed (413), Tests 6411 passed | 19 skipped (6430). (2) pnpm --filter @objectstack/runtime typecheck: exit 0, check:test-typecheck OK, ledger unchanged; all 6 touched test files are in the tsconfig.test.json program (--listFiles). (3) Targeted, at 194b25dc2 (same runtime tree for the touched files): 10 files incl. the new actions-every-verb.hono.integration.test.ts (79 cases), keys/notifications every-verb, mcp-skill, PR 24227's control, fallback-absence-warn, routes, share-links-every-path, endpoint-fallback, streaming-fallback gave Test Files 10 passed (10), Tests 234 passed (234). (4) Reach measurement, raw node:http on a real socket, 4 compositions (CORS off/on x scoping off/on), 14 paths x 9 verbs x anonymous/signed-in = 1008 requests at base 2bcd406a2 and at head d60989609: 368 rows changed, every one from the adapter's 405 to the domain's answer (148 to 401, 172 to the domain's 405, 30 to the dispatcher's 404, 18 HEAD 405 to 401), and no served answer moved. createHonoApp parity measured in process (app.request, PROPFIND for TRACE). (5) Ablations, fix committed first, node scripts/ablation-replace.mjs WRAP plus an EXIT/INT/TERM trap restoring from HEAD; the pins import ./dispatcher-plugin.js from src, so no dist of the mutated module is on the path. Member 1: the 3 /actions declarations removed (anchor 1 to 0, blob 24648a251fc8 to 4bcfc24a0ac7) gave Tests 72 failed | 27 passed (99) (expected 405 to be 401, expected undefined to be 405); the keys pin and the 24227 control stayed green. Restore: blob == HEAD, git diff HEAD empty, marker 0. Member 2: the keys/notifications/mcp-skill declarations removed (3 nested anchors, each 1 to 0) gave Tests 20 failed | 124 passed (144) (expected 'DELETE, GET, HEAD, PATCH, POST, PUT' to be 'POST'; expected 'DELETE, GET, HEAD, POST' to be 'GET'; 405 to be 401/404); the actions pin stayed green. Restore proven at all 3 levels. (6) eslint --no-inline-config over the 7 changed .ts files: exit 0. test:repo not run locally: no file of that project changed, and it is declared to CI (Test Core).", "gates": "Local, at final head 6c8625968: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 62 commands. Each was run with its exit captured before any pipe and recorded as 'COMMAND :: exit N'. --ran prints '62 derived, 61 run, 1 NOT-MEASURED, 0 UNRUN' (exit 0). All 61 that ran exit 0, including check:route-envelope, check:nul-bytes, check:test-source-alias, check:type-check-coverage and check:type-check-debt. NOT MEASURED: pnpm check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET (39 packages have no dist in this worktree); this diff changes no exports, package.json or build config, and CI's Lint & Repo Gates runs it. Not run: check-platform-checklist (no export renamed or removed); check:dev-prereqs and check:skill-frame-freshness (no packages/spec or frame change). Lane blind spot: pnpm exec eslint --no-inline-config over the 7 changed .ts files gave exit 0. CI on head 6c8625968 at PR open, read once: 23 check-runs. 'No other open PR may claim the same issue' success, 'Packed-tarball smoke (opt-in)' skipped, the other 21 in_progress (Lint * 7 lanes, Type Check * 4 lanes, Check Changeset, Governed Surface Queue Guard, Check PR Size, card/claim guards, docs). Test Core / Build Core had not appeared yet. Not polled further.", "mcp_calls": "0", "api_writes": "2 relay strokes (scripts/pm/fleet-write/dispatch.mjs, executed as objectstack-fleet[bot]), each one POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24244/assignees [marchtian], read back byte-identical (15829 bytes); (2) comment, i.e. POST /repos/objectstack-ai/objectstack/issues/24228/comments (this report). Plus git push (not REST). No labels were written: the dispatch named none, and skip-changeset does not apply because a changeset ships.", "line_budget": "n/a, no skills/** path touched", "files_changed": [ ".changeset/24228-actions-every-verb-anonymous-floor.md", "packages/runtime/src/dispatcher-plugin.ts", "packages/runtime/src/actions-every-verb.hono.integration.test.ts", "packages/runtime/src/keys-every-verb.hono.integration.test.ts", "packages/runtime/src/notifications-every-verb.hono.integration.test.ts", "packages/runtime/src/mcp-skill-method-not-allowed.hono.integration.test.ts", "packages/runtime/src/dispatcher-plugin.fallback-absence-warn.test.ts", "packages/runtime/src/domains/keys-actions-method-not-allowed-allow.test.ts" ], "members_measured": [ "/api/v1/actions (3 shapes x unscoped and /environments/:id) - served POST - base: Allow 'POST' from the adapter on every non-POST verb, anonymous too (the 401 floor missing) - head: anonymous 401 with no Allow; signed-in domain 405 with Allow 'POST'; anonymous OPTIONS (CORS off) domain 405 with Allow 'POST' (preflight exemption, same as createHonoApp)", "/api/v1/keys - served POST - base (OPTIONS CORS off; TRACE/PROPFIND any CORS): 'DELETE, GET, HEAD, PATCH, POST, PUT' - head: anonymous 401 with no Allow; signed-in domain 'POST'", "/api/v1/notifications - served GET - base: 'DELETE, GET, HEAD, PATCH, POST, PUT' - head: no Allow (anonymous 401, signed-in 404 ROUTE_NOT_FOUND)", "/api/v1/notifications/read - served POST - base: same six - head: no Allow (401 / 404)", "/api/v1/notifications/read/all - served POST - base: same six - head: no Allow (401 / 404)", "/api/v1/mcp/skill - served GET - base (PUT, PATCH, OPTIONS CORS off, TRACE, PROPFIND): 'DELETE, GET, HEAD, POST' - head: domain 405 with 'GET'", "/api/v1/share-links (root) - served GET, POST - base and head: 'GET, HEAD, POST' - measured-clean, unchanged", "/api/v1/share-links/abc - served DELETE - base and head: 'DELETE, GET, HEAD, PATCH, POST, PUT' - NOT fixed (plugin-sharing family, outside the claim's file surface)", "/api/v1/share-links/abc/resolve - served GET - base and head: same six - NOT fixed (same reason)" ], "deviations": [ "Route: the PM's suggested refusal-only GET/PUT/PATCH/DELETE mounts on /actions were not used. Measured: they would widen Allow on OPTIONS (CORS off) and TRACE/extension verbs (any CORS) from 'POST' to six verbs. The dispatcher's existing fallback seam is used instead, so no new route, no route-ledger row and no census move.", "PR 24227's control pin (domains/keys-actions-method-not-allowed-allow.test.ts): assertions untouched and green, but its docblock paragraph and one test title said the standalone /actions 405 is the adapter's, which this change makes false, so the wording was corrected. Flagged because H4 said 'unchanged'.", "mcp-skill-method-not-allowed.hono.integration.test.ts: its 'PUT falls through to the adapter (boundary)' case pinned the defect (Allow ['DELETE','GET','HEAD','POST']) and was replaced by PUT/PATCH/OPTIONS/TRACE domain pins.", "Runtime strings: one new boot warn (transport with setFallbackHandler but no resolveMountedRoute) and one clause appended to the existing no-setFallbackHandler warn. The regex pins on the existing warn still hold.", "The worktree was cut from origin/main 2bcd406a2 (main had moved past the dispatch base 5a9a80bd2; dispatcher-plugin.ts and the adapter are identical across that range), and origin/main 7be3cc728 was merged before the PR (merge 6c8625968, model-free trailers); install and build were refreshed after it.", "The first full-suite run (at 194b25dc2) spelled 'pnpm --filter @objectstack/runtime test -- --maxWorkers=2'. The bare '--' may have dropped the flag; the whole package ran (413 files). The final-head run used 'exec vitest run --project local --maxWorkers=2'.", "The first gate pass was cut by my own 590 s timeout after 51 commands (check:query-options-erasure interrupted, not recorded). The remaining 11 ran under nohup + tail --pid; each command is recorded exactly once.", "Labels: none written (none named; skip-changeset not applicable)." ], "open_questions": [ { "question": "/share-links/* (plugin-sharing's family on the standalone server) still answers OPTIONS (CORS off) and TRACE/extension verbs (any CORS) with the adapter's 405, Allow 'DELETE, GET, HEAD, PATCH, POST, PUT' on /share-links/abc (served: DELETE) and /share-links/abc/resolve (served: GET). This PR's mechanism cannot reach it: the fallback seam is one handler and the dispatcher's, and a dispatcher answer there would make a second owner. The adapter cannot tell refusal-only mounts apart through IHttpServer's (path, handler) registration (measured). The PR body says 'Fixes #24228' as dispatched. Where does this residual go?", "options": [ "A: keep 'Fixes #24228' (the triage-bound scope is /actions plus the dispatcher's /keys, /notifications and /mcp/skill), and file /share-links as its own member card in the plugin-sharing lane. Cost: one more card. Its route needs a seat ruling: a declared cross-plugin 'domain decides every verb' registry that the dispatcher's fallback consults (runtime + plugin-sharing; if it becomes a service contract, it is a spec contract), or the IHttpServer route (a registration marker or reconciling the Allow contract line with RFC 9110; domain:spec).", "B: rewrite the PR body to 'Part of #24228' (seat write) and keep this card open for /share-links. Cost: the card stays open on a member whose owner and route differ from this PR's.", "C: accept the residual as reachable only by exotic verbs or CORS off. Cost: Done-when line 2 is left unmet for one family." ], "recommendation": "A, because the residual has a different owner (plugin-sharing) and needs a different mechanism, which has to come from a ruling or from domain:spec. Folding it into this card would keep a closed-out fix open on a design question." } ], "out_of_scope_findings": [ "class: a, reach: public door HTTP on the standalone server. OPTIONS /api/v1/share-links/abc with CORS off, and TRACE/PROPFIND with CORS on, answer 405 with Allow 'DELETE, GET, HEAD, PATCH, POST, PUT' while the route serves DELETE; /share-links/abc/resolve is the same while it serves GET. Measured at base 2bcd406a2 and head on a real socket (HonoServerPlugin + createDispatcherPlugin + registerShareLinkRoutes at kernel:ready). Seam: renderer: none, runtime: packages/plugins/plugin-sharing/src/share-link-routes.ts mountFamilyRoute catch-all, read by plugin-hono-server allowedMethodsForPath. Same family as this card: named here for the family's closing card per open_questions[0], not filed as a single-point card. Dedupe words: share-links Allow refusal-only; OPTIONS share-links 405 six verbs; share-link catch-all Allow lists every verb", "class: a, reach: public door HTTP on a createHonoApp host. HEAD /api/v1/mcp/skill answers 405 with Allow 'GET' while GET /api/v1/mcp/skill is served: the catch-all hands dispatch() the raw method 'HEAD', and handleMcpSkillRequest refuses every non-GET. Measured in process through createHonoApp's app.request (the host's HTTP pipeline), not a socket. The standalone server answers HEAD from the GET route. Seam: runtime: packages/runtime/src/domains/mcp.ts handleMcpSkillRequest method check, consumer: none. Dedupe words: HEAD mcp skill 405; createHonoApp HEAD domain non-GET; mcp skill HEAD Allow GET", "carrier: none - noted in the PR's Acceptance notes, not filed. On the /share-links root, the adapter's matchesRoutePattern does not count the '/share-links/*' pattern while the Hono router routes PUT /share-links to it, so allowedMethodsForPath and the router disagree. The Allow happens to be accurate there (GET, HEAD, POST); this is a read-only observation.", "carrier: none - noted, not filed. The signed-in non-face /notifications 404 is spelled ROUTE_NOT_FOUND on the standalone server and RESOURCE_NOT_FOUND on createHonoApp; this predates the PR and is already recorded in #24024's notes." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
ACCEPT → PR #24244 comment
6244569658·domain:cliseat 2 · sessionsession_01QyoWKK9RwUNxBxmevy9Neb· 2026-10-18T02:08Z · open question 1 decided A (/share-linksfiled as #24246;HEAD /mcp/skillfiled as #24245); landing waits on every check of head6c8625968.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Landed: PR #24244 →
cbfa5e433(#24228,Fixes #24228) ·domain:cliseat 2 ·marchtian· sessionsession_01QyoWKK9RwUNxBxmevy9Neb· 2026-10-18T02:48Zmerged: true, merge commitcbfa5e433, merged 2026-10-18T02:47Z through the merge queue (enqueued 2026-10-18T02:28Z)origin/maincbfa5e433is an ancestor oforigin/main, with a single parent (squash). The 8 files and +559 / −29 match what was reviewed at6c8625968gh-readonly-queue/main/*pr-24244*head remains6243737468→ report6244534422→ ACCEPT6244569658on PR fix(runtime): on the standalone server every /actions verb reaches the actions domain, so an anonymous caller gets 401, and no Allow lists a verb mounted only to refuse #24244 → pointer6244572801. No cross-lane declaration was owed: every file is inpackages/runtime./actions×3 on both bases,/keys,/notifications×3,/mcp/skill). Its existing fallback seam asks the transport's router (resolveMountedRoute) and hands each unrouted request on such a path todispatch(), with no body. So on the standalone server an anonymous non-POSTto/actionsgets401, a signed-in one gets the domain's405+Allow: POST, and noAllowlists a verb mounted only to refuse. No route, spec member or adapter change.completedbyFixes.pm:dispatchedis stripped in this act; the other labels stay./share-links/:tokenAllowresidual, plugin-sharing's family, per the seat's decision A) and runtime(mcp): on a createHonoApp host, HEAD /api/v1/mcp/skill answers 405 with Allow: GET while GET is served — the catch-all hands dispatch() the raw HEAD and handleMcpSkillRequest refuses every non-GET #24245 (HEAD /mcp/skillon acreateHonoApphost).req.bodysites) was waiting on this card and is re-priced on its own thread in the next act.Generated by Claude Code