|
42 | 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." |
43 | 43 | }, |
44 | 44 | "operation": { |
45 | | - "status": "planned", |
46 | | - "verifiedAt": "2026-09-03", |
| 45 | + "status": "live", |
| 46 | + "verifiedAt": "2026-09-08", |
47 | 47 | "evidenceScope": "in-repo", |
48 | | - "evidence": "packages/spec/src/ui/action.zod.ts#refuseDeclarativeUpdateContradictions (the mixing rule: every executor-binding key beside `operation: 'update'` is refused at its own path, and `patch` without it); packages/spec/src/stack.zod.ts#collectGlobalUpdateActionErrors (a standalone `operation: 'update'` action without `objectName` is refused by defineStack)", |
49 | | - "authorWarn": true, |
50 | | - "authorHint": "`operation: 'update'` + `patch` parse (the row-level declarative field write ruled on #14092) but nothing performs the write yet: the runtime action dispatcher and the objectui row-action executor are the downstream cards the spec seat files at ACCEPT (Blocked-by #14092). Until they land the button reaches the platform action route with no handler and fails loudly at click; it never writes silently.", |
51 | | - "note": "The discriminator of the declarative single-record field write — one member, `'update'`; `'delete'`/`'custom'` are refused with the reason. PLANNED, deliberately not `live` — the spec-first contract-split convention (the `onSuccess` precedent). Declared by the #14092 maintainer ruling (2026-09-01): the row-level counterpart of a list view's `bulkActionDefs` `operation: 'update'`, spelled with the same words. EXECUTOR CONTRACT the downstream halves must honour: a single-record data-plane update of the CURRENT record, executed AS THE CALLER and never system-elevated (the #14010 `runAs: 'user'` direction, consumed not reopened) — the caller's object/row/field permissions, the object's hooks and validations fire as for a user edit; a caller who cannot read or write the row is refused (the #14143 class); `patch` is merged UNDER the collected `params`; `undoable` captures the prior values of exactly the fields written. The console's action-button forward list does not carry these keys yet and `ActionRunner` dispatches the action on its default `type: 'script'` route, which is where the runtime half will branch on `operation` before looking for a handler. Amend to `live` citing the runtime dispatch branch and the objectui forward/undo reads when those halves land." |
| 48 | + "evidence": "packages/runtime/src/action-execution.ts#isDeclarativeUpdateAction (the executor's own discriminator — a bare `action?.operation === DECLARATIVE_UPDATE_OPERATION`, deliberately with no `type` clause so data at rest whose `type` contradicts its `operation` is still routed by `operation`); packages/runtime/src/domains/actions.ts#handleActionsRequest (the REST `/actions` door: the branch runs ahead of the `type` switch and ahead of the trusted-mode plumbing, and hands off to `executeDeclarativeUpdateAction`); packages/runtime/src/action-execution.ts#invokeBusinessAction (the MCP `run_action` door takes the same branch into the same executor — one implementation, two doors); packages/runtime/src/action-execution.ts#isHeadlessInvokableAction (asked FIRST, so a declarative update is listed as invokable although it carries neither `target` nor `body`); packages/runtime/src/action-execution.ts#headlessActionTypeError (asked FIRST, so the key suppresses the `type` prescription that would otherwise be wrong for it); packages/runtime/src/action-execution.ts#summarizeAction (the MCP listing face projects the declared `operation` and forces `requiresRecord`); packages/spec/src/ui/action.zod.ts#refuseDeclarativeUpdateContradictions (authoring-time: every executor-binding key beside `operation: 'update'` is refused at its own path, and `patch` without it); packages/spec/src/stack.zod.ts#collectGlobalUpdateActionErrors (defineStack refuses a standalone `operation: 'update'` action with no `objectName`)", |
| 49 | + "note": "The discriminator of the declarative single-record field write — one member, `'update'`; `'delete'`/`'custom'` are refused with the reason. FLIPPED `planned` -> `live` 2026-09-08 (#15080) on the runtime half #15079 (PR #15448, merged 2026-09-04): the key now decides dispatch at both server doors, and it is read BEFORE `type` at every one of them — contract point 1 of the executor contract PR #15077 pinned here, and a rule about ORDER, not merely a new branch. Declared by the #14092 maintainer ruling (2026-09-01): the row-level counterpart of a list view's `bulkActionDefs` `operation: 'update'`, spelled with the same words. The EXECUTOR CONTRACT is now honoured in tree rather than promised: the write is a single data-plane update of the CURRENT record executed AS THE CALLER under `wiring.ec` and never `buildActionExecutionContext` (the #14010 `runAs: 'user'` direction), so object/row/field permissions, hooks and validations fire as for a user edit and a caller who cannot read the row is refused (the #14143 class); `patch` merges UNDER the collected `params`; `undoable` captures the prior values of exactly the fields written; an action with no current record is a located 400, never a silent no-op. Pinned end-to-end in packages/runtime/src/action-declarative-update.test.ts, one describe per contract point plus a reverse pin that a handler-less `type: 'script'` action WITHOUT `operation` is unchanged. `evidenceScope: in-repo` is exact and deliberate: this verdict rests on the framework-side readers alone. The console half (objectui#7551) is a SECOND reader and the flip did not wait for it — objectui's `ActionRunner` still dispatches on `type` — so a console-side forward/undo pointer is a later widening of this row's scope, not a precondition of its verdict." |
52 | 50 | }, |
53 | 51 | "patch": { |
54 | | - "status": "planned", |
55 | | - "verifiedAt": "2026-09-03", |
| 52 | + "status": "live", |
| 53 | + "verifiedAt": "2026-09-08", |
56 | 54 | "evidenceScope": "in-repo", |
57 | | - "evidence": "packages/spec/src/ui/action.zod.ts#refuseDeclarativeUpdateContradictions (refused on an action without `operation: 'update'` — it would be silently dropped; `operation: 'update'` with neither `patch` nor `params` is refused as nothing-to-write)", |
58 | | - "authorWarn": true, |
59 | | - "authorHint": "`operation: 'update'` + `patch` parse (the row-level declarative field write ruled on #14092) but nothing performs the write yet: the runtime action dispatcher and the objectui row-action executor are the downstream cards the spec seat files at ACCEPT (Blocked-by #14092). Until they land the button reaches the platform action route with no handler and fails loudly at click; it never writes silently.", |
60 | | - "note": "The static field values of the declarative write, merged UNDER the collected `params` (a param of the same name wins). Passed through verbatim — no transform, no field-existence check here (a lint reference diagnostic). PLANNED, deliberately not `live` — the spec-first contract-split convention (the `onSuccess` precedent). Declared by the #14092 maintainer ruling (2026-09-01): the row-level counterpart of a list view's `bulkActionDefs` `operation: 'update'`, spelled with the same words. EXECUTOR CONTRACT the downstream halves must honour: a single-record data-plane update of the CURRENT record, executed AS THE CALLER and never system-elevated (the #14010 `runAs: 'user'` direction, consumed not reopened) — the caller's object/row/field permissions, the object's hooks and validations fire as for a user edit; a caller who cannot read or write the row is refused (the #14143 class); `patch` is merged UNDER the collected `params`; `undoable` captures the prior values of exactly the fields written. The console's action-button forward list does not carry these keys yet and `ActionRunner` dispatches the action on its default `type: 'script'` route, which is where the runtime half will branch on `operation` before looking for a handler. Amend to `live` citing the runtime dispatch branch and the objectui forward/undo reads when those halves land." |
| 55 | + "evidence": "packages/runtime/src/action-execution.ts#declarativeUpdateWrite (`const patch = action?.patch` — the ONE producer of the write bag `{ ...patch, ...params }`; contract point 4, the static patch sits UNDER the values the dialog collected so a param of the same name wins, and nothing else from the action is merged); packages/runtime/src/action-execution.ts#executeDeclarativeUpdateAction (calls it and hands the bag to a single data-plane `update` of the routed row under the CALLER's own execution context; an empty bag is refused with a located 400 rather than answered 200 for a no-op, and `undoable` reads back the prior value of exactly the keys the bag names) — reached from both server doors, `handleActionsRequest` (REST) and `invokeBusinessAction` (MCP `run_action`); packages/spec/src/ui/action.zod.ts#refuseDeclarativeUpdateContradictions (authoring-time: `patch` is refused on an action without `operation: 'update'` — it would be silently dropped; `operation: 'update'` with neither `patch` nor `params` is refused as nothing-to-write)", |
| 56 | + "note": "The static field values of the declarative write, merged UNDER the collected `params` (a param of the same name wins). Passed through verbatim — no transform, no field-existence check here (a lint reference diagnostic). FLIPPED `planned` -> `live` 2026-09-08 (#15080) on the runtime half #15079 (PR #15448, merged 2026-09-04): the declared object is now the base of the bag one data-plane `update` writes, so authoring a key here changes what lands on the row. Declared by the #14092 maintainer ruling (2026-09-01): the row-level counterpart of a list view's `bulkActionDefs` `operation: 'update'`, spelled with the same words. The `patch`-UNDER-`params` precedence is the bulk def's own rule mirrored word for word, and it is pinned both ways — through the door and as the pure function — in packages/runtime/src/action-declarative-update.test.ts, beside the pin that the wire cannot widen the bag (an undeclared param is refused by ADR-0104 D2) and the pin that `undoable` restores exactly the keys this bag names. `evidenceScope: in-repo` is exact and deliberate: the verdict rests on the framework-side executor alone; the console half (objectui#7551) is a SECOND reader the flip did not wait for." |
61 | 57 | }, |
62 | 58 | "target": { |
63 | 59 | "status": "live", |
|
0 commit comments