Skip to content

Commit 93ea19b

Browse files
os-trumpclaude
andauthored
chore(spec): re-anchor action + object liveness citations to consuming symbols (#13035)
Adoption batch 1 of the #12516 symbol-anchor citation grammar (#13003). Twenty `path:NNN` evidence citations in liveness/action.json and liveness/object.json become `path#symbol`, each re-closed by reading the code on the current tree rather than by shifting a line. Fourteen of the fifteen object.json citations were already wrong — all of them IN RANGE, so the existence check, the line bound and the key-mention check all passed on pointers that named a docblock, a neighbouring const, or (object.enable.clone) a sort normalizer ~6,700 lines from its reader. Three readers had moved package into @objectstack/spec. action.execute is NOT touched here: its cited `.transform` lowering was removed in protocol 17 and the key is now a retiredKey tombstone, so the `live` verdict is falsified. That is a re-classification under ADR-0049 and is reported on #13003 rather than executed in this PR. Part of #13003 Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 Co-authored-by: Claude <noreply@anthropic.com>
1 parent c459da6 commit 93ea19b

3 files changed

Lines changed: 80 additions & 43 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
chore(spec): re-anchor the `action` and `object` liveness ledgers to consuming symbols (#13003)
6+
7+
Adoption batch 1 of the symbol-anchor citation grammar landed by #12516. The
8+
`liveness/` ledgers ship inside this package's npm tarball (they are named in
9+
`files`), so this is a published-data change even though no runtime behaviour
10+
moves and no schema key changes.
11+
12+
Twenty `path:NNN` evidence citations across `liveness/action.json` and
13+
`liveness/object.json` are now written `path#symbol`, each re-closed by reading
14+
the code on the current tree rather than by shifting a line number. A symbol
15+
moves with its consumer, so the pointer survives the in-file drift that rots a
16+
line, and goes red when the consumer is renamed or deleted — a direction a
17+
stale line can never produce.
18+
19+
What the re-closure found, which is the reason the migration is not mechanical:
20+
fourteen of the fifteen `object.json` citations were pointing at the wrong place
21+
already, every one of them IN RANGE and so invisible to all three existing
22+
checks. They had drifted onto a docblock about aggregate-function lowering, a
23+
job-scheduling block, a neighbouring `const`, and — for `object.enable.clone`
24+
a sort-node normalizer roughly 6,700 lines from its actual reader. Three had
25+
additionally moved package: the `ownership`, `managedBy` and `tenancy.enabled`
26+
readers now live in `@objectstack/spec` itself, and one of those citations
27+
carried a parenthetical (`applySystemFields reads schema.ownership`) that the
28+
re-read falsified outright — that function's nine `ownership` occurrences are
29+
all comments about a decision it delegates.

packages/spec/liveness/action.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@
3737
},
3838
"type": {
3939
"status": "live",
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."
40+
"verifiedAt": "2026-08-28",
41+
"evidence": "packages/runtime/src/sandbox/body-runner.ts#actionBodyRunnerFactory (the #4352 gate — `const type = action.type ?? 'script'` decides whether a `body` binds an executable handler at all); packages/runtime/src/action-execution.ts#invokeBusinessAction (`action.type === 'flow'` routes to dispatchFlowAction); packages/runtime/src/action-execution.ts#isHeadlessInvokableAction (gates headless dispatch on the type); packages/runtime/src/action-execution.ts#headlessActionTypeError (names the type it refuses); packages/runtime/src/action-execution.ts#summarizeAction (MCP action summary projects it); packages/runtime/src/domains/actions.ts#handleActionsRequest (the REST route's own `actionType` resolution, read again at the flow branch); 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. 2026-08-28: RE-ANCHORED (#13003, adoption of the #12516 grammar) — re-closed by hand against c459da6bc. All six framework consumers stand and none had left its file, so this is a grammar migration rather than a repair; what it DOES repair is a second, quieter gap: `:524`/`:552`/`:906` were written as bare line suffixes with no path in front of them, which the evidence scanner cannot parse as citations at all (`PATH_RE` needs a repo-rooted token), so three of this entry's six consumers were unfalsifiable prose that no check has ever resolved. They are now anchors of their own."
4343
},
4444
"target": {
4545
"status": "live",
@@ -49,9 +49,9 @@
4949
},
5050
"body": {
5151
"status": "live",
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."
52+
"verifiedAt": "2026-08-28",
53+
"evidence": "packages/runtime/src/sandbox/body-runner.ts#actionBodyRunnerFactory (`const raw = action.body`, in the factory whose own header 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#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. 2026-08-28: RE-ANCHORED (#13003) — re-closed by hand against c459da6bc; both consumers stand where the 08-26 repoint put them, so the lines had not yet rotted and this is the grammar migration, not a second repair. The anchor is what makes that claim re-testable next time: `actionBodyRunnerFactory` is the symbol the header already calls the ONE choke point, so a consumer that moves inside this 798-line file keeps the pointer true and one that is deleted turns it red."
5555
},
5656
"execute": {
5757
"status": "live",

0 commit comments

Comments
 (0)