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
{{ message }}
This repository was archived by the owner on Oct 26, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Oct 26, 2026. It is now read-only.
action rows: follow @objectstack/spec retiring a page action row's own patch — the Undo snapshot must read the named action's patch, and the renderers and registry stop forwarding and publishing the row key #13022
Filing gate ①: a follow-up that landed spec work owes (objectstack-ai/objectstack#24394, PR objectstack-ai/objectstack#24442). The reach is named code at the console pin and a pin test that reds on adoption. Filed by objectstack domain:spec seat 1 (seat post objectstack-ai/objectstack#6017) · os-warren · session session_01WnW7HvfnfHbLg1fpkHX3Mu. ⛔ Not a claim; grade and lane are this repository's triage.
Reader who acts: this repository's triage routes it to the seat that owns packages/core actions and the action renderers. It becomes due when objectui moves to an @objectstack/spec carrying objectstack PR #24442.
What changed upstream
@objectstack/spec 18 retires a page action row's own patch (action:button, action:icon, and the action:group / action:menu member) with a retiredKey() tombstone. A row's operation: 'update' runs the declared action its name names, and the platform action route writes THAT action's patch. The row's patch was never sent; this console read it only for its Undo snapshot. A row with operation: 'update' now also needs a name.
What objectui still does (read at the objectstack .objectui-sha pin 0707b4ae37)
Undo snapshots the wrong fields.packages/core/src/utils/predicate-fields.ts:155 (undoableWrittenKeys) reads the row's patch. After the retirement no row carries one, so a row's snapshot covers only the collected params, not the named action's patch the route wrote. The snapshot must take the declaration's patch (ActionRunner.tsexecuteUpdateOperation, about :1761–:1802).
The four renderers forward the row key. Examples: packages/components/src/renderers/action/action-button.tsx:323 (patch: schema.patch), and the same in action-icon.tsx, action-group.tsx and action-menu.tsx.
The registry publishes a patch input on action:button / action:icon. objectstack's tracked sdui.manifest.json carries it, so its html-tier JSX gate still accepts a JSX patch until the pin bump regenerates the manifest.
A pin reds on adoption.apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:5239 ("the harvested tombstoned keys are recognised, not exempted — and not published either") will red once objectui adopts a spec with this tombstone while the registry still publishes the input.
Acceptance
Undo on an operation: 'update' row snapshots the fields the route writes: the named action's patch, plus the collected params.
No renderer forwards a row patch, and the registry publishes no patch input on the rows; the parity pin passes on the adopted spec.
Claim: PM loop round 9 · on the maintainer's order, verbatim 「13022 12971 两个p1 插队,并发临时调到3,这两个处理完再下班」 (priority:p1; triage 6260698656)
Session: session_0188TsgPV8kQXfuRuKmkj2ks
Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-13022-action-row-patch
Worktree: objectui-issue-13022
Domain: domain:ui
Seat: domain:ui#1
File surface:
packages/core/src/utils/predicate-fields.ts (undoableWrittenKeys) and packages/core/src/actions/ActionRunner.ts (executeUpdateOperation): the Undo snapshot reads the named action's patch, plus the collected params;
the patch forwarding in packages/components/src/renderers/action/action-button.tsx, action-icon.tsx, action-group.tsx and action-menu.tsx, and the tests that pin it (action-forward-precedence.test.tsx among them);
one changeset.
⛔ Not in scope: the inputs blocks of action-button.tsx and action-icon.tsx (the published patch input), and registry-inputs-spec-parity.test.ts.
At the current pin the spec still declares the row patch, so the parity test's REVERSE direction requires the input.
Removing it is pin-coupled, and it rides objectui#12971 (dispatched in parallel on claude/issue-12971-pin-bump-main).
Stop on breach; explain in the report.
Container & model: S, mode:subagent, model: opus
Clause-②: no
Clause-② reason: no published key, input or schema moves here. The runner stopped posting a row patch in objectui#12941. This changes which fields the Undo snapshot captures, and drops a forwarded value no reader uses.
Responsibility: the Undo snapshot regression traces to objectstack PR #24442 retiring the row patch, together with undoableWrittenKeys reading the row and not the declaration.
Thread-read: 6260698656
Serial constraints cleared: read at objectui main47508f3b.
No open PR touches this surface.
action-button.tsx and action-icon.tsx are shared with objectui#12971 (its hunks are the inputs blocks, these are the forwarding lines).
Landing order: objectui#12971 lands first. This branch then merges main, re-runs its gates, and goes ready, so that both halves of this card's acceptance are on main when it closes.
{
"issue": 13022,
"status": "done",
"branch": "claude/issue-13022-action-row-patch",
"pr": "https://github.com/objectstack-ai/objectui/pull/13033",
"session": "session_0188TsgPV8kQXfuRuKmkj2ks (parent's, mode:subagent)",
"premise_still_valid": true,
"summary": "The Undo snapshot of an operation: 'update' action now covers the patch of the declaration the action's name names, plus the collected params. The runner never covers a row's own patch. ActionRunner gains declaredUpdatePatch. It reads the declaration from the objectActions the host publishes beside objectName and objectFields, scoped to the context's own object. When no list was published for that object, or the list does not name the action, it reads the def's own patch: the spread-based hosts hand the declaration in whole. useConsoleActionRuntime and RecordDetailView now publish objectActions: objectDef.actions. action:button, action:icon, action:group and action:menu no longer forward patch; the request body is unchanged. check-action-forward-parity gets four JUSTIFIED entries, because the runner still reads action.patch off whole-dispatched declarations. Zone 2 item 2: the declaration was NOT in hand at the capture site. action:bar draws declarations through these same renderers, so dropping the forward needed the context thread. The call graph is host objectDef.actions, then the ActionProvider context, then new ActionRunner(context), then execute, then executeUpdateOperation, then declaredUpdatePatch, next to captureUpdateUndoData. Zone 1 premise, partly false: predicate-fields undoableWrittenKeys never reads a page row. Its inputs (rowActionDefs, bulkActionDefs, objectActions) are definitions applied whole, so only its doc changed. Item 3: a nameless update row is refused by createServerActionHandler before any request ('Action has no name — a server action must declare one', 0 requests, error toast, no Undo); unchanged. Draft PR #13033, assigned to sunhaolin. Per the dispatch it goes ready only after objectui#12971 lands and this branch merges main.",
"tests": "Union after the final commit, HEAD a5cd587d (declared narrowing, run outside the verify lock: see deviations). pnpm exec vitest run --maxWorkers=2 over packages/core/src/actions/, predicate-fields.test.ts, packages/components/src/renderers/action/, all 12 useConsoleActionRuntime tests, 9 RecordDetailView action/undo tests, AppContent.undoFallbackObject-11080 and AppContent.undoRedoToast-11080, plugin-grid undoProjectedRow-10404, plugin-form affordanceGrantMap-12082, scripts/__tests__/check-action-forward-parity.test.ts and the -closing-keyword suite, and apps/console registry-inputs-spec-parity, public-block-binding-reach and public-contract: 'Test Files 102 passed (102) / Tests 1761 passed (1761)', exit 0. New pins: ActionRunner.declaredPatch-13022.test.ts (core, 7) and action-row-declared-patch-13022.test.tsx (components, 9; each of the four surfaces hands the runner operation and no patch, and the request body is the same with or without a row patch). Ablation 1, the snapshot change, on committed 9f41095f via objectstack scripts/ablation-replace.mjs (WRAP mode): anchor 'const patch = declaredUpdatePatch(action, this.context, objectName);' became 'const patch = patchMembers(action.patch);'. On disk: anchor x1 to x0, replacement x0 to x1, blob 79aef211708b to 1cb9d3ac99ea. Predicted red, observed red: 6 failed / 10 passed of 16, exactly the 3 core and 3 components Undo cases. Restore: blob 79aef211708b equals HEAD, git diff HEAD empty, rerun 16/16 passed. Ablation 2, the renderer forward: patch: schema.patch put back into action:button. The first attempt was refused by the tool (the replacement contained the anchor; no command ran; restored), and the re-run used a two-line anchor. On disk: anchor x1 to x0, blob 70dbbd6e83d5 to c825cec7358a. Observed red: 3 failed / 14 passed of 17 (the two button key-order pins and the button no-patch pin). The request-body pin stayed GREEN with the forward put back, which proves Zone 2 item 1 (no request byte moves). Restore: blob equals HEAD, git diff HEAD empty. No dist leg: the tests import ../ActionRunner relatively, and vitest aliases @object-ui/core to src.",
"gates": "Local, every exit code captured before any pipe, on the final code. check:action-forward-parity=0 (26 justified omissions, 0 known gaps; it was 1 before the JUSTIFIED entries, naming patch on all four surfaces). check:new-line-citations=0 (VERDICT 0 new citations). check:test-path-roots=0. check:control-bytes=0. check:changeset-claims=0. changeset:check=0. node scripts/check-changeset-presence.mjs=0 (13 source files of 3 released packages, 1 changeset). check:handler-key-reads=0. check:pending-changeset-literals=0. check:spec-symbols=0. check:doc-fences=0. check:doc-json=0. check:doc-types=0. check:doc-example-ids=0. node scripts/check-governed-queue-guard.mjs --test (17 paths)=0, NOT GOVERNED. eslint as the package lint runs it, on the changed files: 0 errors, no warning in the new files and no added any. With --no-inline-config there are 6 pre-existing react-hooks/static-components errors on untouched lines, which the inline disables cover. Type-check: @object-ui/core type-check=0 (both programs; the --listFilesOnly count for the new pin is 1; it needed @object-ui/types built). @object-ui/components type-check=0 (--listFilesOnly lists all 4 changed test files; it needed the closure built: types, core, i18n, react, react-runtime, sdui-parser, data-objectstack). The README snippet compiles under tsc --strict --noUnusedLocals against core's built types (exit 0; the typo control exits 2). NOT MEASURED, reason: unbuilt closure (about 30 packages) and the lock was held for over an hour. That covers the @object-ui/app-shell type-check, whose diff is two context spreads. It also covers check:doc-snippets / check:doc-examples / check:readme-exports, which exited 2 'could not run' on the unbuilt tree, and their CI job builds the closure. objectui has no dispatch-gates.mjs, so this list was derived by hand from package.json against the diff, with no --ran reconciliation. PR #13033 check-runs on a5cd587d at report time, 35 in all. completed success: Changeset Bump Policy, Changeset Fixed Group Check, label. skipped: Test (coverage), Test (coverage shard N/8), dependabot. in_progress: Build & E2E, Build Docs, Bundle Analysis, Changeset Claim Re-read, Changeset Declaration, Changeset Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, Lint, Live E2E (informational), Repo Gates, Test (dist pins), Test (shard 1/16 .. 16/16), Type Check. NOT MEASURED: the in_progress CI verdicts, because convergence belongs to the seat.",
"mcp_calls": "0",
"api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches, via scripts/pm/with-fleet.sh --via dispatch and via post-stamped.mjs), landing 3 platform writes as objectstack-fleet[bot]. pr_create: POST /repos/objectstack-ai/objectui/pulls (draft) plus POST /repos/objectstack-ai/objectui/issues/13033/assignees [sunhaolin]; read back as 11918 bytes sent = stored, identical to the end, draft true, assignee sunhaolin. This report: POST /repos/objectstack-ai/objectui/issues/13022/comments. Plus 3 git pushes (empty probe branch, implementation 9f41095f, docs a5cd587d), which are not REST. No label writes; the labels on #13033 are labeler.yml's path labels.",
"open_questions": [
{
"question": "Residual direction. On a host that publishes no declarations for the dispatch's object, a row carries no patch, so its Undo snapshot is params-only. Two cases reach it: PageView's runtime binds no objectName, and a row whose objectName is another object. If the named declaration has a patch, that restore is partial. Keep it, refuse Undo there, or widen the lookup?",
"options": [
"A (this PR): published list first, else the def's own patch, else params-only, as today. No new surface.",
"B: when no list is published for the object and the def carries no patch, offer no Undo. This closes the partial-restore hazard, but also drops Undo for every whole-dispatched param-only action on such hosts (related-list rows, hosts outside the console).",
"C: resolve the declaration by the dispatch's object from every object the host holds (PageView passes objects), mirroring the route exactly. This is the widest fix, and it adds a host-to-runner lookup surface."
],
"recommendation": "A, on the four axes. Business need, measured: no example app or producer authors an undoable update row on a page without a bound object, and the console record page, ObjectView and DeclaredActionsBar all publish. Long term: A keeps one documented two-source rule, while C is the faithful end state if a producer appears. Against AI mistakes: B is stricter, but it silences a working param-only Undo on hosts that dispatch whole declarations. Startup focus: A adds no surface. Revisit as C when a row with an undoable declaration is measured on an object-less page."
}
],
"out_of_scope_findings": [
"carrier: objectui#12971 · noted, not filed. Between this PR and the pin bump, action:button / action:icon still publish a patch input (action-button-icon-inputs-11168 LEAF_KEYS, registry-inputs-spec-parity) that the block no longer forwards. The spec at next.3 still declares it, and the bump removes it. That half was left untouched per Zone 1 item 3."
],
"deviations": [
"The verify lock was held by objectui#12971's measure.sh (pid 3984: a repo-wide pnpm type-check plus a full vitest run at maxWorkers 3) for over an hour. Three attempts under OS_VERIFY_LOCK_SLOT=issue-13022-dev ended in VERDICT queue-timeout (exit 99). The holder was alive and progressing, not stuck. Per the queue clause, the checks were narrowed to the targeted files and builds listed under tests and gates and run outside the lock. The slot stays parked; it blocks nobody.",
"The file surface is wider than the claim's list. packages/app-shell useConsoleActionRuntime.tsx and RecordDetailView.tsx carry the Zone 2 item 2 thread (objectActions in the runner context). scripts/check-action-forward-parity.mjs gained four JUSTIFIED entries, without which the gate reds on the ruled removal. packages/core/README.md and content/docs/guide/layout.md were updated per AGENTS.md #2. The changeset lists @object-ui/app-shell beside core and components.",
"action-button-icon-inputs-11168.test.tsx: only its 'operation: update' case changed, because it pinned the forwarded patch. Its LEAF_KEYS input list was not touched (Zone 1 item 3).",
"Zone 1 item 1 named predicate-fields undoableWrittenKeys as reading the row. As measured it reads declarations only, so it got a doc correction and no code change.",
"Zone 2 item 3 was measured with a throwaway vitest probe file in the worktree. It was deleted before any commit, and git status was clean.",
"Commit trailers use objectui AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude), not the harness reminder's model-named line. The PR footer is the session-URL form.",
"Cleanup: the worktree node_modules was removed and git worktree remove succeeded, without --force, after confirming local HEAD = remote HEAD a5cd587d."
],
"files_changed": [
".changeset/13022-action-row-declared-patch.md",
"packages/core/src/actions/ActionRunner.ts",
"packages/core/src/actions/__tests__/ActionRunner.declaredPatch-13022.test.ts",
"packages/core/src/utils/predicate-fields.ts",
"packages/core/README.md",
"packages/components/src/renderers/action/action-button.tsx",
"packages/components/src/renderers/action/action-icon.tsx",
"packages/components/src/renderers/action/action-group.tsx",
"packages/components/src/renderers/action/action-menu.tsx",
"packages/components/src/renderers/action/__tests__/action-row-declared-patch-13022.test.tsx",
"packages/components/src/renderers/action/__tests__/action-forward-precedence.test.tsx",
"packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx",
"packages/components/src/renderers/action/__tests__/action-button-undoable-11168.test.tsx",
"packages/app-shell/src/hooks/useConsoleActionRuntime.tsx",
"packages/app-shell/src/views/RecordDetailView.tsx",
"scripts/check-action-forward-parity.mjs",
"content/docs/guide/layout.md"
]
}
PR #13033 merged through objectui's merge queue as 61ed5ada68. Its one parent is fe11eb12e5, and the merge is an ancestor of origin/main.
probe
value
merge diff vs the reviewed diff (git patch-id --stable)
identical to the ACCEPT head a5cd587dc1 (66181fe9; 17 files, +688 / −60)
a model name in the merge commit's message
0
CI on a5cd587dc1
green: 33 success, 3 skipped
State change:
Fixes #13022 closed this card completed, and this act removes pm:dispatched.
An operation: 'update' action's Undo now covers the patch of the declaration its name names, read from the objectActions the console and the record page publish, plus the collected params. A page row's own patch is never read.
action:button, action:icon, action:group and action:menu no longer forward patch. The request body is unchanged.
The other half of this card's acceptance is not landed yet.action:button and action:icon still publish the patch input, because the spec at the current pin still declares it. Removing it lands in objectui#12971's PR #13043, together with the pin that retires the key. That PR merges main after this landing (decision D3 = A, 6267645900; landing-order amendment on PR #13033).
Activity
objectstack-fleet commentedon Oct 19, 2026
Triage: p1 +
target:v18·domain:ui·area:api·pm:queue· lands with #12971 · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-19T05:55ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
origin/main:packages/core/src/utils/predicate-fields.ts:153–:155(undoableWrittenKeys) readsdef.patch;packages/components/src/renderers/action/action-button.tsx:323forwardspatch: schema.patch.The premise holds. The spec half is on objectstack
mainas69f342b924(PR feat(spec)!: retire a page action row's own patch — a row's operation: 'update' runs the declared action its name names (#24394) objectstack#24442).target:v18: a consumer that must follow a spec narrowing in the 18 window. Undo would otherwise snapshot the wrong fields..objectstack-shafrom the next.3 release commit to an objectstackmaincommit that carries 586705d85d, 1e001c7df and e15897c0f1, and follow the contract changes since next.3 (the first pin bump under ruling B on objectui#12955) #12971. Any pin bump to the newest objectstackmain(which carries69f342b924) redsregistry-inputs-spec-parity.test.tsuntil this card's change is in. So it lands in deps(pin): move.objectstack-shafrom the next.3 release commit to an objectstackmaincommit that carries 586705d85d, 1e001c7df and e15897c0f1, and follow the contract changes since next.3 (the first pin bump under ruling B on objectui#12955) #12971's PR, or in the PR straight after it on the same pin. deps(pin): move.objectstack-shafrom the next.3 release commit to an objectstackmaincommit that carries 586705d85d, 1e001c7df and e15897c0f1, and follow the contract changes since next.3 (the first pin bump under ruling B on objectui#12955) #12971's Done-when 2 ("follow every contract change since next.3") already covers it.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Claim: PM loop round 9 · on the maintainer's order, verbatim 「13022 12971 两个p1 插队,并发临时调到3,这两个处理完再下班」 (
priority:p1; triage6260698656)Session:
session_0188TsgPV8kQXfuRuKmkj2ksAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-13022-action-row-patchWorktree:
objectui-issue-13022Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/core/src/utils/predicate-fields.ts(undoableWrittenKeys) andpackages/core/src/actions/ActionRunner.ts(executeUpdateOperation): the Undo snapshot reads the named action'spatch, plus the collected params;patchforwarding inpackages/components/src/renderers/action/action-button.tsx,action-icon.tsx,action-group.tsxandaction-menu.tsx, and the tests that pin it (action-forward-precedence.test.tsxamong them);inputsblocks ofaction-button.tsxandaction-icon.tsx(the publishedpatchinput), andregistry-inputs-spec-parity.test.ts.patch, so the parity test's REVERSE direction requires the input.claude/issue-12971-pin-bump-main).Stop on breach; explain in the report.
Container & model:
S,mode:subagent,model: opusClause-②: no
Clause-② reason: no published key, input or schema moves here. The runner stopped posting a row
patchin objectui#12941. This changes which fields the Undo snapshot captures, and drops a forwarded value no reader uses.Responsibility: the Undo snapshot regression traces to objectstack PR #24442 retiring the row
patch, together withundoableWrittenKeysreading the row and not the declaration.Thread-read: 6260698656
Serial constraints cleared: read at objectui
main47508f3b.action-button.tsxandaction-icon.tsxare shared with objectui#12971 (its hunks are theinputsblocks, these are the forwarding lines).main, re-runs its gates, and goes ready, so that both halves of this card's acceptance are onmainwhen it closes.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{ "issue": 13022, "status": "done", "branch": "claude/issue-13022-action-row-patch", "pr": "https://github.com/objectstack-ai/objectui/pull/13033", "session": "session_0188TsgPV8kQXfuRuKmkj2ks (parent's, mode:subagent)", "premise_still_valid": true, "summary": "The Undo snapshot of an operation: 'update' action now covers the patch of the declaration the action's name names, plus the collected params. The runner never covers a row's own patch. ActionRunner gains declaredUpdatePatch. It reads the declaration from the objectActions the host publishes beside objectName and objectFields, scoped to the context's own object. When no list was published for that object, or the list does not name the action, it reads the def's own patch: the spread-based hosts hand the declaration in whole. useConsoleActionRuntime and RecordDetailView now publish objectActions: objectDef.actions. action:button, action:icon, action:group and action:menu no longer forward patch; the request body is unchanged. check-action-forward-parity gets four JUSTIFIED entries, because the runner still reads action.patch off whole-dispatched declarations. Zone 2 item 2: the declaration was NOT in hand at the capture site. action:bar draws declarations through these same renderers, so dropping the forward needed the context thread. The call graph is host objectDef.actions, then the ActionProvider context, then new ActionRunner(context), then execute, then executeUpdateOperation, then declaredUpdatePatch, next to captureUpdateUndoData. Zone 1 premise, partly false: predicate-fields undoableWrittenKeys never reads a page row. Its inputs (rowActionDefs, bulkActionDefs, objectActions) are definitions applied whole, so only its doc changed. Item 3: a nameless update row is refused by createServerActionHandler before any request ('Action has no name — a server action must declare one', 0 requests, error toast, no Undo); unchanged. Draft PR #13033, assigned to sunhaolin. Per the dispatch it goes ready only after objectui#12971 lands and this branch merges main.", "tests": "Union after the final commit, HEAD a5cd587d (declared narrowing, run outside the verify lock: see deviations). pnpm exec vitest run --maxWorkers=2 over packages/core/src/actions/, predicate-fields.test.ts, packages/components/src/renderers/action/, all 12 useConsoleActionRuntime tests, 9 RecordDetailView action/undo tests, AppContent.undoFallbackObject-11080 and AppContent.undoRedoToast-11080, plugin-grid undoProjectedRow-10404, plugin-form affordanceGrantMap-12082, scripts/__tests__/check-action-forward-parity.test.ts and the -closing-keyword suite, and apps/console registry-inputs-spec-parity, public-block-binding-reach and public-contract: 'Test Files 102 passed (102) / Tests 1761 passed (1761)', exit 0. New pins: ActionRunner.declaredPatch-13022.test.ts (core, 7) and action-row-declared-patch-13022.test.tsx (components, 9; each of the four surfaces hands the runner operation and no patch, and the request body is the same with or without a row patch). Ablation 1, the snapshot change, on committed 9f41095f via objectstack scripts/ablation-replace.mjs (WRAP mode): anchor 'const patch = declaredUpdatePatch(action, this.context, objectName);' became 'const patch = patchMembers(action.patch);'. On disk: anchor x1 to x0, replacement x0 to x1, blob 79aef211708b to 1cb9d3ac99ea. Predicted red, observed red: 6 failed / 10 passed of 16, exactly the 3 core and 3 components Undo cases. Restore: blob 79aef211708b equals HEAD, git diff HEAD empty, rerun 16/16 passed. Ablation 2, the renderer forward: patch: schema.patch put back into action:button. The first attempt was refused by the tool (the replacement contained the anchor; no command ran; restored), and the re-run used a two-line anchor. On disk: anchor x1 to x0, blob 70dbbd6e83d5 to c825cec7358a. Observed red: 3 failed / 14 passed of 17 (the two button key-order pins and the button no-patch pin). The request-body pin stayed GREEN with the forward put back, which proves Zone 2 item 1 (no request byte moves). Restore: blob equals HEAD, git diff HEAD empty. No dist leg: the tests import ../ActionRunner relatively, and vitest aliases @object-ui/core to src.", "gates": "Local, every exit code captured before any pipe, on the final code. check:action-forward-parity=0 (26 justified omissions, 0 known gaps; it was 1 before the JUSTIFIED entries, naming patch on all four surfaces). check:new-line-citations=0 (VERDICT 0 new citations). check:test-path-roots=0. check:control-bytes=0. check:changeset-claims=0. changeset:check=0. node scripts/check-changeset-presence.mjs=0 (13 source files of 3 released packages, 1 changeset). check:handler-key-reads=0. check:pending-changeset-literals=0. check:spec-symbols=0. check:doc-fences=0. check:doc-json=0. check:doc-types=0. check:doc-example-ids=0. node scripts/check-governed-queue-guard.mjs --test (17 paths)=0, NOT GOVERNED. eslint as the package lint runs it, on the changed files: 0 errors, no warning in the new files and no added any. With --no-inline-config there are 6 pre-existing react-hooks/static-components errors on untouched lines, which the inline disables cover. Type-check: @object-ui/core type-check=0 (both programs; the --listFilesOnly count for the new pin is 1; it needed @object-ui/types built). @object-ui/components type-check=0 (--listFilesOnly lists all 4 changed test files; it needed the closure built: types, core, i18n, react, react-runtime, sdui-parser, data-objectstack). The README snippet compiles under tsc --strict --noUnusedLocals against core's built types (exit 0; the typo control exits 2). NOT MEASURED, reason: unbuilt closure (about 30 packages) and the lock was held for over an hour. That covers the @object-ui/app-shell type-check, whose diff is two context spreads. It also covers check:doc-snippets / check:doc-examples / check:readme-exports, which exited 2 'could not run' on the unbuilt tree, and their CI job builds the closure. objectui has no dispatch-gates.mjs, so this list was derived by hand from package.json against the diff, with no --ran reconciliation. PR #13033 check-runs on a5cd587d at report time, 35 in all. completed success: Changeset Bump Policy, Changeset Fixed Group Check, label. skipped: Test (coverage), Test (coverage shard N/8), dependabot. in_progress: Build & E2E, Build Docs, Bundle Analysis, Changeset Claim Re-read, Changeset Declaration, Changeset Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, Lint, Live E2E (informational), Repo Gates, Test (dist pins), Test (shard 1/16 .. 16/16), Type Check. NOT MEASURED: the in_progress CI verdicts, because convergence belongs to the seat.", "mcp_calls": "0", "api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches, via scripts/pm/with-fleet.sh --via dispatch and via post-stamped.mjs), landing 3 platform writes as objectstack-fleet[bot]. pr_create: POST /repos/objectstack-ai/objectui/pulls (draft) plus POST /repos/objectstack-ai/objectui/issues/13033/assignees [sunhaolin]; read back as 11918 bytes sent = stored, identical to the end, draft true, assignee sunhaolin. This report: POST /repos/objectstack-ai/objectui/issues/13022/comments. Plus 3 git pushes (empty probe branch, implementation 9f41095f, docs a5cd587d), which are not REST. No label writes; the labels on #13033 are labeler.yml's path labels.", "open_questions": [ { "question": "Residual direction. On a host that publishes no declarations for the dispatch's object, a row carries no patch, so its Undo snapshot is params-only. Two cases reach it: PageView's runtime binds no objectName, and a row whose objectName is another object. If the named declaration has a patch, that restore is partial. Keep it, refuse Undo there, or widen the lookup?", "options": [ "A (this PR): published list first, else the def's own patch, else params-only, as today. No new surface.", "B: when no list is published for the object and the def carries no patch, offer no Undo. This closes the partial-restore hazard, but also drops Undo for every whole-dispatched param-only action on such hosts (related-list rows, hosts outside the console).", "C: resolve the declaration by the dispatch's object from every object the host holds (PageView passes objects), mirroring the route exactly. This is the widest fix, and it adds a host-to-runner lookup surface." ], "recommendation": "A, on the four axes. Business need, measured: no example app or producer authors an undoable update row on a page without a bound object, and the console record page, ObjectView and DeclaredActionsBar all publish. Long term: A keeps one documented two-source rule, while C is the faithful end state if a producer appears. Against AI mistakes: B is stricter, but it silences a working param-only Undo on hosts that dispatch whole declarations. Startup focus: A adds no surface. Revisit as C when a row with an undoable declaration is measured on an object-less page." } ], "out_of_scope_findings": [ "carrier: objectui#12971 · noted, not filed. Between this PR and the pin bump, action:button / action:icon still publish a patch input (action-button-icon-inputs-11168 LEAF_KEYS, registry-inputs-spec-parity) that the block no longer forwards. The spec at next.3 still declares it, and the bump removes it. That half was left untouched per Zone 1 item 3." ], "deviations": [ "The verify lock was held by objectui#12971's measure.sh (pid 3984: a repo-wide pnpm type-check plus a full vitest run at maxWorkers 3) for over an hour. Three attempts under OS_VERIFY_LOCK_SLOT=issue-13022-dev ended in VERDICT queue-timeout (exit 99). The holder was alive and progressing, not stuck. Per the queue clause, the checks were narrowed to the targeted files and builds listed under tests and gates and run outside the lock. The slot stays parked; it blocks nobody.", "The file surface is wider than the claim's list. packages/app-shell useConsoleActionRuntime.tsx and RecordDetailView.tsx carry the Zone 2 item 2 thread (objectActions in the runner context). scripts/check-action-forward-parity.mjs gained four JUSTIFIED entries, without which the gate reds on the ruled removal. packages/core/README.md and content/docs/guide/layout.md were updated per AGENTS.md #2. The changeset lists @object-ui/app-shell beside core and components.", "action-button-icon-inputs-11168.test.tsx: only its 'operation: update' case changed, because it pinned the forwarded patch. Its LEAF_KEYS input list was not touched (Zone 1 item 3).", "Zone 1 item 1 named predicate-fields undoableWrittenKeys as reading the row. As measured it reads declarations only, so it got a doc correction and no code change.", "Zone 2 item 3 was measured with a throwaway vitest probe file in the worktree. It was deleted before any commit, and git status was clean.", "Commit trailers use objectui AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude), not the harness reminder's model-named line. The PR footer is the session-URL form.", "Cleanup: the worktree node_modules was removed and git worktree remove succeeded, without --force, after confirming local HEAD = remote HEAD a5cd587d." ], "files_changed": [ ".changeset/13022-action-row-declared-patch.md", "packages/core/src/actions/ActionRunner.ts", "packages/core/src/actions/__tests__/ActionRunner.declaredPatch-13022.test.ts", "packages/core/src/utils/predicate-fields.ts", "packages/core/README.md", "packages/components/src/renderers/action/action-button.tsx", "packages/components/src/renderers/action/action-icon.tsx", "packages/components/src/renderers/action/action-group.tsx", "packages/components/src/renderers/action/action-menu.tsx", "packages/components/src/renderers/action/__tests__/action-row-declared-patch-13022.test.tsx", "packages/components/src/renderers/action/__tests__/action-forward-precedence.test.tsx", "packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx", "packages/components/src/renderers/action/__tests__/action-button-undoable-11168.test.tsx", "packages/app-shell/src/hooks/useConsoleActionRuntime.tsx", "packages/app-shell/src/views/RecordDetailView.tsx", "scripts/check-action-forward-parity.mjs", "content/docs/guide/layout.md" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Landed ·
domain:uiseat 1 ·session_0188TsgPV8kQXfuRuKmkj2ks· 2026-10-19T13:06ZPR #13033 merged through objectui's merge queue as
61ed5ada68. Its one parent isfe11eb12e5, and the merge is an ancestor oforigin/main.git patch-id --stable)a5cd587dc1(66181fe9; 17 files, +688 / −60)a5cd587dc1State change:
Fixes #13022closed this cardcompleted, and this act removespm:dispatched.operation: 'update'action's Undo now covers thepatchof the declaration itsnamenames, read from theobjectActionsthe console and the record page publish, plus the collected params. A page row's ownpatchis never read.action:button,action:icon,action:groupandaction:menuno longer forwardpatch. The request body is unchanged.The other half of this card's acceptance is not landed yet.
action:buttonandaction:iconstill publish thepatchinput, because the spec at the current pin still declares it. Removing it lands in objectui#12971's PR #13043, together with the pin that retires the key. That PR mergesmainafter this landing (decision D3 = A,6267645900; landing-order amendment on PR #13033).Records:
6261988109;6263350045;6263385634(TIER_DEFAULT).Generated by Claude Code
fix(core,components): an update's Undo covers its named declaration's…