Repository navigation
feat(types)!: retire the PartialSchema export, which had no reader; a census pin keeps it gone (objectui#11608) - #11616
Conversation
… a census pin keeps it gone (objectui#11608) The census at work time found no reader of `PartialSchema` in objectui, objectstack `main` or hotcrm (cloud not reachable from this account). The export and its docblock leave `packages/types/src/index.ts` with no replacement alias, and the #8347 pin drops its `PartialSchema` section and the two imports only that section read. `partial-schema-retired-11608.test.ts` pins the retirement on three instruments: a `@ts-expect-error` row on the root barrel (with a lit sibling control), a vitest read of the source module behind every `exports` entry, and a tracked-tree `git grep` with a lit control. It also compiles the changeset's TO spelling. Claude-Session: https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
✅ 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: card #11608 (body, triage The head. ① Derived judgments1. The removal of the published 2. The census premise — RE-DERIVED, HOLDS, with
3. The edit to objectui#8347's pin 4. The new census pin
5. The changeset's TO spelling, pinned as compiling — RIGHT. The changeset's TO is "the node type's own declared members", or, for a partial value, No other accept-set or public-surface change is implied by the diff: nothing under ② Semver levelThe declaration matches the diff. The release-text interaction — acceptable as shipped; this PR was right not to edit the other note, and nothing must change. The still-pending
③ Boundary flagsDeviation (a): the 8347 pin also lost its Deviation (b):
Serial state. The dev reported it did not re-merge after #11614 landed; the head shows the seat's update-branch merge did that, and the readings above are taken on the merged head. The dev's local vitest and type-check readings are at Dispatch shape. The PR is a draft with the card's assignee mirrored, no labels written by the dev, the branch named by the claim; the file list touches no governed surface ( Check-runs on the headEvery conclusion below is a gate verdict as the platform recorded it; nothing was re-run. Readings, each with its time:
Final reading (6, 2026-10-04T15:28Z): 43 check-runs, 0 still in progress.
Tally: 40 success, 3 skipped (the matrix placeholders and the dependabot leg, which never run on a non-dependabot PR), 0 with any other conclusion, 0 in progress. Implemented-by: VERDICT: PASS Generated by Claude Code |
…`InlineGridColumnSchema`, by reference (objectui#11266) (objectstack-ai#11618) Fixes objectstack-ai#11266 Clause-②: yes (narrowing) `@objectstack/spec` 17.6.0 judges `FormViewSchema.subforms[].columns` with its strict `InlineGridColumnSchema` (objectstack-ai/objectstack#20927, `bee75cebe6`). The `object-form` mirror here still held `z.array(z.any())`, so `objectui validate` accepted columns that `os validate` refuses. This PR makes one column the spec's schema, by reference, on the zod face and on the TypeScript face. Dispatched under claim `5981407176` (`domain:spec`, size S). Session: `https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ`. ## What changes - `packages/types/src/zod/objectql.zod.ts`, the `object-form` mirror's `subforms[]` item: `columns` goes from `z.array(z.any())` to `z.array(stripImportedDefaults(SpecInlineGridColumnSchema))`, imported from `@objectstack/spec/data`. It crosses the objectui#8317 import boundary like every other spec read. The column schema carries no default, so the crossing is the identity and the member IS the spec's object. No hand-copied column shape. - `packages/types/src/objectql.ts`: `ObjectFormSchema`'s `subforms[].columns` goes from `any[]` to the spec's `InlineGridColumn` (its `z.input`, the authoring face), by a type-only import. `ObjectViewSchema['form']` follows, because it picks from `ObjectFormSchema`. - Docs: `content/docs/plugins/plugin-view.mdx` and `packages/plugin-view/README.md`, section "Child records (master-detail)", wrote `columns: ['product', 'quantity', 'price']`. Bare strings: `hydrateColumns` reads `col.name`, the spec refuses them, and with the TypeScript face narrowed that snippet no longer compiles under `check:doc-snippets`. Both now write `{ name: ... }` objects and say what a column is.⚠️ These two files are outside the claim's file surface. They are added because this change makes them false: the snippet gate would red on them. - `.changeset/11266-subforms-columns-mirror.md`: `@object-ui/types: minor`, with a BREAKING banner, FROM / TO migration lines and the `Clause-②: yes (narrowing)` line. ## Premise readings (this worktree, base `2abec3a9`, which is `origin/main` at PR time) - `pnpm-lock.yaml` resolves `@objectstack/spec` at 17.6.0 only (two entries, both 17.6.0). The package `packages/types` resolves answers `17.6.0`. - `@objectstack/spec/data` exports `InlineGridColumnSchema`. - `FormViewSchema.safeParse` of a simple form whose `subforms[0].columns` holds COL: - COL `{ name: 'qty', bogusKey: 1 }` is refused: one `unrecognized_keys` issue at `subforms.0.columns.0`, keys `bogusKey`. - COL `{ name: 'qty' }` is accepted (the control). - Control version: 17.5.0, installed from npm into a scratch directory. Its form view ACCEPTS the bogus key (the `z.any()` era), so the probe can fire. ## The `currency` + `scale` finding - A column that DECLARES `type: 'currency'` and carries `scale` (`{ name: 'amount', type: 'currency', scale: 2 }`) is refused by the spec's `InlineGridColumnSchema` itself, at the zod level: one `custom` issue at path `scale`. **It is pinned.** Through the mirror the same column is refused at `form.subforms.0.columns.0.scale`, and the pin asserts that the message equals the spec schema's own message, read in the same run. No literal text is pinned. - An identity-only column carrying `scale` (`{ name: 'amount', scale: 2 }`) is ACCEPTED by the spec's column schema, at 17.5.0 and at 17.6.0. Its refusal is `defineStack`'s, which resolves `name` through `childObject` to a `currency` field. The mirror judges a document that does not carry the child object's fields, so it cannot reproduce that check. The pin records the mirror ACCEPTING it, so reaching for it later is a deliberate change. The render-time report from objectui#11238 (`reportCurrencyColumnScale` in `plugin-form`'s `hydrateColumns`) stays the backstop. No `plugin-form` behaviour changes: patch round 1 (`b1d035bb`) corrects only its prose, see Acceptance note 2. ## The `@objectstack/spec` range `packages/types/package.json` already reads `^17.6.0` on `origin/main`: objectui#11438 (PR objectstack-ai#11531, the 17.6.0 lockfile move) raised it. So it does not move here. Would this change alone need 17.6.0? No. The mirror reads `InlineGridColumnSchema` only, and 17.5.0 already exports it with the same verdicts on the three probes: bogus key refused with `unrecognized_keys`, typed currency with `scale` refused at `scale`, `{ name }` accepted. What 17.6.0 adds is the spec's own `FormViewSchema` reading that schema. That is the other door's verdict, not this mirror's. `check:spec-floors` is green on the built artifact. ## The pin `packages/types/src/__tests__/object-form-subforms-columns-11266.test.ts`, 17 tests: - **By reference.** The mirror's column schema is `toBe` `stripImportedDefaults(SpecInlineGridColumnSchema)`, and `toBe` the spec object itself. - **On the tolerant face** (`safeValidateSchema`, which `objectui validate` runs) **and on the strict authoring face**, through the object-view `form` slot. That slot is the route an authored document has: `subforms` is a form-VIEW member, which the `object-form` row refuses in its `properties` bag. - `{ name }` is accepted: the lit control. - A bogus key is refused: one `unrecognized_keys` issue at the column, keys `bogusKey`. - Typed currency with `scale` is refused: one `custom` issue at the column's `scale`. - A bare string is refused: `invalid_type` at the column. - Identity-only with `scale` is accepted. - **The flat mirror** gives the same codes at the same column. - **One verdict across the two doors.** For each probe column, `safeValidateSchema` succeeds exactly when the spec's `FormViewSchema` does, read live. - **Type level** (judged by `tsc -p tsconfig.test.json` inside `type-check`): a declared column `Equal`s the spec's `InlineGridColumn`, and a string column is a `@ts-expect-error`. **Ablation.** The fix was committed first. The ablation used objectstack's `scripts/ablation-replace.mjs` in wrap mode, inside `os-verify-lock`. - Mutation: anchor `columns: z.array(stripImportedDefaults(SpecInlineGridColumnSchema)).optional()` became `columns: z.array(z.any()).optional()`. Anchor count went from 1 to 0, and the blob from `27ae77d7` to `d6b4ac83`. - Result: **11 failed, 6 passed (17)**. The six that stay green are the two controls, the two identity-only rows, and the two cross-door rows whose expected verdict is accept. - Restore: the blob after restore is `27ae77d7`, the same as the blob at HEAD, and `git diff HEAD` is empty. The pin rerun: **17 passed**. ## Tests moved - `imported-defaults-8317.test.ts`: `IMPORTED` gains `['InlineGridColumnSchema', SpecInlineGridColumnSchema]`. The census "every symbol the mirrors import is covered by the differential above" requires it. The row is measured as the identity (no default, no `z.lazy`). - No `zod-mirror-parity` `KnownDrift` or `SPEC_DERIVED_PAIRS` row moves: - `ObjectFormSchema` was already spec-derived. - Its `KnownDrift` entry names only the five runtime-slot handler keys. - The TypeScript and zod faces of `columns` now carry the same type. Before, it was `any[]` against `z.any()`, so the ledger saw nothing either way. - `object-form-unmirrored-members-6152.test.ts`: the `subforms` row has no columns in either value, keeps its verdicts, and is not edited. **Repo-wide sweep.** I grepped for `subforms` and `columns` across tests, docs, JSON and scripts. No test anywhere asserts that `objectui validate` accepts a bogus column or a typed-currency column with `scale`, so no pin flips. The two doc examples above were the only documents writing a column the narrowed faces refuse. ## Gate readings, at head `ce55b465` - `pnpm exec vitest run packages/types/` (under the lock): `Test Files 355 passed (355)`, `Tests 9492 passed (9492)`. - `packages/types` `type-check` (the build program, the examples program and the test program): exit 0. - The import side of the narrowed TypeScript face: `type-check` for `@object-ui/plugin-form`, `@object-ui/plugin-view` and `@object-ui/app-shell` exits 0, and each echoes `type-check: Done`. They ran against the closure that `check:doc-snippets --build-filter` names, built with pnpm, not turbo. - `vitest run packages/cli/` plus the `subforms`-adjacent consumer tests (`deriveMasterDetail.currencyScale-10783`, `subformHosts`, `MetadataProvider.merge`): 28 files, 390 tests, all passed. - `eslint --no-inline-config --format json` over the four touched TypeScript files: 4 files, 0 errors. The warnings are existing `no-explicit-any` hits in `objectql.ts` and `objectql.zod.ts`; the pin file has 0. - This is a narrowing of `pnpm lint`, and it is declared. Population: `eslint.config.js` lints `**/*.{ts,tsx}`, so the two edited `.md` / `.mdx` files are outside it. File count: 4, from the JSON output. Invariance: the config sets no `parserOptions.project` or `projectService`, so type-aware linting is off, and this diff cannot move the verdict on any untouched file. The full run belongs to CI. - `check:doc-snippets`: 777 of 777 blocks judged, 0 failed. - Exit 0 for each of: `check:doc-examples`, `check:doc-types`, `check:doc-fences`, `check:doc-example-ids`, `check:doc-example-readers`, `docs:check-links`. - `check:new-line-citations`: 0 new. `check:control-bytes`: OK. - Changeset checkers, exit 0 for each: `check:changeset-claims`, `check:pending-changeset-literals`, `changeset:check` (fixed and no-major), `check-changeset-presence` and `check-changeset-overwrite`. - Exit 0 for each of: `check:spec-symbols`, `check:spec-floors`, `check:installed-pin-claims`, `check:phantom-deps`, `check:unused-deps`, `check:readme-exports`, `check:test-path-roots`, `check:component-surface-parity`. - `check:spec-floors` was first refused with `no-artifact` on the unbuilt `@object-ui/plugin-tree`. I built that package and reran it. - `check-governed-queue-guard --test` over the seven paths: NOT GOVERNED. - **NOT MEASURED locally, left to CI:** the full `pnpm test`, the tree-wide `pnpm lint`, and `check:published-dist`. - **Patch round 1, at head `b1d035bb`:** `@object-ui/plugin-form` `type-check` passes, with its dependency closure built by pnpm, not turbo. `vitest run` on `deriveMasterDetail.currencyScale-10783.test.ts` passes, the renamed test among them. eslint over the two touched files reports no errors; its warnings are existing `no-explicit-any` in code. `check:new-line-citations` reports no new citation, `check:control-bytes` passes, and the changeset checkers exit 0. The readings above are at `ce55b465`; this round touched only the two `plugin-form` files. ## Acceptance notes 1. **The parse output.** `InlineGridColumnSchema`'s `readonlyWhen` and `requiredWhen` carry the spec's ExpressionInput pipe, so `safeValidateSchema` returns a column's string predicate as `{ dialect: 'cel', source }`. Measured: `readonlyWhen: 'record.locked'` comes back as the envelope. The input document is not changed. - This is the spec schema's own output, taken by reference. - Four other imported crossings already return transformed values. I counted them by walking `AnyComponentSchema`: a page's `slots.header` and its `visibleWhen`, `listViews` `exportOptions`, and an action `params` entry. - objectui#8347's Q6 = B ("a string stays a string") was ruled for `BaseSchema.visibleWhen`. Nothing extends it to imported subtrees. - The changeset states it. Flagged for the contract review. 2. **Stale prose, fixed in patch round 1 (`b1d035bb`).** Seat ruling: a PR fixes the sentences it makes false. - `reportCurrencyColumnScale`'s docblock in `packages/plugin-form/src/deriveMasterDetail.ts`, the `deriveMasterDetail.currencyScale-10783` test header and one of its test names said a subform's `columns` is `z.array(z.any())` in the `object-form` mirror and in the spec's `FormViewSchema`, and that the spec does not judge that path. - They now say both validators judge a declared column with `InlineGridColumnSchema`. They keep the report's reason: neither zod face sees an identity-only `{ name, scale }` column whose child field is a currency (only `defineStack` does, at publish, objectstack-ai/objectstack#20927), and nothing runs either validator between a stored or code-built form view and the render. - Comment and test-name bytes only. The TypeScript printer with `removeComments` prints `deriveMasterDetail.ts` identically before and after, and the test file differs only in that one test-name string. The old test name was referenced nowhere else in the tree. - Left as written: the released `@object-ui/plugin-form` and `@object-ui/fields` CHANGELOG text that says the same, which is history. 3. **Looser local copies.** `DrawerForm.tsx` and `ModalForm.tsx` in `plugin-form` declare their own `subforms` item types with `columns` as `any[]`. They receive values from the now-typed face, so nothing breaks, but they are looser than the spec. Carrier: none. Serial: objectui#11608's PR objectstack-ai#11616 is still open. Its files (`packages/types/src/index.ts` and the objectui#8347 pin) are disjoint from these. `origin/main` has not moved since this branch was cut (`2abec3a9`), so the merge before opening was a no-op. --- _Generated by [Claude Code](https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Leehom <pm@objectstack.ai>
Fixes #11608
Clause-②: yes (narrowing)
Retires the
PartialSchemautility type (generic over a node typeT) from@object-ui/types. It had no reader. The census ran at work time before any write; it found no reader in any repository it could read, so the seat's premised ruling (claim5981086062, executing triage5979662969) applies: retire, with no replacement alias.Census, run before the first edit
Hits are listed by file, never as a count. "Lit" means the control query returned a non-empty result in the same checkout, so a zero beside it is a reading.
b403bb36, the branch base (origin/main)git grep -n -w PartialSchemaover every tracked filepackages/types/src/index.ts. The import, and the "PartialSchemaworks as written" section, ofbase-schema-closed-face-8347.test.ts. Listed apart: the pending.changeset/8347-baseschema-closed-face.md, where one bullet describes the type, andpackages/types/CHANGELOG.md(history). A case-insensitive substring query outside CHANGELOGs found nothing more.git grep -l -w SchemaByType: litorigin/mainda40a5f6, read through the owned refrefs/os-dev/issue-11608/os-main(no file edited)git grep -n -w PartialSchema, thengit grep -n -i partialschemagit grep -l '@object-ui/types': lit4054ec26, an anonymous shallow clone ofobjectstack-ai/hotcrmgit grep -n -i partialschemagit grep -l '@object-ui/': lit. There is no@object-ui/typesimport to use as the control: hotcrm's metadata-reference test says the repo declares no@object-ui/*dependency.The objectui hits are the declaration, its own docblock, the #8347 pin, and a pending note that describes the type. None of them reads it. With no reader anywhere the census reached, the ruling is retire. For any consumer the census could not see, cloud included, the changeset's migration is the answer.
What this removes
packages/types/src/index.ts: thePartialSchemaexport and its whole docblock. ⛔ No replacement alias. The siblingSchemaByTypeand everything else stay byte-identical.base-schema-closed-face-8347.test.ts: thePartialSchemarows. That is the import, section 4 ("PartialSchemaworks as written now"), and the two bindings that section fed into the vitest leg, whose length assertion moves with them. TheButtonSchemaimport goes too, because only section 4 read it. Sections 1 to 3 and the header are byte-identical, and the numbering stays coherent because section 4 was the last..changeset/8347-baseschema-closed-face.mdis not edited (see the note under review notes).The census pin
packages/types/src/__tests__/partial-schema-retired-11608.test.tsreads the retirement through three instruments. Each one has a lit control.tsc -p tsconfig.test.json, chained from the package'stype-check) reads a@ts-expect-errorrow on the root barrel (import('../index').PartialSchema). Its control is the siblingSchemaByTypethrough the same import form, with no directive.exportsmap (vitest) reads every entry'stypestarget, maps it to thesrc/module thattscemits it from, and asserts that each module exists and that none of them namesPartialSchema. An entry added later is covered automatically. Control: the same read findsexport type SchemaByTypein the.entry's source. It readssrc/, notdist/, because the per-PRtestjob builds nothing first.git grep -nEfor the word over every tracked file except*CHANGELOG.md,.changeset/and the pin itself. A re-export chain is caught here: whatever module declares the alias names it, and that module is tracked. Control: the same probe findsSchemaByType.The pin also compiles the changeset's TO spelling for
ButtonSchema. It checks that the spelling keeps the same key set, thattypestays required (a@ts-expect-errorrow), and that a misspelled key is refused (a@ts-expect-errorrow).Ablation (one-off proof, nothing left in the tree)
The fix was committed first (
de3dcf5b). The mutation went through objectstack'sscripts/ablation-replace.mjs, which putsexport type PartialSchemaback intoindex.tsright afterSchemaByType. Its own on-disk evidence showed the anchor going from one hit to none, the replacement from none to one, and the blob changing. Both readings were taken on the mutated tree:PartialSchema" listed. -> packages/types/src/index.ts. "the symbolPartialSchemaappears nowhere: ..." listed the restoredindex.tsdeclaration. Both lit controls and the TO-spelling leg stayed green.TS2578: Unused '@ts-expect-error' directive, on the_RetiredFromTheRootBarrelrow.git checkout HEAD --on the absolute path. The blob after the restore equals the HEAD blob (333196a9), andgit diff HEADis empty. After that, all ofpackages/typesvitest andtype-checkwent green atde3dcf5b.Changeset
.changeset/11608-partialschema-retire.md,@object-ui/types: minor. It carries a breaking banner, the line**Clause-②: yes (narrowing)**, and a FROM and TO pair:PartialSchemaimport and annotation.PartialofTintersected with{ type: T['type'] }, written inline.The TO spelling was checked to compile (generic and concrete) in a scratch program, with a control that went red. It is also pinned in the census pin above.
Gates (local, at
de3dcf5b)packages/types:type-check(all threetscprograms) green. vitest overpackages/types/green. Both ran underos-verify-lock.pnpm linton the touched files: eslint over the three touched.tsfiles is clean in its JSON output. This is a declared narrowing. The population is eslint's own config, and the file set is the three paths the JSON names. The invariance: the config enables no type-aware linting (noparserOptions.projectorprojectService), so this diff cannot change the verdict on any untouched file. The fullpnpm lintis CI's.pnpm --filter … run build, no turbo cache touched).dist/index.d.tsnamesSchemaByTypeand notPartialSchema.pnpm check:readme-exportsandpnpm check:doc-snippets.pnpm check:doc-typesis green too, although no doc names the type.pnpm check:new-line-citations(zero new citations) andpnpm check:control-bytes.changeset:check(fixed and no-major),check-changeset-presence,check-changeset-overwrite(one added, nothing pre-existing modified),check:changeset-claimsandcheck:pending-changeset-literals. All green, and none objects to the two pending notes describing the same type.type-check:coverage,check:test-path-roots,check:spec-symbols,check:unreferenced-sources,check:component-surface-parity(report-only) andcheck:published-dist. All exited 0.git grep -w PartialSchemaafter the change, outside CHANGELOGs, hits only the two pending changesets and the new pin. Its control,SchemaByType, is lit.Notes for review
.changeset/8347-baseschema-closed-face.mdhas a bullet saying the alias "works as written". This PR removes the alias. Per the dispatch, the spec(types)!: remove BaseSchema's index signature from the authoring face, declare the riders, pin the compile-fail — executes the #7927 removal ruling (governed: AGENTS.md #0.1) #8347 note is not edited here. This PR's changeset says that it supersedes that bullet. If both ship in the same release, the seat may want to drop or amend that bullet. No changeset checker objected.origin/mainhad not moved pastb403bb36when this was opened, so merging it was a no-op. feat(types,fields)!: camelCase the grid widget's eight field-level keys; the snake_case spellings are refused by name (objectui#11610) #11614 (which also touchespackages/types/src/index.ts, in a disjoint region) was still open.Generated by Claude Code