Repository navigation
fix(types,runner): the retired-key pins and the runner sidebar compile against both the pinned spec and objectstack main (objectui#11330) - #11343
Conversation
…e against both the pinned spec and objectstack main (objectui#11330) objectstack d830d71f gave a retired key a branded `[REMOVED]` mark type in place of bare `undefined`. Four type pins in packages/types asserted the `undefined` spelling, and the runner sidebar handed the retired app `version` to a ReactNode slot, so Spec Main Shape Gate went red. - One test-only helper, `IsRetiredKeyType`, accepts exactly the two retired-key spellings (17.5.0 `undefined`; main's one-property `[REMOVED] …: never` mark under the optional wrapper) and nothing looser, with direction proofs. - Each pin asserts its retired key through it, with a live-key control row. - `LayoutRenderer` drops the `app.version` footer: the key is retired on both sides, so the read could only render nothing. Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ Co-authored-by: Claude <noreply@anthropic.com>
…ed without a bare `{}` (objectui#11330)
`@typescript-eslint/no-empty-object-type` is an error in this repo; the row's
meaning (an object with no property is not the mark) is unchanged.
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
|
Contract reviewServed-tier: Inputs read, and nothing else: card objectui#11330 (body and its three comments: the spec seat's queue note ① Derived judgmentsThe upstream fact, verified at source. At (a) The helper, (b) The four pins. Each retired-key assertion is replaced in place, never deleted: (c) (d) File surface. Exactly the 7 files the claim allowed. No (e) Gate verdicts on this head, read at 2026-10-01T06:16Z: 42 check-runs, 33 success, 3 skipped, 6 in_progress. ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
…jectui#11168 slice 3, round 2) Picks up the Spec Main Shape Gate repair (PR #11343, 47e3ce0) and objectui#11299 (770cc5b). Merged cleanly: no conflicting hunk, so nothing was resolved by hand. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
…Source are the spec's, by reference; GridColumnDefinition retires (objectui#11070, round 7) (objectstack-ai#11352) Refs objectstack-ai#11070 Clause-②: yes (narrowing). The strict face's accept set widens (a grid field's `columns` and an `object-chart` node's `dataSource` binding parse), and the tolerant face narrows (both keys are now judged by their declared type: a grid column the spec refuses, or a non-binding `dataSource` on `object-chart`, is refused where it used to be stripped or kept unjudged). `GridColumnDefinition` retires from `@object-ui/types`, and `GridColumn` in `@object-ui/fields` becomes the spec's `InlineGridColumn`. Priced in `.changeset/11070-grid-columns-chart-binding-round7.md` as minor with a BREAKING banner. `Refs`, not a closing keyword, on purpose. Round 7 of the card, under the seat's claim `5925545318` (remainder items 2 and 3 of `5919314708`). Draft; the dev does not mark it ready. ## What changed **(i) The grid field's `columns`, by reference to the spec.** - `GridFieldMetadata.columns` (`field-types.ts`) and `FormField.columns` (`form.ts`) are `SpecField['inlineColumns']`; the zod mirror `FormFieldSchema` declares `stripImportedDefaults(SpecFieldSchema).shape.inlineColumns`. `form-field-zod-coverage` gains the `columns` row. `FormFieldSchema` was already a registered, spec-derived pair in `zod-mirror-parity`; its `SPEC_DERIVED_PAIRS` note records the new member, and no ledger total moved. - `GridColumnDefinition` is retired (interface and barrel export). Nothing in production read it, and it was not the shape the widget read. - `GridField`'s `GridColumn` is `InlineGridColumn` from `@objectstack/spec/data`, by reference. The renderer notes its per-member JSDoc carried move into one docblock. No read changes: the widget already read exactly the spec's twenty keys. - Fixtures: `fields-grid/*` already write only `name` / `label` / `type`; all nine columns parse against the spec element, so none changes. - `content/docs/fields/grid.mdx`: the typed fence drops a column `defaultValue`; the prose names the spec column and lists the nine cell types; the plaintext fences stop teaching `boolean`, `percent` and `user` column types; the validation sketch drops a column `min` the column does not have. **(ii) `object-chart` · `dataSource`, re-measured and declared.** - `ObjectChartSchema` (TypeScript and the flat zod mirror) declares `dataSource` as the spec's `ElementDataSource` / `ElementDataSourceSchema`, by reference. `OBJECT_CHART_NODE_LEVEL_KEYS` keeps it out of the authored bag, and `ObjectChartBlockSchema` carries it at node level (the mirror's own member), as `ObjectFormBlockSchema` and `ObjectMapBlockSchema` do. - The stale `SHOWCASE_NODE` pin (`object-chart-react-tier-node-10770.test.ts`) drops `dataSource: null` and the comment describing a wrapper write that round 2 removed; a binding row and a `null`-refused row join it. **Rows the declarations force (fixture triage):** `object-chart-properties-bag-11276` (the node-level key set gains `dataSource`, on the type and runtime legs; its "left undeclared" row becomes a "parses at node level, not a bag member" row) and `public-block-responsive-styles-10872` (`DECLARES_DATA_SOURCE` names `object-chart`, the control built to redden exactly when an arm gains the key). `strict-face-read-keys-11070` moves `columns` and `object-chart` to the declared blocks and empties its PENDING list. Comment-only: `deriveMasterDetail.declaredSpelling.test.tsx`, and the two `GridField` tests now type their fixtures `GridFieldMetadata['columns']`. **Changesets.** One new (`@object-ui/types`, `@object-ui/fields` minor). Dated notes on the five pending changesets of this release whose sentence the round makes false: `11276-object-chart-properties-bag`, `10872-flat-arm-responsive-styles`, `11070-strict-face-read-keys`, `6138-fields-schema-block-parity-pr2`, `8209-datetime-widget-faces`. ## Census (i): the grid column, measured at `5262f7dd` | source | shape | |:--|:--| | installed `@objectstack/spec` 17.5.0 | No `grid` field type and no `columns` member on `FieldSchema`. The list is `inlineColumns` (on `master_detail`), elements `InlineGridColumnSchema`: strict, 20 keys (`name` required; `label`, `type` in nine values, `width`, `required`, `options`, `prefix`, `step`, `reference`, `displayField`, `idField`, `multiple`, `accept`, `defaultHidden`, `computed`, `expr`, `scale`, `autofill`, `readonlyWhen`, `requiredWhen`). Named refusals: `field` / `fieldName` / `key` (→ `name`), `title` / `header` (→ `label`), `size`, `expression`, `hidden`; `scale` refused on a `currency` column. | | `GridField`'s local `GridColumn` | The same 20 keys and the same nine-value `type`. Value domain differs on `readonlyWhen` / `requiredWhen` only (local object arm had an optional free-string `dialect`). | | what `GridField` reads | `cfg.columns`, and per column exactly those 20 keys (a census of member reads off the column: 20 distinct, 0 outside the spec; no spread or computed read of a column). | | `GridColumnDefinition` | 7 keys: `name`, `label`, `type` (required, free string), `required`, `defaultValue`, `width`, `validate`. 2 keys not in the spec, `type` wider. Production readers: 0 (two `fields` tests and the docs prose). | | `fields-grid/*` fixtures | 3 documents, 9 columns, keys `name` / `label` / `type` (`text`, `number`, `currency`, `date`); 9 of 9 parse against `InlineGridColumnSchema`. | | `grid.mdx` | typed fence: a column `defaultValue`; plaintext fences: `boolean` (2), `percent`, `user` column types; validation sketch: a column `min`. | | objectstack example apps (`94608a7d7`) | No `grid` field (the spec has none). One producer of the spec element: `app-showcase` `invoice.object.ts` `inlineColumns`, seven identity-only `{ name }` entries. | H1 holds in substance, not as posed: the spec declares no grid `columns`; its `inlineColumns` element is key-for-key `GridColumn`. H2 holds: `GridColumnDefinition` had no production reader, so it retires. No non-spec column spelling is read, so nothing else retires and no producer stop applied. The alignment is a type reference, not a renderer redesign. ## Census (ii): `object-chart` · `dataSource` on `5262f7dd` | fact | reading | |:--|:--| | renderer read | the `object-chart` registration is `elementDataSourceBlock`-wrapped; `ElementDataSourceGate` reads the node's `dataSource` and lands `object` on `objectName` | | producer | the react-page wrapper builds `{ ...props, specType, type }`, no `dataSource` write (round 2) | | strict face | `{ type: 'object-chart', properties: { objectName, chartType }, dataSource: { object } }` refused, unrecognized key `dataSource` | | tolerant face | the same document, and `dataSource: null` or an adapter object, accepted unjudged (the arm is passthrough) | Not resolved by PR objectstack-ai#11328 or round 2, and no ruling needed: declared like the seven other gate-wrapped blocks. ## M3 before / after `scripts/measure-strict-authoring-face.mjs --json` plus the PR objectstack-ai#11069 `findUndeclaredKeys` port for pairs, each on a freshly built `@object-ui/types`. | reading | base `5262f7dd` | main `770cc5ba` | this head `62fe84ae` | |:--|--:|--:|--:| | documents | 601 | 601 | 601 | | nodes | 2229 | 2229 | 2229 | | strict-refused nodes | 94 | 94 | 91 | | strict-only nodes | 63 | 63 | 60 | | red today (tolerant) | 31 | 31 | 31 | | documents refused whole-tree | 88 | 88 | 85 | | port: pairs | 60 | 60 | 59 | | port: tolerant-refused | 28 | 28 | 28 | Main to head: exactly `form · fields[].columns` (3 occurrences, 3 strict-only, the three `fields-grid` documents) leaves; no pair enters or moves, and the tolerant face refuses the same documents. H3 holds. `object-chart` · `dataSource` has no corpus occurrence, so it moves no M3 figure; its pins carry it. ## Verification All on this branch; heavy runs through `os-verify-lock` with 2 workers and a memory watchdog. - `@object-ui/types`: `type-check` exit 0 and `vitest packages/types/` 309 files / 7828 tests passed at `62fe84ae`; build exit 0. - `type-check` exit 0 in all 11 census packages (types, core, components, fields, plugin-form, plugin-charts, plugin-dashboard, plugin-detail, plugin-tree, plugin-grid, schema-catalog) at `3eecfc2c`. - Tests at `3eecfc2c`: `packages/fields/` 226 files passed, 1 skipped (3515 tests); `packages/plugin-form/` 157 / 1822; `packages/plugin-charts/` + `examples/schema-catalog/` 121 / 3233; the 86 test files outside those packages that read the zod faces, 86 / 2110. - Reverse type check: a temporary `plugin-form` file assigning a `boolean` column, a column `defaultValue`, an adapter `dataSource` and a dialect-less `readonlyWhen` object made `tsc` exit 2 with exactly those four errors (TS2322, TS2353, TS2353, TS2322), and a spec-valid control line none; the file was removed. - Ablations, committed first, predicted in writing before each run, through objectstack's `ablation-replace.mjs` (anchor 1 → 0, restore blob == HEAD, `git diff HEAD` empty; subjects resolve from source): - A1, item (i): drop `FormFieldSchema.columns`. Predicted 7 red + tsc red; measured 7 failed / 52 passed, `tsc -p tsconfig.test.json` exit 2 (the parity ledger). - A1b, docs leg: plant the column `defaultValue` back in `grid.mdx`'s typed fence. Predicted 1 block red; measured `check:doc-snippets` 698 judged, 1 failed (TS2353 on `defaultValue`). - A2, item (ii): drop `dataSource` from `ObjectChartBlockSchema`. Predicted 5 red, tsc green; measured 5 failed / 321 passed, tsc exit 0. - Gates at `62fe84ae`: `check:doc-snippets` 698 of 698 judged, 0 failed (closure rebuilt, 35 of 35 tasks); `check:doc-fences`, `docs:check-links`, `check:control-bytes`, `check:changeset-claims`, `check:pending-changeset-literals`, `changeset:check`, `check-changeset-presence`, `check:new-line-citations` (0 new), `check:spec-symbols`, `check:test-path-roots`, `check:doc-types`, `check:doc-examples`, `check:doc-example-ids` all exit 0. `check-governed-queue-guard --test` over the 23 net paths: NOT GOVERNED. - Narrowed lint: the population is `**/*.{ts,tsx}` (the config's `files`), so 16 of the 23 changed paths; `eslint --no-inline-config --format json` on those 16 at head and at the base: 0 new errors, warnings equal per file except `field-types.ts` 5 → 4. The one error (`index.ts`, `no-restricted-imports`) is present at the base too. The config has no type-aware linting and no rule reads another file, so the diff cannot move an untouched file's verdict. ## Notes - **The `Spec Main Shape Gate`.** objectui#11330's repair (PR objectstack-ai#11343) landed on `main` as `47e3ce00` and is merged into this branch, so its fleet-wide red is not expected here; the result is CI's. - **PR objectstack-ai#11333** (round 6) shares `packages/types/src/field-types.ts` on disjoint hunks (`FormulaFieldMetadata` there, the grid metadata here). This branch merges `main` again once it lands. - **Not touched:** `strict-authoring-face.ts`, PR objectstack-ai#11333's surface, `docs/adr/0001-master-detail-subform.md` (a governed record that still names `GridColumnDefinition`). ## Acceptance notes - `grid.mdx`'s typed fence still teaches `allow_reorder: false`. `GridFieldMetadata` declares `allow_reorder`, and `GridField` reads `reorderable`: a probe rendering the widget measured two drag handles with no key, two with `allow_reorder: false`, and none with `reorderable: false`. The widget's other field-level reads (`total_field`, `amount_field`, `amountField`, `allow_duplicate`, `show_line_numbers`, `sort_field`, `add_label`, `reorderable`) are declared on neither face. These are field-level keys, outside this round's `columns` scope; they are in the report for the seat. --- _Generated by [Claude Code](https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #11330
Clause-②: no
What was red
The required
Spec Main Shape Gatecompiles objectui against@objectstack/specbuilt from objectstackmain. objectstackd830d71f(objectstack#21023) changed the TypeScript type aretiredKey()tombstone gives a key: bareundefinedin the pinned 17.5.0, and onmainan inline, anonymous mark under the optional wrapper,{ '[REMOVED] Key retired: run os validate for its migration.': never }orundefined. Five objectui sites were written against theundefinedspelling:packages/types/src/__tests__/asserted[X] extends [undefined]orEqualof X andundefinedon a retired key;LayoutRendererhanded the retired appversionto aReactNodeslot (the sidebar'sv{app.version}footer).The pin, the manifests and
pnpm-lock.yamlare untouched (objectui#9860 option C), and so arescripts/spec-main-shape-gate.mjsand its workflow.Both compiles, reproduced with the gate's own flow
objectstack
mainatb3d7a70864a7(newer than the card's5dbeb7d7b7e1) was checked out in a separate worktree,@objectstack/specwas built andnpm pack --ignore-scriptsed as the workflow does, andnode scripts/spec-main-shape-gate.mjs injectput it into this worktree's install. The verdict is the gate's ownreportstep over the compile log.reportmainb3d7a70864a75262f7dd3fTURBO_FORCE=true turbo run type-check --continue, narrowed to@object-ui/types+@object-ui/runnerand their closure: 15 of 17 tasks ok, failedtypes#type-check,runner#type-checkmainb3d7a70864a7f82cf777baTURBO_FORCE=true pnpm type-check --continue, the WHOLE repository: 81 of 81 tasks ok, 0 cached, 0error TSlines@objectstack/specat that commit."@objectstack/spec@17.5.0(injected copy removed withpnpm install --frozen-lockfile --force; 0[REMOVED] Key retiredhits in the installed declarations)f82cf777bapnpm --filter '@object-ui/runner^...' build, then@object-ui/typesand@object-ui/runnertype-check: exit 0No sixth site surfaced once these five compiled.
The helper: one spelling of "a retired key's type"
packages/types/src/__tests__/retired-key-type.ts(test-only:__tests__/is excluded from the package build, nothing re-exports it) declaresIsRetiredKeyTypeof X, which istruefor exactly two types:undefined(invariant equality, soneverno longer passes the way[X] extends [undefined]let it);mainspelling: the mark, unioned withundefined, where the mark is matched by its STRUCTURE because the spec does not export it (it is inline on purpose): exactly one property, a literal name with the[REMOVED]prefix (not a template index signature), typednever.It matches the prefix rather than the whole sentence, so rewording the sentence upstream does not turn every objectui pull request red again. Everything else is
false:any,unknown,never, a live member, the mark without itsundefinedarm, the mark widened by another member, a mark that admits a value, a second property, no prefix, an empty object, a template index signature. Those are the module's own direction proofs (_NotAnyand its siblings), written asExpectof an invariantEqual, so a helper that answeredtrueorbooleanwhere a row saysfalsedoes not compile. The onlyanyin this diff is the input of_NotAny, the row that proves the helper refuses it.The four pins
Each still asserts the retirement exactly, through the helper, and each gains a live-key control row through the same helper:
false)chartconfig-aria-retired-4044.test.tsariaAdmitsNoValueonchartConfig.ariatitleIsLiveonchartConfig.titledashboard-aria-retired-contract-twins.test.tsariaAdmitsNoValueon the dashboard'sariawidgetsIsLiveonwidgetsrecord-details-top-level-9040.test.ts_LayoutOnTheContractFaceon the spec props'layout_ColumnsOnTheContractFaceIsLiveoncolumnstwins-spec-by-reference-9736.test.tsadmitsNoValueon the page'sassignedProfilessourceIsLiveonsourceThe
@ts-expect-errorlegs on authored values were already green on both sides and are unchanged. The prose beside each assertion that said "typedundefined" now names both spellings.LayoutRenderer: the read is dropped, not narrowedMeasured on both sides: the twin's
AppComponentSchema['version']isundefinedagainst 17.5.0 and the mark-or-undefinedagainstmain. The key is retired on both, and the spec refuses any value at parse, so the footer could only ever render nothing on valid metadata. The read is deleted. No test or pin read it (git grepoverpackages/runnerfinds no otherversionread). Changeset:.changeset/11330-runner-retired-version-footer.md,@object-ui/runnerpatch.One-time proofs (both sides, run from the committed tree, restored and proven restored)
Mutations went through objectstack's
scripts/ablation-replace.mjsin wrap mode: the anchor must hit exactly once, the landing is checked on disk (anchor count 1 to 0, blob hash changed), and the restore is checked as blob equal to HEAD withgit diff HEADempty. The compile istsc -p packages/types/tsconfig.test.json.true). Red on 17.5.0 AND onmain, identically: all 12_Not*direction proofs, plus the four pins' control rows (titleIsLive,widgetsIsLive,_ColumnsOnTheContractFaceIsLive,sourceIsLive). The one extra line, TS6133 on the now-unused type parameter, is the mutation's own artifact.title/widgets/columns/sourcein the assertion). Red on both sides, exactly four errors, one per pin's assertion line.After both:
git diff HEADis 0 bytes.Tests and gates (worktree root; exit codes captured before any pipe; final head
f82cf777ba)main's spec: whole-repository compile exit 0,reportexit 0 (table above).pnpm install --frozen-lockfile --force:pnpm --filter '@object-ui/runner^...' build(15 of 47 projects) exit 0;pnpm --filter @object-ui/types type-checkandpnpm --filter @object-ui/runner type-checkexit 0, both scripts echoed.pnpm exec vitest runon the four pin files pluspackages/runner/: 10 files, 56 tests passed.tsc -p packages/types/tsconfig.test.json --listFilesOnlylists the helper and all four pins.pnpm check:new-line-citationsexit 0 ("0 new citation(s)");node scripts/check-changeset-presence.mjsexit 0;pnpm changeset:checkexit 0;pnpm check:control-bytesexit 0; alsopnpm type-check:coverage,check:phantom-deps,check:esm-specifiers,check:published-tsconfig-exclude,check:test-path-roots,check:spec-symbols,check:installed-pin-claims, all exit 0.eslint .runs) over the 6 changed.ts/.tsxfiles: 0 errors, 7 warnings (--format jsoncounts). Six warnings are on unchanged lines (five inLayoutRenderer, one inrecord-details-top-level-9040.test.ts); the new one isno-explicit-anyon the_NotAnyprobe input. Type-aware linting is not enabled ineslint.config.js, so this diff cannot move a verdict on an untouched file; the repo-widepnpm lintis CI's.Acceptance notes
8ce0077913was stopped on purpose (SIGINT to this session's own turbo PID) when lint flagged a bare empty-object type in the helper. That run is not counted; the reading above is the later full run onf82cf777ba.undefinedarm describes no installed spec any more and can be dropped. The module docblock says so. No tripwire was added, to keep this stop-the-bleed to its file surface.packages/types/src/app.ts's docblock onAppComponentSchemastill says a key the spec retires "surfaces here as an optional member typedundefined". That is true only of the pinned spec now. It is published source outside this card's file surface. Carrier: none.versionno longer shows a "vVERSION" footer in the runner sidebar. The spec refuses that document at parse.pnpm install --frozen-lockfile --force) before the pinned-side gates ran.Session:
https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZGenerated by Claude Code