Skip to content

Commit 28fafd8

Browse files
committed
fix(spec): undoable names the written set, not patch alone
`ActionSchema.undoable`'s `.describe()` said the `patch` names exactly the fields whose prior values are captured. An `operation: 'update'` action also writes what its `params` collect, so on any params-carrying action that sentence describes a strict subset of what the action writes — an Undo built to it restores part of the change and reports the action as undone. The executor already captures the union: `executeDeclarativeUpdateAction` keys `undoData` off `Object.keys(data)`, `data` being the merged write bag `{ ...patch, ...params }` from `declarativeUpdateWrite`. The doc block ~200 lines above in the same file already said "exactly the fields written". The sentence was the outlier; this moves it. `operation` carries exactly one member, `'update'`, so the per-operation capture rule is a one-row rule and is stated as one. Claude-Session: https://claude.ai/code/session_01LvwGppdonww4zGLWZo5rho Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1739f71 commit 28fafd8

1 file changed

Lines changed: 11 additions & 4 deletions

File tree

‎packages/spec/src/ui/action.zod.ts‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,10 +1307,17 @@ const actionObject = () => strictObject({
13071307
// Single-record update actions only. When true, the runtime captures the
13081308
// record's prior field values and offers an "Undo" affordance on the success
13091309
// toast (backed by the client UndoManager) to restore them. `operation:
1310-
// 'update'` is the DECLARED form of such an action (#14092): its `patch`
1311-
// names exactly the fields written, so the capture is exact rather than
1312-
// inferred from a handler's side effects — the anchor this flag lacked.
1313-
undoable: z.boolean().optional().describe("Offer an Undo affordance after this single-record update action succeeds. `operation: 'update'` is the declared form of that action — its `patch` names exactly the fields whose prior values are captured."),
1310+
// 'update'` is the DECLARED form of such an action (#14092), and it is the
1311+
// ONE member the operation enum carries — so there is one capture rule, not
1312+
// one per operation. The set is the write bag `{ ...patch, ...params }`
1313+
// (contract point 4, `patch` UNDER `params`), which is what the executor
1314+
// reads back: `executeDeclarativeUpdateAction` keys `undoData` off
1315+
// `Object.keys(data)`, `data` being that merged bag — NOT `patch` alone.
1316+
// Naming `patch` alone here is what let a consumer build a half-restore and
1317+
// report it as a full undo. Without `operation` nothing declares a write
1318+
// set, so the capture is inferred from a handler's side effects — the
1319+
// un-anchored case this flag started in.
1320+
undoable: z.boolean().optional().describe("Offer an Undo affordance after this single-record update action succeeds. `operation: 'update'` is the one declared operation and the declared form of that action: what the undo captures is the prior value of EVERY field the action writes — the merged write bag, `patch` UNDER the collected `params`, not `patch` alone. An action with no `operation` declares no write set, so nothing anchors the capture there."),
13141321

13151322
/**
13161323
* Result Dialog — describe how to render the API response on success.

0 commit comments

Comments
 (0)