Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .changeset/liveness-action-dispatch-anchors.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
---
'@objectstack/spec': patch
---

liveness ledger: repoint the `action` `type` / `body` / `method` anchors at their real consumers

All three cited `packages/runtime/src/http-dispatcher.ts`, and none of them is read
there. The actions domain was extracted out of that file — it now only delegates
(`handleActions` → `handleActionsRequest` at :1969-1970) — and the reads live in
`action-execution.ts`, `domains/actions.ts`, `sandbox/body-runner.ts` and, for the
client-dispatched `method`, in the renderer repo.

This is the residue of the same extraction that rotted `action.target` and
`action.requiredPermissions`, but it survived the sweep that repaired those, and the
reason is the interesting part. The key-mention check added with that sweep asks
whether the cited file names the key at all — which caught `target` and
`requiredPermissions` because `http-dispatcher.ts` contains **0** occurrences of
either. It cannot catch these three, because they are common English and HTTP words
that the file is full of for unrelated reasons:

- `type` — 9 occurrences, four of them the TypeScript `import type` keyword, the rest
other domains' data (`error.type`, `details.type`, a field-type→JSON-Schema mapper).
- `body` — 68 occurrences: the inbound HTTP request body threaded through every
domain delegate, plus the file's own "body extracted to ./domains/…" comments,
where `body` means a *function* body.
- `method` — 41 occurrences: the inbound HTTP verb (15 `method: string` parameter
declarations, route matching such as `method === 'GET'`) and the ordinary
object-oriented sense in prose.

So the word-bounded check anchors on the coincidence and passes. That is the designed,
honest limit of the signal rather than a defect in it — the census that shipped it said
so — and it means this class is invisible to tooling and only a hand call-graph read
can settle it. Each repointed entry now records which spelling misled the gate, so the
next reader does not have to re-derive it.

`method` additionally CHANGES REALM, joining its siblings `bodyShape` and `bodyExtra`:
`type: 'api'` actions are client-dispatched by design, so the server never read the
verb. Its one in-repo appearance is a diagnostic that interpolates the verb into the
refusal explaining the server does *not* dispatch it — evidence of non-consumption,
and deliberately not cited as a consumer.

No verdict was re-graded: all three were `live` and remain `live`, with the consumer
proven rather than asserted. Citation repair only.
16 changes: 10 additions & 6 deletions packages/spec/liveness/action.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,9 +37,9 @@
},
"type": {
"status": "live",
"verifiedAt": "2026-07-28",
"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)",
"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."
"verifiedAt": "2026-08-26",
"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)",
"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."
},
"target": {
"status": "live",
Expand All@@ -49,8 +49,9 @@
},
"body": {
"status": "live",
"evidence": "packages/runtime/src/http-dispatcher.ts",
"note": "server script (L1/L2) via engine.executeAction→body-runner."
"verifiedAt": "2026-08-26",
"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`)",
"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."
},
"execute": {
"status": "live",
Expand DownExpand Up@@ -139,7 +140,10 @@
},
"method": {
"status": "live",
"evidence": "packages/runtime/src/http-dispatcher.ts"
"verifiedAt": "2026-08-26",
"evidenceScope": "cross-repo",
"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",
"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."
},
"bodyExtra": {
"status": "live",
Expand Down
Loading