From cc4c52948fdb530d3c54de45a52727fa55882f2c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 00:39:06 +0000 Subject: [PATCH] chore(spec): re-classify the action.execute liveness entry dead (#13036) The entry was ledgered `live` on a `.transform` that lowers `execute` -> `target` and drops the alias. No such transform exists: action.zod.ts has exactly two `.transform` calls and both are `lowerRequiresFeature`. The alias and its lowering were removed together in protocol 17 (#3855); the key is a `retiredKey` tombstone with no consumer on any layer. Status flips to `dead` with the ROW STAYING - the rls.priority precedent, a tombstone keeps the key in the walked shape - matching the two already-dead siblings in the same ledger. The falsified `evidence` goes, the #3713 three-readers history is preserved in the note. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 --- .changeset/action-execute-ledger-dead.md | 19 +++++++++++++++++++ packages/spec/liveness/README.md | 10 ++++++++-- packages/spec/liveness/action.json | 6 +++--- packages/spec/liveness/state-counts.md | 4 ++-- 4 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 .changeset/action-execute-ledger-dead.md diff --git a/.changeset/action-execute-ledger-dead.md b/.changeset/action-execute-ledger-dead.md new file mode 100644 index 0000000000..677fd30356 --- /dev/null +++ b/.changeset/action-execute-ledger-dead.md @@ -0,0 +1,19 @@ +--- +'@objectstack/spec': patch +--- + +liveness ledger: re-classify `action.execute` `live` → `dead` (#13036) + +The entry claimed a `.transform` that lowers `execute` → `target` and drops the +alias. No such transform exists: `packages/spec/src/ui/action.zod.ts` has exactly +two `.transform` calls and both are `lowerRequiresFeature`. The alias and its +lowering were removed together in protocol 17 (#3855, landed 2026-07-28); the key +has been a `retiredKey` tombstone ever since, and `packages/cli/src/utils/lower-callables.ts` +declines to bind a function-valued `execute` on purpose so the tombstone fires. + +Data-only: no schema, no runtime, no authoring surface changes — authoring +`execute` already failed `tsc` and the parse before this, and still does. The row +STAYS, per the `rls.priority` precedent a `retiredKey()` tombstone keeps the key +in the walked shape, so deleting the row would report UNCLASSIFIED. `liveness/` is +in this package's `files` array, so these ledgers ship in the npm tarball and this +is published data. diff --git a/packages/spec/liveness/README.md b/packages/spec/liveness/README.md index 466c4e2ecb..fc23866259 100644 --- a/packages/spec/liveness/README.md +++ b/packages/spec/liveness/README.md @@ -282,11 +282,17 @@ as `live`, 10 were wrong** — a 77% error rate for the preview-renderer standar | Verdict | Properties | |---|---| -| `live`, evidence corrected to the real reader | `action.execute` (the spec transform's parse-time lowering — the second reader, objectui's ActionRunner, resolved the `target`/`execute` pair in the *opposite* direction; aligned and the alias dropped from the parsed output in #3713), `action.disabled` (six render surfaces), `flow.status` (engine gates binding + execution since `497bda853`) | +| `live`, evidence corrected to the real reader | `action.execute`† (the spec transform's parse-time lowering — the second reader, objectui's ActionRunner, resolved the `target`/`execute` pair in the *opposite* direction; aligned and the alias dropped from the parsed output in #3713), `action.disabled` (six render surfaces), `flow.status` (engine gates binding + execution since `497bda853`) | | corrected to `dead` + `authorWarn` | `action.shortcut`, `action.bulkEnabled`, `flow.active`, `skill.triggerPhrases`, `tool.category`, `tool.requiresConfirmation`, `tool.active`, `tool.builtIn`, `skill.permissions`*, `agent.knowledge` | \* `skill.permissions` was subsequently pruned outright — it was never enforced. +† `action.execute` did not survive the correction either: the parse-time lowering +this row credits was removed with the alias in protocol 17 (#3855), and the entry +is `dead` since #13036. The tally above stands as the record of what the 2026-07 +sweep found; it is not a current-state claim, which is why this row gets a +footnote rather than a rewrite. + Note the two failure directions the sweep exposed. Most entries **overstated** liveness. But `flow.status` was *understated*: the file-level note still said "status/active gate nothing", true when written and falsified a month later by @@ -806,7 +812,7 @@ marker where the Notes cell goes, never a guess at what belongs there. | object | aspirational tier (versioning/softDelete/search/recordName/keyPrefix) + tags/active/abstract REMOVED (#2377) — tombstoned in UNKNOWN_KEY_GUIDANCE; `enable.trash`/`mru` REMOVED (#2377 close-out) — tombstoned in the now-`.strict()` ObjectCapabilities; `isSystem` + `enable.searchable` CORRECTED to live (#2377 — sharing default-model + global-search opt-out; 2026-06 audit missed both readers); `tenancy.strategy`/`crossTenantAccess` REMOVED post-15.0 (#2763) | | field | healthy — full dead set (vectorConfig/fileAttachmentConfig/dependencies, then referenceFilters/columnName/index) REMOVED (#2377); columnName also dropped the ADR-0062 D7 lint + StorageNameMapping column helpers | | flow | dead count = **5 tombstone entries** + the kept docs field: `active`/`template`/nodes.`outputSchema`/errorHandling.`fallbackNodeId` REMOVED 2026-07-30 (#3896 close-out sweep — `active: false` never stopped a flow, `status` is the enforced lifecycle; faults route via per-node fault edges), plus errorHandling.`retryDelayMs` RENAMED to `backoffMs` 2026-08-04 (#4964). The rename is why the dead column moved while live did not: a rename is a removal on this ledger, so the old spelling is tombstoned (`retiredKey` keeps it in the walked shape) and the new spelling enters as its own `live` row. Read it beside the four above as the one entry here that cost an author nothing — the block was a THIRD encoding of the retry policy #4661 converged, invisible to that pass because it is an anonymous inline block with no exported name, and #4964 spelled its base delay `backoffMs` to match `job.retryPolicy` and a `try_catch` node's `retry`. Remaining dead = `description`, KEPT deliberately: docs-shaped, exempt from enforce-or-remove | -| action | `type:'form'` CORRECTED to live (objectui ActionRunner.executeForm, #2377); dead `timeout` REMOVED (#2377); `disabled` live since objectui#2863; `undoable` CORRECTED to live (#3714); `shortcut` + `bulkEnabled` REMOVED 2026-07-30 (#3896 close-out sweep — no keydown path dispatches shortcuts; the multi-select toolbar reads the view's bulkActions), and they are still the whole dead set. **#7367** (PR #7430) adds `description` as an authorable key, `live` on arrival — the only row this type has gained since that sweep | +| action | `type:'form'` CORRECTED to live (objectui ActionRunner.executeForm, #2377); dead `timeout` REMOVED (#2377); `disabled` live since objectui#2863; `undoable` CORRECTED to live (#3714); `shortcut` + `bulkEnabled` REMOVED 2026-07-30 (#3896 close-out sweep — no keydown path dispatches shortcuts; the multi-select toolbar reads the view's bulkActions). **#7367** (PR #7430) adds `description` as an authorable key, `live` on arrival — the only row this type has gained since that sweep. **#13036** makes the dead set three: `execute` joins it, re-classified `live` → `dead` 2026-08-29 with no key added or removed. Its `live` verdict rested on a `.transform` lowering `execute` → `target` that protocol 17 (#3855) removed along with the alias; the key has been a `retiredKey` tombstone since 2026-07-28, so the row stays (the `rls.priority` precedent) while the verdict does not. The rot was invisible to every citation check — the pointer was in range, in the right file, and the file names the key — and the entry carried no `verifiedAt`, so nothing ever re-asked | | hook | model-healthy; label/description dead but KEPT deliberately (2026-07-30 sweep) — docs-shaped annotation fields, exempt from enforce-or-remove | | permission | CRUD/FLS/RLS live; dead `contextVariables` REMOVED (ADR-0105 D11 — RLS resolves only the `current_user.*` built-ins plus runtime-staged `rlsMembership` sets). 2026-07-30 security-subset re-verification (all 33 entries `verifiedAt`-stamped): `rowLevelSecurity.enabled` was live-with-wrong-evidence and UNREAD — a disabled policy kept contributing its OR-branch grant; ENFORCED same day in rls-compiler (`getApplicablePolicies`), the `positions` ADR-0049 resolution repeated. `rowLevelSecurity.priority` CORRECTED to dead+authorWarn — semantically void under OR-combination (no conflict exists to order), a REMOVE candidate. `rls.label`/`description`/`tags` CORRECTED to dead (benign display, no consumer in either repo). `tabPermissions` was UNDERSTATED ("only hidden read" → the rank merge reads all four values; me-apps dogfood test exercises it). `allowExport` re-verified TRUE end-to-end (server-side 403 gate, not just the /me projection). `objects.allowRestore`/`allowPurge` REMOVED 2026-08-26 (#12497, ADR-0049 — the `restore`/`purge` ops never existed; the 2026-07-30 'live' verdict cited only the evaluator pre-mapping, retired in the same batch; `retiredKey` tombstones, keys return with M2 per the #1883 ruling) | | position | (role's ADR-0090 successor) fully live; all 4 `verifiedAt`-stamped 2026-07-30 | diff --git a/packages/spec/liveness/action.json b/packages/spec/liveness/action.json index d9938f92e0..2a54f403ba 100644 --- a/packages/spec/liveness/action.json +++ b/packages/spec/liveness/action.json @@ -54,9 +54,9 @@ "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." }, "execute": { - "status": "live", - "evidence": "packages/spec/src/ui/action.zod.ts:581 — the .transform lowers execute -> target and DROPS the alias, so authoring it changes what the runtime dispatches; `target` is the one slot every consumer reads", - "note": "RE-VERIFIED 2026-07 (#3686 preview-claim sweep): the prior `live` verdict cited only a metadata-admin PREVIEW panel, which echoes what the author typed. Verdict stands, but the real reader is the parse-time lowering, not a second field reader. DIVERGENCE RESOLVED in #3713: the alias is now consumed at parse time and removed from the output, so the 'both declared' conflict is unrepresentable rather than merely agreed-upon (mirrors agent.knowledge.topics -> sources, #1891). Before that fix three readers disagreed in two directions — this transform kept `target`, objectui ActionRunner did `execute || target`, and the CLI compile step (packages/cli/src/utils/lower-callables.ts) preferred a function on `execute`; all now prefer `target`. Note the server runtime never read `execute` at all (runtime/src/action-execution.ts gates on `target || body` and dispatches on `target`/`name`), which is why lowering is the whole of this property's liveness." + "status": "dead", + "verifiedAt": "2026-08-29", + "note": "REMOVED 2026-07-28 in protocol 17 (#3855, PR #3883) — tombstoned at the schema (retiredKey carries the prescription; authoring it is a tsc error and a parse error) and renamed out of sources by the protocol-17 conversion `action-execute-to-target`. The entry stays because retiredKey keeps the key in the walked shape (the rls.priority precedent); use `target`, the only handler slot — rename the key, the value (a handler / flow / URL ref) is unchanged, and `os migrate meta --from 16` lists the mechanical edits. The tombstone is packages/spec/src/ui/action.zod.ts#execute, and packages/cli/src/utils/lower-callables.ts#lowerActionCallable deliberately declines to bind a function-valued `execute` so the tombstone fires instead of the alias silently working in one authoring style and being rejected in every other. LEDGER CORRECTED 2026-08-29 (#13036) — the VERDICT was falsified, not the citation. This row read `live` on the claim that a .transform lowers execute -> target and DROPS the alias; no such transform exists. action.zod.ts has exactly two .transform calls (:526 and :1668) and both are lowerRequiresFeature, and the docblock above `target` (:559-562) says the alias was removed in protocol 17. The old pointer packages/spec/src/ui/action.zod.ts:581 was IN RANGE in an 1802-line file that names the key, so existence, line bound and key-mention were all green on it — what :581 holds today is a comment about the `global_nav` enum-VALUE retirement's declaration style, unrelated in every respect — and the entry carried no verifiedAt, so the re-verification clock never asked (the #12516 class in its purest form). HISTORY PRESERVED, because it is the argument for `target` being the single handler slot: DIVERGENCE RESOLVED in #3713 — before that fix three readers disagreed in two directions, the parse kept `target`, objectui ActionRunner did `execute || target`, and the CLI compile step (packages/cli/src/utils/lower-callables.ts) preferred a function on `execute`; #3713 made all three prefer `target`, and protocol 17 then removed the alias outright, so the both-declared conflict is unrepresentable rather than merely agreed-upon (mirrors agent.knowledge.topics -> sources, #1891). The server runtime never read `execute` at all — packages/runtime/src/action-execution.ts:525 gates on `target || body` and dispatches on `target`/`name` — so with the parse-time lowering gone there is no reader on any layer." }, "params": { "status": "live", diff --git a/packages/spec/liveness/state-counts.md b/packages/spec/liveness/state-counts.md index d9b6ad9279..72a4515b58 100644 --- a/packages/spec/liveness/state-counts.md +++ b/packages/spec/liveness/state-counts.md @@ -30,7 +30,7 @@ for both corollaries. | `object` | 51 | 0 | 0 | 1 | 52 | | `field` | 89 | 0 | 0 | 2 | 91 | | `flow` | 34 | 0 | 6 | 0 | 40 | -| `action` | 42 | 0 | 2 | 2 | 46 | +| `action` | 41 | 0 | 3 | 2 | 46 | | `hook` | 18 | 0 | 2 | 0 | 20 | | `permission` | 36 | 0 | 6 | 0 | 42 | | `position` | 12 | 0 | 0 | 0 | 12 | @@ -58,4 +58,4 @@ for both corollaries. | `capability` | 12 | 0 | 0 | 0 | 12 | | `qa` | 4 | 0 | 5 | 0 | 9 | | `manifest` | 22 | 0 | 21 | 0 | 43 | -| **total** | **821** | **5** | **78** | **10** | **914** | +| **total** | **820** | **5** | **79** | **10** | **914** |