Repository navigation
feat(types)!: refuse both content channels on the E3 residual (nine object views, detail-view, email, password) and repair the shorthand Omit erasure (objectui#9256) - #10897
Conversation
…op the shorthand Omit erasing inherited members Part of objectui#9256 (the 15-key E3 residual the ruling on objectui#8284, Q2 A, moved onto that card). Twelve registrations have a published arm yet their renderer reads neither content channel: the nine object-* views, detail-view, email and password. Both body and children are now ?: never on the TypeScript face and a by-name refusal on the zod mirror (neitherContentChannel, a pair of retirementTombstone members), each kept a member so the parity ratchet's key sets stay equal. The message names what the node renders instead. InputShorthandSchema and UiCalendarSchema were spelled as a plain Omit over an interface carrying BaseSchema's index signature, which erased every inherited member on the published face. Both now inherit through OmitDeclared, a key-remapping Omit that keeps the named members; the two Omit COLLAPSE rows leave the MirroredUndeclared ledger and the inherited onChange runtime slot enters KnownDrift. br, hr and img are held: their only arm is the 38-tag HtmlElementSchema family, whose renderer draws children for the other 35. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk
…ding a call A computed spread in a zod arm is a shape check-handler-key-read-sites cannot follow, and the gate stops judging an arm whose member set it cannot resolve: the spread blinded it on all eleven arms and turned its detail-view onTabChange waiver stale. Each arm now declares body and children explicitly as retirementTombstone members fed one string from neitherContentChannelGuidance, so the gate reads every arm again (0 reads left unjudged). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk
|
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: Isolated adversarial review of PR objectui#10897 (objectui#9256, family D, ① Derived judgmentsReads — each of the 12 reads neither channel, re-derived at the source.
Shape — PR objectui#9589's family-D shape on both faces. TS:
Collateral.
Producers.
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
One conflict, in packages/types/src/__tests__/zod-mirror-parity.test.ts: both sides rewrote the two KnownDrift header figures. Resolved by RECOUNTING the merged KnownDrift table with the test's own AST reader, not by picking a side: base 0c50f18 49 entries / 89 keys objectui#10874 (c30c8dd) drops ai.zod.ts#AIFormAssistSchema -1 entry / -2 keys objectui#9256 (this branch) adds form.zod.ts#InputShorthandSchema +1 entry / +1 key merged 49 entries / 88 keys Both header sites (the population bullet and the KNOWN_DRIFT ratchet restatement) now read 49 / 88, and the bullet keeps both histories in landing order. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk
✅ 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 review of PR objectui#10897 at the merge head, after record ① Derived judgmentsThe merge itself. Net change at the new head versus net change at the old head. The two unified diffs, normalised by dropping hunk headers and blob ids, differ in three places and nowhere else: the The recount, re-derived from the merged table with the compiler API (the
The dev's table is right in every cell. Both header sites read the merged figure — the population bullet "49 entries in Consistency with what landed on The changeset is byte-identical to the reviewed one and cites no ledger figure; every sentence checked in record ② Semver levelUnchanged: ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
…waiver (objectui#10872, batch 2) (objectstack-ai#10908) Part of objectstack-ai#10872 Clause-②: yes **The hold is lifted.** Seat ruling option (c), posted on objectui#10872, held this PR behind objectui#10909. That card merged as PR objectui#10944 (`dd0d78f74d`): the `element:number` renderer now reads `dataSource.object` (with `properties.filter` AND-combined with the binding's), and the registration declares `dataSource`. So the `dataSource` form this arm accepts now renders its metric. The code here was never narrowed. A base merge of `main` gave one CI run on the combined tree before landing. Batch 2 of objectui#10872: `element:number`, the one ADR-0080 public block with a spec `ComponentPropsMap` row that batch 1 held, now validates on both faces in both binding forms the spec accepts. Its row requires `object`, and the spec's props gate waives exactly that member beside a `dataSource.object`; the arm mirrors that waiver by reference. The card stays open for the blocks still held (Acceptance notes). ## What changed - **`packages/types/src/zod/public-blocks.zod.ts`**: `ElementNumberBlockSchema`, a member of `PublicBlockComponentSchema`. - `properties` is the spec row by reference with `object` alone made optional (below). - `dataSource` is declared as the spec's `ElementDataSourceSchema`, by reference through `stripImportedDefaults`. It is the same schema `PageComponentSchema.dataSource` is. - A node refinement (`elementNumberObjectIsSupplied`) restores the row's requiredness wherever the waiver does not apply. - The module docblock gains the waiver section and drops `element:number` from the held list. - **`packages/types/src/zod/index.zod.ts`**: `ElementNumberBlockSchema` is on the `./zod` barrel. - **`packages/types/src/zod/imported-defaults.ts`**: comment only. The walker docblock's `lazy` paragraph now names the one clean import it rebuilds (see "The waiver"). - **`packages/types/src/zod/README.md`**: the public-block group lists the arm. - **Ledgers**: - one `EXCLUSIONS` row in `zod-mirror-parity.test.ts`; - two `IMPORTED` rows (`ElementNumberPropsSchema`, `ElementDataSourceSchema`) in `imported-defaults-8317.test.ts`, plus `REBUILT_CLEAN` (see "The waiver"); - `element:number` added to the batch-1 pin file's measured population. - **Ratchet**: `NAMESPACED_REFUSED_AT_TYPE` falls 398 → 397 with the batch-1 messages. The bare pin stays 76. A batch-2 row names the armed key, and two in-process `objectui validate` rows cover the change end to end. - **Changesets**: - new: `.changeset/10872-element-number-arm.md` (`'@object-ui/types': minor`, the `Clause-②: yes` line); - dated notes in the objectui#10533 form, frontmatter byte-identical, on `.changeset/10872-public-block-zod-arms.md` (its "less `element:number`" and "Not armed in this release" item stop describing the release) and on `.changeset/8317-strip-imported-defaults.md` (its "identity function on a subtree with nothing to strip" no longer holds for `ElementDataSourceSchema`). - **Merge**: `origin/main` `9f0c84a448` was merged in, including objectui#10897 (Serial). ## H1: measured on `main` (`6a7f24e92`), before any edit Both forms are refused at `type`, on both faces (`safeValidateSchema`, the face `objectui validate` runs, and `StrictAnyComponentSchema`): - `{ "type": "element:number", "properties": { "object": "opportunity", "aggregate": "count" } }` gives `[{"code":"invalid_union","path":"type","message":"Invalid input"}]`, tolerant and strict alike. - `{ "type": "element:number", "dataSource": { "object": "opportunity" }, "properties": { "aggregate": "sum", "field": "amount" } }` gives the same output, tolerant and strict alike. Taught or shipped `element:number` documents, and the form each uses. Each zero was read beside a lit control on the same instrument: - **objectui**: 0 in `content/docs`, `skills`, `examples` (control: `element:text` hits `skills/objectui/rules/protocol.md`). The one producer is the page designer's block inspector (`block-config.ts` `element:number` fields): an object picker writing `properties.object`, which is the **props form**. - **objectstack `origin/main`**: 0 in `examples` (the showcase included; control: `element:text` hits 5 example page files). `content/docs/ui/pages.mdx` and `content/docs/references/ui/page.mdx` name the type in the vocabulary only, with no document. - **objectstack spec fixtures** (by test name): - "should accept element:number component" uses the **props form**; - "PageComponent dataSource integration" uses **both forms at once** (`properties.object` and `dataSource.object`); - the migration `element-number-filter-rule-array` prescribes a **props-form** bag with a rule-array `filter`. - Nothing taught or shipped uses the dataSource-only form for `element:number`. The spec gate's own dataSource-only specimen is `element:record_picker`. ## The waiver, and how it is by reference - **The spec gate**: `validateComponentProps` (`@objectstack/lint`) skips, per `DATASOURCE_SUPPLIED_PROP` / `suppliedByDataSource`, the row issue at `object` when `dataSource.object` is a non-empty string, and judges a bag only when present. Its test "does not report the required `object` prop when `dataSource` supplies it" pins that. - **The bag**: `stripImportedDefaults(ElementNumberPropsSchema).partial({ object: true })`. - zod 4's `.partial(mask)` clones the row's own def: strictness and the custom unknown-key guidance travel. The row's own registry metadata does not, because a clone sets no parent. The row has no top-level `.describe()`, so nothing is lost. - It wraps only the masked member in `ZodOptional` around the row's own `object` schema, and hands every other member through as the same object. - A pin asserts this member by member: every non-`object` member is `toBe` the row's, and `object` unwraps to the row's. - `.partial()` throws on an object carrying refinements. That is the loud answer if the spec ever adds one: the waiver is then re-decided rather than silently losing the check. The row has none on installed 17.4.0 or on objectstack `origin/main`. - **The refinement**: a bag that is a record and omits `object` is refused at `properties.object` (code `custom`, `params.code` `ELEMENT_NUMBER_OBJECT_REQUIRED`), unless `dataSource` is a record whose `object` is a non-empty string (the gate's `strName`). The message names both fixes. It runs with `when: () => true`, the `objectql.zod.ts` named-view precedent, so a missing `object` is reported beside another bag issue, as the gate reports it. - **One deliberate difference from the gate's code**: - The waiver covers an OMITTED `object`, which is how the gate's own docblock states it ("the one prop whose absence this rule does NOT report") and how this card's order states it ("may omit `properties.object`"). - The gate's `suppliedByDataSource` matches the issue by path alone, so it also passes `"object": 7` beside a binding. The arm leaves that to the row to refuse. - A pin row holds it: "the waiver covers an OMITTED `object` only". - **Reading of "neither"**: a node carrying a bag with no `object` and no binding is refused. A node with no bag at all (`{ "type": "element:number" }`) is accepted, as the gate skips a node without `properties` and as every batch-1 arm leaves its bag optional (the batch-1 minimal-document row covers it). - **`dataSource` through the boundary**: `ElementDataSourceSchema` carries no default but reaches a `z.lazy` (the recursive filter clause), and the walker's `lazy` arm always rebuilds. So it is the first clean import the boundary returns as an equal-answering clone rather than the spec's own object. Measured: the lazy count stays 4, because it is the same node already in the graph. - `imported-defaults-8317.test.ts` went red on exactly its two invariants: the identity row for `ElementDataSourceSchema`, and the lazy-owner row whose message says "update the docblock". - So the file now names the set: `REBUILT_CLEAN`, held equal to what the graph re-derives, asserted not reference-equal. The accept-set differential already covers every `IMPORTED` row. - The walker docblock says the same. No strip moved. ## Pins and ablation New pin file `packages/types/src/__tests__/element-number-arm-10872.test.ts`, the batch-1 pin population, and the ratchet file. - **Red on base, green on head.** - `public-blocks.zod.ts` and `index.zod.ts` were set to base `6a7f24e92` (on disk, `ElementNumberBlockSchema` count 0 in both) at head `3293f7a084`: 3 files, `27 failed | 107 passed (134)`. Restored with `git checkout HEAD --`, the HEAD blob hash equal to the disk hash and `git diff HEAD` empty. - Red on base: - minimal `properties.object` document, both faces; - `dataSource.object` document with no `properties.object`, both faces; - neither, refused at `properties.object`; - reported beside another bag issue; - empty or non-string `dataSource.object` supplies nothing; - omitted-only waiver; - spec-invalid member refused at the member, by reference (`format`, and the record-form `filter` the spec migration retired); - undeclared bag member refused by name; - taught and shipped documents validate; - named export and union member; - the three by-reference rows; - the differential's 5 accept-verdict probes; - ratchet: the count row, and the batch-2 row; - the two `objectui validate` rows; - the batch-1 file's `element:number` rows and its union-population row. - The differential's 9 refuse-verdict probes are green on base by construction: base refuses every `element:number` document at `type`. - Head: `5 passed (5)`, `354 passed (354)` over the new pin file, the batch-1 pin file, `imported-defaults-8317`, `zod-mirror-parity` and the ratchet file, at `278ee02ff2`. - **Ablation: remove the waiver.** - At head `3293f7a084`, the line `if (dataSourceSuppliesObject(node)) return;` was replaced by a marker comment through a script that refuses unless the anchor count is exactly 1. On disk after the mutation: anchor count 0, marker count 1. A trap restored it: HEAD blob `c848ddf759` equals the disk hash, anchor 1, marker 0, `git diff HEAD` empty. - Result `3 failed | 131 passed (134)`, and exactly the dataSource-form rows reddened: - "a `dataSource.object` document with no `properties.object` is accepted on both faces"; - the differential row "bag without object, dataSource.object"; - the `objectui validate` row "validates `element:number` in both of its binding forms". - The subject resolves to `src` in both suites (relative imports in `packages/types`; the root vitest alias maps `@object-ui/types/zod` to `src` for the CLI file), so no `dist` build sits between the mutation and the reading. ## The arm-list consumer sweep (a gate) - **Search**: `git grep -l` over every test file for `AnyComponentSchema`, `nodeOracles`, `KNOWN_SCHEMA_TYPES`, `PUBLIC_BLOCKS`, `PublicBlockComponentSchema`, `block-config-schema-parity-8216`, `registry-inputs-spec-parity`, `StrictAnyComponentSchema`, `stripImportedDefaults`, `ElementDataSourceSchema`, and `handler-key-read-sites` (the handler-key checker's arm table). That is 111 files at the final tree. - **Runs**, all under the verify lock: - `packages/types/` and `packages/cli/` whole, plus the 8 `scripts` hits: `294 passed (294)` files, `6398 passed (6398)` tests, at `379833972c`; - the 46 other hits: `46 passed (46)` files, `824 passed (824)` tests, at `379833972c`; - `zod-subpath-single-module-8598.dist.spec.tsx` in the opt-in `dist` project on a rebuilt `@object-ui/types`: `1 passed (1)`. - Before the merge, the same population (110 files) ran green at `3293f7a084`: 63 + 46 files. - **Every file's verdict is PASS**: - `apps/console` (7): `component-input-union-specimens.test.ts`, `element-data-source-input-injection.test.tsx`, `ga-honoured-inputs-author-reach.test.ts`, `html-tier-manifest.test.ts`, `public-contract.test.ts`, `registry-inputs-spec-parity.test.ts`, `unfulfilled-chart-stubs-8760.test.tsx` - `examples/schema-catalog` (2): `overlay-trigger-mirror-6939.test.tsx`, `safe-validate-corpus-6318.test.ts` - `packages/app-shell` (4): `ReportView.dataSourceObjectKey.test.tsx`, `phase1-page-blocks.render.test.tsx`, `block-config-schema-parity-8216.test.ts`, `page-tabs-item-value-8278.test.tsx` - `packages/cli` (8): `check-known-types.test.ts`, `check-unknown-type-case-suggestion.test.ts`, `check-validity-recogniser.test.ts`, `registered-types-validate-ratchet-10859.test.ts`, `retired-view-dashboard-key-9533.test.ts`, `unfulfilled-chart-stubs-retired-8760.test.ts`, `validate-root-path-line.test.ts`, `validate-union-arm-selection.test.ts` - `packages/components` (6): `page-container-authorable-keys.test.tsx`, `react-page-scope.test.tsx`, `record-picker-inputs-spec-parity.test.ts`, `text-input-inputs-spec-parity.test.ts`, `container-declaration-census.test.tsx`, `container-declaration-ratchet.test.tsx` - `packages/core` (2): `html-tier-intrinsics.test.ts`, `public-tier.test.ts` - `packages/layout` (1): `page-header-authorable-keys.test.tsx` - `packages/plugin-calendar` (3): `ObjectCalendar.recordSourceMembers-8314.test.tsx`, `ObjectCalendar.sortMembersReachTheWire-8171.test.tsx`, `dataKeysAreDeclaredAndHonoured-8314.test.ts` - `packages/plugin-dashboard` (3): `dashboardAuthoredInputs.test.tsx`, `objectMetricDrillDownMembers-8071.test.tsx`, `objectMetricTrendMembers-8071.test.tsx` - `packages/plugin-detail` (5): `detail-handler-slots-7804.test.tsx`, `detail-view-handler-slots-9447.test.tsx`, `detailSectionHeaderColorEnum-6955.test.ts`, `recordDetailsInputs.spec-parity.test.ts`, `recordRelatedListInputs.spec-parity.test.ts` - `packages/plugin-form` (1): `ObjectForm.elementDataSource.test.tsx` - `packages/plugin-grid` (3): `gridArrayArmOrderby-8973.test.tsx`, `gridDataInputContract.test.ts`, `gridFilterInputSpelling.test.tsx` - `packages/plugin-kanban` (1): `handlerKeyDispositionsMeasured-7804.test.tsx` - `packages/plugin-map` (1): `index.bareMapKeyRetired-10393.test.tsx` - `packages/plugin-tree` (2): `ObjectTree.overlayTitleI18n.test.tsx`, `registration.publicTier-10064.test.tsx` - `packages/plugin-view` (1): `objectViewHostSurface.test.tsx` - `packages/react` (2): `ElementDataSourceGate.malformedFilter-10789.test.tsx`, `ElementDataSourceGate.test.tsx` - `packages/sdui-parser` (1): `member-kind-8067.test.ts` - `packages/test-support` (1): `spec-tombstones.test.ts` - `packages/types` (49): `action-callback-retired-7068.test.ts`, `ai-insights-retired-8800.test.ts`, `ai-node-faces-agree-10874.test.ts`, `ai-zod-arms-10859.test.ts`, `any-component-union-fanout.test.ts`, `app-hidden-catalogue-flag-7542.test.ts`, `arm-named-export-8784.test.ts`, `bare-kanban-node-key-retired-8802.test.ts`, `block-family-retired-4895.test.ts`, `breadcrumb-object-tree-nameable-7917.test.ts`, `calendar-flat-color-allday-8466.test.ts`, `chart-axis-config-7690.test.ts`, `chat-message-avatar-keys-7295.test.ts`, `combobox-command-optional-6033.test.ts`, `content-channel-e3-residual-9256.test.ts`, `content-channel-family-d-9256.test.ts`, `content-channel-per-component-8284.test.ts`, `dashboard-daterange-spec-10334.test.ts`, `detail-view-field-options-10296.test.ts`, `element-number-arm-10872.test.ts`, `gantt-flat-config-declared-keys.test.ts`, `imported-defaults-8317.test.ts`, `imported-defaults-describe-9034.test.ts`, `imported-defaults-rest-less-tuple-9088.test.ts`, `input-wrapper-class-mirrored-8072.test.ts`, `kanban-calendar-filter-sort-8174.test.ts`, `nav-separator-mirror-refusal-10867.test.ts`, `navigation-spec-parity.test.ts`, `node-recursion-point-8344.test.ts`, `node-slot-registered-arms-8499.test.ts`, `object-chart-axis-config-10518.test.ts`, `object-kanban-group-by-limit-7322.test.ts`, `object-view-unmirrored-keys-7779.test.ts`, `objectql-union-arms-7363.test.ts`, `page-body-arity-8310.test.ts`, `phase2-schemas.test.ts`, `public-block-arms-10872.test.ts`, `registry-meta-carry-9102.test.ts`, `shorthand-input-type-refusal-8762.test.ts`, `spec-subschema-parity.test.ts`, `strict-authoring-face-8345.test.ts`, `text-value-retired-6951.test.ts`, `timeline-item-element-6356.test.ts`, `timeline-readme-schema-example-10824.test.ts`, `tree-view-data-retired-6951.test.ts`, `zod-lazy-getter-identity-7918.test.ts`, `zod-mirror-authors-no-defaults-7735.test.ts`, `zod-mirror-parity.test.ts`, `zod-subpath-single-module-8598.dist.spec.tsx` - `scripts` (8): `body-dialect-census.test.ts`, `check-component-surface-parity.test.ts`, `check-handler-key-read-sites.test.ts`, `check-installed-spec-pin-claims.test.ts`, `check-prompt-component-keys.test.ts`, `component-node-vocabulary-7434.test.ts`, `known-schema-types-derivation-5115.test.ts`, `spec-symbol-ledger-types-7265.test.ts` ## Fix round 1 (head `fe8f6a6aad`) Contract review at `278ee02ff2` was FAIL on sequencing, not code; see the held note at the top. It asked for one text-and-test commit: - **Changeset wording.** `.changeset/10872-element-number-arm.md` now says an undeclared prop "is refused by name (`unrecognized_keys` on `properties`)". The out-of-range `format` and the record-form `filter` stay "refused at that member", which is what the row reports for them. The `dataSource` example is kept: it becomes true of the renderer when objectui#10909 lands. - **Docblock.** The `.partial()` docblock no longer claims the row's `.describe()` text travels. The clone sets no parent, so registry metadata does not inherit, and the row carries no top-level description anyway. Comment only. - **REBUILT_CLEAN, measured where the rebuild happens.** Four new rows in `element-number-arm-10872.test.ts` go through `dataSource.filter`, which is the member whose subtree reaches the recursive filter clause's `z.lazy`, rebuilt by the boundary: - a record filter, accepted; - a nested `$and` / `$or`, accepted; - `filter: 5`, refused; - a non-object clause inside `$and`, refused. Each row asserts the spec's own `ElementDataSourceSchema` verdict first (non-vacuity), then the same verdict on the arm's member, on `PageComponentSchema`, and on the node through both faces. The strict face rebuilds that lazy a second time. `-t 'through the rebuilt binding answers'` gives `4 passed | 28 skipped (32)`. - **Retitle.** The differential is titled as what it is: the spec node schema plus the test's model of the spec gate's documented reading (`specGateAccepts`), not the gate itself. - **Readings at `fe8f6a6aad`**, all under the verify lock: - `element-number-arm-10872.test.ts`, `imported-defaults-8317.test.ts` and the ratchet file give `Test Files 3 passed (3)` and `Tests 228 passed (228)`; - `pnpm --filter @object-ui/types run type-check` exits 0; - `check:control-bytes`, `check:new-line-citations` (0 new), `check-changeset-presence`, `check-changeset-no-major` and `check:changeset-claims` all exit 0. - **Merge-tree:** `git merge-tree --write-tree` against a fresh `origin/main` `733fd5ac65` is clean (exit 0). `main` was not merged in. ## Gates Derived by hand from the root `package.json` and `.github/workflows/`: objectui has no `dispatch-gates.mjs`. Each line gives the verdict the gate prints, at head `278ee02ff2` unless noted. - `node scripts/check-changeset-presence.mjs`: exit 0, "9 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)". - `check-changeset-no-major.mjs` and `check-changeset-fixed.mjs`: exit 0. - `pnpm check:changeset-claims`: exit 0. It is report-only, and it flagged 22 pending changesets naming a touched file. Each was read. The two sentences made false got the dated notes above. Of the rest: - `8344-node-recursion-point-redirect.md`'s module counts are unchanged (no new category module); - `8317-strip-imported-defaults.md`'s "107 component arms" census has been past since batch 1 and is left as a reading. - `pnpm check:pending-changeset-literals`: exit 0. - `pnpm check:handler-key-reads`: exit 0, "OK 129 arm(s) … 50 judged". - `pnpm check:element-data-source-declaration`: exit 0. - `pnpm check:spec-symbols`: exit 0. - `pnpm check:new-line-citations`: "VERDICT new-cross-file-line-citations: 0 new citation(s)". - `pnpm check:control-bytes`: exit 0. - `pnpm check:installed-pin-claims`: OK. - `pnpm check:test-path-roots`: exit 0. - `node scripts/check-governed-queue-guard.mjs --test` over the 12 changed paths: "NOT GOVERNED". - **Type checks**, at `379833972c`, after the merge: - `pnpm --filter @object-ui/types run type-check`: exit 0. Its `tsconfig.test.json` includes the pin files; it reported a TS2559 in the new pin before the fix. - `pnpm --filter @object-ui/types run build`: "dist completeness: 1 package(s) complete". - `pnpm --filter @object-ui/cli run type-check`: exit 0, with the ratchet file in its program (`--listFiles`), on the `@object-ui/cli^...` closure built under the lock. - **ESLint, a declared narrowing.** - Population: the 8 changed `.ts` files, each resolved through the root `eslint.config.js`. - The `--format json` output has 8 files, 0 errors and 0 warnings. - Invariance: the config enables no type-aware linting (no `parserOptions.project` / `projectService`), and no local rule in `eslint-rules/` reads another file. So the diff cannot move the verdict of an untouched file. - **NOT MEASURED: `check:readme-exports`.** - Reason: PREREQUISITE NOT MET. 27 of 40 packages are unbuilt, and the gate's own population floor refuses the run ("the population COLLAPSED"). - This diff's one README edit (`packages/types/src/zod/README.md`) is a prose sentence and one name in a bullet list, with no fenced block and no import binding. - The `readme-exports` workflow builds and runs it. ## Serial - PR objectui#10897 (seat 1), which holds `zod-mirror-parity.test.ts` and `objectql*.ts`, merged as `775e0795bc` while this batch was in flight. - This batch's one row in that file is an `EXCLUSIONS` entry beside the batch-1 public-block rows, outside objectstack-ai#10897's hunks. - `git merge-tree --write-tree` of this branch against objectstack-ai#10897's head `31bb63bcbd` gives exit 0, and against `origin/main` `9f0c84a448` exit 0, both before and after the merge commit. No other open PR was re-listed. - `origin/main` `9f0c84a448` is an ancestor of the head (`merge-base --is-ancestor`, exit 0). ## Acceptance notes (what stays held) - **Still refused at `type`**, unchanged from batch 1 per ruling `5862923926`: - `action:button`, `action:group`, `action:menu`, `action:icon`, and `element:definition-list` / `element:repeater`, waiting on objectstack-ai/objectstack#20371; - `record:line_items`, which has no spec row, per the spec's own string-arm ledger. - **Not declared here**: the flat-props channel and the other envelope keys (`events`, `responsiveStyles`, `aria`, `visibility`, `responsive`). `dataSource` is declared on `element:number` only, because this waiver needs it. - **The ratchet only falls**: namespaced 397, bare 76. - **The renderer gap, now objectui#10909, which this PR is held behind**: measured at runtime. The `element:number` renderer (`ElementNumberRenderer`, `@object-ui/components`) reads its object from the `properties` bag only and never reads `dataSource`. - Probe: a real `SchemaRenderer`, the real registry, and an adapter with `aggregate` / `find` spies. - `{ type: element:number, properties: { object: contact, aggregate: count } }` calls `aggregate('contact', …)` and paints `7`. - `{ type: element:number, dataSource: { object: contact }, properties: { aggregate: count } }` calls neither `aggregate` nor `find` and paints `—`. - So the dataSource form that the spec gate waives, and that this arm accepts, would validate on both doors (`os validate` and `objectui validate`) and render an empty metric if this PR landed first. That is why it is held. - This PR does not narrow the validator around it: a declared-but-unhonoured binding is an implementation gap on the renderer side. - Seam: `spec:PageComponentSchema.dataSource (ElementDataSourceSchema.object)` → `renderer:ElementNumberRenderer`. - **Filed upstream by the seat**: the spec gate's `suppliedByDataSource` waives any issue at `object`, not only its absence (objectstack-ai/objectstack#20400). The arm follows the gate's docblock and refuses a wrong-typed `object` beside a binding. - **Spec drift ahead**: the installed spec (17.4.0) types `ElementDataSourceSchema.filter` as the record form, while objectstack `origin/main` has moved it to the rule-array form (migration `element-data-source-and-object-block-filter-rule-array`). The arm follows at the next spec bump, by reference. Today the spec fixture's rule-array `dataSource.filter` is refused by the installed spec and by this arm alike, which is why the taught-document row carries that fixture without its `filter`. Session `session_014mXUNuFomfj24w7s1pZzhN` (`domain:ui` seat 2 dispatch, os-dev, `mode:subagent`). --- _Generated by [Claude Code](https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Part of #9256
Clause-②: yes
Clause-②
yes, as the claim declared: twelve published arms stop accepting an authoredchildren, and two published TypeScript faces stop erasing the members they inherit. Both narrow an accept set; a contract review is owed before landing.The E3 residual that the maintainer's ruling on objectui#8284 (comment
5861449497, Q2 A) moved onto this card: keys with a published arm whose renderer reads neither content channel. It also carries the ruling's execution parameter 3, theInputShorthandSchema/UiCalendarSchemaTypeScriptOmiterasure. objectui#9256 stays open for the rest of family D.What changed
object-grid,object-form,object-kanban,object-map,object-tree,object-view,object-gantt,object-calendar,object-chart,detail-view,email,password.body?: neverandchildren?: never, each with a docblock naming what the node renders instead.bodyandchildrenare tworetirementTombstonemembers fed ONE string from the newneitherContentChannelGuidancebuilder inzod/tombstone.zod.ts. Both stay MEMBERS, sozod-mirror-parity's key sets stay equal.bodywas already refused on these arms (byBaseSchema, objectui#6771), except on theemail/passwordTypeScript face, which theOmiterased. It is restated becauseBaseSchema's refusal nameschildrenas the remedy, andchildrenis dead here too.Omiterasure repaired:InputShorthandSchemaandUiCalendarSchemanow inherit throughOmitDeclared(form.ts, not exported). It is a key-remapping mapped type: it drops exactly the named keys and keeps every other named member and the index signature.ui:calendargains no refusal of its own. Its face now re-exposes whatCalendarSchemadeclares, so the published TypeScript face refusesbody,childrenand amodeoutside the declared enum, which the erased face accepted. Its mirror (a.extend()of that one) already refused them.ui:calendarrenderer anyway (renderers/form/calendar.tsx, typedCalendarSchema). It readsdefaultValue,modeandvalue, and no channel.br,hr,img. See "Held, and the question it leaves" below.Per-key measurement, taken on
origin/main0c50f1863before any editInstrument. The TypeScript compiler API, ⛔ not grep. It builds one program per workspace package from that package's own
tsconfig.json: 40 programs over 1573 source files, on a BUILT tree (turbo build 42/42 first), with@object-ui/typesresolving topackages/types/dist. It files every.body/.childrenread (property access, string element access, object destructuring) under the declared type of its receiver.ButtonSchema1,DivSchema1,CardSchema1,ContainerSchema1 (eachchildren).body/childrenreads under that typeobject-gridplugin-grid:object-grid(+ consoleregisterLazyof the same plugin)ObjectGridRenderer(any) →ObjectGrid·ObjectGridSchemaobject-formplugin-form:object-formObjectFormRenderer(any) →ObjectForm·ObjectFormSchemaobject-kanbanplugin-kanban:object-kanban(+ lazy)ObjectKanbanRenderer(any) →ObjectKanban·ObjectKanbanSchemaobject-mapplugin-map:object-map(+ lazy)ObjectMapRenderer(any) →ObjectMap·ObjectMapSchemaobject-treeplugin-tree:object-tree(+ lazy)ObjectTreeRenderer(any) →ObjectTree·ObjectTreeSchemaobject-viewplugin-view:object-view(and theviewalias)ObjectViewRenderer(any) →ObjectView·ObjectViewSchemaobject-ganttplugin-gantt:object-gantt(+ lazy)ObjectGanttRenderer(any) →ObjectGantt·ObjectGanttSchemaobject-calendarplugin-calendar:object-calendar(+ lazy)ObjectCalendarRenderer(any) →ObjectCalendar·ObjectCalendarSchemaobject-chartplugin-charts:object-chart(+ lazy)ObjectChartBlock(any) →ObjectChart·ObjectChartSchemadetail-viewplugin-detail:detail-viewDetailViewRenderer(any) →DetailView·DetailViewSchemaemailui:email(field:emailstands down)anywrapper spreads the node intoInputRenderer·InputSchemaOmit) → both refusedpasswordui:password(field:passwordstands down)emailemailemailbrui:brany), void tag: passesundefinedas the childschema?.childrenread is behind the void-tag guard)any)hrui:hrimgui:imgThe residual count stays 15: no key reads
childrentoday. 12 are narrowed and 3 are held.any-typed receivers, attributed rather than assumed.any-typed. Each hop body was read: none contains a channel read.childrendestructured off internal components' own props (SchemaRendererpasses no node child as a React child);document.bodyanderr.body;TreeNode.childrenon the record hierarchyObjectTreebuilds;FeedItem.body;record:alert's ownprops.body, which is another registration.schema?.childrenread sits behindisVoid ? undefined : ….Delegation hosts, checked for pass-through. Every
SchemaRenderercall in the ten object/detail packages was traced. The node it renders is built one of three ways:ObjectForm/ModalForm/DrawerForm/TabbedForm/SplitForm/WizardFormall build theirformnode that way, and theformrenderer DOES drawchildren, which is why this was checked;DetailView'sheader/footer/ tabcontent,ObjectChart's drill-down table;object-*node —ObjectViewassemblesobject-kanban,object-calendar,object-gallery,object-timeline,object-gantt,object-map,object-treeandobject-chartfrom named keys, and every one of those reads neither channel (for gallery and timeline:packages/plugin-listandpackages/plugin-timelinehold no node-channel read).No host spreads its own node into a node that reads
children.Generic traversers (
validateChildrenin core,sdui-parservalidateTree, the metadata-admin preview canvases, the vscode validator) walk children to validate or preview. They render nothing and are node-agnostic, as for every row family D already holds.objectui#9910 cross-check. None of the 12 narrowed registrations declares a
childrenslot input: a grep over their registration files finds 0 hits, and the lit control ishtml-elements.tsx/div.tsx, which do.Producers.
pnpm census:body-dialect --keysover the 15, plusui:calendarandinput, withdiv/card/page/button/p/h1as lit controls in the same pass: 8981 files, 2744 candidate nodes.childrenon any of the 15.bodyonui:calendar: it was the family-D TRIPWIRE test line (test bucket), moved by this change.div170 /card183 /page47children.The
Omitrepair — measured on the BUILT d.tsA probe program under
packages/componentsresolved@object-ui/typestopackages/types/dist/index.d.ts(--traceResolution). It carried 15@ts-expect-errorpins (the 10 object/detailchildren,emailchildren,passwordbody,emaillabel: 42, and theui:calendarchildrenandmode: 'default'), a lit control (const n: number = 'x'), and 12 channel-free controls.dist/form.d.tsdeclaresOmitDeclaredlocally and both heritage clauses use it.Red on base, then green — one-shot, the restore proven by state
The predictions were written to a file BEFORE the mutation. The mutation was
git checkout 0c50f1863 -- THE-SEVEN-DECLARATION-FILES, with every test file at HEAD. It was proven by 7/7 blob hashes equal to BASE plus anchor counts (neitherContentChannel0,OmitDeclared0). The run sat undertrap … EXIT INT TERMwith absolute paths.14fd13c51)childrenfamilies + 12body-message rows + 1 nested rowtsc -p packages/types/tsconfig.test.jsonEqpins) in the new file, plus 3 inzod-mirror-paritynaming the two re-collapsed pairs and the staleKnownDriftrow; 0 elsewhereThe restore was proven by 7/7 blob hashes equal to HEAD,
git diff HEADempty andgit statusclean, both after the restore and at the end.bodyrows on the ten object/detail arms are GREEN on base, by design:BaseSchemaalready refusedbodythere. Only their MESSAGE changes, and the message rows are the ones that turn red.?: neveris erased before vitest runs, so the TypeScript half is read bytscalone. The vitest run cannot see it.Serial constraints
0c50f1863). PRs objectui#10714, fix(core,plugin-view,types): the density fold tests an own key; the flat-property warning and NamedListView's docblock stop naming retired channels (objectui#10868) #10883 and fix(plugin-view): the host delegation reads a named view's remaining protocol members off the named view (objectui#10758) #10884 were open then; all three have since merged, and PR objectui#10875 was the base.layout.ts(TabItem) andlayout.zod.ts(TabItemSchema): untouched, becausebr/hr/imgare held and neither file is edited.content-channel-family-d-9256.test.ts: the only edits are forced ones, clear of PR objectui#10714's hunks, which sit in the ListItem and Tabs sections:ui:calendarTRIPWIRE said "when the collapse is repaired this line goes RED … turn it back into a@ts-expect-errorpair". The repair fired it.uiCalendarBodyarray entry (32 → 31) and the now-unusedUiCalendarSchematype import.objectql.ts: the insertion afterObjectViewSchema's last member is clear of PR objectui#10883'sNamedListViewdocblock edit.object-view-unmirrored-keys-7779.test.ts(PR objectui#10884): untouched.5863631182):KnownDriftheader figures inzod-mirror-parity.test.tsand landed first (c30c8dd4c). This branch mergedmainat that commit (merge commit31bb63bcb, no rebase, no force-push) and recounted the figures from the merged table; see "Merge round" under Gates.git merge-treeof the merge head againstmainat6a7f24e92, which carries all of them, reports no conflict.zod/index.zod.tsand thepackages/cliratchet (objectui#10872) are untouched.Collateral that the repair and narrowing made true or false — each measured, none widened
zod-mirror-parity.test.ts:OmitCOLLAPSE rows (32 + 26 keys) leaveMirroredUndeclared. Its figures move from 4 / 60 to 2 / 2, and the class note records the emptying.any. So this is not the "declare the keys" remedy that ledger refuses.InputSchema'sonChangeruntime slot. It entersKnownDriftas inherited drift: one entry, one key. After the merge with PR objectui#10890 (which tookai.zod.ts#AIFormAssistSchemaout, one entry and two keys), the table holds 49 entries / 88 keys, and both header sites say so.KnownDriftand re-arms here when a multi-arm entry lands.object-view-spec-parity.test.ts:body/childrenare redeclared envelope keys onobject-viewnow. They are triaged intoSANCTIONED_LOCALas refusal-only surface, likeviewTabBar, and the declared-surface figure moves 24 → 26.CalendarSchema's texts on both faces said the TypeScriptOmit"collapses … and carries no member". That became false; they are corrected.Omitheritage (index.ts,form-barrel-mirror-9406.test.ts) are corrected.scripts/check-handler-key-read-sites.mjscaught my first spelling. It was a computed spread (...helper(…)) that the gate cannot resolve, so it stopped judging all eleven arms and itsdetail-viewonTabChangewaiver read stale. The fix, the second commit, declares both members explicitly. Now 49 / 49 reads are judged and 0 are left unjudged.Held, and the question it leaves
br/hr/imgread neither channel, but their only published arm isHtmlElementSchema: ONE object over 38 tags, whose renderer drawschildrenfor the other 35 (p,h1, …). Refusing the key on three tags means splitting that family arm, or refining it per tag. Both change a published contract's shape.5859897857) reads: "leaving the three void tags to the tier because their arm is the shared HtmlElementSchema family whose per-tag split was ruled out (objectui#9067)".childrenslot on a void tag, sovalidateTreereportsnot-a-container.That the brief listed them among the keys to narrow is recorded as a conflict in the report, ⛔ not resolved here by guessing.
Gates (at HEAD
14fd13c51, exit codes captured by redirect-then-capture; heavy runs through the shared verify lock)@object-ui/typesbuild (tsc + vite + dist completeness)@object-ui/typestype-check(tsc --noEmit+ examples +tsconfig.test.json)pnpm exec vitest run packages/types/(parity, node-slot, arm, mirror, family-D and the new pins)pnpm exec vitest run scripts/type-check: Donepnpm --filter @object-ui/types lintcheck:control-bytes·check:new-line-citations(0 new) ·changeset:check·check-changeset-presence·check:changeset-claims·check:pending-changeset-literalscheck:doc-types·check:doc-snippets·check:doc-examples·check:skill-examples·check:doc-fences·check:doc-example-ids(against the built types)check:handler-key-reads·check:component-surface-parity·check:prompt-keys·check:registry-bare-names·check:spec-symbols·check:readme-exports·pnpm check(CLI self-check)--testover the 14 pathsNOT MEASURED:
check:sdui-registration-pinsexits 2 without a console build. The diff touches no registration and nosideEffectsarray. Left to CI.pnpm testshards beyondpackages/typesandscripts/, plustest:distand E2E: left to CI.Merge round — head
31bb63bcb(mergesmainatc30c8dd4c)The one conflict was the two
KnownDriftheader figures inzod-mirror-parity.test.ts. It was resolved by RECOUNTING the merged table with the test's own AST reader, not by picking a side:KnownDrift0c50f1863ai.zod.ts#AIFormAssistSchemac30c8dd4c)form.zod.ts#InputShorthandSchema14fd13c51)31bb63bcbThe population bullet and the KNOWN_DRIFT restatement both read 49 / 88, and the bullet keeps both histories in landing order. The other ledgers read the same as before:
MirroredUndeclared2 / 2,UnmirroredDeclared12 / 84,RuntimeOnlyDeclared3 / 7,WiderThanDeclared6 / 7. The merge head differs frommainby exactly this PR's 14 files.31bb63bcbzod-mirror-parity+content-channel-e3-residual-9256@object-ui/typesbuild ·type-check(incl.tsconfig.test.json)pnpm exec vitest run packages/types/(incl. PR objectui#10890'sai-zod-arms-10859/ai-node-faces-agree-10874and the family-D file PR objectui#10714 edited)pnpm exec vitest run scripts/check:control-bytes·check:new-line-citations·changeset:check·check-changeset-presence·check:changeset-claims·check:pending-changeset-literals·check:handler-key-readsAcceptance notes — out of scope, not fixed here
inputreads neither channel as well.InputSchema: 0 channel reads, 17 keys read; the registration declares nochildrenslot.Omitcarries nothing") is gone with this repair: a tombstone onInputSchemanow propagates on both faces.PartialSchema's docblock inindex.tssays every generic re-spelling of itsOmitcollapses for the samekeyof Treason.OmitDeclaredis a generic spelling that does not: this PR measures it on two faces.Omitsubject. objectui#6397, that alias's card, is closed.InputSchemaas "family E, frozen" andDetailViewSchemaas unattributable. Both readings are superseded by this change.Generated by Claude Code