fix(app-shell,data-objectstack,plugin-view,types): the console reads stored view rows by the spec's declared spellings and drops the reads nothing writes (objectui#11013) - #11209
Conversation
…identity by the declared spellings objectui#11013 item 1 (ruling 甲 on objectstack#20051, stage ii). - `viewItemObjectName` reads `data.object ?? object`; the undeclared `objectName` leg is gone. The metadata door refuses a row bound by `objectName` alone, and every adapter write path stamps `object`. - The console no longer stamps `objectName` onto the rows `listViews()` returns. No reader consumed the stamp; a saved view's whole-body toolbar save wrote it back into the stored row. - The `view-ref` picker (`viewRefCatalog`) and the Studio view preview (`resolveObjectName`) read `object`, not `objectName` / `object_name`. - `viewRowId` reads `name` only; a top-level `id` / `_id` is no identity. Filter/sort row ids and `exportOptions` need no change on this end: no reader keys state by a row id across a reload, and the one reader of the export formats outside `ListView` (`ObjectGrid`) reads `exportOptions.formats`. Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ Co-authored-by: Claude <noreply@anthropic.com>
…writes, and read the toolbar policy as userActions objectui#11013 item 2 (ruling 甲 on objectstack#20051, stage ii). - Both relays of a view into the `list-view` node stop reading nine keys off the view: `allowExport` and the renderer flags `wrapHeaders`, `clickIntoRecordDetails`, `addRecordViaForm`, `addDeleteRecordsInline`, `collapseAllByDefault`, `fieldTextColor`, `prefixField`, plus the `editRecordsInline` spelling of `inlineEdit` on the object page. No console surface writes any of them onto a view, no view in this repository authors one, and the spec's view schema refuses each by name. The object-view node's own value (the objectui#5097 host-composition read) still reaches `ListView`. - A view's `allowExport: false` no longer withholds its `exportOptions`. - `NamedListView.allowExport` retires as a `?: never` tombstone: ruling A on objectui#7924 kept it only because both relays read it. - The object page's `object-view` node and plugin-view's non-grid route read the view's toolbar policy as `userActions.search` / `.sort` / `.filter` (through `normalizeListViewSchema`, which folds a stored bare `show*` flag), not as the bare flags. - The relay census declares each dropped rung's absence with its evidence; the objectui#7779 census re-points its `allowExport` pins; the objectui#10694 panel probe edits `resizable` instead of `wrapHeaders`. Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ Co-authored-by: Claude <noreply@anthropic.com>
…declared members through the flatten objectui#11013 item 3 (ruling 甲 on objectstack#20051, stage ii). `listViews()` flattened a ViewItem record to its `config` plus `name` / `label` / `isDefault` / `_draft`, so the keys the console writes at the record's top level (`isPinned`, `sortOrder`, `visibility`, `columnState`) did not come back on the row the switcher reads. The switcher sorts saved views by the `sortOrder` it reads off that row, so a reordered record lost its place on reload wherever the per-browser order cache did not mask it. The flattened row now also carries the record's `object` and the spec's round-trip keys declared on the `viewItem` member, read off `VIEW_CONSOLE_ROUND_TRIP_KEYS` rather than hand-listed. app-shell's view-config save derives its carried row-state keys from the same record instead of retyping them. A reload round trip per key is pinned against a stub metadata store. Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ Co-authored-by: Claude <noreply@anthropic.com>
…port stays declared objectui#11013 retires `NamedListView.allowExport` in the same release, so the objectui#7924 entries' "`allowExport` stays declared" no longer holds. Each gets a dated note in the form those entries already carry, pointing at the objectui#11013 entry; their original text is kept as the reading of their change. Surfaced by `pnpm check:changeset-claims`. Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ Co-authored-by: Claude <noreply@anthropic.com>
…ead, and `unknown` for the new row types objectui#11013 follow-up, no behaviour change. The toolbar-policy fold on the object page is read inline: `activeView` is a fresh object each render while a draft is open, so the memo bought nothing and drew an exhaustive-deps warning. `viewRefCatalog` and the `listViews` flatten type their rows `Record<string, unknown>`. Per-file lint warning counts for the three files return to the merge base's, except the one `only-export-components` hint for the exported `viewRefCatalog`. Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
|
Generated by Claude Code |
Contract reviewServed-tier: Inputs, and nothing else: card objectui#11013 (body and all seven comments — the ruling quote, claim ① Derived judgmentsRuling 甲 on objectstack#20051 (
Remaining ② Semver level
③ Boundary flagsGate verdicts on the head (43 check-runs, every one concluded before this record was posted): 39 success, 3 skipped, 1 failure, none still in progress.
Dev flags and open questions, each answered:
Implemented-by: VERDICT: PASS |
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Delta record. This head is the GitHub update-branch merge of ① Derived judgmentsCarried over from record
So every judgment in ① of record ② Semver levelCarried over from record ③ Boundary flags
Implemented-by: VERDICT: PASS |
Fixes #11013
Clause-②: yes
Context
options.KINDwith the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 objectstack#20051 (record5856781584, batch Fix TypeScript build errors in plugin packages #227 item 2), stage ii: "…and objectui aligns its reads to the declared spellings". The spec end is feat(spec): declare the console's round-trip keys on the stored view wire (#20456) objectstack#20474. It is installed here as@objectstack/spec@17.5.0, which exportsVIEW_CONSOLE_ROUND_TRIP_KEYSfrom@objectstack/spec/ui.5908989184(seatdomain:ui#1, sessionsession_0122Knsowci76D2rBWReCzzZ).5909173910corrects5908394824. 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 objectstack#20051 stage (iv) no longer waits on this card. It is dispatched there under the maintainer's instruction on that card: 「20051 不考虑现有的数据」. At objectstack's pin, the console's own save path already writesobjectand only the declared row-state keys. So the reads aligned here matter for rows written earlier. No consumer-side fallback for legacy rows was added beyond what the card names.What changed: one commit per work item
08a159633)viewItemObjectName(data-objectstack) readsdata.object ?? object. TheobjectNameleg is gone.objectName: sv.objectName || sv.object || objectNameonto the rowslistViews()returns. No reader consumed that stamp; the handlers act on the route's object name. A saved view's whole-body toolbar save wrote the stamp back into the stored row.view-refpicker now readsobject(viewRefCatalog). It used to readobjectName ?? object ?? object_name.object(resolveObjectName). It used to readobjectNameon the body and on the draft. This site was not in the card's census. It is fixed in place as the same defect class and the same gate family, and no open PR touches the file.viewRowIdreadsnameonly. A top-levelid/_idis no identity.filter[].id/sort[].idandexportOptionsneeded no change; see the measurements below.userActionsfor the toolbar policy (af24d4e7c)list-viewnode stop reading nine keys off the view:allowExport,wrapHeaders,clickIntoRecordDetails,addRecordViaForm,addDeleteRecordsInline,collapseAllByDefault,fieldTextColor,prefixField, and theeditRecordsInlinespelling ofinlineEdit. The two relays are plugin-view'srenderListViewcomposition and the object page's relay over it.ListView. That node read is the objectui#5097 host-composition read, and it is untouched.allowExport: falseno longer withholds itsexportOptions.NamedListView.allowExportbecomes a?: nevertombstone. Ruling A on objectui#7924 kept it declared only because both relays read it.userActions.search/.sort/.filter. They used to read the bareshowSearch/showSort/showFiltersflags off the view. The sites are theobject-viewnode the object page builds and plugin-view's non-gridgenerateViewSchemaroute.ObjectView.relayRungCensus-7559.test.ts) declares each dropped rung's absence with its evidence kind; the objectui#7779 census re-points itsallowExportpins; the objectui#10694 panel probe editsresizableinstead ofwrapHeaders.listViews()record flatten (67048225b)object. It also carries the spec's round-trip keys declared on theviewItemmember:isDefault,isPinned,sortOrder,visibility,columnState. The set is read offVIEW_CONSOLE_ROUND_TRIP_KEYS, not retyped.VIEW_ROW_STATE_KEYSfor the view-config save is derived from the same record.Also in this PR:
bcd3c6f67) states each dropped key's effect in user terms.c1d3b684aadds dated notes to two pending objectui#7924 changeset entries. Both said "allowExportstays declared", which this change makes false;pnpm check:changeset-claimssurfaced them.b2df69071is a lint tidy with no behaviour change.PM mechanism assumptions, measured
objectNamereader already falls back toobject." This is partially falsified.viewItemObjectNamedid readobjectbeforeobjectName. The other two sites readobjectNameFIRST, withobjectas the fallback: the object-page stamp (sv.objectName || sv.object) and the picker (objectName ?? object ?? object_name). A fourth reader,ViewPreview'sresolveObjectName, read it third and fifth.objectNamewould come from: no door accepts one.ViewMetadataSchemarefuses a flattened overlay bound byobjectNamealone, and also a ViewItem record bound by it alone. The same bodies bound byobjectparse; that is the lit control. The test isviewItemObjectName.declaredSpelling-11013.test.ts.object:createView,updateViewConfigandupdateView, all measured in the same file.objectat every site, as before.configto the spec'sListViewSchemakeys plus the declared row-state keys. The create path goes through the spec-driven inspector. The pin toggle and reorder writeisPinned/isDefault/sortOrder. The settings overlay writesrowHeight/sort/hiddenFields/columnState/inlineEdit.packages/*/src,apps/**andexamples/**:git grepfor the key as a property. The only hits are the readers, the type declarations, and two React props handed toListViewdirectly:StudioDesignSurface'saddDeleteRecordsInline: true, andInterfaceListPage's list-view nodeallowExport: false. Neither is a view row or an authored view.isPinnedfinds the pin toggle's write.examples/**,apps/**): 0 files for each of the nine. The controlinlineEditfinds 9 files.ListViewSchemarefuses all nine withunrecognized_keys; the controlexportOptionsis accepted.options.KINDwith the strict per-kind schema and persist the PARSED body — the door half of objectui#10380 objectstack#20051.loadViewOverridesoverride merge." This is partially falsified.config+name/label/isDefault/_draft.isPinned/visibility/columnStateonto the TAB. It does so for records thatMetadataProviderlists intoobjectDef.listViews, which is whereloadViewOverridestakes its ids.sortOrderoff thesavedViewsrow only, sosortOrdernever came back through the merge. A reordered record lost its place on reload, wherever the per-browserviewOrder:cache did not mask it.listViews.roundTripKeys-11013.test.ts.filter[].id/sort[].idacross a reload." Confirmed.custom/builders and renderers, and app-shell views. It looked for(filter|sort|condition|item|…).idand forlocalStorage/sessionStorage.filter-builder.tsx/sort-builder.tsx.view-config-utils.tsmints one when a row has none.exportOptionsneeded no change on this end: the only reader outsideListView,ObjectGrid, readsexportOptions.formats.ListView.tsx's bare-array tolerance is untouched. The card retires it after rows are stored parsed, and the file is in PR objectui#10278's area.Acceptance notes
NamedListView.allowExporttombstone. It follows from dropping the read. The alternative, keeping the type declared with no reader, is the declare-and-ignore shape ADR-0049 forbids. The seat's disposition on spec(ui)+objectui: declare the console's round-trip keys on the stored view overlay (#20051 stage ii, ruling 甲) objectstack#20456 is also "no producer, no declared spelling".NamedListViewno longer typesObjectViewSchema.listViews(objectui#7928), so the break is on an exported legacy type only.normalizeListViewSchema. This is the one fold the relays' ownuserActionsrung already runs, so no new tolerance is added. A stored view that still carries a bareshow*flag keeps its answer, which objectui#7924's changeset states as policy ("that fold stays"). ReadinguserActionsraw would change those rows at these two sites only.MetadataProvider.applyViewItemstill flattens a record toconfig+ identity. Its tab gets the round-trip keys back through the override merge. It was noted and not changed; thelistViewscomment says so.UnifiedViewConfig(types/src/designer.ts) still declaresallowExport/addRecordViaForm/show*. Nothing constructs that type. Carrier: none.ResourceEditPage'ssourceObjectNamefalls back todraft.objectNamefor every metadata type. That is not a view-row read.data-objectstack/src/index.ts, in its import block and filter translation, away from these hunks.types/src/objectql.tsatObjectKanbanSchema, away fromNamedListView.types/src/objectql.tsand its objectui#7779 census, for the tombstoneViewPreview.tsx, the in-place fix aboveVerification
All readings below are at head
b2df69071. Exit codes were captured before any pipe. Heavy runs went throughos-verify-lock.sh.Build and type-check
pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' run build: exit 0. That closure holds 29 packages, includingtypes,data-objectstackandplugin-view. The build ran atc1d3b684a;b2df69071changed no exported type.pnpm --filter PACKAGE run type-check: exit 0 for all four packages.typesandplugin-viewwere checked atc1d3b684aand are unchanged since.data-objectstackandapp-shellwere re-checked atb2df69071.--listFiles:tsconfig.test.json: 5 of 5tsconfig.test.json: 1 of 1 (it holds theallowExport@ts-expect-errorpin)tsconfig.test.json: 2 of 2tsconfig.json: 2 of 2 new tests, 74 test files in allTests
pnpm exec vitest run packages/data-objectstack/ packages/types/ packages/plugin-view/: exit 0.Test Files 421 passed (421),Tests 8177 passed (8177).pnpm exec vitest run packages/app-shell/ --shard=N/4, N = 1 to 4, each exit 0:Reverse verification
Each ablation was one-off: mutate, run the pin, restore. Every leg went through
ablation-replace.mjsin WRAP mode, with the anchor required to hit exactly once and restore proven against HEAD's blob. After the run,git diff HEADis empty. The predicted direction was red for every leg, and every leg went red:objectNameleg back inviewItemObjectNameviewItemObjectName.declaredSpelling-11013id/_idback inviewRowIdObjectView.setDefaultViewIdentityobjectName ?? object ?? object_nameback inviewRefCatalogResourceEditPage.pickerLoadFailureobjectNameback inresolveObjectNameViewPreview.declaredObject-11013activeView?.wrapHeadersback in plugin-viewObjectView.viewRowDeclaredReads-11013activeView?.showSearchback on the object-view nodeallowExportrung and gate back in app-shelllistViews.roundTripKeys-11013activeView?.showSearchback on plugin-view's non-grid routeObjectView.viewRowDeclaredReads-11013A4's first attempt was a no-op. Its replacement contained the anchor, so the tool refused, the anchor count did not drop, and no test ran. It was redone with a non-overlapping replacement; the row above is the redone run.
Gates
Each exit 0:
node scripts/check-changeset-presence.mjs: "21 source file(s) of 4 released package(s) changed, and this change declares 1 changeset(s)"pnpm check:control-bytescheck-changeset-no-major,check-changeset-fixedcheck-changeset-overwrite. It is report-only. It names the two dated-note edits, and their front matter is unchanged.pnpm check:new-line-citations: "0 new citation(s)"check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shapecheck:spec-symbols,check:metadata-write-doors,check:test-path-roots,check:pending-changeset-literalscheck:icon-record-names,check:docs-route-closurecheck:changeset-claims. It is report-only; its two findings got the dated notes.Lint, narrowed and proven
eslint.config.jslints**/*.{ts,tsx}. All 21 changed or added.ts/.tsxfiles are in it.pnpm exec eslint --format jsonover those 21 files reports 21 files and 0 errors, with inline config honoured aspnpm linthonours it.ResourceEditPage.tsxgains onereact-refresh/only-export-componentshint, for the exportedviewRefCatalog.ViewPreview.tsxloses one.parserOptions.project, so linting is not type-aware. It loads no import plugin, and no rule ineslint-rules/reads another file. So this diff cannot move an untouched file's verdict.pnpm lintis CI's run.NOT MEASURED
check:doc-examples. Reason: PREREQUISITE NOT MET. It needs theapp-shell,cli,plugin-ai,plugin-gantt,plugin-map,plugin-markdownandplugin-timelinedists, which are outside the built closure. No doc example writesallowExporton aNamedListViewor names another changed symbol. It is declared to CI.Mergeability
846cec0ef. Of this branch's files, onlydata-objectstack/src/index.tschanged there, by PR objectui#11192.git merge-treeof HEAD against that main exits 0.Generated by Claude Code