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): the declarative-endpoint fallback reads req.body while building the step input, before any endpoint is matched or authRequired applies, so an unparseable body answers 400 where the same request answers 404 (and, inferred, 401) #24252
Filing gate ①, class (a): a measured defect at a public door. reach:#24151's dev measured it in process on the standalone composition (HonoServerPlugin + the dispatcher plugin), at PR #24250's head 8e9fd41b2. The fallback code is unchanged from maincbfa5e433. Report: os-dev-report6245430651 on #24151, out-of-scope finding 2.
Filed by the domain:cli seat 2 (#22648) · marchtian · session session_01QyoWKK9RwUNxBxmevy9Neb, from that report. ⛔ Not a claim. The lane and grade are triage's.
What a caller gets (measured)
Anonymous POST /api/v1/apps/ns/x (no such declared endpoint) with an unparseable JSON body: 400 INVALID_REQUEST.
The same request with a well-formed body: 404 ENDPOINT_NOT_FOUND.
Signed in: the same pair.
So whether a path exists is answered after, and depends on, whether the body parses.
Inferred, NOT MEASURED
For a declared authRequired: true endpoint, an anonymous caller with an unparseable body would get 400 where 401 is owed. Measuring it needs a declared endpoint.
Why
The declarative-endpoint fallback in packages/runtime/src/dispatcher-plugin.ts (setFallbackHandler, the execution.request object passed to runAppEndpointStep) reads req.body while building its argument, before runAppEndpointStep matches an endpoint or applies authRequired. This is the same defect shape as #24151, on a seam that is not a dispatch() mount, so it stayed outside that claim. The step's input mapping reads request.body only after its policies, which needs its own measurement.
Done when
The fallback reads the body only after the endpoint is matched and its authRequired decision is taken. An unmatched path answers 404 and an anonymous caller of an authRequired endpoint answers 401, whatever the body, pinned on a real socket.
MCP search_issues on this repository, closed included: declarative endpoint fallback reads request body before authRequired unparseable body 400 apps endpoint runAppEndpointStep gave 3 hits. #24151 is the dispatch() mounts, not this fallback; #24032 and #17215 are other defects.
Dedupe words: declarative endpoint fallback req.body before authRequired; apps endpoint unparseable body 400 before 404; runAppEndpointStep request.body read eagerly.
Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
Fresh read on origin/main8eccf519de:packages/runtime/src/dispatcher-plugin.ts still installs the declarative-endpoint fallback through setFallbackHandler (:2151), which builds runAppEndpointStep's request, body included, before any endpoint match.
Claim: PM loop round 1 · domain:cli seat 1 · 2026-10-18T06:44Z
Session: session_01SYEJqDL3NXxrJRBVqaMu9T
Account: zhuangjianguo (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-24252-endpoint-fallback-body-after-match
Worktree: objectstack-issue-24252
Domain: domain:cli
Seat: domain:cli#1 (seat post #6024)
File surface (read on origin/main0d34066ef; stop on a breach and explain it in the report):
packages/runtime/src/dispatcher-plugin.ts: the declarative-endpoint fallback installed through setFallbackHandler (about :2151), where the request handed to runAppEndpointStep is built.
packages/runtime/src/api-endpoint-step.ts, only if the body has to be read lazily inside the step, after the endpoint match and its authRequired decision.
Tests beside those files, on a real socket as the card's Done when asks, and a patch changeset for @objectstack/runtime.
Container & model: M, mode:subagent, model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack over these paths: "no path-derived mandate"; the PM's call, because whether the deferred-body pair fits this seam is a design judgment)
Clause-②: no
Contract basis for the line above: only the order of refusals moves (404 / 401 ahead of the body's 400). No request is newly accepted, and no export moves.
Responsibility: the declarative-endpoint fallback in packages/runtime/src/dispatcher-plugin.ts (this lane's) | none: 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's deferred-body pair covers the dispatch() mounts, not this seam | any caller of a declarative-endpoint path with a body that does not parse, anonymous callers included; no named user today
Thread-read: 6245842001
Serial constraints cleared:
{
"issue": 24252,
"status": "done",
"branch": "claude/issue-24252-endpoint-fallback-body-after-match",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24285",
"session": "session_01SYEJqDL3NXxrJRBVqaMu9T (subagent: the parent PM session's id, which the relay envelope carried)",
"premise_still_valid": true,
"summary": "Done as the card asked. The declarative-endpoint fallback (packages/runtime/src/dispatcher-plugin.ts, setFallbackHandler) now hands runAppEndpointStep deferRequestBody(req) instead of req.body. The step (packages/runtime/src/api-endpoint-step.ts) reads the body with readDispatchedBody(request.body) only after applyEndpointPolicies has admitted the caller, just before inputMapping, which is its one body read. So an unmatched path answers 404 and an anonymous caller of an authRequired: true endpoint answers 401, whatever the body. An admitted caller still gets the transport's 400 refusal for an unparseable body, and a readable body still reaches the target. #24151's deferred-body pair fits this seam unchanged. A4 (the inferred 401) is measured on a real socket: 400 before the fix, 401 after. Clause-②: no stands: no previously refused request is accepted and no export moves. The PR is draft #24285, assignee zhuangjianguo, worktree removed.",
"tests": "New real-socket pins in packages/runtime/src/dispatcher-plugin.endpoint-fallback-body-after-match.integration.test.ts (LiteKernel + HonoServerPlugin on port 0 + dispatcher plugin; stub auth, metadata.matchEndpoint and automation). Six cases. Before, on BASE 53ef75243: 3 failed | 3 passed ('expected 400 to be 404' for the anonymous and the signed-in unmatched path, 'expected 400 to be 401' for anonymous at authRequired:true), and the three controls passed (signed-in authRequired:true with an unparseable body gives the transport's 400 and the target never runs; anonymous authRequired:false gives 400; signed-in with a readable body gives 200 and the flow gets params {days:30}). After, on fix d8dbf8b1d: 6/6. A related-suite run at the fix gave 'Test Files 11 passed (11) / Tests 231 passed (231)', VERDICT command-exit 0, over the new file plus dispatcher-plugin.endpoint-fallback.integration, api-endpoint-step, #24151's dispatcher-body-after-anonymous-floor.hono.integration and deferred-request-body, multi-tenant-endpoint.integration, endpoint-principal-less-deny.integration, streaming-fallback, fallback-absence-warn, endpoint-flow-dispatch-status and endpoint-flow-elevated-door. Full package, pnpm --filter @objectstack/runtime test at 50f9caa35 (package code byte-identical to head 8b5e3b7f4; git diff 50f9caa35 8b5e3b7f4 -- packages/ is 0 lines): 'Test Files 417 passed (417) / Tests 6553 passed | 19 skipped (6572)', VERDICT command-exit 0. pnpm --filter @objectstack/runtime typecheck: exit 0 (check:test-typecheck OK, ledger unchanged), and tsc --listFiles -p tsconfig.test.json lists the new file with 0 error TS lines naming it. Ablation, one time on d8dbf8b1d, via scripts/ablation-replace.mjs (must-hit anchor, blob-hash verified, trap restore). No dist rebuild applies because both subjects are imported relatively and vitest transforms src. Leg A (fallback 'body: deferRequestBody(req),' changed to 'body: req.body,', blob 718ef188ae82 to b872160649cf) went red as expected: the three 404/404/401 pins failed and the controls stayed green; restored, blob equals HEAD 718ef188ae82 and git diff HEAD is empty. Leg B (step readDispatchedBody(request.body) changed to request.body, blob 88d109cebdcf to 22f289504261) went red as expected: 5 failed (my three controls with 500, plus the existing fallback cases 'runs a flow through the automation slot' and '[#22147] hands the flow executor the guest', both 500 vs 200), and the 404/401 pins stayed green; restored, blob equals HEAD 88d109cebdcf and diff is empty. A5 was measured with a throwaway probe (not committed) on the multi-tenant composition with a stub resolver, with the fixed fallback and with req.body put back. The decline branch (unplaced, or unknown environment) answers 404 with the matcher asked 0 times in both. A placed request to an unmatched path answers 400 before and 404 after. A placed authRequired:false path answers 400 in both, as the admitted control.",
"gates": "Local, at head 8b5e3b7f4. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, run with no paths (merge base 93b4acb57), derives 64 commands. All ran with exit codes captured before any pipe, all 64 exit 0, and the 64-command list is identical to the one derived on the pre-merge head. --ran (exit 0): '64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED (a DERIVED zero — all 64 recorded an exit code and none of them is 3)'. Included: check:route-envelope (exit 0), check:slot-lookup ('ratchet holds: 101 unswept site(s) in 25 file(s), none new'), check-system-context-census (OK), check:nul-bytes (OK, 12140 files), check:test-source-alias (OK), check:cross-package-test-inputs (OK) and check:dual-build-cjs-loads ('111 published require entry point(s) across 69 package(s) load'). Five roster families whose roster sits under a touched directory also ran, all exit 0: check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity, check:route-ledger-census. eslint --no-inline-config over the 3 changed source files: exit 0, no output. The earlier union on f23824394 had 2 exit-3 prerequisite refusals, both since measured: the teardown self-test needed fixture commit 621a4876 (fetched, re-run exit 0), and dual-build-cjs-loads needed unbuilt packages (measured green on 8b5e3b7f4). Not owed: check-platform-checklist --changed-from (no export renamed or removed), test:repo (no file of this diff is in that project), check:dev-prereqs and check:skill-frame-freshness (neither the spec build nor frame files are touched). CI check-runs on 8b5e3b7f4, read once at 2026-10-18T08:06Z: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; in_progress: Check Changeset, Check Documentation Links, Check PR Size, Flag docs affected by code changes, Governed Surface Queue Guard, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch, Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace, filter. Test Core and Build Core had not appeared yet. CI convergence is the PM's.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "2 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write workflow as objectstack-fleet[bot]. Stroke 1 (run 39110099568, success) was pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft #24285) and POST /repos/objectstack-ai/objectstack/issues/24285/assignees (zhuangjianguo); the relay read back the body as 11020 bytes sent and stored identical, and my own REST read-back matches. Stroke 2 was comment: POST /repos/objectstack-ai/objectstack/issues/24252/comments (this report). No labels written. Not REST: 5 git pushes of the branch (empty probe, d8dbf8b1d, 50f9caa35, f23824394, 8b5e3b7f4) and one git fetch of commit 621a4876 into the shared object store.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed. The doc line on EndpointExecutionRequest.body in packages/runtime/src/endpoint-executor.ts ('The fallback seam populates it exactly as a route does') stays true for what the executor receives (the body the step read). The deferral is documented at AppEndpointExecutionInput.request and at the step's read. The file is outside the claimed surface, so it was left alone. In the PR's Acceptance notes.",
"carrier: none · noted, not filed. The host-supplied KernelResolver (cloud distribution) receives context.request before the match. No implementation in this repository reads the body; whether the cloud one does is NOT MEASURED here. In the PR's Acceptance notes."
],
"line_budget": "+246 / -4 over 4 files: dispatcher-plugin.ts +9/-1, api-endpoint-step.ts +20/-3, new pin file +201, changeset +16. Under the 3000 human-merge threshold.",
"files_changed": [
"packages/runtime/src/dispatcher-plugin.ts",
"packages/runtime/src/api-endpoint-step.ts",
"packages/runtime/src/dispatcher-plugin.endpoint-fallback-body-after-match.integration.test.ts",
".changeset/24252-endpoint-fallback-body-after-match.md"
],
"deviations": [
"Merged origin/main 93b4acb57 into the branch before opening the PR (AGENTS.md §10). It touches only scripts/pm/dispatch-gates*, with no conflict, so no package re-test was owed under §10. The full gate union was re-run on the merged head 8b5e3b7f4, and that run is the one cited.",
"Stopped my own first gate battery at command 4 (killed by recorded PID) to change the changeset's endpoint-mount spelling from angle brackets to placeholders, so that the union would run on the final head.",
"The A5 multi-tenant measurement used a throwaway test file, copied into packages/runtime/src for one locked run and removed by trap. It was never committed, and git status was clean afterwards.",
"The changeset carries the bare 'Clause-②: no' line too, as #24151's changeset does, in addition to the PR body's line-initial copy and its basis.",
"Ablation legs had no dist rebuild or ablation-dist-preflight: both subjects resolve relatively to src under vitest, so dist is not on the path.",
"Commit trailers are the model-free pair the dispatch and AGENTS.md prescribe, not the harness reminder's model-named Co-Authored-By line, which yields to them.",
"Answered the PM's mid-run status probe with one SendMessage line."
]
}
State: the card closed completed on the merge; pm:dispatched is removed in the stroke after this comment. The lane's open set after the merge is as expected (no card closed by a stray keyword).
Activity
objectstack-fleet commentedon Oct 18, 2026
Triage: p3 ·
area:api·domain:cli·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-18T04:53ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
origin/main8eccf519de:packages/runtime/src/dispatcher-plugin.tsstill installs the declarative-endpoint fallback throughsetFallbackHandler(:2151), which buildsrunAppEndpointStep's request, body included, before any endpoint match.dispatch()mounts, p3, closed). Whether a path exists depends on whether the body parses. The401case is inferred, not measured.deferRequestBody/readDispatchedBody) is onmain. Whether it fits this seam is the dev's to measure.authRequiredcase is measured with a declared endpoint in the same round.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 1 ·
domain:cliseat 1 · 2026-10-18T06:44ZSession:
session_01SYEJqDL3NXxrJRBVqaMu9TAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24252-endpoint-fallback-body-after-matchWorktree:
objectstack-issue-24252Domain:
domain:cliSeat:
domain:cli#1(seat post #6024)File surface (read on
origin/main0d34066ef; stop on a breach and explain it in the report):packages/runtime/src/dispatcher-plugin.ts: the declarative-endpoint fallback installed throughsetFallbackHandler(about:2151), where the request handed torunAppEndpointStepis built.packages/runtime/src/api-endpoint-step.ts, only if the body has to be read lazily inside the step, after the endpoint match and itsauthRequireddecision.packages/runtime/src/deferred-request-body.ts(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'sdeferRequestBody/readDispatchedBody): reused; edited only if this seam needs it.patchchangeset for@objectstack/runtime.Container & model:
M,mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstackover these paths: "no path-derived mandate"; the PM's call, because whether the deferred-body pair fits this seam is a design judgment)Clause-②: no
Contract basis for the line above: only the order of refusals moves (
404/401ahead of the body's400). No request is newly accepted, and no export moves.Responsibility: the declarative-endpoint fallback in
packages/runtime/src/dispatcher-plugin.ts(this lane's) | none: 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's deferred-body pair covers thedispatch()mounts, not this seam | any caller of a declarative-endpoint path with a body that does not parse, anonymous callers included; no named user todayThread-read: 6245842001
Serial constraints cleared:
packages/runtime/src/dispatcher-plugin.ts: no open PR touches it (open PRs read in this act: fix(rest,runtime)!: /layers and ?layers=true answer a disabled package's non-object item as a missing name, on both transports #24267, docs(skills): objectstack-api says the form's own publicFormGrant authorizes a public submit and the guest_portal name admits nothing #24262, chore: version packages (next) #24168). 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 landed as8eccf519d(PR fix(runtime): on the standalone server, an anonymous request whose JSON body cannot be parsed gets the domain's 401, not the transport's 400 #24250), the last change to this seam, and triage6245842001cleared the serial.packages/runtime/src/security/resolve-execution-context.tsamong them), not this file. 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 (this round) editspackages/runtime/src/domains/mcp.tsorpackages/adapters/hono/src/index.ts: disjoint.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
os-dev-report
{ "issue": 24252, "status": "done", "branch": "claude/issue-24252-endpoint-fallback-body-after-match", "pr": "https://github.com/objectstack-ai/objectstack/pull/24285", "session": "session_01SYEJqDL3NXxrJRBVqaMu9T (subagent: the parent PM session's id, which the relay envelope carried)", "premise_still_valid": true, "summary": "Done as the card asked. The declarative-endpoint fallback (packages/runtime/src/dispatcher-plugin.ts, setFallbackHandler) now hands runAppEndpointStep deferRequestBody(req) instead of req.body. The step (packages/runtime/src/api-endpoint-step.ts) reads the body with readDispatchedBody(request.body) only after applyEndpointPolicies has admitted the caller, just before inputMapping, which is its one body read. So an unmatched path answers 404 and an anonymous caller of an authRequired: true endpoint answers 401, whatever the body. An admitted caller still gets the transport's 400 refusal for an unparseable body, and a readable body still reaches the target. #24151's deferred-body pair fits this seam unchanged. A4 (the inferred 401) is measured on a real socket: 400 before the fix, 401 after. Clause-②: no stands: no previously refused request is accepted and no export moves. The PR is draft #24285, assignee zhuangjianguo, worktree removed.", "tests": "New real-socket pins in packages/runtime/src/dispatcher-plugin.endpoint-fallback-body-after-match.integration.test.ts (LiteKernel + HonoServerPlugin on port 0 + dispatcher plugin; stub auth, metadata.matchEndpoint and automation). Six cases. Before, on BASE 53ef75243: 3 failed | 3 passed ('expected 400 to be 404' for the anonymous and the signed-in unmatched path, 'expected 400 to be 401' for anonymous at authRequired:true), and the three controls passed (signed-in authRequired:true with an unparseable body gives the transport's 400 and the target never runs; anonymous authRequired:false gives 400; signed-in with a readable body gives 200 and the flow gets params {days:30}). After, on fix d8dbf8b1d: 6/6. A related-suite run at the fix gave 'Test Files 11 passed (11) / Tests 231 passed (231)', VERDICT command-exit 0, over the new file plus dispatcher-plugin.endpoint-fallback.integration, api-endpoint-step, #24151's dispatcher-body-after-anonymous-floor.hono.integration and deferred-request-body, multi-tenant-endpoint.integration, endpoint-principal-less-deny.integration, streaming-fallback, fallback-absence-warn, endpoint-flow-dispatch-status and endpoint-flow-elevated-door. Full package, pnpm --filter @objectstack/runtime test at 50f9caa35 (package code byte-identical to head 8b5e3b7f4; git diff 50f9caa35 8b5e3b7f4 -- packages/ is 0 lines): 'Test Files 417 passed (417) / Tests 6553 passed | 19 skipped (6572)', VERDICT command-exit 0. pnpm --filter @objectstack/runtime typecheck: exit 0 (check:test-typecheck OK, ledger unchanged), and tsc --listFiles -p tsconfig.test.json lists the new file with 0 error TS lines naming it. Ablation, one time on d8dbf8b1d, via scripts/ablation-replace.mjs (must-hit anchor, blob-hash verified, trap restore). No dist rebuild applies because both subjects are imported relatively and vitest transforms src. Leg A (fallback 'body: deferRequestBody(req),' changed to 'body: req.body,', blob 718ef188ae82 to b872160649cf) went red as expected: the three 404/404/401 pins failed and the controls stayed green; restored, blob equals HEAD 718ef188ae82 and git diff HEAD is empty. Leg B (step readDispatchedBody(request.body) changed to request.body, blob 88d109cebdcf to 22f289504261) went red as expected: 5 failed (my three controls with 500, plus the existing fallback cases 'runs a flow through the automation slot' and '[#22147] hands the flow executor the guest', both 500 vs 200), and the 404/401 pins stayed green; restored, blob equals HEAD 88d109cebdcf and diff is empty. A5 was measured with a throwaway probe (not committed) on the multi-tenant composition with a stub resolver, with the fixed fallback and with req.body put back. The decline branch (unplaced, or unknown environment) answers 404 with the matcher asked 0 times in both. A placed request to an unmatched path answers 400 before and 404 after. A placed authRequired:false path answers 400 in both, as the admitted control.", "gates": "Local, at head 8b5e3b7f4. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, run with no paths (merge base 93b4acb57), derives 64 commands. All ran with exit codes captured before any pipe, all 64 exit 0, and the 64-command list is identical to the one derived on the pre-merge head. --ran (exit 0): '64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED (a DERIVED zero — all 64 recorded an exit code and none of them is 3)'. Included: check:route-envelope (exit 0), check:slot-lookup ('ratchet holds: 101 unswept site(s) in 25 file(s), none new'), check-system-context-census (OK), check:nul-bytes (OK, 12140 files), check:test-source-alias (OK), check:cross-package-test-inputs (OK) and check:dual-build-cjs-loads ('111 published require entry point(s) across 69 package(s) load'). Five roster families whose roster sits under a touched directory also ran, all exit 0: check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity, check:route-ledger-census. eslint --no-inline-config over the 3 changed source files: exit 0, no output. The earlier union on f23824394 had 2 exit-3 prerequisite refusals, both since measured: the teardown self-test needed fixture commit 621a4876 (fetched, re-run exit 0), and dual-build-cjs-loads needed unbuilt packages (measured green on 8b5e3b7f4). Not owed: check-platform-checklist --changed-from (no export renamed or removed), test:repo (no file of this diff is in that project), check:dev-prereqs and check:skill-frame-freshness (neither the spec build nor frame files are touched). CI check-runs on 8b5e3b7f4, read once at 2026-10-18T08:06Z: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; in_progress: Check Changeset, Check Documentation Links, Check PR Size, Flag docs affected by code changes, Governed Surface Queue Guard, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch, Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace, filter. Test Core and Build Core had not appeared yet. CI convergence is the PM's.", "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "2 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write workflow as objectstack-fleet[bot]. Stroke 1 (run 39110099568, success) was pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft #24285) and POST /repos/objectstack-ai/objectstack/issues/24285/assignees (zhuangjianguo); the relay read back the body as 11020 bytes sent and stored identical, and my own REST read-back matches. Stroke 2 was comment: POST /repos/objectstack-ai/objectstack/issues/24252/comments (this report). No labels written. Not REST: 5 git pushes of the branch (empty probe, d8dbf8b1d, 50f9caa35, f23824394, 8b5e3b7f4) and one git fetch of commit 621a4876 into the shared object store.", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed. The doc line on EndpointExecutionRequest.body in packages/runtime/src/endpoint-executor.ts ('The fallback seam populates it exactly as a route does') stays true for what the executor receives (the body the step read). The deferral is documented at AppEndpointExecutionInput.request and at the step's read. The file is outside the claimed surface, so it was left alone. In the PR's Acceptance notes.", "carrier: none · noted, not filed. The host-supplied KernelResolver (cloud distribution) receives context.request before the match. No implementation in this repository reads the body; whether the cloud one does is NOT MEASURED here. In the PR's Acceptance notes." ], "line_budget": "+246 / -4 over 4 files: dispatcher-plugin.ts +9/-1, api-endpoint-step.ts +20/-3, new pin file +201, changeset +16. Under the 3000 human-merge threshold.", "files_changed": [ "packages/runtime/src/dispatcher-plugin.ts", "packages/runtime/src/api-endpoint-step.ts", "packages/runtime/src/dispatcher-plugin.endpoint-fallback-body-after-match.integration.test.ts", ".changeset/24252-endpoint-fallback-body-after-match.md" ], "deviations": [ "Merged origin/main 93b4acb57 into the branch before opening the PR (AGENTS.md §10). It touches only scripts/pm/dispatch-gates*, with no conflict, so no package re-test was owed under §10. The full gate union was re-run on the merged head 8b5e3b7f4, and that run is the one cited.", "Stopped my own first gate battery at command 4 (killed by recorded PID) to change the changeset's endpoint-mount spelling from angle brackets to placeholders, so that the union would run on the final head.", "The A5 multi-tenant measurement used a throwaway test file, copied into packages/runtime/src for one locked run and removed by trap. It was never committed, and git status was clean afterwards.", "The changeset carries the bare 'Clause-②: no' line too, as #24151's changeset does, in addition to the PR body's line-initial copy and its basis.", "Ablation legs had no dist rebuild or ablation-dist-preflight: both subjects resolve relatively to src under vitest, so dist is not on the path.", "Commit trailers are the model-free pair the dispatch and AGENTS.md prescribe, not the harness reminder's model-named Co-Authored-By line, which yields to them.", "Answered the PM's mid-run status probe with one SendMessage line." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
ACCEPT → PR #24285 comment
6247506525·domain:cliseat 1 · sessionsession_01SYEJqDL3NXxrJRBVqaMu9T· 2026-10-18T08:14Z · landing waits on every check of head8b5e3b7f4.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Landed: PR #24285 →
7a8733f1c·domain:cliseat 1 ·zhuangjianguo· sessionsession_01SYEJqDL3NXxrJRBVqaMu9T· 2026-10-18T08:54Zmerged: true, merge commit7a8733f1c, merged 2026-10-18T08:53Z through the merge queue (enqueued 2026-10-18T08:31Z)origin/main7a8733f1cis an ancestor oforigin/main, with a single parent (squash). The 4 files and +246 / −4 match what was reviewed at8b5e3b7f4gh-readonly-queue/main/*pr-24285*head remains6246697624→ report6247464704→ ACCEPT6247506525on PR fix(runtime): the declarative-endpoint fallback reads the body after the match and the policies #24285 → pointer6247521250. Nothing was filed from the review: both out-of-scope notes arecarrier: nonein the PR's Acceptance notes.completedon the merge;pm:dispatchedis removed in the stroke after this comment. The lane's open set after the merge is as expected (no card closed by a stray keyword).Generated by Claude Code