Repository navigation
feat(types)!: DetailViewFieldSchema.options reads the spec's authoring SelectOptionSchema (#7759 group F) - #10307
Conversation
…ring SelectOptionSchema Ruling F1 on objectui#7759: a structural mirror points at the spec's authoring schema, never at the declaration's runtime read model (SelectOptionMetadata). The inline option shape that admitted a number/boolean value is gone; the remaining visibleWhen envelope difference is recorded in the parity ledgers as an expected divergence, with its one-level-up propagation to DetailViewSchema fields/sections and DetailViewSectionSchema fields.
|
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
|
…pe and value rule Contract-review follow-up on PR #10307: the spec's expression-input pipe survives stripImportedDefaults, so a bare-string visibleWhen parses to a { dialect: 'cel', source } envelope. Pin that output shape, the refusal of a dialect-less envelope and of an empty string, and the whitespace-only acceptance on spec 17.4.0 that the next spec bump tightens. The changeset now states the SystemIdentifierSchema value rule exactly.
✅ 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: ① Derived judgmentsThe first review passed The first review (at
This delta closes the three non-blocking gaps:
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
…lled spec's envelope The Spec Main Shape Gate compiles the parity ledger against objectstack main, where SelectOptionSchema.visibleWhen requires a non-blank source. There the options row and its two container rows (DetailViewSchema fields/sections, DetailViewSectionSchema fields) no longer measure WIDER, while on the pinned 17.4.0 they do. The ledger entries stay literal; the reconciliation withholds exactly those rows when the mirror's own input face no longer admits a source-less envelope. A runtime tripwire in the pin file goes red at the pin bump to force their removal.
✅ 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: ① Derived judgmentsDelta over the PASS at The delta moves exactly two test files: Gate soundness
② Semver levelUnchanged. The delta is test-only. ③ Boundary flagsNone. There is no published-surface change, and the new type aliases live in a Implemented-by: VERDICT: PASS Generated by Claude Code |
… navigation item type objectstack#19789 widened the spec's navigation item union with `doc`, and `NAV_TYPE_META` is a Record keyed by that spec-derived union, so objectui stopped compiling against @objectstack/spec built from objectstack main (the Spec Main Shape Gate, TS2741 on NavigationDesigner). - `doc` entry: `appDesigner.navTypeDoc`, `bg-blue-100 text-blue-700`, `BookOpen`. - The map is typed `Record<NavigationItemType | 'doc', ...>`: the pinned spec 17.4.0 has no `doc`, so a plain `doc:` key is an excess property there. The `| 'doc'` goes at the pin bump that ships `doc`. - `appDesigner.navTypeDoc` fallback in useDesignerTranslation and all ten locale packs. - Guard test: every discriminant of the installed spec's NavigationItemSchema renders a row with a resolved type badge (spec subset of map, not equality). - `doc` is deliberately NOT in QUICK_ADD_TYPES: an empty doc item fails the spec's book-or-doc requirement; authoring it is objectui#10188. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC (cherry picked from commit 6f970da)
✅ 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
|
# Conflicts: # packages/types/src/__tests__/zod-mirror-parity.test.ts
✅ 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
|
…th faces (objectui#10334) (objectstack-ai#10338) Closes objectstack-ai#10334 Part of objectstack-ai#7759 Clause-②: yes ## What `DashboardComponentSchema.dateRange` now takes `@objectstack/spec`'s `DashboardSchema.dateRange` **authoring** member by reference, on both faces. This settles the objectui#7759 group F residue row `complex.zod.ts#DashboardComponentSchema::dateRange`. - `dateRange` left `DASHBOARD_SPEC_EXCLUDED`. Both the Zod mirror (`specFieldsExcept(stripImportedDefaults(SpecDashboardSchema).shape, …)`) and the TypeScript twin (`Omit` over the spec `Dashboard` input type) read that one list, so both now project the spec member. - The hand-written mirror element (a stripping `z.object` with `defaultRange: z.string()`) and the hand-written TS member were deleted. - Ledger: `'dateRange'` left `WiderThanDeclared['complex.zod.ts#DashboardComponentSchema']`, and the `WIDER_ARMS` row was removed. The WIDER header figures were re-derived to 8 / 10 / 11 and 5 / 5 / 0 / 1 (after merging main at `2d76f4e67`; main read 8 / 11 / 12 and 5 / 6 / 0 / 1), with a history sentence. The pin 'the WIDER ledger's header figures are derived from its ARMS' is green on them. - `page-app-dashboard-spec-parity.test.ts`: `dateRange` was removed from the Dashboard `omitted` list. - Docs: `content/docs/guide/dashboard-filters.md` now says the shape is the spec's and that unknown presets and keys are refused. ## Measurement (step 1): why the mirror was wider I ran a tsc probe of the old mirror input against the old TS member, key by key. It gave `Type '"defaultRange"' is not assignable to type 'never'`, so **only `defaultRange`** was wider: a bare `string` against the 14-member `DateRangeDefaultRange`. `field` and `allowCustomRange` agreed. The mirror also stripped unknown keys, which is not type-visible, whereas the spec object is strict. ## Rule applied: rule 1 (spec-declared) and F1 The spec declares the key in both places I checked: - installed 17.4.0 (`dist/ui/index.d.ts`): `dateRange: ZodOptional of a strict ZodObject`, with `field?` string, `defaultRange` a `ZodDefault` enum of the 14 names (`DATE_RANGE_DEFAULT_RANGES`, the presets plus `custom`), and `allowCustomRange` a `ZodDefault` boolean; - objectstack `main` `b81da66d` (`packages/spec/src/ui/dashboard.zod.ts`): the same key set, `strictObject` with named alias refusals, and `z.enum(DATE_RANGE_DEFAULT_RANGES).default('this_month')`. The read site, `resolveDashboardFilterDefs` in `@object-ui/core`, already implements every arm: each preset lifts to `{ preset }` through `PRESET_RANGES`, `custom` starts empty, `field` falls back to `created_at`, and `allowCustomRange` is read with `!== false`. So no renderer change was needed, and I judge the protocol right. The mirror still authors no default, because every crossing goes through `stripImportedDefaults`. ## Evidence (HEAD `2d76f4e67`, after merging `origin/main` past objectui#10300 `a05c3506`) - `pnpm --filter @object-ui/types type-check` (tsc, examples, test config): exit 0, run under the verify lock. - `pnpm exec vitest run packages/types/`: `Test Files 228 passed (228)`, `Tests 5099 passed (5099)`. `core` and `plugin-dashboard` type-check, and the three `dateRange` consumer suites pass (84 tests). Spec Main Shape Gate: a spec built from objectstack main `9d81af71` was injected, and `types` type-checks with 0 errors. - New pin `packages/types/src/__tests__/dashboard-daterange-spec-10334.test.ts`, 7 tests: - type equality of the twin and of the mirror input with `Dashboard['dateRange']`, plus a negative control; - `invalid_value` at `dateRange.defaultRange` for `last_7_dayz`; - `unrecognized_keys` at `dateRange`; - all 14 names accepted; - no default injected; - verdict parity with the spec schema over 8 samples. - **Reverse verification**, with the fix committed first and a trap restore that uses absolute paths: I wrote the base-commit `complex.zod.ts` and `complex.ts` into place. The on-disk markers confirmed the mutation (bare-string `defaultRange` 1, `'dateRange'` in the exclusion list 1, old TS member 1). - The pin gave `Tests 4 failed | 3 passed (7)`. - `tsc -p tsconfig.test.json` exited 2 with 3 errors: the pin's mirror equality, plus `WiderLedgerMismatch` on `complex.zod.ts#DashboardComponentSchema` and on `dateRange`. - Restore used `git checkout HEAD -- FILE`. Afterwards `git diff HEAD` was 0 bytes, and the HEAD and disk blob hashes matched for both files. - **Forward compatibility (Spec Main Shape Gate reproduced locally).** I built `@objectstack/spec` from objectstack `main` `b81da66d`, ran `npm pack`, then `node scripts/spec-main-shape-gate.mjs inject`, which exited 0 and re-pointed zod 4.6.1. Against that spec: - types type-check exit 0; - the pin, the parity ledger, the Dashboard spec parity and twins-9736 gave `72 passed`. - The pinned 17.4.0 install was restored afterwards with `pnpm install --force`, verified by the spec 17.4.0 manifest declaring zod `^4.4.3`. - Downstream: turbo build of the `@object-ui/plugin-dashboard^...` closure (`12 successful, 12 total`), then `pnpm --filter @object-ui/core type-check` and `pnpm --filter @object-ui/plugin-dashboard type-check`, all exit 0. The three dateRange consumer suites (DashboardRenderer.filters, dashboardAuthoredInputs, core dashboard-filters) gave `80 passed`. - eslint `--no-inline-config --format json` over the 5 changed TS files: 5 files, 0 errors. There are 13 `no-explicit-any` warnings, all on untouched pre-existing lines. Type-aware linting is not configured (no `parserOptions.project` in `eslint.config.js`), so the diff cannot move any untouched file's verdict. - These gates exited 0: `check-changeset-presence`, `check:spec-symbols`, `check:new-line-citations` (`0 new citation(s)`), `check-changeset-no-major`, `check:control-bytes`, `check:changeset-claims`, `check:pending-changeset-literals`, `check:installed-pin-claims`, `check:doc-fences`, `check-doc-links`. - NOT MEASURED: `check:doc-snippets`. Reason: it exited 2 with PREREQUISITE (it needs a scoped workspace build), so it is left to CI. The doc edit adds no code block. ## Changeset `.changeset/10334-dashboard-daterange-spec-authoring.md` is `minor` on `@object-ui/types`, and it states the breaking change to the validator only. An unknown `defaultRange` and unknown keys inside `dateRange` are now refused where before they were accepted or stripped. The TS type is unchanged in effect. ## Acceptance notes - Shared ledger: objectui#10294, objectstack-ai#10302, objectstack-ai#10307 and objectstack-ai#10300 have all landed. This head re-derives the WIDER header figures from the merged `WIDER_ARMS`, and the header pin is green. The next ledger PR in the chain, objectui#10387, re-derives after this one lands. - Observation, left to the seat: the spec declares `defaultRange` with `.default('this_month')`, while an authored `dateRange` that omits `defaultRange` starts with no window at the read site. This PR does not change that. --- _Generated by [Claude Code](https://claude.ai/code/session_01877XiBYSaRCk2CU7cMSg3S)_ Co-authored-by: Claude <noreply@anthropic.com>
Closes #10296
Part of #7759
Clause-②: yes
What this does
views.zod.ts#DetailViewFieldSchema.optionsnow reads the spec's AUTHORINGSelectOptionSchema(@objectstack/spec/data) by reference, wrapped instripImportedDefaultslike every other crossing. That follows ruling F1 on objectui#7759 (5617221514, adopted in 5617465269): a structural mirror points at the spec's authoring schema, ⛔ never at the declaration's runtime read model (SelectOptionMetadata). The TypeScript declaration is unchanged.Before: an inline option shape. It admitted a number or boolean
value, which the declaration refuses, and silently stripped every other key.After: the spec's strict schema.
valuemust be a machine identifier.iconanddisabledare refused by name (unrecognized_keys).description,defaultandvisibleWhenare accepted as the spec declares them.Re-derived population (origin/main
8b1f06619,WIDER_ARMS, CONCRETE rows only)Sibling branches were fetched into private refs, and each row was checked for whether it disappears there.
DashboardComponentSchema::header/::globalFiltersDashboardComponentSchema::dateRangeDashboardComponentSchemaregion (PR objectui#10273)FilterBuilderSchema::fields,FilterFieldSchema::operators,ContainerSchema::maxWidth,HeaderBarSchema::variantFormSchema::modeFormSchema::layoutCalendarSchema::defaultValue/::valueSliderSchema::defaultValue/::value,TooltipSchema::contentDataTableSchema::columns/::renderCellEditor,TableColumnSchema::cell,FieldConditionSchema::custom,FieldConstraintsSchema::validate,FormFieldSchema::condition/::validation,FormSchema::fieldsHeaderBarSchema::logoHeaderBarregion; still present on the objectui#10286 branchDetailViewFieldSchema::optionsChatbotSchema.bodyis already settled onmain. objectui#8572 (PR #9639) retired it on both faces as a?: nevertombstone, and the row left the ledger. Nothing is left to do for it here.The ledger reading: an expected divergence, not a zero
After the mirror points at the authoring schema, the two faces differ on exactly one member:
visibleWhen. The spec's input envelope requiresdialect. The read model's wire (objectui#2212) makesdialectoptional andsourcerequired. Measured withtsc, this is the only incompatibility.label,valueand the whole-object check agree otherwise.visibleWhenlocally to reach a zero-row ledger. That would narrow and widen a spec-declared shape inside objectui, which the ordering note 5617614225 forbids.WiderThanDeclaredandWIDER_ARMS, re-annotated as EXPECTED DIVERGENCE (read model). The old reason, the inline shape, is gone.KnownDriftgainsDetailViewFieldSchema: 'options'. The same cause is measured one level up, so it also gainsDetailViewSectionSchema: 'fields'(new entry), andDetailViewSchemagainsfieldsandsections. The header figures were re-derived to 48 entries / 89 keys. The restated figure under "KNOWN_DRIFT is a ratchet" was updated too.SPEC_DERIVED_PAIRSgainsviews.zod.ts#DetailViewFieldSchema, because the census test derived it from the source.Evidence (HEAD
a8446a2ae)pnpm --filter @object-ui/types type-check(all three tsconfigs): exit 0.pnpm exec vitest run packages/types/gaveTest Files 215 passed (215)andTests 4929 passed (4929). These ran under the verify lock, on bytes identical to the commit.packages/types/src/__tests__/detail-view-field-options-10296.test.ts: 6 tests. It assertscodeandpathfor each refusal, next to an accepting control, plus verdict parity with the spec schema.5 failed | 1 passed (6)(the accepting control stays green), andtsc -p tsconfig.test.jsonfailed with exit 2 onLedgerMismatchnamingviews.zod.ts#DetailViewFieldSchema. Aftergit checkout HEAD -- FILE:git diff HEADwas 0 bytes and the blob hash matched HEAD.@object-ui/plugin-detail(turbo, 11 tasks).pnpm --filter @object-ui/plugin-detail type-checkexit 0. Its two tests that import the zodDetailViewSchema/DetailViewSectionSchemapassed:2 passed,21 passed.--no-inline-configon the 3 changed source files:--format jsongave 3 files, 0 errors, and 2 warnings on lines this diff does not touch. Type-aware linting is not configured, so the diff cannot move any verdict on an untouched file.check-changeset-presence,check:spec-symbols,check:new-line-citations(0 new),check:control-bytes,check-changeset-no-major,check:changeset-claims,check:pending-changeset-literals,check:installed-pin-claims: all exit 0.check:spec-floors: NOT MEASURED. It needs a whole-workspace build (no-artifact), so it is left to CI.Acceptance notes
DashboardComponentSchema::dateRange: STOP, because it sits in PR objectui#10273's excluded region. For whoever takes it:@objectstack/spec17.4.0 exportsDATE_RANGE_DEFAULT_RANGES/DateRangeDefaultRangefrom@objectstack/spec/ui, so rule 1 likely applies (the mirror takes the spec's vocabulary). That is not measured here.HeaderBarSchema::logo: STOP, because it sits in objectui#10286'sHeaderBarregion. Proposal F routed it to ADR-0049 enforce-or-remove together withvariant.visibleWhenas an expected divergence (this PR) and overriding it with objectui's wire, which would give a zero-row ledger but a local reshaping of a spec-declared key.The session is
https://claude.ai/code/session_01877XiBYSaRCk2CU7cMSg3S(PM seatdomain:spec#1, dispatched dev).Generated by Claude Code