Skip to content

fix(types): the last four "no error, no warning" clauses name what the parser tier answers (objectui#10981) - #11006

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-10981-silence-clause-closure
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-10981-silence-clause-closure

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #10981
Clause-②: no — message, .describe() and docblock text only; every accept or refuse verdict stays byte-identical.

Implemented by the os-dev agent dispatched from the domain:spec @ objectui seat, session https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm (mode:subagent). Base 4b742f41d. PR #10986 landed on zod/objectql.zod.ts meanwhile, so main 665025908 was merged in before the final push. Head ebd156ef5.

What changed

Only the false clause changes, on four sites. Each clause names the code the parser tier answers for that key and value shape, as measured on the live registry (next section).

site face before after
zod/form.zod.ts, the FormSchema.mode refusal (objectui#10286) zod message and .describe(), one string through retirementTombstone "rendered the same form — no error, no warning" "rendered the same form — no render-time error or warning; only the parser tier's unknown-prop warning noticed it"
data-display.ts, the TimelineSchema.events docblock (objectui#6170) TS face, emitted .d.ts "drew an EMPTY rail, with no error and no warning" "drew an EMPTY rail, with no render-time error or warning; only the parser tier's unknown-prop warning noticed it"
base.ts, the data-table sentence in the BaseSchema.bind docblock TS face, emitted .d.ts "renders its header over an empty body, with no error and no warning" "renders its header over an empty body, with no render-time error; nothing on the page says why, but a render-time console warning ([ObjectUI] DataTable bind:, objectui#6575) and the parser tier's unknown-prop warning both name it"
zod/objectql.zod.ts, the note on OBJECT_GRID_EXPORT_OPTIONS_GUIDANCE (objectui#7762) source note on a non-exported constant; it ships in the emitted JS, in no .d.ts "no error, no warning, no console line" "no render-time error, warning or console line (only the parser tier's type-mismatch warning noticed it)"
  • Why base.ts has its own wording. That site was not silent at render either. data-table has logged [ObjectUI] DataTable bind: for an authored bind since objectui#6575 (aff3d7aef, 2026-08-28). The docblock was written a day later (99a3c2db6, 2026-08-29). merge-base --is-ancestor aff3d7aef 99a3c2db6 answers 0, with the control leg 94d4876df also answering 0. So "only the parser tier noticed it" would be false there too.
  • Why exportOptions names type-mismatch. The object-grid registration declares exportOptions as type: 'object'. So checkType answers an array, not the undeclared-key branch.
  • New pin: packages/types/src/__tests__/silence-clause-parser-tier-closure-10981.test.ts.
  • Optional item, taken: packages/components/src/renderers/__tests__/header-bar-unknown-prop-parser-tier-10981.test.tsx, a new file beside container-declaration-ratchet.
  • Changeset: .changeset/10981-silence-clause-parser-tier-closure.md, @object-ui/types: patch.

The parser tier, measured before each clause was written

Instrument. The same as PR #10980: a one-shot probe, deleted and never committed, under packages/components/src/renderers/__tests__/.

  • It imported the components renderers, fields and 18 of the 19 plugin-* packages (all but plugin-designer). plugin-tree has no vitest alias, so it came in by relative source path.
  • The manifest was built from every KNOWN registry key through manifestFromConfigs, the way container-declaration-ratchet.test.tsx builds it.
  • Each leg ran validateTree on a control and on the control plus the one key, and reports only the diagnostics that key added.
  • Each leg also rendered through the real SchemaRenderer, inside PredicateScopeProvider and SchemaRendererProvider with a stub data source, all inside act. console.warn and console.error were spied, and the HTML was compared with the control's.
site keys value shapes the parser tier adds render leg
FormSchema.mode form, ui:form. Bare form resolves to ui:form; view:form is skipFallback edit, read, disabled, create, view (the objectui#10286 pin's set) exactly one unknown-prop on mode, 10 of 10 legs 0 warnings, 0 errors on every variant. HTML equals the control's except for one mode attribute on the form element
TimelineSchema.events timeline (resolves to view:timeline), view:timeline, plugin-timeline:timeline (the TimelineRenderer registration TimelineSchema describes) 2 events; []; items plus events exactly one unknown-prop on events, 7 of 7 byte-identical HTML, 0 warnings, 0 errors
BaseSchema.bind on data-table data-table, ui:data-table bind: 'users' and bind: 'app.settings.users', each with data absent and with data: [] exactly one unknown-prop on bind, 8 of 8 byte-identical HTML (the "No results" body in both), 0 errors, and exactly 1 console warning on every variant: [ObjectUI] DataTable bind: … is ignored
exportOptions object-grid, plugin-grid:object-grid ['csv', 'xlsx'], ['csv'], [] exactly one type-mismatch ("expected an object"), 6 of 6 the control is { formats: ['csv', 'json'] }, the default an array loses to. Byte-identical HTML, the export control and the rows present in both, 0 warnings, 0 errors

Controls.

  • form with an invented key draws unknown-prop. view:form with mode: 'edit' draws nothing, because that registration declares mode.
  • On object-grid: an exportOptions object draws nothing, a string draws type-mismatch, and an invented key draws unknown-prop. So the probe tells the two codes apart on one node.
  • accordion with a body list draws not-a-container, the third code.
  • With data absent, the data-table control already carries missing-required-prop on data; the table above counts only what bind adds.
  • The first control render in the file logged one react-i18next initialisation warning. No variant did.

The same answer before each retirement (source reading, not executed). The mode, events and exportOptions sentences are past tense.

  • FormSchema.mode, at a05c35063^, the parent of the objectui#10286 commit: the ui:form registration declared the same 13 inputs as today, none named mode. plugin-form's form was namespace: 'view', skipFallback: true. validate.ts had the same base-prop set, without mode, and one unknown-prop branch.
  • TimelineSchema.events, at 195052fff^, the parent of the objectui#6170 tombstone commit: neither plugin-timeline:timeline nor view:timeline declared events. A git log -G for an events input name over packages finds no registration that ever declared one, back to the shallow boundary (2026-07-20).
  • exportOptions, at f08d1a86a^, the parent of the objectui#7762 refusal: GRID_QUERY_INPUTS declared exportOptions as type: 'object'. It had done so since 375efb402 (2026-08-17), and objectui#7762 was filed on 2026-09-05. checkType gave a single non-enum arm type-mismatch, as today.
  • BaseSchema.bind is present tense, measured today.
  • The history reads ran on a deepened checkout (git fetch --deepen=4000, shallow boundary 2026-07-20).

Census over joined text (the closure nail)

Instrument. A one-shot script. It enumerates the non-test .ts files of packages/types/src from the ref with git ls-tree and reads each one from the ref. It joins JSDoc and line-comment continuations and '…' + '…' concatenations, and unescapes quotes. Then it counts:

  • A: "no (adjective) error" and "no (adjective) warning" within 60 characters, with no sentence break;
  • B: the same, in reverse order;
  • C: "no (adjective) error or / nor / and (adjective) warning", the family's corrected shape;
  • D: "without (an) error or / and warning";
  • E: "neither error nor warning";
  • F: "silent…" within 80 characters of a negated warning, in either order;
  • G, the wide net: any negated warning ("no warning", "without a warning", "never warns", …);
  • H, the wider net over other nouns: "no diagnostic", "no console line", "without complaint", "not a warning".
reading base 4b742f41d main 665025908 head ebd156ef5
files enumerated 67 67 67
A 4 4 0
B, D, E 0 0 0
C, and how many are followed by "the parser tier's" 302, 300 302, 300 302, 302
F 1 1 0
G 6 6 2
H 25 25 24
control: the objectui#10928 clause 282 282 282
control: the objectui#10959 tail 18 18 20
  • Positive control. C fires on every one of the 300 known objectui#10928 / objectui#10959 control strings. A fires on exactly the four sites the card names, which were found independently.
  • Every non-control hit at base, read.
    • A is the four sites and nothing else. The two C hits without the parser tier's clause are two of the four (base.ts, data-display.ts). F is the fourth site again.
    • G adds two hits that say "no warning window" (objectql.ts, zod/objectql.zod.ts, objectui#6475). That is a deprecation window, not a silence claim, so they stay.
    • H is outside the card's phrasing family. It is listed under Acceptance notes and not changed.

Every other byte, and only comment and string lines

  • Substitute the old clause back for the new one on joined text: each head file equals its base file, 4 of 4, against 4b742f41d and again against 665025908.
  • The first four ablation legs put the old clause back with the anchor tool. Each mutated blob equals the base blob byte for byte: eba1c495f, 4dfce4ac6, b1abfe909 and e2c561734 are the base blobs of the four files.
  • git diff -U0 over the four files: every changed line is a * JSDoc line or a + '…' string-literal line.
  • The objectui#10286, objectui#6170, objectui#7762 and bind pins pass unchanged; they are in the full types run.
  • Emitted dist, after pnpm --filter @object-ui/types build at head:
    • the old clauses, both spellings: 0 files;
    • the mode clause: zod/form.zod.js, zod/index.zod.js;
    • the events clause: data-display.d.ts; the bind clause: base.d.ts;
    • the exportOptions note: zod/objectql.zod.js, zod/index.zod.js (the emitted JS keeps comments).

Reverse verification (ablation), after the fix was committed

Instrument. objectstack's scripts/ablation-replace.mjs in WRAP mode, run from this worktree.

  • The anchor must hit exactly once and the on-disk blob must move. The tool proves each restore: blob equals HEAD and git diff HEAD is empty.
  • The driver re-proved each restore independently, and ran inside an EXIT / INT / TERM trap.
  • The types pin imports ../zod/* source and reads the files from disk. The components pin resolves the registry from source through the vitest aliases. So no build sits between a mutation and a run.
leg red red tests
the old clause back into FormSchema.mode 3 / 9 the mode arm, the spelling walk, the pairs walk
the old clause back into TimelineSchema.events 3 / 9 the events docblock, the pairs walk, the parser-tier walk
the old clause back into BaseSchema.bind 3 / 9 the bind docblock, both walks
the old clause back into the exportOptions note 2 / 9 the note, the pairs walk
the note names unknown-prop in place of type-mismatch 1 / 9 the note
zod/ai.zod.ts, which the pin does not name: an objectui#10928 clause loses its parser-tier half 1 / 9 the parser-tier walk
header-bar.tsx starts declaring title (components pin) 4 / 6 both population legs, both lit controls
  • Every leg restored to its HEAD blob (3a595a747, 33e3a4770, a12a28cf1, 4e75a80cf twice, a40e67ff6, ce3850854) with an empty git diff HEAD. After the run, git status --porcelain was empty.
  • The first header-bar attempt was a no-op. Its replacement contained the anchor, so the tool refused (anchor count 1 to 1) and ran no test. It was re-run with a replacement that does not contain the anchor. The table shows the second run.

The pins

silence-clause-parser-tier-closure-10981.test.ts. It follows the house style of PR #10956 and PR #10980. @object-ui/types imports no workspace package, so it holds the prose.

  • Zod face. FormSchema.mode is read through its refusal: code invalid_type and path ['mode'] first, then the clause. The walk covers every spelling of the objectui#10286 pin, and the .describe() text.
  • exportOptions. The bare array is refused with invalid_type at ['exportOptions'], with exactly the .describe() string. Then the note above OBJECT_GRID_EXPORT_OPTIONS_GUIDANCE must carry the clause and name no unknown-prop.
  • TS face. The TimelineSchema.events and BaseSchema.bind docblocks.
  • The closure walk. It reads every non-test .ts file of packages/types/src from disk, with the root taken from the pin's own import.meta.url.
    • No joined text may pair "no error" with "no warning" (patterns A, B, D, E).
    • Every C phrasing must have "the parser tier's" within 80 characters after it.
    • Non-vacuity, by name: the four site files are reached, and both earlier waves' clauses are found.

header-bar-unknown-prop-parser-tier-10981.test.tsx. It answers the open question from PR #10956 and PR #10980, on the live components registry.

  • On header-bar and ui:header-bar, each of the 9 refused keys draws exactly one unknown-prop naming the key. This holds in every value shape the objectui#10387 and objectui#10286 pins refuse.
  • The control draws nothing.
  • The lit control shows crumbs declared, and no refused key declared.

Dispatch assumptions, checked

  1. Holds. There are exactly four sites, one per file, on 4b742f41d and on 665025908. The census was widened to patterns A–H above, with controls.
  2. Holds, and made more exact.
    • form with mode draws unknown-prop: for all five spellings, on form and ui:form.
    • A bare timeline with events draws unknown-prop. The bare key resolves to view:timeline. The docblock describes TimelineRenderer, which is registered as plugin-timeline:timeline (skipFallback). That registration also answers unknown-prop, so the clause is true for the node the docblock names and for the bare key.
    • data-table with bind draws unknown-prop. But "only the parser tier noticed it" would be false there, because a render-time console warning fires too (objectui#6575). The site names both.
    • object-grid with an exportOptions array draws type-mismatch: for three array shapes, on both keys.
  3. Holds, by source reading at the parent of each retirement. See above.
  4. Changed during the run. PR fix(plugin-form): MasterDetailForm resolves its I18nLabel title, submitText and cancelText (objectui#10935) #10986 landed (8aa68b159) on zod/objectql.zod.ts. main 665025908 was merged in before the final push, with no conflict. The note edited here is away from that hunk. PR feat(types)!: ObjectMapConfigSchema is strict, so objectui validate refuses an undeclared map key (objectui#5157) #10997 had not landed at 665025908.

Gates, head ebd156ef5

All ran after the merge commit. Exit codes were captured before any pipe.

  • pnpm --filter @object-ui/types build → 0. Its check-dist-completeness verified 134 emitted files.
  • pnpm --filter @object-ui/types type-check → 0. tsc -p tsconfig.test.json --listFiles includes the new pin.
  • pnpm --filter @object-ui/types lint → 0, with 0 errors. The 295 warnings are existing no-explicit-any ones; none is in the new file.
  • pnpm exec vitest run packages/types/ → 0: 272 files and 5993 tests passed.
  • Outside types → 0: 27 files and 931 tests passed.
    • The script tests that read packages/types/src: body-dialect-producer-scan, changeset-polarity-census, check-action-forward-parity, check-changeset-claims, check-component-surface-parity, check-doc-component-types, check-doc-example-types, check-handler-key-read-sites, check-readme-exports, check-spec-symbol-derivation, component-node-vocabulary-7434, one-authority-per-exported-name-6273, check-pending-changeset-literals, spec-symbol-ledger-types-7265, unconsumed-widget-option-claim-6186, vite-types-zod-lazy;
    • component-fixture-declared-keys in examples/schema-catalog;
    • the new components pin, container-declaration-ratchet, the four header-bar component tests, skill-guide-data-table-binding and data-table-bind-diagnostic;
    • the parser pins body-dialect-6771 and containment-declared-slot-9910.
  • pnpm --filter '@object-ui/components^...' build, then pnpm --filter @object-ui/components type-check → 0. tsconfig.test.json --listFiles includes the new components pin. eslint over the two new test files → 0, with no findings.
  • check-changeset-presence, check-changeset-no-major, check-changeset-fixed, check-changeset-overwrite → 0. check-changeset-claims → 0; it is report-only.
  • pnpm check:control-bytes, check:new-line-citations (0 new), check:pending-changeset-literals, check:test-path-roots, check:phantom-deps and check:self-import → 0.
  • check-governed-queue-guard --test over the 7 paths → NOT GOVERNED.
  • Declared to CI: repo-wide pnpm lint, full pnpm test and the other workflow gates.

Acceptance notes

Not changed here, and left for the seat to route.

  • Same false claim, outside this package. skills/objectui/rules/protocol.md, the "Readers only." paragraph, says a bind on data-table renders its header over an empty body "no error, no warning". The render leg and the parser leg above both contradict it. It is on the governed skills/** surface, outside this card.
  • Same claim in unpublished test prose. The headers of base-bind-declared.test.ts and object-grid-export-options-refusal-7762.test.ts repeat it. mirror-partial-record-narrowing-8516.test.ts has a different "no error and no warning" claim, about grid columns. All three are test files, outside the census.
  • Pending changesets. .changeset/7762-object-grid-export-options-bare-array-refusal.md and .changeset/6575-data-table-bind-diagnostic.md describe the pre-fix behaviour in the old words. Pending changesets are out of scope here.
  • Adjacent wording, left in place.
    • The base.ts heading "the one documented silent failure" stays. The corrected sentence now says in which sense it is silent: nothing on the page.
    • The exportOptions guidance string says an array "is silently dropped for the csv/json default". That is present tense about the renderer, and "silently" alone is outside the phrasing family.
  • H, the "no diagnostic" nouns (24 at head). Most are scoped to a named tier or instrument: "on either face" (objectql.ts), "zero unresolved-module diagnostics" from tsc, checkType by name, the TS index signature. A few are unscoped claims about an authored value: ai.ts (maxResults), crud.ts, data-display.ts (three) and zod/data-display.zod.ts. They were not measured here.
  • Observation. A mode on a form reaches the DOM as an inert mode attribute on the form element. The visible form is identical.
  • Boundary. view:form declares mode (enum create, edit, view). It is a different registration and node type, which the FormSchema docblock does not describe.
  • A parser-tier finding, reported to the seat. validateTree answers bind with unknown-prop on list and tree-view too, although both read bind through useDataScope. No registration declares a bind input, and the parser's base-prop set has no bind.

Generated by Claude Code

…e parser tier answers (objectui#10981)

Closes the family of objectui#10928 and objectui#10959. Only the false
clause changes; every accept or refuse verdict is byte-identical.

- FormSchema.mode refusal (objectui#10286): names `unknown-prop`.
- TimelineSchema.events docblock (objectui#6170): names `unknown-prop`.
- BaseSchema.bind docblock (data-table): names the render-time console
  warning of objectui#6575 and the parser tier's `unknown-prop`.
- exportOptions note (objectui#7762): names `type-mismatch`, the code
  the object-typed registration input answers an array with.

New pin: silence-clause-parser-tier-closure-10981.test.ts, one string
per site plus a closure walk over packages/types/src.

Claude-Session: https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm
Co-authored-by: Claude <noreply@anthropic.com>
…he live registry (objectui#10981)

The objectui#10959 clause says only the parser tier's `unknown-prop`
warning noticed a refused `header-bar` key. The types pin can hold the
prose only; this holds the behaviour where the registry lives, on
`header-bar` and `ui:header-bar`, for every refused key and value shape.

Claude-Session: https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm
Co-authored-by: Claude <noreply@anthropic.com>
…osure

Brings in PR #10986 (objectui#10935), which touched zod/objectql.zod.ts
away from the `exportOptions` note this branch edits.

Claude-Session: https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 52 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/10337-widget-input-serializer-key-list.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    Clause 2 of the WidgetInput docblock said sdui-parser's manifest serializer forwards six keys per input and listed them without of. manifestFromConfigs has forwarded of since objectui#8067, so a reader of the published declaration learned that of is not published when it is. The clause now reads "a fixed key list per input, of included since objectui#8067" and lists all of them, the uncounted wording ComponentInput's own docblock in base.ts already uses. The clause's conclusion is unchanged: none of the five tombstoned ComponentInput keys is on that list. Comment only, with no type or runtime change.

.changeset/5674-retire-plugin-component-input.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    Migration. Replace PluginComponentInput with ComponentInput — they have named the identical declaration since objectui#4972 converged the plugin-scoped interface onto base.ts, so this is a rename with no shape change:

.changeset/5903-objectgantt-declared-keys.md

  • names src/zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    Both halves move together. The TS declaration (packages/types/src/objectql.ts) and its zod mirror (src/zod/objectql.zod.ts) gain the same ten keys at the same requiredness — all optional — and no KnownDrift entry is added. navigation is taken from @objectstack/spec's NavigationConfigSchema by reference rather than restated, matching ObjectGridSchema.navigation.

.changeset/5905-componentinput-inputtype-tombstone.md

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    The write was measured as a no-op before it was deleted, and re-measured on this branch's base rather than inherited from the card. A structural census over every inputs: array in the repository (211 regions, all tracked TS/TSX/JS sources) scores inputType at exactly ONE authoring site — the plugin-markdown registration — against name 953, type 969, label 966, description 194, enum 119, required 86 and binding 4 in the same pass over the same regions, so the instrument was not blind. The other 192 in-repo inputType hits are a DIFFERENT face: FormField.inputType (zod/form.zod.ts), the text-input renderer's prop, and SchemaBuilder.inputType, none of which sit on a ComponentInput. The publication path is unchanged and was re-confirmed: packages/sdui-parser/src/index.ts forwards exactly seven keys per input — name, type, of, required, enum, binding, description — so an authored inputType could not reach the published sdui.manifest.json even in principle.

  • names base.ts → packages/types/src/base.ts — edited by this change

    Stale wording corrected in the same pass, because this change falsifies it: base.ts and zod/base.zod.ts both said the fork was "recorded for a ruling; until then this stays a live, writable key", and widget.ts called it "the open fork". All three now record the ruling. A reader who greps the source instead of the card thread was meeting an open fork that no longer existed.

.changeset/5905-componentinput-retire-constraint-keys.md

  • names packages/types/src/base.ts → packages/types/src/base.ts — edited by this change

    The retirement kit: ?: never on the interface (packages/types/src/base.ts), so authoring one is a tsc error at the registration site; retirementTombstone() on the Zod mirror (packages/types/src/zod/base.zod.ts), so an authored value is REFUSED at parse time with code: 'invalid_type', the key named in the issue path, and the migration note as the message. Deleting the members outright was the option NOT taken: ComponentInputSchema is a non-strict z.object, which strips an undeclared key silently — one silent no-op traded for another. Pinned in packages/types/src/__tests__/component-input-retired-constraint-keys.test.ts.

.changeset/5928-classname-style-props-rename.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    Why the name had to move. The const declares exactly two keys — className and style, the CSS passthrough attributes a node exposes. The TypeScript StyleProps (base.ts) is the Tailwind-SCALE vocabulary: padding, margin, gap, backgroundColor, textColor, borderWidth, borderColor, borderRadius. Measured on this branch's base with an AST read of both files: 2 keys against 8, sharing ZERO keys. In this package the …Schema suffix otherwise means "runtime mirror of the like-named declaration", so the shared name asserted a mirror relationship that does not exist — and building objectui#5684's parity registry by name pairing duly put the two together and reported drift on a pair that has no counterpart at all.

.changeset/6067-component-meta-derive-from-canonical.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    packages/core/src/registry/Registry.ts declared its own ComponentMeta: thirteen keys, of which nine were restated from @object-ui/types' base.ts, four were registry-only (tier, namespace, skipFallback, labelling), and tags / description were absent — although both are declared on the canonical type and on the ComponentMetaSchema zod mirror. Two of the three authorities agreed and the registration surface did not, so those two keys were unwritable at exactly the declaration most component registrations import. That is the same two-key delta objectui#5893 had just closed inside @object-ui/types, arriving a third time on a third declaration, and objectui#5671 had already made the identical move for the sibling type ComponentInput in this very file.

.changeset/6349-name-authority-batch-3.md

  • names form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    @object-ui/components — ComboboxOption now IS @object-ui/types' declaration. The component declared its own { value, label }, a strict subset of the ComboboxOption that @object-ui/types declares for ComboboxSchema.options and mirrors in form.zod.ts ({ value, label, disabled? }). The component now re-exports the types declaration (through the @object-ui/types/form subpath — the root barrel does not publish the name), so the name ComboboxOption exported from @object-ui/components gains the optional disabled?: boolean member. Every value that type-checked before still does — nothing narrows and no key changes type; the one thing that moves is keyof ComboboxOption, so a consumer that EXHAUSTS the type (a Record over its keys) will need the new key. Note that the Combobox component itself does not read option.disabled — that member was already declared on the @object-ui/types face and is now visible on this one too; it is recorded as a separate finding, not changed here.

.changeset/6349-types-internal-name-collisions-batch-1.md

  • names data-display.ts → packages/types/src/data-display.ts — edited by this change

    BreadcrumbItem / BreadcrumbSchema — re-pointed, because one copy was stale. Both were declared in data-display.ts and in navigation.ts. The data-display pair was not a second dialect but a strict SUBSET: no key declared differently on either side, and missing BreadcrumbItem.icon / onClick / siblings and BreadcrumbSchema.maxItems. Everything that reads a breadcrumb was already on the navigation declaration — registry.ts maps the 'breadcrumb' component type to it, src/index.ts re-exports it under the bare names, zod/navigation.zod.ts mirrors it (icon, onClick, siblings, maxItems included), the ui:breadcrumb renderer consumes it, and the component's own documentation page documents icon and maxItems. data-display.ts now re-exports the one authority.

.changeset/6396-previous-values-dom-leak.md

  • names packages/types/src/zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    Scope is the runtime leak only. The declared key stays exactly as declared (packages/types/src/form.ts, packages/types/src/zod/form.zod.ts are untouched): it has a live consumer, so there is nothing here for the enforce-or-remove channel.

.changeset/6881-retire-data-table-toolbar.md

  • names data-display.ts → packages/types/src/data-display.ts — edited by this change

    What was measured. The key was declared on both published faces — data-display.ts (toolbar?: SchemaNode[], "Table toolbar actions/content") and the Zod mirror (SchemaNode | SchemaNode[]) — documented, mirrored, and read by NOTHING: data-table.tsx, the registered renderer for type: 'data-table', contains the word only in two prose comments and never reads schema.toolbar. The sibling emptyAction slot on the same interface IS mounted through SchemaRenderer, so the census zero is a reading, not a blind query. An author who wrote a toolbar got a green document and a blank result, with no signal anywhere that said so — the declared-vs-enforced failure mode that is worst for AI-authored metadata, which has nothing but the declaration to go on.

.changeset/6938-checkbox-wrapper-class.md

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    packages/components/src/renderers/form/checkbox.tsx:36 reads cn("flex items-center space-x-2", schema.wrapperClass) — classes on the wrapper div around the box and its label — and neither the TypeScript interface in packages/types/src/form.ts nor the zod mirror in zod/form.zod.ts declared the key. It compiled through BaseSchema's index signature and parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on FileUploadSchema and FilterBuilderSchema (objectui#6150); the checkbox was left out only because its doc page's schema block is a six-line summary.

.changeset/6940-rowactions-boolean-mirror.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    The list view's same-named rowActions in zod/objectql.zod.ts — z.array(z.string()), the legacy bare-name action list on ObjectGridSchema — is a different key that is correct as it stands, is in parity with its own TS twin (rowActions?: string[]), and is not touched.

.changeset/6951-tree-view-data-retired.md

  • names data-display.ts → packages/types/src/data-display.ts — edited by this change

    Two published faces, one retirement — and why a tombstone, not a deletion. The TypeScript interface TreeViewSchema (@object-ui/types, data-display.ts) declares data?: never; the Zod mirror TreeViewSchema (@object-ui/types/zod, data-display.zod.ts) declares data as a retirementTombstone(). BaseSchema already declares data?: any (z.any().optional() on the mirror), so DELETING the member would not have refused the key — it would have ADMITTED it, unvalidated, through the base member, and the renderer would have drawn an empty tree. The tombstone on the extended schema shadows the base member on both faces; the pin measures the base accepting the very document the extended schema refuses.

.changeset/6972-markdown-inert-keys-retired.md

  • names data-display.ts → packages/types/src/data-display.ts — edited by this change

    What was measured, on this branch's base. sanitize was declared ?: boolean with @default true on both published faces — data-display.ts and the Zod mirror — documented, and read by NOTHING. Worse than an ordinary inert key, it implied a switch that does not exist: sanitization is unconditional. rehypePlugins in plugin-markdown/src/MarkdownImpl.tsx is a module-level const array whose last link is [rehypeSanitize, sanitizeSchema], handed to ReactMarkdown as-is — no ternary, no if, no runtime assembly. MarkdownRenderer forwards exactly content and className, and MarkdownImplProps accepts only those two. A repo-wide grep for schema.sanitize over packages/ and apps/ returns nothing, against a control of 20 .tsx files reading schema.content in the same query shape, so the zero is a reading, not a blind query. An author writing sanitize: false believed they turned XSS filtering off; one writing sanitize: true believed they turned it on. Neither was true.

.changeset/7088-hidden-jsdoc-single-hide-path.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    Why a comment was worth a changeset: the JSDoc is the authority a later docs correction is measured against, and this one nearly propagated. While splitting the schema-reference hidden row, a reader checked it against base.ts and almost "corrected" the table's "Inverse of visible" — the half that describes shipped behaviour — toward the declaration. That row is unchanged and stays. SchemaRenderer.hiddenVisibleSynonymy.test.tsx now pins the claim the comment makes: the two keys produce the same rendered output, and hidden: true leaves no node that could carry a visibility style.

.changeset/7113-chart-data-model.md

  • names objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    .extend() with a NEW key still works and preserves the fold and the refinement; .optional(), z.discriminatedUnion, z.toJSONSchema and safeValidateSchema are all unaffected. Nothing in this repository calls the throwing combinators on either const, and the published surface already ships refined mirrors (objectql.zod.ts, complex.zod.ts, form.zod.ts, app.zod.ts), so the class is not new — but it is a real behaviour change on a published export and it belongs in the release note rather than in a reviewer's file.

  • names form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    .extend() with a NEW key still works and preserves the fold and the refinement; .optional(), z.discriminatedUnion, z.toJSONSchema and safeValidateSchema are all unaffected. Nothing in this repository calls the throwing combinators on either const, and the published surface already ships refined mirrors (objectql.zod.ts, complex.zod.ts, form.zod.ts, app.zod.ts), so the class is not new — but it is a real behaviour change on a published export and it belongs in the release note rather than in a reviewer's file.

.changeset/7200-object-form-section-style-keys-undeclared.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    The authored-metadata type now agrees with @objectstack/spec, whose FormSectionSchema is a strict object declaring neither key, and with the ruling's rationale (maintainer 2026-09-01, verbatim): "retire the reads … Declaring the keys was weighed and not adopted: it would formally invite free Tailwind strings into authored metadata, the exact class the boundary exists to keep out." A ?: never tombstone was not used: ObjectFormSection has no zod mirror (ObjectFormSchema in zod/objectql.zod.ts does not declare sections), so there is no parse door to refuse at, and a tombstone is still a declaration in completion and in the published .d.ts.

.changeset/7265-types-user-filter-field-derives.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    zod/objectql.zod.ts declared two schemas under names @objectstack/spec/ui already exports. They were triaged separately, by reading their sites, and went different ways.

.changeset/7322-object-kanban-group-by-limit.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    Breaking for authored metadata: ObjectKanbanSchema.groupField is RETIRED (objectui#7322, ADR-0049 enforce-or-remove), and the two keys the object-kanban renderer actually reads — groupBy and limit — are now DECLARED and validated on both published faces: the TypeScript interface in objectql.ts and the Zod mirror in zod/objectql.zod.ts.

.changeset/7352-drill-down-config-mirror.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    DrillDownConfigSchema is the zod mirror of DrillDownConfig, and both declarations that carry drillDown reference it — ChartSchema (zod/data-display.zod.ts) and ObjectDataTableSchema (zod/objectql.zod.ts) — so the published validator under @object-ui/types/zod reads the key for the first time (objectui#7352).

.changeset/7363-objectql-union-arms.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    ObjectGallerySchema and ObjectDataTableSchema are members of ObjectQLComponentSchema on both faces — the TS union in objectql.ts and the zod union in zod/objectql.zod.ts — so AnyComponentSchema, and with it validateSchema / safeValidateSchema / objectui validate, has an arm for object-gallery and object-data-table nodes (objectui#7363).

.changeset/7493-componentinput-retire-label-defaultvalue-advanced.md

  • names packages/types/src/base.ts → packages/types/src/base.ts — edited by this change

    The retirement kit: ?: never on ComponentInput (packages/types/src/base.ts), so authoring one is a tsc error at the registration site; retirementTombstone() on ComponentInputSchema (packages/types/src/zod/base.zod.ts), so an authored value is REFUSED at parse time with code: 'invalid_type', the key named in the issue path, and the migration note as the message (one string, both channels). Pinned in packages/types/src/__tests__/component-input-retired-keys-7493.test.ts, which also holds a tree-scoped absence census over every inputs: array under packages/** and apps/**.

.changeset/7530-predicate-envelope-declared.md

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    • ExpressionWire (type, main entry) — the TypeScript wire union, in packages/types/src/expression.ts. - ExpressionWireSchema (@object-ui/types/zod) — its runtime twin, hoisted out of zod/form.zod.ts (where it was module-private) into zod/expression.zod.ts and imported by both base.zod.ts and form.zod.ts. One envelope type, reused by reference; no second spelling.
  • names form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    • ExpressionWire (type, main entry) — the TypeScript wire union, in packages/types/src/expression.ts. - ExpressionWireSchema (@object-ui/types/zod) — its runtime twin, hoisted out of zod/form.zod.ts (where it was module-private) into zod/expression.zod.ts and imported by both base.zod.ts and form.zod.ts. One envelope type, reused by reference; no second spelling.

.changeset/7722-wrapper-class-five-more.md

  • names data-display.ts → packages/types/src/data-display.ts — edited by this change

    Each of renderers/form/switch.tsx, textarea.tsx, date-picker.tsx, select.tsx and renderers/data-display/list.tsx reads schema.wrapperClass onto its wrapper element, and neither the TypeScript interface (form.ts, data-display.ts) nor the zod mirror (zod/form.zod.ts, zod/data-display.zod.ts) declared the key. The reads compiled through BaseSchema's index signature (objectui#5155) and the values parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on CheckboxSchema (b74a8598d), FileUploadSchema and FilterBuilderSchema (objectui#6150); these five were left out only because their doc pages never listed it.

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    Each of renderers/form/switch.tsx, textarea.tsx, date-picker.tsx, select.tsx and renderers/data-display/list.tsx reads schema.wrapperClass onto its wrapper element, and neither the TypeScript interface (form.ts, data-display.ts) nor the zod mirror (zod/form.zod.ts, zod/data-display.zod.ts) declared the key. The reads compiled through BaseSchema's index signature (objectui#5155) and the values parsed through .passthrough(), admitted unexamined. The same key, on the same class of read, is declared on CheckboxSchema (b74a8598d), FileUploadSchema and FilterBuilderSchema (objectui#6150); these five were left out only because their doc pages never listed it.

.changeset/7735-zod-mirrors-stop-authoring-defaults.md

  • names form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    What changed. All 41 .default() call sites under packages/types/src/zod/ are removed — layout.zod.ts 22, crud.zod.ts 11, form.zod.ts 5, views.zod.ts 2, app.zod.ts 1. @object-ui/components reconciles the third face a separate finding found: flex's registration defaultProps.align seeded 'center', the value its own renderer never applies, so a designer-made node laid out differently from a hand-authored one; it now seeds 'start'.

.changeset/7917-export-breadcrumb-object-tree-zod-schemas.md

  • names objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    AnyComponentSchema declares 107 node component types. 105 of them could be named on the ./zod barrel — ButtonSchema.safeParse(node), which is what a designer, a form builder or a targeted test needs. The arms declaring type: 'breadcrumb' (navigation.zod.ts) and type: 'object-tree' (objectql.zod.ts) could not: both were already export const in their own module, but index.zod.ts — the package's only zod entry point — did not re-export them, so the schemas existed, were maintained, and were applied by the union while no consumer could name them.

.changeset/8331-data-table-empty-action-primitive-node.md

  • names packages/types/src/data-display.ts → packages/types/src/data-display.ts — edited by this change

    Behaviour change on a published surface, deliberately. DataTableSchema.emptyAction is declared SchemaNode on both published faces — packages/types/src/data-display.ts and its Zod twin in packages/types/src/zod/data-display.zod.ts — and SchemaNode is BaseSchema | string | number | boolean | null | undefined. The empty-state render path additionally required typeof … === 'object', so an authored emptyAction: 'Create the first record' rendered nothing and reported nothing: declared wider than enforced, failing in the direction that loses the author's content without a diagnostic. A node that renders nothing today therefore starts rendering.

.changeset/8478-describe-line-addresses.md

.changeset/8478-zod-pins-form-layout.md

.changeset/8499-node-slot-registered-arms.md

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    • SemanticElementSchema (zod/layout.zod.ts) — the seven HTML sectioning tags renderers/layout/semantic.tsx registers: aside main header nav footer section article. - HtmlElementSchema (zod/layout.zod.ts) — the 37 safe flow/inline tags renderers/basic/html-elements.tsx registers (h1…h6, p, a, ul, img, …), plus the per-tag keys that module forwards to the DOM (href, target, rel, title, src, alt, width, height, dateTime, cite). ⚠️ Dated note, 2026-09-27 — that set has since gained code — objectui#10756. At this change TAGS and this arm both named 37 tags; both now name 38, and the parity pin counts 38. The rest of this entry is kept as the reading of this change. - InputShorthandSchema (zod/form.zod.ts) — email / password, the two aliases renderers/form/input.tsx registers onto the input renderer with inputType pinned. inputType is deliberately NOT declared on this arm: the wrapper spreads its own value last, so an authored one is overwritten. ⚠️ Dated note, 2026-09-28 — inputType is now declared on this arm, as a refusal — objectui#8762. Later in this same release the arm declares inputType on both faces and refuses it by name (?: never on the TypeScript face, a retirementTombstone on the zod mirror, at path inputType), with guidance pointing at { "type": "input", "inputType": "email" }. So "inputType is deliberately NOT declared on this arm" no longer holds; the reason does, since the wrapper still spreads its own value last. The rest of this entry is kept as the reading of this change. - UiCalendarSchema (zod/form.zod.ts) — ui:calendar, the date-picker primitive renderers/form/calendar.tsx registers under exactly that key (skipFallback, because bare calendar belongs to the plugin-calendar view).

.changeset/8735-objectql-mirror-docblocks-not-defaulted.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    Correct four zod/objectql.zod.ts docblocks that described the behaviour objectui#8317 removed. Since that change the zod mirrors strip imported @objectstack/spec defaults at this package's import boundary, but the docblocks on HttpRequestSchema, ListColumnSchema, SelectionConfigSchema and PaginationConfigSchema still said, in the present tense, that method, prefix.type, type and pageSize are defaulted on parse — the opposite of what each export does. Each now says the key is declared and accepted but NOT defaulted on parse.

.changeset/8801-object-kanban-allow-collapse-retired.md

  • names packages/types/src/zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.

.changeset/8871-page-node-refuses-breadcrumbs.md

  • names packages/types/src/base.ts → packages/types/src/base.ts — edited by this change

    Accept-set change, deliberately. A page document carrying breadcrumbs used to parse GREEN and render nothing. PageNodeSchema never declared the key and no renderer ever read it, so the array survived purely through BaseSchema's .passthrough(). On the TypeScript face, tsc previously accepted it too, through BaseSchema's own [key: string]: any index signature (the [key: string]: any member of BaseSchema in packages/types/src/base.ts) — the same open door the zod mirror walked through at runtime.

.changeset/8885-object-chart-drilldown-title-compareto.md

  • names packages/types/src/zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    ObjectChart.tsx reads all three off schema, and until now neither published copy declared any of them: not the TS interface (packages/types/src/objectql.ts) and not the zod mirror (packages/types/src/zod/objectql.zod.ts). They rode BaseSchema's index signature / .passthrough() and arrived unvalidated. drillDown was the sharpest case — this component's registry inputs advertise it to the designer palette, and @objectstack/spec publishes ChartDrillDownSchema for exactly this carrier, so an author was offered a key that neither published shape mentioned.

.changeset/8913-object-kanban-columns-declared.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    What moved. ObjectKanbanSchema gains columns on both halves that move together — the TypeScript interface (objectql.ts) and its Zod mirror (zod/objectql.zod.ts). Retiring the bare kanban node type key (objectui#8802) removed the only face that judged a lane, and object-kanban had never declared the key, so it rode BaseSchema's [key: string]: any / .passthrough(): read by the renderer at three sites, named by no published face.

.changeset/8990-object-kanban-groupby-optional.md

  • names packages/types/src/zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    @objectstack/spec declares the key optional — groupBy: z.string().optional() on ObjectKanbanPropsSchema — while this package required it on the TypeScript declaration (packages/types/src/objectql.ts) and on the Zod mirror (packages/types/src/zod/objectql.zod.ts). objectui was therefore narrower than the protocol on a published key: ObjectKanbanSchema.safeParse and safeValidateSchema refused an object-kanban node the protocol accepts, and such a node could not be annotated with its own type.

.changeset/8992-user-actions-collapse-and-docblock.md

  • names objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    objectql.zod.ts's UserActionsSchema read stripImportedDefaults(Spec).extend({ group, hideFields, rowColor }), an extension that existed only because @objectstack/spec did not declare those three keys while normalizeListViewSchema folded objectui's legacy showGroup / showHideFields / showColor onto them. The protocol adopted all three in 17.3.0 (objectui#5435's ruling), so the extension is now a second local copy of a protocol declaration — the shape two faces start drifting from — and it collapses into the plain by-reference re-export its own note always said it would become.

.changeset/9067-zod-barrel-named-arms.md

  • names zod/form.zod.ts → packages/types/src/zod/form.zod.ts — edited by this change

    • InputShorthandSchema (zod/form.zod.ts) — the email / password shorthand arm. - UiCalendarSchema (zod/form.zod.ts) — ui:calendar, the date-picker primitive renderers/form/calendar.tsx registers, a different component from the calendar plugin view that owns the bare literal.

.changeset/9256-list-timeline-content-channels.md

  • names packages/types/src/data-display.ts → packages/types/src/data-display.ts — edited by this change

    These two were held out of the previous family-D slice for a SERIAL constraint on packages/types/src/data-display.ts and never for a verdict. Readership was re-derived for both rather than inherited: a TypeScript compiler-API sweep files every .body / .children read under the declared type of its receiver and answers zero for ListSchema and TimelineSchema while its live controls fire. timeline's bare-key owner is any-typed, so it was attributed directly as well — packages/plugin-timeline contains no channel read of any kind.

.changeset/9309-object-gallery-filter-destination-typed.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    ObjectGallerySchema.filter is typed as the destination its own docblock names — QueryParams['$filter'] — on both faces, the TS interface in objectql.ts and the zod mirror in zod/objectql.zod.ts (objectui#9309).

.changeset/9365-bridgelistview-present-tense.md

.changeset/9375-base-i18n-vocabulary-docblock.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    base.ts said of the INLINE locale map (label / description) and the KEYED bundle reference (ariaLabel) that they "each accept the other's shape vacuously", and the label docblock said the same thing one property over ("a keyed ref typed into this slot is accepted only vacuously"). That was true when objectui#4580 Q2-B wrote it; the installed pin has since closed it, and the sentence now reads as live in published .d.ts bytes while the tree says the opposite.

.changeset/9511-record-id-is-a-string.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

.changeset/9549-tree-filter-declared.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    ObjectTreeSchema.filter is declared on both faces, in the shape objectui#9309 settled for ObjectGallerySchema.filter: QueryParams['$filter'] by indexed access on the TS interface in objectql.ts, and the same two-arm union (array first) on the zod mirror in zod/objectql.zod.ts (objectui#9549).

.changeset/9606-object-kanban-card-title.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    Both published faces of the object-kanban arm now name the key: the zod mirror ObjectKanbanSchema in zod/objectql.zod.ts and its TypeScript twin, the ObjectKanbanSchema interface in objectql.ts. Both declare it OPTIONAL, at the same requiredness the other face uses, so the two faces accept and refuse the same documents. (Located and cited by SYMBOL: line addresses in zod/objectql.zod.ts have drifted before, and this change is itself about a drifted mirror.)

.changeset/component-meta-converge-and-deprecate-alias-5893.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    @object-ui/types published ComponentMeta twice, from two different declarations: base.ts and plugin-scope.ts (the latter published as PluginComponentMeta). They were structural copies, not an alias pair. plugin-scope.ts' ComponentMeta is now export type { ComponentMeta } from './base.js' — the disposition objectui#4580 ruled for the identical shape, a structural copy would reproduce the defect the moment either side moved, and the same move objectui#5671 made for the sibling type ComponentInput in the same file.

.changeset/componentinput-reexport-4972.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    @object-ui/core's ComponentInput (registry/Registry.ts) and @object-ui/types' plugin-scoped ComponentInput (plugin-scope.ts, published as PluginComponentInput) were structural copies of the interface in @object-ui/types' base.ts. Both are now re-exports of that one declaration, which is the disposition objectui#4580 ruled for the identical shape — a structural copy would reproduce the defect the moment either side moved — and the way core/src/types/index.ts already handles SchemaNode.

.changeset/default-children-retired-5051.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    If you author plugins against the published register-meta table, drop the key. It is gone from skills/objectui/guides/plugin-development.md, which had been teaching it. A meta that still declares it stays valid: ComponentMetaSchema is a plain z.object, and measured on zod 4.4.3 that STRIPS unknown keys rather than rejecting them — so the key is silently dropped from the parse output instead of failing validation. TypeScript authors get the loud signal instead: all three ComponentMeta declarations (@object-ui/types base.ts and plugin-scope.ts, @object-ui/core Registry.ts) no longer offer it, so re-declaring it is now a compile error.

.changeset/plugin-component-input-deprecate-5674.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    @object-ui/types' entry point publishes ComponentInput as PluginComponentInput. Until objectui#4972 that alias pointed at a genuinely different declaration: plugin-scope.ts restated its own nine-key ComponentInput. objectui#5671 converged that declaration onto base.ts, so the alias became a second published name for the same type, carrying no information the first does not. Two published names for one type is a shape that costs readers a step and gives AI-authored code a coin-flip between spellings.

.changeset/table-renderer-declared-column-contract-5350.md

  • names packages/types/src/data-display.ts → packages/types/src/data-display.ts — edited by this change

    renderers/complex/table.tsx resolved a heading as col.header || col.label and a cell as row[col.accessorKey || col.name]. Neither label nor name is declared on TableColumn, which declares header and accessorKey — both required (packages/types/src/data-display.ts). This was the fourth site of the column-alias family, after data-table, ObjectDataTable and ObjectGrid (objectui#5350).

.changeset/zod-base-schema-mirror-parity-4605.md

  • names base.ts → packages/types/src/base.ts — edited by this change

    The new pin is DERIVED rather than a hand-written key list: it reads the mirror's own .shape and compares each key against the declaration, so the next widening of base.ts that forgets this file turns it red with no list to maintain. It reads .shape and not keyof z.inputANGLE-BRACKETS(…) because that spelling was measured vacuous — .passthrough() collapses the inferred key union to bare string, and a pin written over it resolved never while five keys were demonstrably narrow. Two guards pin the derivation against both degenerations (never and string).

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with f6fb83f0c (merge-base with origin/main): 6 file(s) changed outside .changeset/, read against 1711 pending declaration(s) that publish a body (2314 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3104.5 KB 3104.5 KB
Main entry chunk (gzip) 149.8 KB 350 KB
Entry file index-CE-V7fMT.js —
Status FAIL —

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.

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ❌ over its ceiling
Per-chunk ceilings ✅ pass
Per-chunk membership (declared packages) ✅ pass
Ceiling sensitivity (headroom) ✅ pass
Ceiling freshness (checkout vs. base branch) ✅ pass

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 558.92KB 133.97KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.62KB 63.16KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.53KB 4.89KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.39KB 14.98KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.56KB 36.67KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.49KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 162.67KB 41.95KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.44KB 22.41KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Bundle Analysis is red on this head, and it is not this PR's. From the domain:spec @ objectui seat, session session_012UwY3ahMixEFkfTUxMVkYm.

  • This head. The run on ebd156ef5 reads 「Console eager closure is 3104.5 KB gzipped across 330 chunks — 0.0 KB over the 3104.5 KB budget」 against MAX_EAGER_CLOSURE_GZIP_BYTES = 3,179,000.
  • main. main's own push run on 665025908 (this head's merged base) is red with the same figures. The four named eager chunks carry the same content hashes in both builds (index-CE-V7fMT.js, vendor-objectstack-DFL78Wux.js, ui-components-o5o_LJhI.js, framework-tHOrM-aI.js).
  • Cause. objectui#10996 records it: main alone measures 3,179,017 B, 17 B over the ceiling, after PR objectui#10986 (8aa68b1) added about 549 B to the eager plugin-form chunk. Bundle Analysis does not run on merge_group, so that PR landed over the line.
  • This diff's runtime bytes. Its only non-comment runtime text is the longer FormSchema.mode refusal string, in the types-zod chunk. The rest is docblocks, a changeset and two tests.

No fix exists yet. objectui#10996 names the two remedies the gate script documents (re-pin or shrink), and it is triage's and the owning lane's to execute. This seat does not widen this PR with it. Once a fix lands on main, this branch takes a main merge so the check re-measures on the fixed base.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ebd156ef5d37955d4025195c9ebf55f6063840be
Local-runs: none

Inputs: card objectui#10981 (body; triage 5872673096; claim 5873069682; os-dev-report 5873853507), PR #11006 (body, the 7-file list, git diff origin/main... at the head), the 43 check-runs on the head (re-read at 2026-09-28T16:19Z, none in_progress), and read-only git show / git grep / git log against origin/main and the head. objectui#10996 and main's own push run at 665025908 for the Bundle Analysis question. Nothing built, run or re-run.

① Derived judgments

Gates on the head. 43 check-runs, all completed, every one on this head: 39 success, 3 skipped (dependabot, the two coverage matrix stubs), 1 failure (Bundle Analysis). Green includes Lint, Type Check, Test with all eight shards and Test (dist pins), Build & E2E, Spec Main Shape Gate, Governed Surface Queue Guard, Line Citation Gate, Control Byte Scan, the four changeset checks, Skill Example Check, Skill Guide Path Check and Skill Eval Token Check. The dev's report was posted while the shards read in_progress, which its report called out honestly; they closed green. The one red is attributed at the end of this section.

Each clause against the code it describes (registrations read on origin/main; packages/sdui-parser/src/validate.ts read on origin/main, where the undeclared-key branch pushes severity: 'warning', code: 'unknown-prop' and checkType answers a wrong shape with code: 'type-mismatch', severity warning unless an enum arm is declared).

  1. form + mode → the parser tier's unknown-prop warning: right. The ui:form registration (packages/components/src/renderers/form/form.tsx, ComponentRegistry.register('form') declares fields, defaultValues, submitLabel, cancelLabel, showCancel, layout, columns, validationMode, resetOnSubmit, disabled, className, fieldContainerClass, children; no mode. BASE_PROPS has no mode. Bare form resolves to ui:form because plugin-form's view:form is namespace: 'view', skipFallback: true; that registration declares mode and is a different node, as the PR's boundary note says. The verdict is byte-identical by construction: retirementTombstone(guidance) is z.never({ error: guidance }).optional().describe(guidance) and only guidance moved.
  2. timeline + events → unknown-prop: right. view:timeline (packages/plugin-timeline/src/index.tsx) declares objectName, variant; plugin-timeline:timeline (renderer.tsx, skipFallback) declares variant, items, dateFormat, scale, rowLabel, minDate, maxDate, className. Neither declares events. The events?: never tombstone and the rest of its docblock are untouched; the clause ships on the .d.ts face.
  3. data-table + bind → a render-time console warning AND unknown-prop: right, and the separate wording is the correct call. The data-table registration (packages/components/src/renderers/complex/data-table.tsx) declares no bind, and BASE_PROPS has none, so the parser answers unknown-prop. At render the same file destructures bind: authoredBind ("Read ONLY to diagnose it") and dataTableBindDiagnostic.ts exports DATA_TABLE_BIND_DIAGNOSTIC_PREFIX = '[ObjectUI] DataTable bind:'. History checked: the diagnostic landed in aff3d7aef (2026-08-28, PR fix(components): diagnose an authored bind on data-table at render #6663 for objectui#6575); the docblock was written in 99a3c2db6 (2026-08-29, PR fix(types): declare bind on BaseSchema and its zod mirror #6574). So the retired sentence was false at render from the day it was written, and the sibling wording "only the parser tier noticed it" would have been false here too. The docblock's pointer to skill-guide-data-table-binding.test.tsx stays true: that file filters console.warn by the diagnostic's prefix.
  4. object-grid + exportOptions array → type-mismatch, not unknown-prop: right. packages/plugin-grid/src/index.tsx declares { name: 'exportOptions', type: 'object' }; armAccepts('object', …) refuses arrays (!Array.isArray(value)), so checkType returns type-mismatch as a warning (no enum arm). The edit is the source note above OBJECT_GRID_EXPORT_OPTIONS_GUIDANCE; the guidance string, the strictObject refusal and its error map are unchanged, so the zod verdict is byte-identical.
  5. The optional header-bar live-registry pin: right. packages/components/src/renderers/navigation/header-bar.tsx declares crumbs, search, actions, rightContent; none of the nine refused keys. @object-ui/components already depends on @object-ui/sdui-parser (workspace:*), and the sibling container-declaration-ratchet.test.tsx uses the same imports and the same import '../index', so the new file adds no phantom dependency.
  6. The closure nail, re-derived read-only: git grep -i -E "no error, no warning|no error and no warning" over packages/types/src/** minus __tests__ gives exactly the four site files on origin/main and zero hits at the head. The dev's census A 4 → 0 stands; the census added no fifth site, which matches the claim's file surface.
  7. Public surface: no export added, removed or renamed. What changes for a consumer is text: the BaseSchema.bind and TimelineSchema.events docblocks on the emitted .d.ts, and FormSchema.shape.mode.description plus its refusal message. The accept and refuse sets are unchanged on every face. No Clause-② arm is implied.
  8. Governed surfaces: the 7-file list touches none of docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md; Governed Surface Queue Guard is success. Ordinary landing path.
  9. File surface against claim 5873069682: the four measured sites, one pin under packages/types/src/__tests__/, one .changeset/*.md, and the optional components pin as a new test file only. Within bounds; no breach.
  10. The new types pin, read: SELF_DEPTH_BELOW_REPO_ROOT = 5 resolves packages/types/src/__tests__/FILE to the repo root from bare import.meta.url, the spelling AGENTS.md prescribes; the bind and exportOptions clauses fall outside the walk's COMBINED pattern by construction (error; and error, warning) and are held by their own substring pins, so the walk's non-vacuity by name is the right control.

Bundle Analysis red, attributed. The head's run prints an eager closure of 3104.5 KB against a 3104.5 KB ceiling, "0.0 KB over" (comment 5873872677). main's own push run at 665025908 (run 36445522812) is failure with the same figure. objectui#10996 (comment 5873786983) measured main 6650259 at 3,179,017 B against MAX_EAGER_CLOSURE_GZIP_BYTES = 3,179,000, 17 B over, and named the cause: objectui#10986 (8aa68b1) added +549 B to the eager plugin-form chunk and landed through the queue. This PR's diff owns none of that red. Its only edit to an emitted JS string is the FormSchema.mode guidance in packages/types/src/zod/form.zod.ts, which sits behind the lazy boundary objectui#10065 pinned (scripts/vite-types-zod-lazy.ts); the other three edits are a comment and two docblocks (stripped from the bundle, or .d.ts only); the two new files are tests. Bound from the gate's own rounding: a head at or above 3,179,059 B would print 3104.6 KB, so the head sits within 42 B of main. The exact byte figure was not readable here: the job-log endpoint redirects to a host this container's proxy refuses, so the bound rests on the gate's comment and objectui#10996's reading, both cited above.

② Semver level

  • .changeset/10981-silence-clause-parser-tier-closure.md declares @object-ui/types: patch: right. @object-ui/types is in the fixed group of .changeset/config.json (released), and the diff publishes changed message, .describe() and .d.ts docblock text from it. Not skip-changeset (objectui has no such label, and the diff publishes bytes); not minor (no new authorable key, no widened accept set, no new export). @object-ui/components gains only a test file under src/; check-changeset-presence counts src/** and is satisfied by the one declaration, and the fixed group versions both packages together. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report: all success.
  • The changeset body names the four sites and the two codes, says text-only, cites cards as bare objectui#NNNN, carries no model identifier and no angle brackets: right.
  • Clause-②: no on the PR body: right. No arm applies: retirementTombstone stays z.never, exportOptions stays strictObject with the same error map, and the new pin asserts invalid_type at ['mode'] and at ['exportOptions'] exactly as before. The changeset owes no Clause-② line of its own, since it is not breaking.

③ Boundary flags

Dev report 5873853507. deviations: none declared and none owed (the report template carries no such key; mcp_calls 0; api_writes lists three relay writes, none refused). open_questions: empty, nothing to answer. premise_still_valid: true: agreed, the census re-derived above.

out_of_scope_findings, each answered or escalated.

(a) skills/objectui/rules/protocol.md, the "Readers only." paragraph, still says a bind on data-table renders its header over an empty body "no error, no warning" (1 hit on origin/main and at the head). Verified false by the same two legs this PR corrects in base.ts. Escalated to the seat, not this PR's: it is published skill text (class b, release-text reach) on the governed skills/** surface, which lands only behind an authorized APPROVED review, and the skills-tree rule wants the whole-file and whole-package before/after readings in that PR body. File the follow-up card with the dev's dedupe words and dispatch it as a governed-surface PR; the one-sentence fix is the base.ts wording. A second reason to file it now: the corrected base.ts sentence keeps its "(protocol.md, and pinned in …)" parenthetical, which cites a document that presently contradicts the sentence's silence clause while still supporting its non-reader claim; fixing protocol.md makes that citation whole again. Same paragraph family, unpublished and NOT routed: the header of packages/components/src/renderers/complex/dataTableBindDiagnostic.ts narrates the pre-objectui#6575 state as "no error, no warning and no diagnostic"; a source comment, repair when touched.

(b) The parser's BASE_PROPS (packages/sdui-parser/src/validate.ts) lacks bind, so validateTree draws unknown-prop for bind on readers such as list and tree-view too. Verified as source: BASE_PROPS is type, id, className, style, visible, visibleOn, disabled, disabledOn, children, under a comment reading "mirrors BaseSchema"; BaseSchema (packages/types/src/base.ts) has declared bind since objectui#6574, and by the same source reading also declares hidden and placeholder, which the set lacks as well (not measured through validateTree here; the seat's card should measure those two before naming them). Escalated to the seat as its own card in the parser lane.

Bearing on this PR's BaseSchema.bind wording: right to publish as it stands. The sentence describes the parser tier as it is today, measured 8 of 8 by the dev on the live registry and confirmed by the declaration read above; the card's closure criterion asked for exactly the parser's measured code, and no gate contradicts it. What a fix for (b) owes it depends on the route: adding bind to BASE_PROPS silences the parser on bind for every node, data-table included, so that PR must re-measure and rewrite the data-table sentence in base.ts (keeping the console-warning half, which is pinned behaviour) and the BIND_CLAUSE constant in silence-clause-parser-tier-closure-10981.test.ts, by hand, because that pin holds prose and no parser pin covers data-table + bind; declaring bind as an input on the readers instead (list, tree-view, the object-* widgets) leaves data-table undeclared, keeps the sentence true, and is the route consistent with the docblock's own "Readers, and the one documented silent failure" section and with objectui#6575's ruling that data-table must not start reading bind. Recommendation for the seat's card: name the reader-declaration route as preferred, and add a components-level parser pin for data-table + bind → unknown-prop in the shape of this PR's header-bar pin, which this card did not require and this PR did not add.

(c) Three items noted, not filed: the old words in three test headers (base-bind-declared.test.ts, object-grid-export-options-refusal-7762.test.ts, mirror-partial-record-narrowing-8516.test.ts), unpublished, repair when touched; the two pending changesets 6575-data-table-bind-diagnostic.md and 7762-object-grid-export-options-bare-array-refusal.md, verified to still narrate the pre-fix state in the old words, which is what they describe and belongs to their own PRs' inputs, no card owed; and the H "no diagnostic" nouns outside the phrasing family (ai.ts, crud.ts, data-display.ts, zod/data-display.zod.ts), not measured and not to be filed until one is measured false. All three accepted as noted.

Landing note for the seat. The head is not green: Bundle Analysis is failure on main's own bytes (①). This record is a contract verdict; it does not make the head green. Arm nothing while a red check sits on the head; the remedy is objectui#10996's (re-pin or shrink, the maintainer's decision), after which a git merge origin/main on this branch produces a fresh run.

Implemented-by: claude/issue-10981-silence-clause-closure
Reviewed-by: session_012UwY3ahMixEFkfTUxMVkYm

VERDICT: PASS

…osure

Picks up PR #11018 (objectui#10996), which re-pinned the eager-closure baseline and ceiling that turned main's own Bundle Analysis red.

Claude-Session: https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Regen-provenance: 5874097593 · ebd156e → 6230156 · git merge origin/main (6c57c77) → (empty)

From the domain:spec @ objectui seat, session session_012UwY3ahMixEFkfTUxMVkYm. The contract-review record 5874097593 (PASS on ebd156ef5) carries to 623015608 under the carry-over arm of the 2026-09-20 ruling. This was checked on the committed trees:

  • 623015608 is a merge commit, with parents ebd156ef5 and 6c57c779e (main, which carries PR fix(ci): re-pin the console eager-closure baseline and ceiling on main's own reading (objectui#10996) #11018).
  • The PR's own delta has the same patch-id at both heads (git diff MERGE_BASE HEAD | git patch-id --stable gives b3d3cc4b2d4fc16cb5145fc86e22b8d949725ea3 at each), and it touches the same 7 files.
  • Three of those files also moved between the heads: data-display.ts, zod/form.zod.ts and zod/objectql.zod.ts. They moved only because main moved; the PR's per-file delta on each is unchanged. So no path is left unexplained.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3104.9 KB 3149.4 KB
Main entry chunk (gzip) 149.8 KB 350 KB
Entry file index-BC1mo5Ql.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.60KB 6.18KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 558.92KB 133.97KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.62KB 63.16KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.53KB 4.89KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.39KB 14.98KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.56KB 36.67KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.49KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 162.67KB 41.95KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.54KB 22.44KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 20:00
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 28, 2026
…osure

Picks up PR #11020 (objectui#9256), whose metric-card refusal texts the merge queue ran this branch's closure walk against.

Claude-Session: https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm
Co-authored-by: Claude <noreply@anthropic.com>
… the next sentence (objectui#10981)

The walk required "the parser tier's" within 80 characters after every
"no … error or warning" phrasing. objectui#9256's `metric-card` refusal
(PR #11020) names the parser tier correctly, but in the sentence after,
to record why it stays silent in a widget slot. So the merge queue went
red on a true text. The bound is now the end of the sentence after the
phrasing. `MAX_WINDOW` only guards a text with no sentence end.

Claude-Session: https://claude.ai/code/session_012UwY3ahMixEFkfTUxMVkYm
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3105.0 KB 3149.4 KB
Main entry chunk (gzip) 149.3 KB 350 KB
Entry file index-D6Wh1Pcm.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.72KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 558.92KB 133.97KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.62KB 63.16KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.53KB 4.89KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.39KB 14.98KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.56KB 36.67KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.49KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 162.67KB 41.95KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 89.54KB 22.44KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e3a23eba9ec663c807cadeec0f63ad8d86c5f815
Local-runs: none

Inputs: card objectui#10981 (body; triage 5872673096; claim 5873069682; os-dev-report 5873853507; ACCEPT 5874123780; the two addenda 5877230704 and 5877836304), PR #11006 (body, the 7-file list, its 7 thread comments including record 5874097593, stand-down 5873980420, provenance 5877260576 and the head's own Console Performance Budget comment 5877870785), the 43 check-runs on the head (re-read at 2026-09-28T20:41Z, none in_progress), git diff origin/main... at the head after fetching the branch into a ref of my own, and read-only git show / git grep / git log / git patch-id against origin/main (f6fb83f0c), ebd156ef5, 623015608 and the head. The merge-group run 36476318704 and its jobs, read from the REST API. Nothing built, run or re-run; no worktree, no checkout.

① Derived judgments

What moved since the PASS on ebd156ef5, measured. Two commits: f78eb960a, a merge of main f6fb83f0c (PR #11020, objectui#9256) with the model-free trailer pair; and e3a23eba9, which touches one file. The net diff against origin/main is the same 7 files (+379 / −5). Per-file git patch-id --stable, each head's delta taken against its own merge-base with main: .changeset/10981-silence-clause-parser-tier-closure.md, header-bar-unknown-prop-parser-tier-10981.test.tsx, base.ts, data-display.ts, zod/form.zod.ts and zod/objectql.zod.ts carry the same patch-id at ebd156ef5 and at e3a23eba9; only silence-clause-parser-tier-closure-10981.test.ts differs. The dev's claim that six of seven deltas are byte-identical stands. main's move from 665025908 to f6fb83f0c touched none of the registrations or the parser the four clauses describe (form.tsx, plugin-timeline/src, data-table.tsx, plugin-grid/src/index.tsx, header-bar.tsx, sdui-parser/src/validate.ts: empty diff); PR #11020's ten files are all under packages/types plus its changeset.

The dequeue, verified. Run 36476318704 is event: merge_group, conclusion: failure, on the queue branch for this PR; job 109110810183 Test (shard 1/8) is failure at its step Run tests (shard 1/8). The cause the dev names is derivable from the texts: on f6fb83f0c the two objectui#9256 metric-card sentences are the only "no … error or warning" phrasings whose parser-tier clause sits outside the old fixed window. On the zod string, joined the way the pin joins it, the phrasing starts at offset 44 and is 31 characters long, so the old window ended at offset 155; "the parser tier's" ends at offset 156. One character. On the complex.ts docblock the clause is in the following sentence. So the old bound failed on its own construction (a character count), not on the texts.

(a) The new walk bound, read. windowAfter takes the text from the phrasing to the end of the sentence AFTER the one the phrasing sits in; a sentence ends at ., ! or ? followed by an optional closing quote and then whitespace or the end of the text; MAX_WINDOW = 600 caps the slice.

  • Not vacuous. The PAIRED patterns (the retired "no error … no warning" shapes) are unchanged and still walk every file. The COMBINED test still enumerates every C phrasing and demands "the parser tier's" inside the window. The non-vacuity control is unchanged from the reviewed head: the four site files must be reached, zod/navigation.zod.ts must carry the objectui#10959 tail and ai.ts the objectui#10928 clause. The dev's ablation legs 1 to 3 show the walk red when the next-sentence clause is deleted, when the same-sentence clause is replaced, and when the clause is pushed one sentence further on; leg 4 shows the walk red together with the two mode pins when this PR's own clause is replaced. Read-only cross-check of the population: git grep over non-test packages/types/src finds 275 lines carrying a C phrasing on both refs, and "parser tier" on 322 lines at the head against 318 on main, the four extra being this PR's four sites.
  • Not too loose. The rule is structural, not a count: the rest of the phrasing's own sentence and the whole next sentence. MAX_WINDOW can only shorten the window (it binds only when no sentence end is found within 600 characters), never extend it past the next sentence. The two shapes it admits are exactly the family's two documented shapes: "; only the parser tier's … noticed it" in the same sentence, and a next-sentence record for a site where the parser tier is genuinely silent. What it cannot judge is truth: a next sentence that names the parser tier for some other reason would pass. That is the same class of limit the 80-character bound had, and the pin is a prose pin by design (@object-ui/types cannot import the parser); the truth of the only two sentences that use the next-sentence shape is judged under (c). On the head's tree there is no third case: every C phrasing but the complex.ts docblock names the parser tier inside its own sentence (line-level git grep at the head finds one C line without "parser tier" on the same line, the complex.zod.ts continuation, whose clause is on the next line of the same string).
  • Not special-cased by file. windowAfter names no file. complex.ts, complex.zod.ts and metric-card appear only in the explanatory comment beside the rule. The non-vacuity file list is the reviewed head's. No published text moved in e3a23eba9.
  • Two heuristic edges, noted and not blocking. The sentence-end regex accepts . followed by a closing quote and whitespace, so a zod string's final .' followed by a comma is not seen as an end and the "own" sentence runs on to the next . ; and e.g. counts as an end. The second only shortens the window. The first can lengthen the own sentence when the phrasing sits in a string's last sentence, so the "next sentence" is then one further on; the 600-character cap bounds it, and no phrasing at the head sits in that position. Repair when the pin is next touched, in the same spirit as the rule's own comment.

(b) The dev's ablation record (addendum 5877836304), read and not re-run. Its shape is what the pin predicts: nine tests (two mode, two exportOptions, two TS-face, three walk). Legs 1 to 3 red exactly the COMBINED walk (1/9 each): the complex.ts next-sentence clause deleted; the complex.zod.ts same-sentence clause replaced by "and nothing noticed it"; one sentence inserted so the clause is two sentences on. Leg 4 red 3/9 (the mode arm, the spelling walk, the COMBINED walk), which is the set that reads FORM_CLAUSE plus the walk. Each restore is recorded as blob equal to HEAD with an empty git diff HEAD, and the first no-op attempt at leg 4 is declared rather than hidden. Accepted as a record of one run; the gates below are the re-derivation.

(c) The two metric-card sentences are a legitimate "genuinely silent, recorded" outcome. Verified on origin/main:

  • packages/sdui-parser/src/validate.ts: the visitor recurses through node.children.forEach(visit) and nothing else. checkDashboardWidgetOptions (dashboard-widget-options.ts) reads a host's widgets array shallowly, only for options keys on dataset-bound widgets, never visits the widget node, and emits only unconsumed-widget-option.
  • packages/plugin-dashboard/src/index.tsx: the dashboard registration declares widgets as type: 'array' with no of, so checkType accepts any array and checkMemberTypes returns null on an empty arm list. The metric-card registration declares title, value, icon, trend, trendValue, description: no children input.
  • So a metric-card node in a widget slot is never visited by validateTree: no not-a-container, no unknown-prop, no retired-body answer for its children or body. The claim "walks children, never widgets" is right, and both sentences scope themselves to the widget slot (the DashboardWidgetSlotComponentSchema arm; "in a widget slot nothing else noticed it"). Outside a widget slot the same registration would draw not-a-container for a child list, and neither sentence claims otherwise.
  • Under the card's closure criterion ("corrected to name what the parser really does, or recorded as truly silent with its measurement") these are the second outcome, and they carry their reason in the sentence. They are PR feat(types)!: refuse the content channels on the nineteen public-block arms and metric-card (objectui#9256) #11020's text, reviewed there under Clause-②: yes; this PR's only obligation toward them was that its nail not reject a true sentence, and e3a23eba9 meets it without touching them.

(d) ① ② ③ of record 5874097593 on this head. ① The four clauses: the six carrying files are patch-id-identical, the registrations and the parser they describe did not move on main, so items 1 to 5 and 7 to 10 of that record stand unchanged. Its item 6, the census, re-derived read-only on this round's refs: git grep -i -E "no error, no warning|no error and no warning" over non-test packages/types/src gives exactly base.ts, data-display.ts, zod/form.zod.ts, zod/objectql.zod.ts on origin/main f6fb83f0c and zero hits at the head; no fifth site arrived with PR #11020. ② unchanged, below. ③ unchanged, below.

Gates on the head. 43 check-runs, all completed, every one on e3a23eba9: 40 success, 3 skipped (dependabot, the two coverage matrix stubs), 0 failure. Green includes Lint, Type Check, Test with all eight shards, Test (dist pins), Build & E2E, Build Docs, Spec Main Shape Gate, Governed Surface Queue Guard, Line Citation Gate, Control Byte Scan, the four changeset checks, Changeset Claim Re-read, Skill Example Check, Skill Guide Path Check, Skill Eval Token Check and Bundle Analysis. Test (shard 1/8), the job that failed in the queue, is success here. Bundle Analysis is success and the head's comment 5877870785 prints an eager closure of 3105.0 KB against the 3149.4 KB ceiling PR #11018 re-pinned (objectui#10996), so the red the earlier record attributed to main is gone and nothing in this PR's delta touched an emitted eager chunk. Governed surfaces: none in the 7-file list.

② Semver level

  • .changeset/10981-silence-clause-parser-tier-closure.md is byte-identical to the reviewed head and declares @object-ui/types: patch: right. The package is in the one fixed group (with @object-ui/components), and the diff publishes changed message, .describe() and .d.ts docblock text. Not skip-changeset (bytes publish); not minor (no new authorable key, no widened accept set, no new export). This round's only delta is a test file under packages/types/src/__tests__/, which publishes nothing. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report: all success.
  • Clause-②: no on the PR body: right and still true. retirementTombstone stays z.never, exportOptions stays strictObject with the same error map, the pin still asserts invalid_type at ['mode'] and at ['exportOptions'], and the six carrying files did not change. No arm applies; the changeset owes no Clause-② line of its own.

③ Boundary flags

  • Addenda 5877230704 and 5877836304: no open_questions, no out_of_scope_findings, no deviation declared; mcp_calls 0; api_writes one relay comment each plus one plain push each; no PR body edit, no ready, no auto-merge. Nothing to answer.
  • The earlier record's ③ items were routed by ACCEPT 5874123780 (objectui#11008 for the parser's BASE_PROPS gap, objectui#11009 for the governed protocol.md copy; the noted-not-filed items). They stand; nothing on this head reopens them.
  • New from this round, mine:
    1. The sentence-end edges named under (a). Non-blocking; repair when the pin is next touched.
    2. The PR body still narrates head ebd156ef5 (its "Gates" and census sections). The card's addenda carry the two later rounds, so nothing is lost; the seat may refresh the body's head line. No change to the diff is owed.
    3. The two metric-card sentences are held by prose only, like the rest of the walk. No behavioural pin says that validateTree never visits a widget slot, which is the fact both sentences rest on. Out of this card's scope. Escalated to the seat as a candidate row for the parser lane (objectui#11008's neighbourhood): a dashboard node whose widgets carry a metric-card with children, asserted to draw no diagnostic, in the shape of this PR's header-bar pin.

Landing note for the seat. Every check on the head is green and this record is PASS on it. The PR is out of draft with no auto-merge armed; the ordinary path applies (dispatched PR: the dispatching seat readies and queues).

Implemented-by: claude/issue-10981-silence-clause-closure
Reviewed-by: session_012UwY3ahMixEFkfTUxMVkYm

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 797a30f Sep 28, 2026
44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants