Repository navigation
feat(types)!: a list view's legacy options bag is the spec list overlay's bag by reference (objectui#6152, round 12) - #12014
Conversation
…ay's bag by reference (objectui#6152, round 12) ListViewSchema.options is @objectstack/spec's own list-overlay bag, read off VIEW_METADATA_MEMBERS.listOverlay: strict, the eight kinds that name a block, each the list-view slot with every key optional. The bag's kanban / calendar / gallery / timeline are the round-11 blocks, so each named refusal gives one message in both nestings; options.grid is refused with the spec's guidance. The writers move with it: app-shell's relay writes kanban.columns, no gallery.imageField and no timeline.descriptionField; ListView's gallery gate also reads options.gallery.coverField; the plugin-list README authors the top-level blocks and a dataset-bound chart. ListViewTimelineConfig is NonNullable<ListViewSchema['timeline']>, and ListViewGalleryConfig is retired. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
…the legacy bag too (objectui#6152, round 12) The bag now nests the declared kanban slot's own block, so the arm is a member there and both nestings report the same code and message. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
…ut it is (objectui#6152, round 12) The typed options bag no longer declares grid (the spec's list-overlay bag refuses it), so the grid branch's read of a row stored before the view write door judged the bag is typed at the read site. No change in what renders; the read retires with the other legacy readers. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
…y (objectui#6152, round 12) The typed options bag refuses the legacy groupField by name; neither test is about the alias, and the gate reads groupByField in the bag too. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
✅ 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: PR objectui#12014, round 12 of card objectui#6152 (draft, base ① Derived judgmentsZod accept sets (
Published TypeScript face (
Test instruments and pins:
Docs and changesets:
File surface against the claim 6070030626 as amended in 6070475966: every one of the 26 files is on the amended surface, "the tests beside these", or a dev-declared, seat-conditioned addition (the one ② Semver level
③ Boundary flagsDev-declared deviations (report 6071238136), each answered:
The census report's deviations (6070437445): nothing built there reached this head (the measured-then-reverted bag edit left
The seat's conditions on the round: the bundle delta was to be measured against Noted, not flagged:
Escalations: none. No maintainer decision is needed for this head to land. Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #6152 (round 12)
Clause-②: no
This is the round's implementing PR. The seat answered the round's census (report
6070437445) with Q1 → A, Q2 → B and Q3 → A on objectui#6152 (comment6070475966), under claim6070030626, and amended the file surface there. Whether the card closes after this round is the seat's call, so this body carries no closing keyword. Session:https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw.What changes
@object-ui/types, narrowed (breaking).@objectstack/spec's authoring list view declares nooptionsbag. Its one home is the flattened list overlay on the view write door, judged there under objectui#10380's ruling A.ListViewSchema.optionswas a record ofanywith three named refusals, so every door accepted what that door refuses. It is now that member's own bag, by reference:stripImportedDefaults(VIEW_METADATA_MEMBERS.listOverlay.shape.options.unwrap()), extended with this package's round-11kanban,calendar(.partial()),galleryandtimelineblocks. So the bag is strict, has the spec's eight kinds, each the list-view slot with every key optional, and keeps the spec's own error map, including the guidance foroptions.grid. Each named refusal gives one message underoptions.KINDand under the top-levelKIND.optionsunrecognized_keysatoptionsgridunrecognized_keysatoptions, with the spec's guidancekanban.swimlaneField,timeline.descriptionField,tree.titleField, …)unrecognized_keysat that kindkanban.groupField/kanban.cardFieldsinvalid_type, by namegroupByField/columnsgallery.imageFieldinvalid_type, by namecoverFieldtimeline.dateFieldinvalid_type, by namestartDateFieldcalendar.defaultViewinvalid_type, by nameobject-calendarelement's flatdefaultViewchart.xAxisField/yAxisFields/categoryField/valueField/aggregationunrecognized_keysatchartchart: { dataset, dimensions, values }The three refusals already in place (
kanban.groupBy, objectui#8365;calendar.dateField/endField, objectui#8355) keep their messages and now reportinvalid_type, where they reportedcustomwhile the bag was a record. The TypeScript face follows:ListViewSchema['options']has the eight kinds and no index signature.@object-ui/types, the two config types.ListViewTimelineConfigis now the non-nullListViewSchema['timeline'](Q2 → B): the spec slot, strict and.partial(), withdateFieldrefused by name.ListViewGalleryConfigis retired (Q3 → A); the remedy is the spec'sGalleryConfig, which the package re-exports.@object-ui/app-shell, the producers (Q1 → A). InObjectView.tsx,kanbanViewOptionswritescolumnswhere it wrotecardFields;timelineViewOptionsstops writingdescriptionField;galleryViewOptionsstops writingimageFieldbesidecoverField. Measured on the card before this round: the board's node and$selectare identical undercolumns, and nothing drew the timeline's nesteddescriptionField. The legacy READS in these producers stay.@object-ui/plugin-list. One capability-gate rung:availableViewsalso readsoptions.gallery.coverField(the objectui#8193 kanban precedent), so the coverField-only bag the relay now writes still offers the Gallery view. One more, compile-forced line inListView.tsx, beyond the seat's "one rung": the grid branch'soptions.gridspread is typed at the read site as the stored input it is, because the typed bag no longer declaresgrid. It renders exactly as before. The README's two examples author the top-level per-kind blocks and a dataset-bound chart, and no longer show the bag.@object-ui/plugin-timeline.ObjectTimelineProps.schema.timelinetakes the newListViewTimelineConfig; the prop's docs say what it is.What did not move
The renderers' reads:
ListViewstill merges eachoptions.KINDunder the top-level block and still reads the legacy spellings (groupField,cardFields,imageField,dateField, the chart axes,tree.titleField, thegridspread), andObjectTimelinestill readstimeline.dateField. The render path parses nothing, so a row stored before these doors closed renders as before.packages/plugin-viewis untouched (PR objectui#12010 holds it).Fixtures the narrowing flips, re-spelled with their tests
calendar-date-alias-refusal-8355.test.ts: surface 2 now pinsinvalid_typeand asserts the message equals surface 1's; its "nothing else is judged" scope control becomes an undeclared-key control.kanban-stray-group-by-refusal-text-9653.test.ts,ListView.strayGroupByRefused-8365.test.tsx: the bag channel's code isinvalid_type.list-view-dataset-chart-filter-refusal-10327.test.ts: the object-bound bag control becomes a bag that binds no dataset (what it guards is unchanged).object-chart-legacy-axis-keys-retired-10608.test.ts: block (e) pinned the bag ACCEPTING the legacy axes; it now pins the refusal, with a dataset-bound control.imported-defaults-8317.test.ts: the new crossing is measured.VIEW_METADATA_MEMBERSis a record of schemas, not a schema, so it cannot be the direct argument ofstripImportedDefaults. The census gains one enumerated category,SCHEMA_RECORDS, which accepts a read only when the whole member chain is that argument, and a test that each entry is a non-Zod record of schemas read only that way;IMPORTEDgains the bag as a row.ObjectView.kanbanGroupByRetired-8213(its residual set reaches zero),galleryBinding-7547,kanbanLane-8193,kanbanSummarizeField-11629.ListView.test.tsx: two typed fixtures writegroupByField.timeline-date-bindingandObjectTimeline.absentDateAxisRefusal-7459: both keep the alias fixture (it models a stored block the reader must still bind), and each gains a real@ts-expect-erroron a typedListViewTimelineConfigcarryingdateField.New pins
packages/types/src/__tests__/list-view-options-bag-6152.test.ts: the installed overlay bag as a precondition (key set derived by the spec's own rule, each kind strict and fully optional); the mirror IS that bag (same keys, same error map, the round-11 blocks by identity); verdict equality with the spec's bag over 17 documents, plus the sanctioned default difference; every named refusal underoptions.KINDthrough all four doors with the top-level message; undeclared keys and kinds through all four doors; the TypeScript face,ListViewTimelineConfig, and the retiredListViewGalleryConfigimport as a@ts-expect-error.packages/plugin-list/src/__tests__/ListView.galleryOptionsBagCanonical-6152.test.tsx: the Gallery toggle both ways.packages/app-shell/src/views/ObjectView.optionsBagParses-6152.test.ts: each exported producer's block parses at the bag's door, with a control that the door still refuses the three retired writes.Verification, on
372fa8f8fpackages/types/,packages/plugin-list/,packages/plugin-timeline/,scripts/__tests__/, app-shell'sObjectView.*andInterfaceListPage.*tests, and every test naming the bag's kinds or the two config types (80 extra files):Test Files 790 passed | 2 skipped (792),Tests 18251 passed | 39 skipped (18290), VERDICT command-exit 0.turbo run build --filter='./packages/*', then the console): 39 of 39 tasks. Type-check, after it:types,app-shell,core,i18n,plugin-calendar,plugin-gantt,plugin-grid,plugin-list,plugin-timeline,plugin-tree,plugin-view,console,example-byo-backend-console, eachDone. plugin-timeline's test leg reads@object-ui/typesthrough its rebuiltdist, and its two@ts-expect-errors compile only against the narrowed type, so a stale.d.tswould be red there.ablation-replace.mjswith an on-disk anchor count and a restore checked against the HEAD blob: removing the new gate rung reds exactly the two coverField arms of the gallery pin (2 failed, 5 passed, as predicted); restoring the record bag reds 89 of the bag pin's 119 tests.check:doc-snippets(784 of 784 blocks, 0 failed),check:readme-exports,check:doc-types,check:doc-examples,check:docs-route-closure, doc links,check:doc-fences,check:doc-example-ids: green..changeset/6152-list-view-blocks-strict.md: round 11's pending changeset said the bag was unchanged, and it carries an append-only dated note now, its text and frontmatter otherwise unchanged.check:new-line-citations0 new,check:control-bytesOK, governed guard: NOT GOVERNED (26 paths)..ts/.tsxfiles (eslint --no-inline-config): one error,no-restricted-importsonFormFieldinpackages/types/src/index.ts, identical at the base; nothing new. The repo-wide lint is CI's.Bundle
check:eager-closure, each tree built the same way under the lock:origin/main7ebff39913,391,414 B gzipped; this branch 3,391,315 B (1a067c36a; the one later commit changes test fixtures only). Delta −99 B, under the 3,391,484 B ceiling. The constant is untouched.Acceptance notes
options.gridread-site typing inListView.tsxis the one line beyond the seat's fence; without itplugin-listdoes not compile, and it changes nothing at runtime.ListViewTimelineConfigkeeps its export name, now as the derived type, soObjectTimelinePropskeeps one consumer of it.@objectstack/spec17.5.0) keep rendering through the untouched readers; retiring those readers is the later, renderer-side round on objectui#6152.Generated by Claude Code