You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(spec): action.undoable is live, not experimental — two objectui readers gate it (#3714) (#3829)
The liveness ledger marked `action.undoable` `experimental` on a #1992-era note
("no runtime reader yet ... objectui has an UndoManager but does not key off
this field"). True when written, falsified since: objectui wired two readers,
both gating real behaviour — app-shell useConsoleActionRuntime.tsx:409 builds
the undo operation the success toast's Undo button invokes (:147), and
RecordDetailView.tsx:545 restores the record's prior field values (:404).
Because the CLI liveness lint warns on `experimental` as well as `dead`,
authoring a *working* property produced a "declared but NOT enforced at runtime"
warning — telling authors and AI to skip a shipped feature.
- ledger: `experimental` -> `live` with the two readers as evidence
- README: action row 33|1|2 -> 34|0|2; `undoable` joins `flow.status` as the
second understatement, with the cost of understating spelled out
- protocol docs: drop the "currently has no effect" claim
- CLI lint test: experimental-path assertion repoints to agent.memory, plus a
regression guard that `undoable` is now silent
Schema, parsed shape and runtime are unchanged — only the classification.
Copy file name to clipboardExpand all lines: content/docs/protocol/objectui/actions.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -184,7 +184,7 @@ interface Action {
184
184
confirmText?: string; // Confirmation message before execution
185
185
successMessage?: string; // Toast shown after success
186
186
errorMessage?: string; // Toast shown on failure (overrides the raw error)
187
-
undoable?: boolean; // Offer an Undo affordance after a single-record update succeeds (experimental — see note below)
187
+
undoable?: boolean; // Offer an Undo affordance after a single-record update succeeds
188
188
refreshAfter?: boolean; // Reload the view after execution (default false)
189
189
resultDialog?: ResultDialog; // One-shot reveal of API response values
190
190
shortcut?: string; // Keyboard shortcut, e.g. "Ctrl+S"
@@ -334,7 +334,7 @@ refreshAfter: true
334
334
- `confirmText`— message shown in a confirm dialog before the action runs.
335
335
- `successMessage`— toast shown after a successful run. When omitted the UI shows a generic "Action completed" toast, so set this for any action whose outcome isn't self-evident.
336
336
- `errorMessage`— toast shown when the action fails; overrides the raw server error with author-controlled copy.
337
-
- `undoable`— marks a single-record update action as offering an **Undo** affordance in the success toast to restore the record's prior values. **Experimental:** the flag is declared in the schema but no runtime consumer keys off it yet (objectui ships an `UndoManager` but does not read this field), so setting it currently has no effect.
337
+
- `undoable` — marks a single-record update action as offering an **Undo** affordance in the success toast, which restores the record's prior field values. The action runtime snapshots the record before the update and only builds the undo operation when this flag is set, so an action that omits it gets no Undo. Single-record updates only: there is nothing to snapshot when the action isn't scoped to one record.
338
338
- `refreshAfter`— reload the current view after success.
339
339
- `mode` — a semantic hint (`create` / `edit` / `delete` / `custom`). Pure metadata with no runtime branching: today only the AI confirmation heuristic reads it (a `delete` mode nudges an AI invocation toward requiring approval), while the UI does not branch on it.
Copy file name to clipboardExpand all lines: packages/spec/liveness/README.md
+13-5Lines changed: 13 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -74,12 +74,20 @@ as `live`, 10 were wrong** — a 77% error rate for the preview-renderer standar
74
74
Note the two failure directions the sweep exposed. Most entries **overstated**
75
75
liveness. But `flow.status` was *understated*: the file-level note still said
76
76
"status/active gate nothing", true when written and falsified a month later by
77
-
`497bda853`. **A ledger entry is a claim with a timestamp; code moves under it
78
-
in both directions.**
77
+
`497bda853`. `action.undoable` was the same shape (#3714): marked `experimental`
78
+
on a #1992-era "no runtime reader yet" note that objectui falsified with two
79
+
readers. **A ledger entry is a claim with a timestamp; code moves under it
80
+
in both directions.** An entry is worth re-verifying, not trusting indefinitely
81
+
— see the methodology below.
79
82
80
83
When in doubt, the honest status is `dead` + `authorWarn`: an author who gets a
81
-
warning for a property that turns out to work loses nothing; an author who gets
82
-
silence for a property that does nothing ships a bug.
84
+
warning for a property that turns out to work loses nothing *at runtime*; an
85
+
author who gets silence for a property that does nothing ships a bug. But the
86
+
ledger is also read as a capability catalogue — by authors and by AI — so an
87
+
understated entry does have a cost: `undoable` sat behind a "declared but NOT
88
+
enforced" warning for a month while it worked, which is an invitation to skip a
89
+
shipped feature. Erring toward `dead` is the right default *and* a debt to
90
+
re-verify.
83
91
84
92
### How to verify a claim without fooling yourself
85
93
@@ -259,7 +267,7 @@ EOF
259
267
| object | 40 | – | 0 | 1 | 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) |
260
268
| field | 55 | – | 0 | – | healthy — full dead set (vectorConfig/fileAttachmentConfig/dependencies, then referenceFilters/columnName/index) REMOVED (#2377); columnName also dropped the ADR-0062 D7 lint + StorageNameMapping column helpers |
261
269
| flow | 26 | – | 5 | – | dead = description/template/nodes.outputSchema/errorHandling.fallbackNodeId (engine uses fault edges) + `active` CORRECTED to dead 2026-07 (deprecated no-op — `status` is what gates binding/execution since 497bda853; the file `_note` claiming otherwise is fixed) |
262
-
| action |33|1| 2 | – |`type:'form'` CORRECTED to live (objectui ActionRunner.executeForm, #2377); dead `timeout` REMOVED (#2377); `disabled` live for real since objectui#2863 (six surfaces); `shortcut` + `bulkEnabled` CORRECTED to dead 2026-07 — registered into ActionEngine but their accessors have no non-test caller (#3686 sweep) |
270
+
| action |34|0| 2 | – |`type:'form'` CORRECTED to live (objectui ActionRunner.executeForm, #2377); dead `timeout` REMOVED (#2377); `disabled` live for real since objectui#2863 (six surfaces); `shortcut` + `bulkEnabled` CORRECTED to dead 2026-07 — registered into ActionEngine but their accessors have no non-test caller (#3686 sweep); `undoable` CORRECTED to live 2026-07 — understated, two objectui readers gate the toast's Undo and the record restore (#3714) |
263
271
| hook | 11 | – | 2 | – | model-healthy; only label/description dead (benign) |
264
272
| permission | 32 | – | 0 | – | CRUD/FLS/RLS live; dead `contextVariables` REMOVED (ADR-0105 D11 — RLS resolves only the `current_user.*` built-ins plus runtime-staged `rlsMembership` sets) |
265
273
| position | 4 | – | – | – | (role's ADR-0090 successor) fully live |
Copy file name to clipboardExpand all lines: packages/spec/liveness/action.json
+3-2Lines changed: 3 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -74,8 +74,9 @@
74
74
"note": "objectui toast (error counterpart of successMessage; added #1990)."
75
75
},
76
76
"undoable": {
77
-
"status": "experimental",
78
-
"note": "Declared + demoed in #1992 (example-crm reassign) but no runtime reader yet: neither service-automation nor objectui consume the action's `undoable` flag (objectui has an UndoManager but does not key off this field). Promote to live once a consumer is wired."
77
+
"status": "live",
78
+
"evidence": "objectui: app-shell useConsoleActionRuntime.tsx:409 gates building the undo operation the success toast's Undo button invokes (:147); app-shell RecordDetailView.tsx:545 gates restoring the record's prior values (:404); plumbed through by components action/action-button.tsx:113",
79
+
"note": "STATUS CORRECTED 2026-07 (#3714) — this entry UNDERSTATED liveness, the mirror image of the preview-renderer over-claims #3686/#3711 fixed. The #1992-era note (\"no runtime reader yet ... objectui has an UndoManager but does not key off this field\") was true when written and has since been falsified: objectui wired two readers, both gating real behaviour, and action-button.tsx:113 forwards the flag precisely because — per its own comment — \"without this the flag is dropped and the handler never builds the undo operation\". Authoring side: ActionDefaultInspector.tsx \"Offer undo\" checkbox. Consequence while the entry stood: the CLI liveness lint warns on `experimental` too, so authoring a WORKING property produced a \"declared but NOT enforced\" warning."
0 commit comments