Repository navigation
fix(core,components): an update's Undo covers its named declaration's patch, and no action renderer forwards a row patch (objectui#13022) - #13033
Merged
objectstack-fleet[bot] merged 2 commits intoOct 19, 2026
Conversation
… `patch`, and no action renderer forwards a row `patch` (objectui#13022) A page action row's `operation: 'update'` runs the declared action its `name` names; the platform action route writes that declaration's `patch`. `@objectstack/spec` 18 retires the row's own `patch` (objectstack#24442), which reached no request since objectui#12941 and was read only for the Undo snapshot. - ActionRunner: `declaredUpdatePatch` reads the declaration `name` names from the `objectActions` the host publishes beside `objectName`, else the def's own `patch` (a host that dispatches the declaration whole). - useConsoleActionRuntime and RecordDetailView publish `objectActions`. - action:button / icon / group / menu stop forwarding `patch`. - check-action-forward-parity: JUSTIFIED entries for the four omissions. Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
…e host's `objectActions` (objectui#13022) Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
席位核对 — ACCEPT ·
|
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contributor
Author
|
Landing order amended · This amends item 9 of the ACCEPT
Generated by Claude Code |
sunhaolin
pushed a commit
that referenced
this pull request
Oct 19, 2026
Brings in objectui#13022 (PR #13033), which landed first by the seat's decision D3 = A on objectui#12971: the Undo snapshot reads the named declaration's patch and no action renderer forwards a row patch. The action renderers merged cleanly: this branch owns the inputs blocks, #13033 the forwarding lines, and both are kept. Claude-Session: https://claude.ai/code/session_0188TsgPV8kQXfuRuKmkj2ks Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #13022
Clause-②: no
An
operation: 'update'action's Undo snapshot now covers thepatchof the declared action itsnamenames, plus the collected params. No action renderer forwards a page action row's ownpatchany more.This is the runner-and-renderer half of the card. The published-input half is pin-coupled and rides objectui#12971: the
patchinput thataction:buttonandaction:iconpublish,action-button-icon-inputs-11168.test.tsx's input list, andregistry-inputs-spec-parity.test.ts. At today's pin (@objectstack/spec18.0.0-next.3) the spec still declares the rowpatch, and the parity test's reverse direction requires the input. This PR goes ready only after objectui#12971 lands and this branch has mergedmain.Why
objectstack-ai/objectstack#24442 (
69f342b924) retires a page action row's ownpatchin@objectstack/spec18, onaction:button,action:iconand theaction:group/action:menumember. A row'soperation: 'update'runs the declared action itsnamenames. The platform action route resolves that declaration by name and writes the declaration'spatchunder the params it is sent. The row's ownpatchhas reached no request since objectui#12941 (PR #12944). The runner read it for the Undo snapshot only, so the snapshot covered the wrong fields. Once no row carries apatch, a row's snapshot covered the collected params and missed the fields the route wrote.What changed
packages/core/src/actions/ActionRunner.tsdeclaredUpdatePatch(action, context, objectName). It reads thepatchof the declaration the action'snamenames from theobjectActionsthe host publishes for the dispatch's object (newreadContextObjectActions). It is scoped likereadContextObjectFields: only the context's own object.patch. A host that dispatches an object's action whole hands that in:ObjectGridrows,DeclaredActionsBar,RelatedRecordActionsBridgeandpage:header.executeUpdateOperationbuildswrittenfrom thatpatch, which giveswrittenFieldsandredoData. The request is unchanged:{ ...action, params: { ...collected } }.ActionDef.patchand method docs say what the key is now.useConsoleActionRuntimeandRecordDetailViewpublishobjectActions: objectDef.actionsin the runner context, besideobjectFields.action-button.tsx,action-icon.tsx,action-group.tsxandaction-menu.tsxdroppatch: schema.patch/patch: action.patchand the comments that described the forward.operationis still forwarded.scripts/check-action-forward-parity.mjsgets fourJUSTIFIEDentries,action:button:patchand the three siblings (issue 13022). Without them the gate reds, because the runner still readsaction.patchoff a whole-dispatched declaration. The entry says why the omission is correct.predicate-fields.ts: doc only (see "Premise" below).packages/core/README.mdgains "Which fields anoperation: 'update'Undo covers", which documents the host'sobjectActions.content/docs/guide/layout.mdno longer listspatchamong the forwarded keys and says the node runs its named declaration..changeset/13022-action-row-declared-patch.md: patch for@object-ui/core,@object-ui/componentsand@object-ui/app-shell.Call graph used (dispatch Zone 2, item 2)
The declaration was not in hand at the capture site. A page row reaches
executeUpdateOperationas the def the renderer composes from the node.action:bardraws a declared action through the sameaction:button/action:menurenderers, and a spread-based host hands the declaration itself. The runner context heldobjectNameandobjectFieldsonly.The shortest thread mirrors objectui#11122's
objectFields. The hosts'objectDef.actions(the list the route resolves names against) goes into the context, then throughActionProvidertonew ActionRunner(context), thenexecute, thenexecuteUpdateOperation, thendeclaredUpdatePatch. That puts it next tocaptureUpdateUndoDataat the capture site.Premise (dispatch Zone 1 and Zone 2, measured on
origin/main47508f3b)undoableWrittenKeys(predicate-fields) does not read a row. Its inputs arelistViewPredicates'rowActionDefs(the host-resolved row defs),bulkActionDefsandobjectActions. All of them are definitions their writer applies whole, and no page action row reaches it. So it already reads the declaredpatchthat the$selectharvest owes the row. Only its doc changed, to say so. The runner change reads the same declarations on the console hosts.action-row-declared-patch-13022.test.tsxhas the test "the request a row sends carries no field value, with or without apatchon the row". It asserts{ recordId: 't1', params: {} }through the realcreateServerActionHandler. It stays green with the forward put back (ablation 2 below), so dropping the forward moves no request byte.action-button-undoable-11168"on a record page" pins the same body.operation: 'update'row today. Measured with a throwaway probe through the real runner andcreateServerActionHandler(not committed). The answer is{ success: false, error: 'Action has no name — a server action must declare one' }, with 0 requests and an error toast, and the action never reaches the Undo capture. Unchanged here.Tests (union after the final commit, head
a5cd587d)pnpm exec vitest run --maxWorkers=2overpackages/core/src/actions/,predicate-fields.test.ts,packages/components/src/renderers/action/, everyuseConsoleActionRuntime*test, nineRecordDetailViewaction/undo tests, bothAppContent.undo*tests,plugin-gridundoProjectedRow-10404,plugin-formaffordanceGrantMap-12082, bothcheck-action-forward-parityscript suites andapps/consoleregistry-inputs-spec-parity/public-block-binding-reach/public-contract. Result: 102 files, 1761 tests passed.ActionRunner.declaredPatch-13022.test.ts(core, 7 tests) covers:patchis snapshotted for a row with none;patch;patchgives a params-only snapshot;patchand no list gives no Undo.action-row-declared-patch-13022.test.tsx(components, 9 tests) covers:operationand nopatch;patch;patch, plus a collected param;patchis never covered;action-forward-precedence.test.tsx:patchleaves both key-order lists.action-button-icon-inputs-11168.test.tsx, theoperation: updatecase only: the def carriesoperationand nopatch.action-button-undoable-11168.test.tsx: the host publishes the declarations, and the rows carry nopatch.Ablation (both legs through
objectstack/scripts/ablation-replace.mjs, on committed9f41095f)const patch = declaredUpdatePatch(action, this.context, objectName);becameconst patch = patchMembers(action.patch);, the pre-change read.79aef211708bto1cb9d3ac99ea.79aef211708b, equal to HEAD,git diff HEADempty, and the rerun gave 16/16.patch: schema.patch,put back intoaction:button's forward.70dbbd6e83d5toc825cec7358a.action:buttonkey-order pins and "action:button hands the runneroperationand nopatch". The request-body pin stayed green, which is item 1. "thepatcha row carries itself is never covered" also stayed green, because the published declaration wins in the runner as well.70dbbd6e83d5equals HEAD, andgit diff HEADis empty.No build or
distleg: the tests import../ActionRunnerrelatively, and vitest aliases@object-ui/coretosrc.Gates (local, exit codes captured before any pipe)
check:action-forward-parity0: 26 justified omissions, 0 known gaps.check:new-line-citations0 (0 new citations).check:test-path-roots0.check:control-bytes0.check:changeset-claims0.changeset:check0.node scripts/check-changeset-presence.mjs0: 13 source files of 3 released packages, 1 changeset.check:handler-key-reads0.check:pending-changeset-literals0.check:spec-symbols0.check:doc-fences0,check:doc-json0,check:doc-types0,check:doc-example-ids0.node scripts/check-governed-queue-guard.mjs --teston the diff: NOT GOVERNED.eslinton the changed files, as the packagelintruns it: 0 errors, and no warning in the new files.tsctype-check:pnpm --filter @object-ui/core type-check0, with both programs.tsconfig.test.json --listFilesOnlylists the new pin.@object-ui/componentstype-check0.--listFilesOnlylists all 4 changed test files.--strict --noUnusedLocalsagainst core's built types. Control: a typo reds it.Not measured locally, declared to CI:
@object-ui/app-shelltype-check, because its ~30-package closure was not built. Its diff is two context spreads.check:doc-snippets,check:doc-examplesandcheck:readme-exports, which answered "could not run" on an unbuilt tree. Their CI job builds the closure first.The shared verify lock was held for over an hour by another seat's full-suite measurement. These runs were narrowed to the files above and run outside the lock. The full farm is CI's.
Acceptance notes
The residual direction, not changed here. A host that publishes no declarations for the dispatch's object reads the def's own
patch. A row carries none, so a row on such a host keeps today's params-only snapshot. Two cases reach it:PageView, whose runtime binds noobjectName;objectNamenames an object other than the host's.Both console record surfaces,
ObjectViewandDeclaredActionsBarpublish. Making "no declaration found" refuse Undo instead would also refuse it for every whole-dispatched param-only action on such a host. The seat may want that ruled; it is in the report.Between this PR and objectui#12971,
action:button/action:iconstill publish apatchinput that the block no longer forwards. The spec at today's pin still declares it, and objectui#12971 removes it with the pin bump.objectActionsridesActionProvider'sJSON.stringify(context)memo key, besideobjectFields.Adds no product-specific widget, dialog, hook or string (see AGENTS.md › Product-neutral renderer)
Generated by Claude Code