Skip to content

Commit 33c5fd3

Browse files
huangyiireneclaude
andauthored
fix(spec): repoint the liveness anchors for action type / body / method at their real consumers (#12517)
* fix(spec): repoint the liveness anchors for action `type`/`body`/`method` The three entries cited packages/runtime/src/http-dispatcher.ts, which reads none of them: the action consumer was extracted into domains/actions.ts + action-execution.ts (+ sandbox/body-runner.ts), and the dispatcher now only delegates. Unlike the `target`/`requiredPermissions` rot repaired earlier, these three are common English/HTTP words, so the word-bounded key-mention check anchors on the coincidence and passes — call-graph verification by hand was the only way to see it. Part of #12215 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5LFCYBJ3q2s6yW6oMLxwy * changeset for the action liveness anchor repoint Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5LFCYBJ3q2s6yW6oMLxwy --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 14b1145 commit 33c5fd3

2 files changed

Lines changed: 53 additions & 6 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
liveness ledger: repoint the `action` `type` / `body` / `method` anchors at their real consumers
6+
7+
All three cited `packages/runtime/src/http-dispatcher.ts`, and none of them is read
8+
there. The actions domain was extracted out of that file — it now only delegates
9+
(`handleActions``handleActionsRequest` at :1969-1970) — and the reads live in
10+
`action-execution.ts`, `domains/actions.ts`, `sandbox/body-runner.ts` and, for the
11+
client-dispatched `method`, in the renderer repo.
12+
13+
This is the residue of the same extraction that rotted `action.target` and
14+
`action.requiredPermissions`, but it survived the sweep that repaired those, and the
15+
reason is the interesting part. The key-mention check added with that sweep asks
16+
whether the cited file names the key at all — which caught `target` and
17+
`requiredPermissions` because `http-dispatcher.ts` contains **0** occurrences of
18+
either. It cannot catch these three, because they are common English and HTTP words
19+
that the file is full of for unrelated reasons:
20+
21+
- `type` — 9 occurrences, four of them the TypeScript `import type` keyword, the rest
22+
other domains' data (`error.type`, `details.type`, a field-type→JSON-Schema mapper).
23+
- `body` — 68 occurrences: the inbound HTTP request body threaded through every
24+
domain delegate, plus the file's own "body extracted to ./domains/…" comments,
25+
where `body` means a *function* body.
26+
- `method` — 41 occurrences: the inbound HTTP verb (15 `method: string` parameter
27+
declarations, route matching such as `method === 'GET'`) and the ordinary
28+
object-oriented sense in prose.
29+
30+
So the word-bounded check anchors on the coincidence and passes. That is the designed,
31+
honest limit of the signal rather than a defect in it — the census that shipped it said
32+
so — and it means this class is invisible to tooling and only a hand call-graph read
33+
can settle it. Each repointed entry now records which spelling misled the gate, so the
34+
next reader does not have to re-derive it.
35+
36+
`method` additionally CHANGES REALM, joining its siblings `bodyShape` and `bodyExtra`:
37+
`type: 'api'` actions are client-dispatched by design, so the server never read the
38+
verb. Its one in-repo appearance is a diagnostic that interpolates the verb into the
39+
refusal explaining the server does *not* dispatch it — evidence of non-consumption,
40+
and deliberately not cited as a consumer.
41+
42+
No verdict was re-graded: all three were `live` and remain `live`, with the consumer
43+
proven rather than asserted. Citation repair only.

packages/spec/liveness/action.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@
3737
},
3838
"type": {
3939
"status": "live",
40-
"verifiedAt": "2026-07-28",
41-
"evidence": "packages/runtime/src/http-dispatcher.ts; objectui @732b1bf core ActionRunner.ts:513-541 switches every variant to its own executor (executeScript/Url/Modal/Flow/API/Form/Navigation)",
42-
"note": "api/script/flow wired; url thinner; modal PARTIAL (maps to serverActionHandler, not a real modal); form LIVE via objectui ActionRunner.executeForm (routes a type:'form' action to the FormView at /forms/:target, forwarding the current record id) — the 2026-06 audit mis-classified as dead (objectui renderer not re-verified; fixed the 'Log Time does nothing' report). Build-time lint-view-refs.ts validates the form target resolves to a form view. RE-VERIFIED 2026-07 (#3714 follow-up): `api` -> executeAPI (:974, string-or-ApiConfig endpoint, method/headers/queryParams/responseType) and `form` -> executeForm (:920) both resolve; content/docs/ui/actions.mdx had been telling authors the opposite (\"no runtime executor / renderer today\") and was corrected in the same pass."
40+
"verifiedAt": "2026-08-26",
41+
"evidence": "packages/runtime/src/sandbox/body-runner.ts:385 (the #4352 gate — `const type = action.type ?? 'script'` decides whether a `body` binds an executable handler at all); packages/runtime/src/action-execution.ts:1309 (`action.type === 'flow'` routes to dispatchFlowAction), :524 (isHeadlessInvokableAction), :552 (headlessActionTypeError names the type it refuses), :906 (MCP action summary projects it); packages/runtime/src/domains/actions.ts:591 (the REST route's own `actionType` resolution); objectui @732b1bf core ActionRunner.ts:513-541 switches every variant to its own executor (executeScript/Url/Modal/Flow/API/Form/Navigation)",
42+
"note": "api/script/flow wired; url thinner; modal PARTIAL (maps to serverActionHandler, not a real modal); form LIVE via objectui ActionRunner.executeForm (routes a type:'form' action to the FormView at /forms/:target, forwarding the current record id) — the 2026-06 audit mis-classified as dead (objectui renderer not re-verified; fixed the 'Log Time does nothing' report). Build-time lint-view-refs.ts validates the form target resolves to a form view. RE-VERIFIED 2026-07 (#3714 follow-up): `api` -> executeAPI (:974, string-or-ApiConfig endpoint, method/headers/queryParams/responseType) and `form` -> executeForm (:920) both resolve; content/docs/ui/actions.mdx had been telling authors the opposite (\"no runtime executor / renderer today\") and was corrected in the same pass. 2026-08-26: REPOINTED (framework half only) — the evidence led with packages/runtime/src/http-dispatcher.ts, which reads this key nowhere. That file's 9 word-`type` occurrences are ALL something else: four are the TypeScript `import type` keyword (:9, :13, :14, :21) and the rest are other domains' prose or data (`error.type` :862, a field-type→JSON-Schema mapper :926, `details.type` :1156, a metadata type list :1634, an inbox query param :1765). The action consumer was extracted into domains/actions.ts + action-execution.ts (+ the sandbox body-runner, which is where the type gate has always lived) and http-dispatcher.ts now only delegates (handleActions → handleActionsRequest at :1969-1970). WHY THE GATE COULD NOT SEE THE ROT — and this is the part that differs from the `target`/`requiredPermissions` siblings repointed the day before: those were caught because their cited file had 0 occurrences of the key, whereas `type` is a common English word AND a TypeScript keyword, so the word-bounded key-mention check anchors on the coincidence and PASSES. This entry was repaired by a hand call-graph read, not by tooling; the check's designed, honest limit is what left it standing. The objectui half is unchanged and stays as measured @732b1bf (2026-07-28) — it was not re-graded here."
4343
},
4444
"target": {
4545
"status": "live",
@@ -49,8 +49,9 @@
4949
},
5050
"body": {
5151
"status": "live",
52-
"evidence": "packages/runtime/src/http-dispatcher.ts",
53-
"note": "server script (L1/L2) via engine.executeAction→body-runner."
52+
"verifiedAt": "2026-08-26",
53+
"evidence": "packages/runtime/src/sandbox/body-runner.ts:356 (`const raw = action.body` in actionBodyRunnerFactory, whose own header at :342 calls it \"the ONE choke point where an `action.body` becomes an executable handler\" — both bind paths reach it: AppPlugin's collectBundleActions walk and engine.setDefaultActionRunner); packages/runtime/src/action-execution.ts:525 (isHeadlessInvokableAction gates headless dispatch on `action?.target || action?.body`)",
54+
"note": "server script (L1/L2) via engine.executeAction→body-runner. 2026-08-26: REPOINTED — the evidence cited packages/runtime/src/http-dispatcher.ts, which reads this key nowhere. Its 68 word-`body` occurrences are the inbound HTTP request body — the `body: any` parameter threaded through the handleX delegates (:890, :961, :1732, :1969 …) and the result envelope at :728 — plus the file's own \"Thin delegate — body extracted to ./domains/…\" extraction comments, where `body` means a function body. The consumer this note already NAMED in prose (body-runner) is where the read has always been; the surrounding action path was extracted into domains/actions.ts + action-execution.ts, and http-dispatcher.ts now only delegates (handleActions → handleActionsRequest at :1969-1970). WHY THE GATE COULD NOT SEE THE ROT: `body` is both a common English word and the name of the HTTP request member this dispatcher is built around, so the word-bounded key-mention check added in #11457 anchors on 68 unrelated hits and passes — the designed, honest limit of that signal, which is why this needed a hand call-graph read. Note the shape the repoint exposes: the NOTE's pointer (\"via engine.executeAction→body-runner\") stayed true the whole time while the EVIDENCE pointer rotted — the same split recorded on `requiredPermissions`, one layer over."
5455
},
5556
"execute": {
5657
"status": "live",
@@ -139,7 +140,10 @@
139140
},
140141
"method": {
141142
"status": "live",
142-
"evidence": "packages/runtime/src/http-dispatcher.ts"
143+
"verifiedAt": "2026-08-26",
144+
"evidenceScope": "cross-repo",
145+
"evidence": "objectui @7b6f7f889: packages/core/src/actions/ActionRunner.ts:1774 (`method = action.method || 'POST'` on the string-endpoint branch of executeAPI) and :1780 (`config.method || action.method` on the ApiConfig branch), both landing on the real request at :1800 `const fetchInit: RequestInit = { method, headers }` and gating payload attachment at :1795/:1801",
146+
"note": "The HTTP verb of an inline `type:'api'` action. 2026-08-26: REPOINTED, and the repoint CHANGED THE REALM for exactly the reason its siblings `bodyShape`/`bodyExtra` did — `type:'api'` actions are client-dispatched by design, and action-execution.ts:557-563 (the `api` branch of headlessActionTypeError, :551-568) refuses them server-side with the message \"it dispatches on `target`, not through the action registry\". So packages/runtime/src/http-dispatcher.ts never read this key at any point: its 41 word-`method` occurrences are the inbound HTTP verb (15 `method: string` parameter declarations on the domain delegates, plus route matching such as `method === 'GET'` at :2067 and the log line at :2178) and the ordinary object-oriented sense in prose (\"this method\", :837/:2047/:2108/:2135). The ONE in-repo occurrence of `action.method` — action-execution.ts:561 — is a DIAGNOSTIC that interpolates the verb into the very refusal explaining the server does NOT dispatch it, so it is evidence of non-consumption and deliberately not cited above as a consumer. WHY THE GATE COULD NOT SEE THE ROT: `method` is this dispatcher's most-used noun, so the word-bounded key-mention check anchors incidentally and passes — the designed, honest limit of that signal."
143147
},
144148
"bodyExtra": {
145149
"status": "live",

0 commit comments

Comments
 (0)