Repository navigation
spec(ui)+objectui: declare the console's round-trip keys on the stored view overlay (#20051 stage ii, ruling 甲) #20456
Description
Activity
- addedenhancementNew feature or requestNew feature or requestpriority:p2Medium: important, M3Medium: important, M3
on Sep 28, 2026 objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsClaim: PM loop round 3
Session:session_01ARcDurZ5j34RdqsGgc4jgH
Account:os-warren(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-20456-stored-overlay-round-trip-keys
Worktree:objectstack-issue-20456
Domain:domain:spec
Seat:domain:spec#4(seat post #18917)
File surface: the spec end of thisSeam:card, stage (ii) of #20051's ruling 甲 (5856781584).packages/spec/src/ui/**, where the stored view overlay's wire is declared (todayview.zod.ts/assembled-views.zod.ts; the dev measures the exact home). Each round-trip key the console reads back is declared there, with its meaning, as a spec symbol that stage (iii)'s ADR note can cite. Plus tests.packages/spec/api-surface/**/export-origins/**, regenerated by the tools if the symbol is exported..changeset/:minor,Clause-②: yes, as the ruling orders.- Read only: objectui's console at the
.objectui-shapin, for the census of keys read back from a stored view row; and productionsys_metadata, or NOT MEASURED with the reason. - ⛔ No change to what is persisted: the three GUARD pins stay, and flipping them is stage (iv)'s. ⛔ Not objectui in this PR. Per the cross-repo rule (an objectui-consumed
Seam:goes spec first, layer by layer), the objectui end ("aligns its reads to the declared spellings") is filed by this seat at ACCEPT as an objectui card carryingBlocked-by:this PR.
(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: default judgment tier(no path-derived mandate).Clause-②: yes, so an at-tier contract review is owed before enqueue (ruling item 4).
Clause-②: yes
Thread-read: none
Serial constraints cleared: read at 2026-09-28T15:13Z onorigin/mainfbeb56e4. - Of the 12 open PRs, none touches
packages/spec/src/ui/view*.ts,assembled-views.zod.tsor a stored-overlay reader. No live claim names them. - spec(ui):
ViewFilterRule.operator's input type isunknown(az.preprocess); type it as the canonicalViewFilterOperator, or admit the alias spellings? (the last site of #19920's family) #20450 (ViewFilterRule.operator's input type, p3, unclaimed) sharesview.zod.tsbut a different node. Whoever claims it second declares this card's hunks.
Ruling carried:
5856781584· #20051 ruling 甲 · batch #227 item 2 · maintainer 「开始总监决裁」. Stage (i) #20186 is closed. Card filed by the triage seat (filing gate ③).domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgH
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 20456,
"status": "done",
"branch": "claude/issue-20456-stored-overlay-round-trip-keys",
"pr": "#20474",
"session": "session_01ARcDurZ5j34RdqsGgc4jgH (mode:subagent, the PM's session). Identity = the branch named by claim 5872888895, the newest Claim: on the card, verified before any edit; no second claim posted, and the card's assignee was never written",
"premise_still_valid": true,
"summary": "Spec end of stage (ii), ruling 甲 (5856781584). Census at objectui .objectui-sha pin dd3f7e1b: a compiler-API syntax walk of the console's stored-view readers (154 keys read; LIT isPinned 11 sites plus an injected fixture key found at 1, DARK fabricated pinnedAtEpoch 0), crossed with the console's write bodies run through ViewMetadataSchema. The parse dropped isPinned / sortOrder on the flattened list overlay, visibility on the list overlay and the ViewItem record, and the settings-overlay marker _isOverride, which the floor did not list and which is the one whose loss at stage (iv) would turn every toolbar overlay into a saved view. In packages/spec/src/ui/view.zod.ts, viewSwitcherRowStateFields() declares isPinned / sortOrder / visibility once, each with a .describe() meaning, for ViewItemWireSchema and VIEW_METADATA_MEMBERS.listOverlay. listOverlayRoundTripFields() adds _isOverride: true on the list overlay, and the overlay's existing isDefault gains its meaning. New export VIEW_CONSOLE_ROUND_TRIP_KEYS maps the six round-trip keys (isDefault, isPinned, sortOrder, visibility, columnState, _isOverride) to their members; it is the symbol stage (iii)'s ADR note can cite, closed by view-console-round-trip-keys.test.ts. Persistence is untouched and the three GUARD pins are green. objectName, a top-level id, the filter / sort row ids and the bare-array exportOptions map to existing declared spellings; see open_questions. Draft PR #20474, changeset @objectstack/spec minor, line-leading Clause-②: yes.",
"tests": "spec vitest --project local at 2530b59 (branch merged with origin/main e956924): 570 files, 16730 passed, 1 todo, exit 0. spec vitest --project repo: 38 files, 690 passed, exit 0. spec typecheck: VERDICT command-exit 0. New closure pin src/ui/view-console-round-trip-keys.test.ts: 34 passed. GUARD pins, after building the @objectstack/objectql^... closure (VERDICT command-exit 0): metadata-protocol protocol.graft-folded-form-sections.test.ts + protocol.graft-normalized-operators.test.ts 2 files 42 passed; objectql protocol-meta.test.ts 95 passed; none of the three edited. Ablation on committed d19cbad via scripts/ablation-replace.mjs, WRAP mode, under the lock: anchor ' visibility: z.enum(VIEW_SWITCHER_VISIBILITY_GROUPS).optional()' hit x1 and went to x0, replacement x0 to x1, blob d2aacf7a to 3dd7dd8a. Closure pin red, 6 failed / 28 passed of 34 (declared-member and parse-keeps for viewItem and listOverlay, and both typed-refusal cases): the expected direction. Restore proven: blob after restore d2aacf7a equals HEAD, git diff HEAD empty, anchor count back to 1, marker count 0. The test imports ./view.zod (spec source), so no dist leg. Reverse type check against the rebuilt dist/ui/index.d.ts from a scratch consumer: visibility 'everyone' on a ViewItemWire gives TS2322, exit 2; with that line removed, exit 0 (visibility 'team' and VIEW_CONSOLE_ROUND_TRIP_KEYS._isOverride typecheck). The only commit after 2530b59 is a47aeb5, one code comment.",
"mcp_calls": "0",
"api_writes": "3, each through the fleet-write relay as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls (draft forced), relay run 36450790382, body read back byte-identical (13608 bytes); (2) label-write --issue 20474 --assign os-warren, POST /repos//issues/20474/assignees, relay run 36450954224, read-back matches (no label written); (3) this os-dev-report comment via post-stamped, POST /repos//issues/20456/comments. git pushes (not REST): 5 on the branch, the empty-branch probe then 3ee095b, 8315ac6, d19cbad and a47aeb5 (the last carries merge 2530b59). Reads: unauthenticated REST GETs of #20456, #20051, #20186 and their comments, pulls?head, pulls/20474 and the head's check-runs; a depth-1 fetch of objectui at the pin into a scratch clone.",
"gates": "Derived at the final head a47aeb5 with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 108 commands over 9 paths (merge base e956924). All 108 run, each exit code written to disk before any pipe. --ran reconciliation: '108 derived famil(ies) accounted for — 106 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)', 0 UNRUN. check:skill-examples first exited 3 (client-react had no dist); after building @objectstack/client and @objectstack/client-react it ran exit 0, and its ran-list line records the re-run. The 2 NOT MEASURED are check:dual-build-cjs-loads and check:type-check-debt, both exit 3 PREREQUISITE NOT MET: they need the whole-workspace build, which does not fit in a foreground turn on this shared box. CI carries them: Build Core, whose step is dual-build-cjs-loads, was success on a47aeb5; Lint & Repo Gates was in_progress at report time. Verdict lines at a47aeb5: check:generated '✓ All 15 generated artifacts are up to date'; check:api-surface '@objectstack/spec public API surface + factory signatures unchanged ✓' (after gen:api-surface); check:authorable-surface exit 0; check:docs '226 generated files in sync'; check:issue-citations 'every citation this change adds resolves' (round 1 caught a re-added #9933 comment citation, fixed in a47aeb5); check:nul-bytes exit 0; check:doc-authoring clean. The 6 artifact-roster families whose roster sits in a touched directory (check-changeset-fixed, check:meta-url-spelling, check:spec-changes, check:authz-resolver, check:error-code-casing, check:filter-alias-parity): all exit 0. Control-byte self-scan of the 9 changed files: grep exit 1, no match. CI snapshot at a47aeb5: 33 runs, 23 success, 2 skipped, 8 in_progress (Test Core 6 shards, Type Check workspace, Lint & Repo Gates), 0 failed.",
"line_budget": "n/a: no governed or skills surface. 385 insertions / 33 deletions against merge base e956924, under the 5000-line threshold.",
"files_changed": [
".changeset/20456-view-console-round-trip-keys.md (+29)",
"content/docs/references/ui/view.mdx (+12/-10, generated by gen:docs)",
"packages/spec/api-surface/ui.json (+1, generated)",
"packages/spec/export-origins/ui.json (+1, generated)",
"packages/spec/src/system/metadata-form-zod-reconciliation.test.ts (+3/-3, ledger quotes of the changed describes)",
"packages/spec/src/ui/view-console-round-trip-keys.test.ts (+169, the closure pin)",
"packages/spec/src/ui/view-metadata-schema.test.ts (+1/-1, a comment that called _isOverride undeclared)",
"packages/spec/src/ui/view-union-diagnostics.test.ts (+6/-3, the three put.* parse-output pins now keep isPinned / sortOrder)",
"packages/spec/src/ui/view.zod.ts (+163/-16)"
],
"deviations": [
"PM mechanism assumption 3 ('parsing a stored row keeps it') is not applied to filter[].id / sort[].id, which the ruling's floor names. They stay VIEW_CONSOLE_ROW_DECORATIONS, removed before the parse, because the console re-mints a missing id on read (view-config-utils.ts:159 and :319 at the pin). Declaring them so the parse keeps them would need a wire twin of the row schemas or an id on the authoring rule, and this file has already refused both. objectName is likewise not declared: its declared spelling is object, which every reader already falls back to. See open_questions[1].",
"The typed declarations refuse ill-typed values the strip used to swallow (non-boolean isPinned, non-integer sortOrder, visibility outside the four groups, isOverride other than true). The changeset keeps the ruled Clause-② line with no arm and states this class in prose. See open_questions[0].",
"Beyond the declarations, three edits in the same file: the columnState describes (ViewColumnStateSchema and the ViewItem wire field) no longer say 'per-user', because ADR-0017 as amended makes a stored view row environment metadata; the reconciliation test's quotes of the changed describes were updated to match; and VIEW_ITEM_SURFACE gains a visibility refusal prescription. The authoring guidance texts that still say 'per-user' are left as they are and noted in the PR.",
"Round 1 of the gate run (archived) was spoiled by my own comment-only commit landing mid-run: 4 spec dist readers refused with PREREQUISITE NOT MET. The spec and objectql were rebuilt and the whole union re-run at the final head a47aeb5; the counts above are that run's.",
"The spec suites and the GUARD pins ran at 2530b59. The final head a47aeb5 differs by one code comment in view.zod.ts, and the gate union ran at a47aeb5.",
"origin/main moved to 5b674f5 (a driver-sql change, disjoint from this diff) after the merge at e956924. The branch was not re-merged.",
"Production sys_metadata: NOT MEASURED. This container has no deployed-environment connection: no OS_DATABASE_URL / TURSO* / PG* variable (an env grep answers empty). The PR names what stage (iv)'s production count should look for."
],
"open_questions": [
{
"question": "Clause-② arm. The declarations also refuse ill-typed values of the four keys, which were stripped and stored before. Does the changeset keep the ruled 'Clause-②: yes', or read 'Clause-②: yes (narrowing)', which makes it BREAKING with an ADR-0087 disposition?",
"options": [
"A: keep as ruled and shipped: 'Clause-②: yes', minor, no banner, the refused class stated in prose. Business: the census found zero console writes affected (the pin writes a boolean, reorder an integer, the marker only true). Long-term: leaves a narrowing without the carrier the arm exists for. AI-error: same runtime behaviour, but no BREAKING line for an upgrading agent to grep. Startup: no extra marker.",
"B: 'Clause-②: yes (narrowing)', a BREAKING line in the changeset, and the marker 'not-required (no-migration-prescription)' (nothing to rewrite: the fix is to correct or delete the value). Business: the same zero impact. Long-term: the arm's own definition, 'widens one surface and narrows another', fits this diff exactly. AI-error: the changelog tells an upgrading agent why a stored row with a bad value now answers 422. Startup: one marker line, still minor under the launch-window convention."
],
"recommendation": "B on the long-term and AI-error axes: the arm is the declared carrier for exactly this, and it costs one line. I did not apply it because the ruling characterised stage (ii) as the non-breaking stage and the dispatch spelled the line. A one-commit change if the seat agrees."
},
{
"question": "Census dispositions that add no key. The ruling's floor names objectName, sort[].id, filter[].id and the exportOptions shape. They are mapped to existing declared spellings (object; VIEW_CONSOLE_ROW_DECORATIONS, removed before the parse; the exportOptions object form the parse already lifts to) rather than declared as new wire keys. Does the seat accept that mapping as satisfying 'declares each … and objectui aligns its reads to the declared spellings'?",
"options": [
"A (shipped): map to the existing spellings, with no new keys; the objectui card aligns the reads. Business: object sits beside objectName on every row; a row id is re-minted on read, so no user state is lost. Long-term: one spelling per field, and no wire twin of the filter / sort row schemas. AI-error: no second dialect, and no id on filter rules for an AI to learn to emit. Startup: nothing added.",
"B: declare objectName on the overlays, and keep row ids through the parse on the wire door. Business: keeps bytes that no reader needs. Long-term: two spellings of the bound object, plus a parallel row-schema tree. AI-error: invites objectName and UUID row ids into authored bodies. Startup: new surface with no pull."
],
"recommendation": "A on all four axes. The mapping is pinned: objectName and id are asserted undeclared, and object and name asserted declared, in the closure test."
},
{
"question": "For the objectui card: allowExport is read off stored view rows (ObjectView.tsx:2838-2839 at the pin), no console surface writes it, and it has no declared spelling. showSearch / showFilters / showSort have userActions.search / .sort / .filter. Which way does the objectui end go for allowExport?",
"options": [
"A: objectui drops the allowExport read on view rows (no producer; export stays governed by exportOptions and permissions).",
"B: spec declares a userActions.export toggle and objectui aligns to it."
],
"recommendation": "A on the startup and business axes: no producer writes it, and B would be a new capability with zero pull."
}
],
"out_of_scope_findings": [
"carrier: the seat's objectui end card (Blocked-by this PR) · noted, not filed · objectui listViews flattens a ViewItem record to config + name / label / isDefault / _draft (data-objectstack index.ts:5466-5472 at the pin). So isPinned / sortOrder / visibility / columnState, written at the record's top level by the pin toggle and the config save, are not surfaced by that reader; they come back only through the loadViewOverrides override merge. Static reading at the pin, not run · dedupe words: listViews record flatten drops row state · pinned saved view record lost on reload · ViewItem record isPinned not read back",
"carrier: 承接者:无 · noted in PR #20474 Acceptance notes, not filed · the authoring refusal texts for isPinned / sortOrder / columnState (VIEW_ITEM_SURFACE and ListViewShapeSchema guidance in view.zod.ts) call them 'per-user' state, while ADR-0017 as amended makes a stored view row environment metadata · dedupe words: per-user wording view guidance · isPinned per-user refusal text · columnState per-user prescription",
"carrier: the seat's objectui end card · noted, not filed · boundary, not class c: a stored visibility 'private' gets a lock icon in the switcher (ViewTabBar.tsx:442 at the pin) and restricts nobody. It is declared here with a meaning that says so, and authoring guidance now names it · dedupe words: view visibility private lock icon · switcher visibility not access control"
]
}objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsSeat answers to the dev report's three
open_questions(PR #20474) · 2026-09-28T16:43Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), claim5872888895. ⛔ Not a new claim. Each answer is a technical classification or scope call (a non-escalation class). None changes ruling 甲 (5856781584): stage (ii) still shipsminorwithClause-②: yes, and nothing persisted changes.-
Clause-② arm → B:
Clause-②: yes (narrowing), with a**BREAKING**line naming the refused class, and the ADR-0087 markernot-required (no-migration-prescription). The declarations refuse ill-typed values the strip used to swallow: a non-booleanisPinned, a non-integersortOrder,visibilityoutside the four groups, and_isOverrideother thantrue. The arm's definition, 「widens one surface and narrows another」, fits this diff exactly, and the census found zero console writes affected. It staysminorunder the launch-window convention (precedent:.changeset/17779'syes (narrowing)). This is one commit on the branch. -
Census dispositions → A, accepted as satisfying the ruling:
objectNamemaps to the declaredobject;filter[].id/sort[].idstay row decorations stripped before the parse, because the console re-mints a missing id on read (view-config-utils.ts:159/:319at the pin);- the bare-array
exportOptionsmaps to the object form the parse already lifts to.
The ruling's purpose is that stage (iv) loses no user state. With one spelling per field and ids re-minted on read, it loses none. The closure pin asserts both directions:
objectNameand the rowidare undeclared, andobjectandnameare declared. Aligning objectui's reads toobjectis exactly the ruling's 「objectui aligns its reads to the declared spellings」, and it goes on the objectui card. -
allowExport→ A: the objectui card drops theallowExportread on view rows. No producer writes it, export stays governed byexportOptionsand permissions, and a newuserActions.exporttoggle would be a capability with zero pull.
The objectui end card is filed by this seat at ACCEPT, carrying
Blocked-by:PR #20474. It carries answers 2 and 3, and the dev'slistViewsrecord-flatten note (data-objectstackindex.ts:5466-5472at the pin).domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgH
Generated by Claude Code
-
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 20456,
"status": "done",
"branch": "claude/issue-20456-stored-overlay-round-trip-keys",
"pr": "#20474",
"session": "session_01ARcDurZ5j34RdqsGgc4jgH (mode:subagent). Patch round on claim 5872888895, executing the seat answers in comment 5874463510 (Q1 = B, Q2 = A, Q3 = A)",
"premise_still_valid": true,
"summary": "Q1 = B applied. The changeset now reads 'Clause-②: yes (narrowing)', line-leading. It adds a BREAKING paragraph naming the refused class (non-boolean isPinned, non-integer sortOrder, visibility outside the four groups, _isOverride other than true: each now refused at parse where the strip used to swallow it) with its remedy (correct the value or delete the key), and the ADR-0087 marker 'not-required (no-migration-prescription)' in the gate's comment form. The level stays minor. origin/main had moved onto two files in this diff (packages/spec/api-surface/ui.json and export-origins/ui.json, from #20420's component-props rows), so main 75b2169 was merged with scripts/pm/os-regen-merge.sh and both artifacts were regenerated on the merged tree. New head fc5a47d is pushed. PR #20474's body carries 'Clause-②: yes (narrowing)' on line 3, read back byte-identical.",
"tests": "No source file changed in this round, so no suite was re-run. The only non-merge edits are the changeset and the two regenerated artifacts. check:generated at fc5a47d: '✓ All 15 generated artifacts are up to date', against a spec dist rebuilt on the merged tree (VERDICT command-exit 0). Sibling entries kept: ActionButtonPropsSchema and ElementRepeaterPropsSchema are each present 1x in api-surface/ui.json on origin/main and at the head, the implementation 'export const ActionButtonPropsSchema' is present in component.zod.ts, and VIEW_CONSOLE_ROUND_TRIP_KEYS is present 1x at the head.",
"mcp_calls": "0",
"api_writes": "1 REST write this round: issue_patch through the fleet-write relay (PATCH /repos//issues/20474, body only), relay run 36454411512. This os-dev-report comment via post-stamped is the second. No label or assignee write. git push (not REST): 1 push, a47aeb5..fc5a47d, carrying 82078f5 (the merge of origin/main 75b2169), b426de7 (the regeneration) and fc5a47d (the changeset).",
"gates": "At fc5a47d, each exit code written to disk before any pipe: check-adr-0087-registration --base origin/main exit 0 ('1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … [BREAKING+clause-②-narrowing] not-required (no-migration-prescription)'); check-adr-0087-registration --self-test 0; check-changeset-no-major --base origin/main 0; check-changeset-no-major --self-test 0; check-empty-changeset --base origin/main 0; check-empty-changeset --self-test 0. The rest that dispatch-gates --commands derives through the changeset path: check:changeset-gate-self-tests 0; check:objectui-changeset 0; check:pm-changeset-deadline-census 0; check:published-files 0; release-rehearsal-clone --self-test 0. Also run: check-changeset-fixed (roster under .changeset) 0; check:nul-bytes 0; check-issue-citations 0; spec check:generated 0. That is 15 of 15 at exit 0. The full derivation at fc5a47d lists the same 108 families as the previous round (sorted diff empty). CI snapshot at fc5a47d: 39 runs, 13 success, 4 skipped, 22 in_progress, 0 failed.",
"line_budget": "n/a: no governed or skills surface. 385 insertions / 33 deletions against merge base 75b2169, under the 5000-line threshold.",
"files_changed": [
".changeset/20456-view-console-round-trip-keys.md (the narrowing arm, the BREAKING paragraph and the ADR-0087 marker; the separate 'Values that are now refused' section folded into the BREAKING paragraph)",
"packages/spec/api-surface/ui.json (regenerated on the merged tree: main's 17 component-props entries plus VIEW_CONSOLE_ROUND_TRIP_KEYS)",
"packages/spec/export-origins/ui.json (regenerated on the merged tree, likewise)"
],
"deviations": [
"The merge's first hand-off commit (step 3 of os-regen-merge.sh) was refused by pre-commit because both merged ui.json artifacts were stale. Following the hook's instruction, spec was rebuilt, gen:api-surface and gen:export-origins run, and the regenerated files staged. The staged diff was checked against HEAD (main's 17 additions) and against origin/main (only this branch's export) before committing b426de7; the os-regen marker cleared.",
"PR-body read-back: the stored body is byte-identical to the one sent (13894 bytes, diff 0). The platform appended no bare footer on this PATCH, although platform-readings records a +58-byte append for this cell. The body was sent with its footer block stripped, per the edit-side prescription, so it now ends at the objectui-end list, with no footer.",
"Besides the Clause-② line, the one PR-body patch also updated the acceptance note that had posed Q1 as open (now naming the seat's answer, comment 5874463510) and added one Verification sentence recording the merge to fc5a47d. No other text changed."
],
"open_questions": [],
"out_of_scope_findings": []
}objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsReview: ACCEPT · PR #20474 at head
fc5a47d08d0c842125d9ccc97c91b7e858647d4b· 2026-09-28T17:20Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), reviewer of record under claim5872888895. The readings below were taken on GitHub or on the tree by this seat.check reading Ruling #20051 ruling 甲 ( 5856781584), stage (ii), spec end: every key the console writes and reads back from a stored view row is declared with its meaning on the stored-overlay wire. Nothing persisted changes.Shape Draft, base main. Body line 1Fixes #20456;Clause-②: yes (narrowing)stands alone on line 3. The attribution footer was restored by the seat after the patch round.Scope 9 files, +385/−33: view.zod.ts(six declarations, the new exportVIEW_CONSOLE_ROUND_TRIP_KEYS, describe corrections, avisibilityauthoring refusal), the closure pinview-console-round-trip-keys.test.ts, three movedput.*pins, the ledger quotes, a comment, the generatedview.mdx/api-surface/ui.json/export-origins/ui.json, and the changeset.Contract review At-tier PASS 5874993923, same head. The census (objectuidd3f7e1b, the pinmaincarries; lit and dark controls) is supported, with every cited line checked. Each declaration is typed right with a true meaning and closed by the pin. Persistence is untouched:metadata-protocol/objectqlhave an empty diff, and the three GUARD pins are unedited and green. The seat's answers Q1–Q3 (5874463510) are judged right, and Q2 loses no user state at stage (iv).Changeset @objectstack/specminor,Clause-②: yes (narrowing), a BREAKING paragraph naming the refused class and its remedy, and the ADR-0087 markernot-required (no-migration-prescription).Check Changesetis green ×3.CI at this head 43 success, 6 skipped, all on the roster ( check-expected-skips.mjs --pr 20474, exit 0).mergeable_state:clean.origin/mainb810ddb6is three commits past the base75b21692and shares no path with this diff (generated artifacts included).git merge-treeis clean.Governed / size Not governed ( check-governed-merges.mjs --pr 20474: 0 of 9 paths), 418 changed lines.Escalations from the record, carried:
- The production
sys_metadatacount is NOT MEASURED (no deployed-environment connection), and stage (iv) cannot land without it → recorded on spec(ui)+metadata save: judge a flattened view overlay's top-leveloptions.KINDwith the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 #20051 (5875020099) as stage (iv)'s prerequisite, with the full key list to count. - Eight renderer flags the console reads but never writes (
wrapHeaders,clickIntoRecordDetails,addRecordViaForm,addDeleteRecordsInline,collapseAllByDefault,fieldTextColor,prefixField,editRecordsInline) are missing from the PR body's read-only list. They are theallowExportclass, owe this PR nothing, and are carried onto the objectui card and spec(ui)+metadata save: judge a flattened view overlay's top-leveloptions.KINDwith the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 #20051.
Filed / carried:
- The objectui end → filed objectui#11013, carrying
Blocked-by: objectstack-ai/objectstack#20474. It covers aligning reads toobject/name/ theexportOptionsobject form, dropping the reads with no producer (allowExport+ the eight), and thelistViewsrecord flatten. - The authoring-guidance texts that still say 「per-user」 (
view.zod.ts~2483-2488, ~5081-5084) → Acceptance notes, carrier: none.
Landing next: ready + auto-merge through the relay. At MERGED, this card closes by
Fixes, and objectui#11013 unlocks once the objectstack pin carries this change.domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgH
Generated by Claude Code
- The production
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsLanded: PR #20474 →
maine967cbd2418fa0bb763b61d8146681d0b0f33751· 2026-09-28T17:42Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), landing record for claim5872888895. ACCEPT5875039727on the at-tier PASS5874993923. Queue path: ready at 17:21Z and straight into the merge queue (checks already green), merged at 17:42Z.Verified on
origin/main:e967cbd2has one parent,4b2d9041, and is an ancestor oforigin/main.- Its diff against that parent is 9 files, +385/−33, the same as the PR.
- Content control:
export const VIEW_CONSOLE_ROUND_TRIP_KEYSis inpackages/spec/src/ui/view.zod.tsatorigin/mainand absent at the parent. - No queue branch for feat(spec): declare the console's round-trip keys on the stored view wire (#20456) #20474 remains.
Close-out:
- This card was closed
completedby the PR'sFixesline.pm:dispatchedcomes off in this act. - spec(ui)+metadata save: judge a flattened view overlay's top-level
options.KINDwith the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 #20051 stage (ii), spec end, is done. Its objectui end is objectui#11013 (Blocked-by:feat(spec): declare the console's round-trip keys on the stored view wire (#20456) #20474, now merged). The cross-repo unlock is the objectstack.objectui-sha/ the published@objectstack/speccarryingVIEW_CONSOLE_ROUND_TRIP_KEYS. Stage (iv)'s prerequisites are on spec(ui)+metadata save: judge a flattened view overlay's top-leveloptions.KINDwith the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 #20051 (5875020099).
domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgH
Generated by Claude Code
Filing gate ③: a ruled task. Stage (ii) of ruling 甲 on #20051 (
5856781584, batch #227 item 2, maintainer 「开始总监决裁」).Seam: the stored view overlay's round-trip keys: objectstack
packages/specdeclares them, objectui's console reads them backFiled by the triage seat (seat post #6015,
session_01AavokzJ5DndAwitDXvKy4U) in place ofdomain:specseat 2, which the ruling names as filer and which is vacant (#18549). The maintainer asked the triage seat directly, verbatim: 「#20051 的子卡 你为什么不能立」. ⛔ Not a claim.domain:specseat that dispatches this card. ASeam:card claims both ends.#20051|round-trip keys|appendix \(c\)3 times outside spec(ui)+metadata save: judge a flattened view overlay's top-leveloptions.KINDwith the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 #20051: spec(ui): a flattenedviewKind: 'list'view overlay with nocolumnsis refused by the list overlay member, then ACCEPTED by the form overlay member, so its list keys are stripped unjudged and a wrong 200 stores it #20186, spec(ui): a flattened view overlay acceptsowner/hiddenat the save door and nothing reads either, sohidden: truesaves clean and the view stays listed #20230 and metadata: a page saved withouttypeis stored and served without it, soPageSchema's declaredtypedefault (record) never reaches a consumer that reads the served body #20101, all closed and none of them this stage. No open duplicate.What the ruling orders (item 1, stage ii, verbatim)
Why now
Stage (i), #20186, is closed, so this stage is dispatchable. The end state is the persisted document being the parsed body (stage iv). ⛔ That storage change is not part of this card. The ruling forbids it before stages (ii) and (iii) close, because doing it now turns 「stored and unread」 into 「200, then the user's pinned / sort / visibility state silently dropped」.
Measured floor (seat 2's analysis
5853397704, dev reading at8d1f7abthrough the realsaveMetaItem)Storing the parsed body today would strip:
isPinned/sortOrderon the flattened overlay;visibilityon the ViewItem row;sort[].id/filter[].idon the personalization PUT.It would also rewrite
exportOptions ['csv']into an object and fill defaults. objectui readsisPinned/sortOrder/visibilityback from the stored row (ObjectView.tsxVIEW_ROW_STATE_KEYS,ViewTabBar).Acceptance
minor, withClause-②: yes(the declarations widen the wire) and an at-tier contract review (ruling item 4).sys_metadatafor stored views that carry undeclared top-level keys, or record it as NOT MEASURED with the reason. Stage (iv) cannot land without that count.Part of #20051