Repository navigation
objectui Import Wizard: an import undo that leaves rows unreverted (success: false with failures, after #24380) shows no message, and the job is still listed as undoable #13015
Description
Unblocked: #12971 closed completed when PR #13043 merged as 8aa8e3e1e5 (objectui's pin is now objectstack main f4755a68a797); re-derived with no new blocker (unlock comment below).
Filing gate ①, class (a): a public door's answer is dropped by its UI consumer. The UI shows nothing where the server reports a refusal. reach: every console user whose import undo a control refuses for some rows, once PR objectstack-ai/objectstack#24431 (objectstack-ai/objectstack#24380) lands. Filed by domain:cli seat 1 (seat post objectstack-ai/objectstack#6024) · zhuangjianguo · session session_01SYEJqDL3NXxrJRBVqaMu9T, from objectstack-ai/objectstack#24380's dev report (PR objectstack-ai/objectstack#24431, Acceptance notes). The seat checked both sides in code. ⛔ Not a claim. Lane, grade and repo:* are triage's. The fix lands in objectstack-ai/objectui.
What the server answers (PR objectstack-ai/objectstack#24431, ruling 6257104178 on objectstack-ai/objectstack#24380)
POST /api/v1/data/import/jobs/:jobId/undo runs its reversal writes under the object's declared controls for every caller but the system principal. A row a control refuses is not reverted. The answer is a 200 with success: false, failed: N and a failures array of { id, action, code, error } per unreverted row (UndoImportJobResponseSchema in @objectstack/spec's api/export.zod.ts). The job is not stamped reverted and stays undoable, and undoing it again retries only those rows. The PR's pins (packages/rest/src/import-undo-controls.test.ts) drive this.
What the console does with it
@objectstack/client'sdata.undoImportJobreturns the body unchanged.unwrapResponse(packages/client/src/index.ts, about:7406) unwraps only an envelope that hasdata, and throws on nosuccess: false.- objectui
packages/plugin-grid/src/ImportWizard.tsxuseImportJobUndo(about:1088, at objectuimain) runsawait ds.undoImportJob(jobId); notifyDataChanged(...). It never reads the answer, andonErrorfires only on a throw. - So a partial undo shows nothing. The refreshed list still offers Undo on the job, with no reason and no per-row refusal text. Before security: the import undo door writes with skipAutomations and skipStateMachine for any caller who may undo — what an undo owes an app's controls (detail withheld under the disclosure ruling) objectstack#24380 every undo answered
success: true, so this branch was unreachable.
What done looks like
When the answer has success: false, the wizard says how many rows stayed unreverted and shows each failures[].error. Those are the app's own refusal sentences, for example a hook's. It also says that undoing again retries only those rows. A full undo is unchanged. A test drives a partial answer.
Why it waits on objectstack-ai/objectstack#24380
The answer shape lands with PR objectstack-ai/objectstack#24431. Until then, the server never answers success: false.
Dedupe
- objectstack
search_issues"Import Wizard undo import job result failures ignored partial undo no message": QA run · records-forms.import-job-undo-cancel (1/1) · 55a806a7 · 2026-10-17 · 1 PASS / 0 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN objectstack#24124 (closed, a QA run) and data import undo: 撤销一次「historical」导入无法还原被保留的 readonly / 审计字段 — undo 的 writeCtx 缺 preserveAudit (#3493 follow-up) objectstack#3549 (closed, preserveAudit). - objectui
search_issues"ImportWizard useImportJobUndo undoImportJob answer": console(import): cancelling a background import reports "0 imported" while the server already committed the processed rows — users re-import instead of undoing #11650 (closed, cancel reporting). - None is this defect. The control query reached the undo subject (data import undo: 撤销一次「historical」导入无法还原被保留的 readonly / 审计字段 — undo 的 writeCtx 缺 preserveAudit (#3493 follow-up) objectstack#3549).
- Dedupe words: ImportWizard undo partial failures no message; undoImportJob success false ignored; import undo failures UI.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 19, 2026
Triage: moved from objectstack-ai/objectstack#24433 · p2 ·
bug·domain:ui·area:records·pm:blockedon objectstack-ai/objectstack#24380 · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-19T02:56ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
Blocked-by:names security: the import undo door writes with skipAutomations and skipStateMachine for any caller who may undo — what an undo owes an app's controls (detail withheld under the disclosure ruling) objectstack#24380.origin/main:packages/plugin-grid/src/ImportWizard.tsx:1103runsawait ds.undoImportJob(jobId); notifyDataChanged(…). It never reads the answer, and thecatchfires only on a throw. The premise holds.pm:dispatched), a partial undo shows the user nothing and still offers Undo. No data is wrong.success: falsebefore it. The unlock scan reads theBlocked-by:line.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Unlock:
pm:blocked→pm:queue·domain:uiseat 1 ·session_0188TsgPV8kQXfuRuKmkj2ks· 2026-10-19T14:34Z⛔ Not a claim.
completedwhen PR chore(deps): build @objectstack/* from objectstack main 2ec6827962 and follow its contract (objectui#12971) #13043 merged as8aa8e3e1e5. objectui's.objectstack-shais now objectstackmainf4755a68a797(18.0.0-next.5).f4755a68a797carriesadb7910f81(PR fix(rest)!: an import undo runs under the object's declared controls for every caller but the system principal objectstack#24431,UndoImportJobResponseSchema.failures).Blocked-by:line in the body is replaced with anUnblocked:line in the same act.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Claim: PM loop round 1 (
priority:p2,bug)Session:
session_014MXhk1yjWh37d634hLy3p7Account:
huangyiirene(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-13015-undo-partial-failuresWorktree:
objectui-issue-13015Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/plugin-grid/src/ImportWizard.tsx(useImportJobUndo, about :1088 to :1110 at objectuimainc219165, and the two job lists that call it),packages/types/src/data.ts(ImportJobUndoResultgains the server'sfailures, about :1177), the user-facing strings the wizard adds inpackages/i18n/src/locales/**, tests beside them, and one.changeset/13015-undo-partial-failures.md. A@objectstack/specfloor in the consuming package's manifest and the lockfile specifier, only if the code reads the answer through the spec package. Stop on breach; explain in the report.Container & model:
M,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuiover this surface reads no path-derived mandate)Clause-②: yes
Responsibility: the risk is produced by objectui's own
useImportJobUndo(ImportWizard.tsx:1103), which awaitsds.undoImportJob(jobId)and never reads the answer; the platform path that covers it: none (@objectstack/client'sunwrapResponsereturns the body unchanged and does not throw onsuccess: false, per the card); who reaches it: every console user whose import undo a control refuses for some rows. The server answerssuccess: falsewithfailuressince objectstack-ai/objectstack#24431, which objectui's pinf4755a68a797carries (unlock6269775908).Thread-read: 6269775908
Serial constraints cleared: no open objectui PR touches
ImportWizard.tsx,packages/types/src/data.tsor the locale packs (open PRs #13066 and #13050, file lists read at this claim). No in-flight claim names those files (#13052, #12931, #12932 read). The locale packs are shared files, not single-claim paths: whichever PR lands second mergesmain.Why
Clause-②: yes:ImportJobUndoResultis exported from@object-ui/typesand gainsfailures, and new keys in@object-ui/i18n's packs widen itsTranslationKeys. Both widen published faces.Claimed at: 2026-10-19T18:13Z
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{ "issue": 13015, "status": "done", "branch": "claude/issue-13015-undo-partial-failures", "pr": "https://github.com/objectstack-ai/objectui/pull/13071", "session": "session_014MXhk1yjWh37d634hLy3p7 — the parent's (this run is a subagent; the same id the commits' Claude-Session trailer carries)", "premise_still_valid": true, "summary": "useImportJobUndo (packages/plugin-grid/src/ImportWizard.tsx) now reads the undo answer. On success: false it hands the answer to the list that ran Undo, and both lists (History, cancelled-result screen) render a shared ImportUndoOutcomeNotice. It says how many rows stayed (failed) and that undoing again retries only those rows (new count family grid.import.undoPartial, all ten packs), and lists each distinct failures[].error, capped at 10 distinct with the existing grid.import.moreErrors. Undo stays offered because the re-read job is still undoable; readers still get notifyDataChanged; a full undo is unchanged. ImportJobUndoResult (packages/types/src/data.ts) is now the spec's UndoImportJobResponse by reference instead of a hand copy that lacked failures, and @object-ui/types floors @objectstack/spec at ^18.0.0-next.5 (R, measured). Bounded in-place fix, named in the PR body with a base probe: the History list's reload cleared the error slot an undo wrote to in the same tick, so a thrown undo there also showed nothing; the undo outcome now has its own slot. Premises H1, H2 and H4 held. H2 was measured on the installed client at the pin, so the producer side does not move. H3: derived per the data.ts header convention; check:spec-symbols did not require it.", "files_changed": [ ".changeset/13015-undo-partial-failures.md", "packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts", "packages/plugin-grid/src/ImportWizard.tsx", "packages/plugin-grid/src/ImportWizard.undoPartial-13015.test.tsx", "packages/types/package.json", "packages/types/src/data.ts" ], "diff_lines": "15 files changed, 337 insertions(+), 33 deletions(-) — git diff --shortstat c2191654a..13af833", "tests": "All at 13af833 (source unchanged since 92728ce). New file ImportWizard.undoPartial-13015.test.tsx: 'Tests 4 passed (4)' (History partial: count + retry sentence + both sentences once each + survives the reload + Undo still offered + one data change; History retry: full answer clears it and shows Undone; History throw shows its message after the reload; cancelled-result partial: same, and Undo still offered). pnpm exec vitest run packages/plugin-grid/: 'Test Files 229 passed (229) / Tests 2077 passed (2077)', VERDICT command-exit 0. pnpm exec vitest run packages/i18n/ packages/types/: 'Test Files 472 passed (472) / Tests 11870 passed | 13 skipped (11883)', VERDICT command-exit 0 (the skips are the env-gated describe.runIf(DRIVEN) timezone suites). Ablations, through objectstack scripts/ablation-replace.mjs, no build needed (relative import to src, @object-ui/types aliased to src); each restored with blob == HEAD and an empty git diff HEAD. (1) Deleted the answer read 'if (!answer.success) onOutcome(...)', anchor 1 to 0: '3 failed | 1 passed' (the three partial pins red, the throw pin green). (2) The History reload also clears the undo slot (the base mechanism), anchor 1 to 0: '3 failed | 1 passed' (the three History pins red, the cancelled pin green); its first attempt was refused by the tool before any test ran (the replacement contained the anchor) and was not counted. Base probe (ImportWizard.tsx at c2191654a, temporary test, deleted after): a thrown History undo's message is absent after the reload, '1 failed'. Reverse type check through the rebuilt packages/types/dist: an old-shape literal gives TS2741 'failures' missing, action 'update' gives TS2322; probe removed.", "gates": "Local, worktree at 13af833, exit captured before any pipe. turbo build closure (plugin-grid^..., data-objectstack^..., types; concurrency 2) 0. type-check: @object-ui/types 0, @object-ui/plugin-grid 0 (tsconfig.test.json lists the new test, reads types dist), @object-ui/data-objectstack 0 (the downstream consumer naming ImportJobUndoResult). lint: plugin-grid 0, types 0 (0 errors; the warnings predate this branch). check:i18n-keys 0; check:i18n-drift 0 (0 en values changed, 3 keys added); check:i18n-dead-keys 0 (new keys not listed); check:spec-symbols 0; check:spec-floors 1 whole-repo (14 no-artifact findings for packages outside the built closure), scoped reading @object-ui/types 0 findings at ^18.0.0-next.5; check:new-line-citations 0 ('0 new citation(s)'); check:control-bytes 0; check-changeset-presence 0; check-changeset-no-major 0; check-changeset-fixed 0; check-governed-queue-guard --test 0 (NOT GOVERNED); pnpm install --frozen-lockfile --offline 0 (lockfile unmoved). --ran reconciliation: not applicable, because dispatch-gates.mjs refuses objectui; the list was hand-derived from the brief plus objectui's package.json. CI on PR #13071 at 13af833, snapshot 2026-10-19T18:53Z (total_count 35 = 35 returned): success = Changeset Bump Policy, Changeset Claim Re-read, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, label; skipped = Test (coverage), Test (coverage shard), dependabot; in_progress = Lint, Type Check, Repo Gates, Test (dist pins), Test shards 1-16, Build & E2E, Build Docs, Bundle Analysis, Live E2E (informational). Status: in_progress.", "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "3 REST writes, sent as 2 relay dispatches (each POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): pr_create = POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos/objectstack-ai/objectui/issues/13071/assignees [huangyiirene]; comment = POST /repos/objectstack-ai/objectui/issues/13015/comments (this report). Plus git push x3 of the branch (not REST). Reads over gh api, not writes.", "deviations": [ "Bounded in-place fix beyond the card's literal ask: a thrown undo in the History list was wiped by the list's reload (base probe red). The separate undoOutcome slot the partial notice needs fixes it too. Named in the PR body with evidence; all four conditions hold (same class, mechanical, claimed file, no new gate surface).", "The card says 'shows each failures[].error'. Each DISTINCT sentence is shown once, and past 10 distinct sentences the rest are counted (existing grid.import.moreErrors). Reason: an undoable job holds up to 5000 rows (IMPORT_JOB_UNDO_MAX_ROWS at the pin), and one hook typically refuses many rows with the same sentence. The headline count covers every row.", "Floor: raised only @object-ui/types (the package whose dist binds UndoImportJobResponse). @object-ui/plugin-grid reads failures only through @object-ui/types and keeps ^18.0.0-next.1.", "The types suite ran as one repo-root vitest call over packages/i18n/ and packages/types/ (objectui AGENTS.md's root form; the package script is the same vitest root), not via pnpm --filter test.", "@object-ui/app-shell type-check was not run locally: it names only undoImportJob: undefined, an optional method whose type did not change. Declared to CI (Type Check).", "The plugin-grid suite outran the 10-minute foreground cap and the harness moved that call to the background. I waited on its PID in the foreground (tail --pid) and read the redirected log; nothing was left running.", "The harness attribution reminder asked for a model-named Co-Authored-By. Per objectui AGENTS.md, the commits carry the model-free pair Co-authored-by: Claude + Claude-Session. The PR body uses the session-URL footer form from my definition." ], "open_questions": [], "out_of_scope_findings": [ "carrier: objectui#13065 (queued) · this PR's @object-ui/types floor ^18.0.0-next.5 also clears that card's two pre-existing floor-too-low findings (checkPageMainRegionWidth, checkPageRecordBindingPlacement; next.5's dist/ui/index.d.mts carries checkPageMainRegionWidth, next.2's does not). Scoped check:spec-floors reads 0 for types. The PR body names objectui#13065 with no closing keyword. Noted, not filed.", "carrier: 承接者:无 · docs: no content/docs page and no package README describes import undo (AGENTS.md #2 docs-driven); outside this claim's file surface; recorded in the PR's Acceptance notes. Noted, not filed." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Claim: PM loop round 1 (
priority:p2,bug) · amended 2026-10-19T18:57Z:Clause-②:arm, as measured on PR #13071 at13af833· supersedes claim6274050895on the same session and branchSession:
session_014MXhk1yjWh37d634hLy3p7Account:
huangyiirene(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-13015-undo-partial-failuresWorktree:
objectui-issue-13015Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/plugin-grid/src/ImportWizard.tsx(useImportJobUndo, about :1088 to :1110 at objectuimainc219165, and the two job lists that call it),packages/types/src/data.ts(ImportJobUndoResultgains the server'sfailures, about :1177), the user-facing strings the wizard adds inpackages/i18n/src/locales/**, tests beside them, and one.changeset/13015-undo-partial-failures.md. A@objectstack/specfloor in the consuming package's manifest and the lockfile specifier, only if the code reads the answer through the spec package. Stop on breach; explain in the report.Container & model:
M,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuiover this surface reads no path-derived mandate)Clause-②: yes (narrowing)
Responsibility: the risk is produced by objectui's own
useImportJobUndo(ImportWizard.tsx:1103), which awaitsds.undoImportJob(jobId)and never reads the answer; the platform path that covers it: none (@objectstack/client'sunwrapResponsereturns the body unchanged and does not throw onsuccess: false, per the card); who reaches it: every console user whose import undo a control refuses for some rows. The server answerssuccess: falsewithfailuressince objectstack-ai/objectstack#24431, which objectui's pinf4755a68a797carries (unlock6269775908).Thread-read: 6269775908
Serial constraints cleared: no open objectui PR touches
ImportWizard.tsx,packages/types/src/data.tsor the locale packs (open PRs #13066 and #13050, file lists read at this claim). No in-flight claim names those files (#13052, #12931, #12932 read). The locale packs are shared files, not single-claim paths: whichever PR lands second mergesmain.Why
Clause-②: yes (narrowing): the delivered diff widens@object-ui/i18n'sTranslationKeys(thegrid.import.undoPartialfamily), and it narrows@object-ui/types:ImportJobUndoResultis now the spec'sUndoImportJobResponseby reference, sofailuresis required of a customDataSource'sundoImportJobanswer (compile-breaking for an implementer returning the old shape; the changeset states it). Both facts are true, andclause2-line.mjsspells that caseyes (narrowing). The narrowing face owes aCONTRACT_REVIEW_TIERreview before the queue.Amended at: 2026-10-19T18:57Z
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{ "issue": 13015, "status": "done", "round": "patch round 1 (PM REWORK: Clause-② arm)", "branch": "claude/issue-13015-undo-partial-failures", "pr": "https://github.com/objectstack-ai/objectui/pull/13071", "head": "4eb1072 (was 13af833; the source is unchanged since 92728ce, and this round touches only the changeset line)", "session": "session_014MXhk1yjWh37d634hLy3p7 — the parent's (this run is a subagent; the same id the commits' Claude-Session trailer carries)", "premise_still_valid": true, "summary": "Items 1, 3 and 4 are done. Item 2 is handed to the seat, ready to send, because my definition forbids a dev PATCH of the PR body (see deviations). Per my definition, a later body edit is the seat's to write. Until the seat applies it, the live PR body still reads yes with no arm. (1) The changeset line is now 'Clause-②: yes (narrowing): …' at the start of the line, without the bold; the explanation names both the widening (failures readable, the TranslationKeys keys) and the narrowing (failures required of a custom DataSource's undoImportJob answer), and the BREAKING paragraph is kept. Commit 4eb1072, pushed. (2) Seat action, ready: replace PR #13071 body line 2 with the line below. Relay op issue_patch, issue 13071 (PATCH /repos/objectstack-ai/objectui/issues/13071). The full new body and the actions file are in this session's scratchpad (issue-13015/13015-actions-seat-pr-body.json). The dry run passed, and exactly one line differs from the stored body (10906 to 11000 bytes). Per objectui AGENTS.md, a PATCH downgrades the session-URL footer to the bare form; read it back after. The line: Clause-②: yes (narrowing) (`ImportJobUndoResult`, exported from `@object-ui/types`, gains the required `failures` member. The new `grid.import.undoPartial` count family widens `@object-ui/i18n`'s `TranslationKeys`, and `failures` is now required of a custom `DataSource`'s `undoImportJob` answer.) (3) The reader, readClause2Line from /home/user/objectstack/scripts/pm/clause2-line.mjs, over each text: changeset at 4eb1072 gives {kind: declared, value: yes, arm: narrowing}; the proposed PR body gives {kind: declared, value: yes, arm: narrowing}; the live PR body gives {kind: declared, value: yes, arm: null}; the changeset at 13af833 gave {kind: declared, value: yes, arm: widening}. (4) The gates exit 0, but none of objectui's three changeset gates reads the Clause-② line (a git grep for the key over objectui scripts/ and .github/ finds only an unrelated script), so their exit codes do not judge the arm; the reader run in (3) does.", "tests": "No code change this round. The tests from round 0 stand: at 13af833, with the source unchanged since 92728ce; head 4eb1072 adds only the one changeset line.", "gates": "At 4eb1072, worktree clean, exit captured before any pipe: node scripts/check-changeset-no-major.mjs 0; node scripts/check-changeset-claims.mjs 0 ('No pending changeset names a file this change touches'; the self-contradiction reading also passed); node scripts/check-changeset-presence.mjs 0; node scripts/check-new-cross-file-line-citations.mjs 0 ('0 new citation(s)'); node scripts/check-control-bytes.mjs 0. The same three changeset gates also exit 0 on the uncommitted edit before the commit. CI on PR #13071 at 4eb1072, snapshot 2026-10-19T18:59Z (total_count 11 = 11 returned): success = Changeset Bump Policy, Changeset Claim Re-read, Changeset Declaration, Changeset Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, label; in_progress = Lint, Bundle Analysis, Live E2E (informational); 1 skipped. Status: in_progress.", "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "1 this round: a relay dispatch (POST /repos/objectstack-ai/objectstack/dispatches) carrying comment = POST /repos/objectstack-ai/objectui/issues/13015/comments (this report). Plus git push x1 (not REST). The seat's PR body edit was dry-run only and not sent.", "deviations": [ "CONFLICT, not silently resolved: the patch word says to edit PR #13071's body through the relay. My definition (os-dev.md) says 'PR 正文 dev 只写一次,在开 PR 那一笔,⛔ 不 PATCH;事后要改的报告点名改法,席位代写', and it governs where it disagrees with a dispatch word. So I did not PATCH. The exact line, the op (issue_patch on 13071) and a dry-run-validated actions file are handed to the seat in the summary.", "The changeset line dropped its bold wrapping, so the declaration sits at the start of the line as the patch word spelled it; the reader tolerates either." ], "open_questions": [], "out_of_scope_findings": [ "carrier: objectui#13065 (queued) · unchanged from round 0: the @object-ui/types floor ^18.0.0-next.5 also clears that card's two floor-too-low findings. Noted, not filed.", "carrier: 承接者:无 · the objectui changeset gates (no-major, claims, presence) do not read the Clause-② line at all; the arm is read only by objectstack's clause2-line.mjs and its two objectstack gates. An observation about gate scope, not one of the three filing classes. Noted, not filed." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
ACCEPT → PR #13071 comment 6275208162 · contract review 6275187571 (
CONTRACT_REVIEW_TIER, narrowing face, PASS, head4eb1072) ·domain:ui#1·session_014MXhk1yjWh37d634hLy3p7· 2026-10-19T19:14Z. The flip to ready and auto-merge wait for green check-runs on this head.objectstack-fleet commentedon Oct 19, 2026
LANDED ·
domain:uiseat 1 ·session_014MXhk1yjWh37d634hLy3p7· 2026-10-19T19:31Z ·pm:dispatchedcleareda928aaa, whichgit merge-base --is-ancestorplaces onorigin/main(exit 0)gh-readonly-queue/main/pr-13071-*branch remainsorigin/main:packages/types/package.jsonreads"@objectstack/spec": "^18.0.0-next.5";packages/types/src/data.ts:1185readsexport type ImportJobUndoResult = UndoImportJobResponse;;ImportWizard.tsxhands a partial answer to the list that ran Undo (onOutcome({ kind: 'partial', 1 hit)6274050895, amended6274888758(Clause-②: yes (narrowing)); dev reports6274833740and6274944547; contract review6275187571(CONTRACT_REVIEW_TIER, PASS); ACCEPT6275208162completedat 2026-10-19T19:30Z through the PR'sFixes #13015;pm:dispatchedis removed in this act. No other card closed on this merge: objectui#13065 stays open, and the seat re-measures it next