Skip to content

fix(types,components)!: retire grid's flat smColumns / mdColumns / lgColumns / xlColumns; the breakpoint object is the one spelling (objectui#11505) - #11510

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11505-grid-flat-columns-retired
Oct 2, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11505-grid-flat-columns-retired

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11505
Clause-②: no (narrowing)

Dev run for objectui#11505 under the domain:spec seat's claim (comment 5961122544), session https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC. It executes triage's ruling A (comment 5960494324): the grid node's flat smColumns / mdColumns / lgColumns / xlColumns channel is retired, and the breakpoint object of columns is the one spelling. No alias, no dual spelling, no deprecation window.

Note on spelling: GitHub strips tag-shaped text from bodies, so the SDUI node tag and placeholders below are written as words (GRID-TAG, N, C).

The accept-set change, key by key

Surface Before (base fcdc8ec9, measured) After
strict authoring face (StrictAnyComponentSchema) each key refused at every value as unrecognized_keys at the node root refused at the key's own path, invalid_type, with the RETIRED message naming columns: { md: N } (per key's breakpoint)
tolerant face (safeValidateSchema, what objectui validate runs) every value passed through BaseSchema.passthrough(), unexamined (mdColumns: 'wide' validated) the same named refusal as the strict face
TypeScript face (GridSchema in packages/types/src/layout.ts) assignable as any through the BaseSchema index signature ?: never tombstone with a RETIRED docblock, so tsc refuses it by name
grid registration inputs columns, smColumns, mdColumns, lgColumns, xlColumns, gap, className, children columns, gap, className, children
grid registration defaultProps columns: 1, mdColumns: 2, lgColumns: 4, gap: 4 columns: { xs: 1, md: 2, lg: 4 }, gap: 4
renderer (grid.tsx) read each key over the breakpoint object; any flat key switched off the bare count's mobile-first ramp reads none of them; a document that reaches it unvalidated draws exactly what it draws without them; never forwarded to the DOM
SDUI manifest (validateTree over the live registry) smColumns: 13 drew invalid-enum (registered input) each key draws a warning / unknown-prop, "GRID-TAG has no prop" naming the key

Widening: none. Narrowing: the four keys, which the tolerant face accepted at any value.

objectui validate's printer, read through the CLI's own validate command on this branch (a one-off probe, not committed): { "type": "grid", "columns": 4, "mdColumns": 2 } and { "type": "grid", "mdColumns": "wide" } both exit 1 with Path: mdColumns, Code: invalid_type and the message RETIRED (objectui#11505, ADR-0049) — mdColumns on grid was a second spelling of the md member of columns: … Instead: write the count, 1 to 12, as the md member of the breakpoint object, columns: { md: N }, and delete the key. A bare columns: C beside it becomes the object's xs: C; with no columns at all, add xs: 2 to keep the two columns the grid drew below md. The migrated { "type": "grid", "columns": { "xs": 4, "md": 2 } } exits 0.

Zone 2 readings, measured before trusting them

H1, the seed renders identically: confirmed. Through the real SchemaRenderer, the base registration's seed (columns: 1, mdColumns: 2, lgColumns: 4, gap: 4) drew grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4, and the new seed columns: { xs: 1, md: 2, lg: 4 } draws the same string. The pin row ("the seeded grid draws the classes the flat seed drew") is green on the base and on this change. The bare 1 never ramps (the ramp needs a count above 1), so the flat overrides had nothing to interact with here.

The migration the refusal prescribes was measured the same way, flat document on the base against the object document on this branch:

Flat document (base) Drew on base Prescribed object Draws now
columns: 1, mdColumns: 2, lgColumns: 4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 { xs: 1, md: 2, lg: 4 } same
columns: 4, mdColumns: 2 grid grid-cols-4 md:grid-cols-2 gap-4 (ramp switched off) { xs: 4, md: 2 } same
smColumns: 3 (no columns) grid grid-cols-2 sm:grid-cols-3 gap-4 { xs: 2, sm: 3 } same
columns: { xs: 1, md: 3 }, mdColumns: 2, xlColumns: 6 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-6 gap-4 { xs: 1, md: 2, xl: 6 } same
columns: 2 and all four flat keys (1, 2, 3, 4) grid grid-cols-2 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 { xs: 2, sm: 1, md: 2, lg: 3, xl: 4 } same

This is why the refusal and the changeset say "a bare columns: C becomes xs: C" and "with no columns, add xs: 2": an object without xs draws one base column, while an absent columns drew two.

H2, refusal by name on the tolerant face: done as objectui#11465 did, both faces. Zod: a local retiredGridColumnsKey(key, breakpoint) over the house retirementTombstone, message RETIRED (objectui#11505, ADR-0049) — KEY on grid … Instead: …. TypeScript: ?: never plus a RETIRED docblock. Measured that the TS half is needed: with packages/types/src/layout.ts reverted to the base, tsc -p tsconfig.test.json in packages/types reports 10 errors (4 Expect rows, 4 unused @ts-expect-error rows, and 2 in zod-mirror-parity.test.ts, whose drift ledger catches a zod-only declaration), so without the tombstones { type: 'grid', mdColumns: 2 } compiles.

H3, producers: the registration was the only one, re-measured. Census of \b(sm|md|lg|xl)Columns\b over every tracked file, plus the lowercase attribute spellings:

Where Base After
renderer reads (schema.KEY in packages/*/src) 4, all in grid.tsx 0
registration 4 inputs and 2 seed keys in grid.tsx 0 (comments only)
PageDesigner none: it seeds a new grid from its own DEFAULT_PALETTE, and no designer, shell or console code reads a registration's inputs or defaultProps (ComponentRegistry.getConfig / getMeta in plugin-designer, app-shell, apps/console: zero hits) none
examples/schema-catalog documents none none
docs (content/docs, skills/, READMEs, apps/site) none two retirement sentences (grid.mdx, schema-reference.md)
tests grid-dom-attribute-whitelist.test.tsx (canary), layout-spacing-sets-11474.test.tsx (ledger), one prose line in the catalog's grid-columns-key.test.tsx re-judged below, plus the two new pins
scripts one illustrative comment in check-component-surface-parity.mjs re-pointed at a live intersection spelling
objectstack checkout only sdui.manifest.json, the recorded console manifest (see Acceptance notes) unchanged here

No corpus document authored a flat key, so none needed fixing.

Fixtures and documents re-judged

  • grid-dom-attribute-whitelist.test.tsx (objectui#4787): the canary keeps its four flat keys, re-labelled as retired keys a document that skipped validation still carries; they must still not reach the DOM. Case 1b no longer clears them (they no longer override the object). Case 2's positive control now expects md:grid-cols-4 (the bare count's ramp) and not md:grid-cols-2: the flat mdColumns: 2 is read by nothing.
  • layout-spacing-sets-11474.test.tsx: REGISTERED_NOT_DECLARED is empty, and its two-way population row now holds it empty; the four grid.*Columns positions leave the enumeration because the inputs are gone.
  • examples/schema-catalog/test/grid-columns-key.test.tsx: its docblock said the registration exposes the five column inputs; it now says columns alone.
  • content/docs/components/layout/grid.mdx, content/docs/api/schema-reference.md: one sentence each naming the retirement and the object form.
  • scripts/check-component-surface-parity.mjs: the comment that illustrated the intersection-typed schema parameter with the grid's flat-key intersection now cites UIActionSchema & { actionType?: string } (action-button.tsx), a live one. Comment only; the script's own suite ran (below).

Pins

  • packages/types/src/__tests__/grid-flat-columns-retired-11505.test.ts: type level, each key reads undefined on both faces and a @ts-expect-error per key; runtime, every former value refused at the key's own path on the strict face, the tolerant face and the mirror; the message is the RETIRED one with Instead: and columns: { BP: N }; the migrated documents parse on both faces; lit controls (the node without the keys parses everywhere, an undeclared key stays green on the tolerant face and is unrecognized_keys on the strict one).
  • packages/components/src/__tests__/grid-flat-columns-retired-11505.test.tsx: the registration's only column input is columns; the seed is the breakpoint object with no flat key; the seeded grid draws grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4; each flat key is read by nothing (five shapes); each prescribed object draws what its flat form drew on the base; the SDUI manifest answers each key with unknown-prop, and the object form with nothing; a flat key no longer switches the ramp off.

Reverse verification (committed fix, base restored, then HEAD restored)

From the committed head 4d08cf87, grid.tsx, zod/layout.zod.ts and layout.ts were checked out from the base fcdc8ec9 (on-disk check: retiredGridColumnsKey 0 hits, the if (schema.mdColumns) read 1 hit, mdColumns?: never 0 hits) and the four pin files run. Vitest resolves @object-ui/* to src through the root config's aliases, so no rebuild was involved. Result: Tests 27 failed | 26 passed (53), exit 1. Red: every flat-key row in both new pins (registration, seed shape, read-by-nothing, the manifest rows, the ramp row, the zod refusals), the enumeration ledger row plus the four grid.*Columns positions, and whitelist cases 1b and 2. Green on the base, as intended: the seeded-classes row, every migrated-object row, the manifest's object-form control and the lit controls. Restore: git checkout HEAD -- on the three paths, proved by blob hashes equal to HEAD's and an empty git diff HEAD, under an EXIT/INT/TERM trap.

Pending changesets swept (read as text against what this diff moves)

Each gets a dated, append-only note in the house form:

  • .changeset/11491-grid-columns-set.md: its components bullet named the four inputs and validateTree answering smColumns: 13 with invalid-enum.
  • .changeset/7097-grid-2xl-breakpoint.md: "the designer's flat smColumns…xlColumns channel are unchanged".
  • .changeset/8516-8556-mirror-partial-record-narrowing.md (the card's rider): it says the CLI reports { xxl: 6 } as Path: columns → xxl, Code: invalid_key. Measured through the CLI's printer on this branch, it reports Unrecognized key: "xxl", Path: columns, Code: unrecognized_keys, a top-level issue. The note attributes the change to objectui#11073's zod 4.6 resolution, as the rider does; this run measured the present answer, not the transition.
  • .changeset/11441-known-types-retired-layout-keys.md (the optional rider): its migration line names gap with no objectui#11474 note; the note mirrors its sibling's. Every ResponsiveGrid gap step (0 to 6 and 8, read off GAP_CLASSES) is one of grid's ten.

Left alone, each still true as written:

  • grid-dom-attribute-whitelist-4787.md: its mdcolumns / smcolumns are a leak measurement taken at that change.
  • 8505-grid-columns-breakpoint-narrowing.md: it is about the six keys of the columns object.
  • 11441-retire-nav-responsive-grid-layout.md: it already carries the objectui#11474 and objectui#11491 notes, and the columns object it names is unchanged.
  • 11474-layout-spacing-sets.md: gap only.
  • 7735-zod-mirrors-stop-authoring-defaults.md: the grid.columns default row is still let baseCols = 2.
  • 3965-mint-box-neutral-container.md, 9910-children-slot-declare-and-pin.md, 6740-flex-is-container.md, layout-renderers-dom-props-5574.md, dom-leak-sweep-components-5574.md: grid containment, DOM whitelist and the object spelling, which are unaffected.

New entry: .changeset/11505-grid-flat-columns-retired.md, minor for @object-ui/types and @object-ui/components, with the breaking meaning and the migration stated.

Gates, all at 4d08cf87

Gate Verdict
pnpm --filter '@object-ui/components^...' build (closure, includes types) exit 0
pnpm --filter @object-ui/types run type-check (three legs; new pin confirmed in the test program with --listFilesOnly) exit 0
pnpm --filter @object-ui/components run type-check (both new or edited pins confirmed in the program) exit 0
pnpm --filter @object-ui/example-schema-catalog run type-check exit 0
pnpm exec vitest run packages/types/ 342 files, 9123 tests passed
pnpm exec vitest run packages/components/ 355 passed, 1 skipped (a pre-existing time-zone skipIf), 3612 tests passed, 24 skipped
radius: examples/schema-catalog/, packages/cli/, packages/sdui-parser/, scripts/__tests__/check-component-surface-parity.test.ts, check-doc-component-types.test.ts, check-doc-snippet-types.test.ts, core schema-validator.test.ts, react SchemaRenderer.dataPropArm-9571.test.tsx, console registry-inputs-spec-parity.test.ts 91 files, 3486 tests passed
pnpm check:doc-snippets (closure built with its own --build-filter) exit 0, 776 of 776 blocks judged, 0 failed
pnpm check:doc-examples exit 0
pnpm check:doc-types, pnpm check:doc-fences exit 0 each
pnpm check:component-surface-parity (report-only) exit 0; ui:grid reads, interface and inputs now agree: columns, gap, children (plus style / className)
node scripts/check-changeset-no-major.mjs, check-changeset-presence.mjs, check-changeset-fixed.mjs exit 0 each
pnpm check:changeset-claims, pnpm check:pending-changeset-literals exit 0 each
pnpm check:new-line-citations, pnpm check:control-bytes exit 0 each
eslint, narrowed to the 9 touched code files (--no-inline-config --format json) 9 files, 0 errors, 1 warning (no-explicit-any on the renderer's props type, present at the same line on the base)
pnpm check:sdui-registration-pins NOT MEASURED: exit 2, no console build on disk. The gate pins registration KEYS a sideEffects array keeps at bundle time; this diff moves no array and no key, only grid's inputs and seed. Left to CI.

The eslint narrowing: the population is the 9 touched .ts / .tsx / .mjs files (eslint's own JSON output counts 9); the root eslint.config.js is the only config and enables no type-aware linting (projectService / parserOptions.project absent), so this diff cannot move any untouched file's verdict. The repo-wide lint is CI's.

Deviations

  • Node: the container's v22.22.0 is below jsdom's engine floor, so a nodejs.org v22.23.3 tarball (SHA256 checked against SHASUMS256.txt) went on PATH from this run's scratch directory. Nothing system-wide.

Acceptance notes

  • objectstack's tracked sdui.manifest.json (the recorded console manifest the CLI and lint validate against) still lists smColumns … xlColumns on grid, as type: number, so it predates objectui#11491's enums too. It is re-recorded when objectstack bumps its objectui console pin; that bump carries this change there. Carrier: the next objectstack console-pin bump.
  • Two comments kept on purpose: grid.tsx's whitelist comment and whitelist case 3 quote the mdcolumns / smcolumns attributes as measured under objectui#4787. Those are historical readings, not claims about today.

Generated by Claude Code

claude added 3 commits October 2, 2026 20:50
…mns/xlColumns; the breakpoint object is the one spelling (objectui#11505)

The grid registration offered four flat per-breakpoint column inputs and
seeded two of them; the renderer read them over the breakpoint object, and
no GridSchema face declared them. Triage ruling A: the four inputs and the
renderer's four reads are removed, the registration seeds
columns: { xs: 1, md: 2, lg: 4 }, and both zod faces refuse the four keys
by name (retirementTombstone, naming the breakpoint object member to
write); the TypeScript face carries them as `?: never` tombstones.

The enumeration pin's REGISTERED_NOT_DECLARED ledger empties, and the grid
DOM whitelist canary keeps the four keys as unread, unforwarded keys.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
… unknown-prop (objectui#11505)

Built from the live registry the way the containment pin builds it: each
retired flat key draws `unknown-prop`, and the breakpoint object, the one
spelling, draws nothing.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
… flat column keys as retired (objectui#11505)

One changeset for the retirement (minor, breaking meaning stated). Dated,
append-only notes on the pending entries whose readings this change made
false: objectui#11491's (the four registered inputs), objectui#7097's (the
designer's flat channel), and the card's riders on objectui#8516/#8556's
CLI output and objectui#11441's `gap` migration line. The grid docs page and
the schema reference name the retirement; the surface-parity gate's
intersection-spelling comment points at a live example.

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

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 28 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/11068-grid-declared-keys.md

  • names schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What was wrong. ObjectGridSchema declared eight keys the grid never read. An author who wrote description, emptyState, name, placeholder, rowSpecActions or bulkSpecActions on an object-grid got no type error, no validator refusal and no effect. The reference example in schema-reference.md taught description and showFilters as if they worked. It now authors only keys the grid reads.

.changeset/3917-retire-action-condition-branch.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    • ActionCondition is removed from @object-ui/types (and from the barrel export). - ActionSchema.condition is retyped to the predicate the runtime actually honours: boolean | string | { dialect?: string; source: string } — the same three arms ActionRunner's own ActionDef.condition carries, and the same vocabulary visible and disabled use. - ActionConditionSchema is removed from @object-ui/types/zod (and from the zod barrel); the condition key now validates against that predicate union. - The two teaching sites (content/docs/core/enhanced-actions.mdx Conditional Execution, content/docs/api/schema-reference.md ActionSchema table) are rewritten to the live vocabulary: condition is a gate; a branch is expressed as separate actions with mutually exclusive conditions.

.changeset/4895-retire-block-schema-family.md

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

    ⚠️ Not this family, and not touched. The live slotted record-page vocabulary — PageNodeSchema.kind === 'slotted' with slots?: PageSlotMap (packages/types/src/layout.ts), rendered by usePageAssignment / PageBlockCanvas / PageBlockInspector in @object-ui/app-shell — shares the words "block" and "slot" with the retired family and shares no declaration, type or file with it. Neither is the type: 'component' NAVIGATION item kind ({ type: 'component', componentRef }, NavigationItemSchema in zod/app.zod.ts), a different declaration in a different module.

.changeset/5632-svg-host-dom-passthrough.md

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

    Both renderers forwarded their whole prop bag to the SVG they render, so every authored SDUI key on the node became an attribute — 14 per target, and icon="check" on all 71 icon nodes in the schema catalog. The two nodes declare different keys, and each renderer consumes its own node's keys by name: IconSchema (packages/types/src/layout.ts) against the pass-through docblock in renderers/basic/icon.tsx, and SpinnerSchema (packages/types/src/feedback.ts) against the one in renderers/feedback/spinner.tsx. icon and color are IconSchema's alone — SpinnerSchema declares neither. Read those two declarations for what they carry; this paragraph deliberately copies no member list. So the SDUI pass-through list withholds nothing they need.

.changeset/6170-retire-timeline-dead-keys.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Also in this change: the in-repo example packages/types/examples/data-display-examples.json (its timeline node authored all three) is migrated to items / variant; the two content/docs/api/schema-reference.md snippets that authored events: [] now author items: []; and the plugin-timeline docs callout says retired rather than deprecated.

.changeset/6896-retire-chart-inline-data.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ One correction to the record the ruling rests on. The ruling states zero authorship of a populated series[].data outside tests across packages/ / apps/ / examples/. The re-measurement finds one such site inside those roots — packages/types/examples/data-display-examples.json (2 series) — plus four outside them, in documentation: content/docs/api/schema-reference.md (3) and content/docs/core/report-schema.mdx (1).

.changeset/6939-kanban-column-cards.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

.changeset/6951-text-value-retired.md

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

    Two published faces, one retirement. The TypeScript interface TextSchema (@object-ui/types, layout.ts) declares value?: never; the Zod mirror TextSchema (@object-ui/types/zod, layout.zod.ts) declares value as a retirementTombstone(), so the key stays DECLARED and is refused BY NAME — a plain deletion would have let an authored value ride BaseSchema's .passthrough() into a silent blank, which is worse than the tolerated fallback it replaces. The value?: string members of TextSpanSchema and TabsSchema in the same file are other schemas' contracts and are unchanged.

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

    Two published faces, one retirement. The TypeScript interface TextSchema (@object-ui/types, layout.ts) declares value?: never; the Zod mirror TextSchema (@object-ui/types/zod, layout.zod.ts) declares value as a retirementTombstone(), so the key stays DECLARED and is refused BY NAME — a plain deletion would have let an authored value ride BaseSchema's .passthrough() into a silent blank, which is worse than the tolerated fallback it replaces. The value?: string members of TextSpanSchema and TabsSchema in the same file are other schemas' contracts and are unchanged.

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

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What was measured, on this branch's base. TreeViewSchema declared two spellings for its one inline-nodes slot — nodes (read second) and data (read third: boundData || schema.nodes || schema.data || [] at renderers/data-display/tree-view.tsx:105), both declared by objectui#6150. data had been REQUIRED until 777e5c6f4 (PR fix(types): tree-view mirror stops requiring the limb it reads third (group 2 of objectui#6939) #7533) made it optional, so this retirement starts from a declared-and-optional member on both faces. The in-repo corpus at the retirement: seven tree-view nodes under examples/schema-catalog and packages/types/examples plus one content/docs fence — six on nodes, two on data (packages/types/examples/data-display-examples.json and content/docs/api/schema-reference.md), both rewritten; no package source authored either spelling.

.changeset/7664-kanban-arm-plugin-dialect.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Migration. Author boards in the plugin dialect — objectName + groupBy for an object-bound board, or columns[].cards[] with badges for a static one. Replace DeclarativeKanbanSchema imports with KanbanSchema (from @object-ui/types, or the Zod KanbanSchema from @object-ui/types/zod; @object-ui/plugin-kanban re-exports the same KanbanSchema type). Delete draggable (drag-and-drop is always on) and column color (style a lane through className). content/docs/api/schema-reference.md's kanban section now documents this dialect.

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

  • names layout.zod.ts → packages/types/src/zod/layout.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/7997-detail-view-related-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Documentation. packages/plugin-detail/README.md and content/docs/api/schema-reference.md stop teaching the retired array and gain a migration block each.

.changeset/8268-testid-emitted-as-data-testid.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    The other direction — retiring the promise — was considered and declined. It is what c1fe272ad did for BaseSchema.hidden, but that key had a working behaviour to describe and zero named consumers, and the ruling's decline turned on exactly that. This promise already has carriers outside the type declaration: content/docs/api/schema-reference.md states it as a table row and authors testId in that page's own base-schema example, @object-ui/cli's OBJECTUI_STRUCTURAL_KEYS identifies a file as an ObjectUI schema node by this key, at this change ObjectGridSlotKey / ObjectFormSlotKey pin it, SchemaBuilder.testId() writes it, and ADR-0054 C4 — shipped — reads "the renderer emits data-testid … derived from metadata".

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

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

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

  • names zod/layout.zod.ts → packages/types/src/zod/layout.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/8505-grid-columns-breakpoint-narrowing.md

.changeset/8653-listview-title-retired-rowactiondefs-pinned.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    title — retired. ListView resolved its export filename through schema.label || (schema as any).title. @objectstack/spec/ui's ListViewSchema refuses title by name (unrecognized_keys: ['title']) while ObjectGridPropsSchema accepts it; packages/types mirrors the platform contract rather than ruling over it, so declaring title on ListViewSchema would have made this repo accept what the platform save gate rejects. That asymmetry is also why objectui#6639 could take the declare branch for ObjectGridSchema.title one package over and this site could not. A parse-based census of apps/ examples/ content/ and packages/ found zero list-view nodes authoring title, so the retirement costs no author a filename. Over that same corpus the instrument reports three object-grid nodes carrying the key: two authored ones, both in content/docs/api/schema-reference.md, plus one that is not authored at all — packages/plugin-view/src/ObjectView.tsx composes title: schema.table?.title onto a grid node it builds, so it is a producer writing the key rather than an author declaring it. ObjectGrid's own title reads are untouched — they remain declared, ruled and read.

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

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — 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 layout.ts → packages/types/src/layout.ts — edited by this change

    What was measured, on this branch's base 93127bd6f. Zero readers, with a point-access probe rather than a bare word: on that base \.breadcrumbs scores 0 tree-wide (exit 1) against \.breadcrumb\b's 12 files tree-wide (10 under packages/) as the lit control. At head the same two probes read 16 and 13 and \.breadcrumbs is exit 0 over 4 files — every hit one of this branch's own four files (this changeset, the refusal pin, layout.ts, zod/layout.zod.ts) quoting the probe string, and the pin's own exclusions put head back at exit 1. The base reading is the measurement; the head reading is this branch's echo of it. The bare word would have lied — it also names Sentry's own unrelated concept (app-shell/src/observability/sentry.ts) and appears in two comments listing UI surfaces (core/src/utils/record-title.ts, layout/src/NavigationRenderer.tsx), so a bare probe reports five readers that do not exist.

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

    What was measured, on this branch's base 93127bd6f. Zero readers, with a point-access probe rather than a bare word: on that base \.breadcrumbs scores 0 tree-wide (exit 1) against \.breadcrumb\b's 12 files tree-wide (10 under packages/) as the lit control. At head the same two probes read 16 and 13 and \.breadcrumbs is exit 0 over 4 files — every hit one of this branch's own four files (this changeset, the refusal pin, layout.ts, zod/layout.zod.ts) quoting the probe string, and the pin's own exclusions put head back at exit 1. The base reading is the measurement; the head reading is this branch's echo of it. The bare word would have lied — it also names Sentry's own unrelated concept (app-shell/src/observability/sentry.ts) and appears in two comments listing UI surfaces (core/src/utils/record-title.ts, layout/src/NavigationRenderer.tsx), so a bare probe reports five readers that do not exist.

.changeset/9092-inline-locale-declared-face.md

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

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)

.changeset/9187-record-highlights-layout-two-values.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ The census behind this narrowing covers this repository only, and it found no in-repo authoring to migrate: every in-tree layout: 'grid' belongs to a different component (detail-view in content/docs/api/schema-reference.md and phase2-schemas.test.ts, ai-recommendations in packages/plugin-ai/README.md), and the one in-repo consumer of this interface that writes a layout (p1-spec-alignment.test.ts) writes 'horizontal'. So no document in this repository stops type-checking. A TypeScript consumer outside this repo that wrote grid is not observable from here and gets a compile error (TS2322) naming the key — which is why the FROM/TO is spelled out above.

.changeset/calendar-view-schema-converge.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Runtime renderer behaviour is unchanged. @object-ui/plugin-calendar's README and content/docs/api/schema-reference.md are repaired to the converged surface in the same change, so no copy of the old contradiction survives.

.changeset/grid-dom-attribute-whitelist-4787.md

  • names grid.tsx → packages/components/src/renderers/layout/grid.tsx — edited by this change

    grid.tsx ended in a bare {...gridProps} spread that removed only data-obj-* and style, so everything else SchemaRenderer hands a registered component reached the element. Measured on a canary node, eight attributes leaked: columns="4", gap="4", mdcolumns="2", smcolumns="2", name="grid_node", props="[object Object]", colorvariant="x" (the flattened props container) and an unknown authored zzcanary="leak". A responsive columns object rendered as columns="[object Object]". Layout was unaffected, so every catalog grid example rendered with them — the reason this went unnoticed.

.changeset/issue-5373-retire-crud-schema.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    crud had four declaration faces and no registered renderer, for the whole life of the key: the TS interface (packages/types/src/crud.ts), the zod mirror (packages/types/src/zod/crud.zod.ts), a dedicated branch in validateSchema that affirmatively PASSED it, and CRUDBuilder in @object-ui/core. A node spelling it painted the OBJUI-001 "Unknown component type" panel, and content/docs/api/schema-reference.md published it as reference material — so a reader (or an AI author) who copied the page got a red panel.

  • names api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Authoring crud is now REFUSED BY NAME rather than passed or silently ignored. validateSchema returns an error with code: 'RETIRED_TYPE' on schema.type — at any depth, since it is what validateChildren recurses with — so assertValidSchema throws and isValidSchema answers false. The message names the migration: object-grid for the record table with its toolbar, filters, pagination and row/batch actions, object-form for the create/edit form, and detail for the record view. api/schema-reference.md is rewritten around those shapes.

.changeset/layout-default-jsdoc-7361.md

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

    The published @default documentation on two layout.ts members now matches the value the renderer actually applies. ContainerSchema.maxWidth documented 'lg' while container.tsx applies schema.maxWidth ?? 'xl', and the shared FlexLayoutProps.align documented 'center' while flex.tsx applies schema.align || 'start' and stack.tsx applies schema.align || 'stretch'. The renderers are unchanged — they are the authority for what runs — so only the docblocks moved; align now states both consumers in prose instead of carrying a single @default, because one member shared by two deliberately divergent component types cannot have one correct default.

.changeset/object-view-unmirrored-keys-7779.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    listViews stays unmirrored, on the ruling's own fallback clause. The declaration's value is the local NamedListView — 64 declared top-level members (⚠️ re-taken at objectui#8980, which added the seventeen the protocol declares on this surface to the 47 this entry first measured), of which the renderer reads 21 off a named view. data is one of the 21 now: it used to reach the renderer through an as any cast on the named-view config in packages/plugin-view/src/ObjectView.tsx and be declared nowhere, and the objectui#8980 ruling declared it by name — objectui#7928's open half, answered. The spec's ViewSchema.listViews is a record of the STRICT ObjectListViewSchema, which requires columns and refuses options, ObjectQL tuple filters and default — that is, it refused the named views this package's own README and content/docs/api/schema-reference.md taught when this entry was written ({ label: 'All Users' } fails at columns; filter: [["owner", "=", "..."]] fails at filter.0), and objectui#8255 has since rewritten them in the spec shape. Mirroring the spec value would have lost documented behaviour; mirroring the local value would enforce 43 unread members (64 declared, minus the 21 that are both declared and read) into the contract — the very thing ruling B refused for the six local keys. The key therefore stays in the parity ledger with that measurement, pinned, until the maintainer decides its value type. It is not papered over with z.any().

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

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What makes this site different from its three siblings is that the alias was not merely tolerated, it was published. content/docs/api/schema-reference.md §TableSchema shipped a copyable { "name": "id", "label": "#" } example and a property row reading "Column definitions with name, label, …", while packages/types declared the opposite pair. Docs and type disagreed about one type they both call TableColumn, each internally consistent. Retiring the alias without correcting the page would have turned a documented, working example into a silently broken one, so both halves land together: the page now authors accessorKey/header. The same row also advertised a render property that TableColumn has never declared — the renderer's hook is cell — and that claim is dropped rather than re-spelled.

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.

Compared the checked-out tree with 9d9ed5495 (merge-base with origin/main): 11 file(s) changed outside .changeset/, read against 2049 pending declaration(s) that publish a body (2678 pending in total). · run

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3551.1 KB 3574.6 KB
Main entry chunk (gzip) 150.3 KB 350 KB
Entry file index-CrP9x3sp.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
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.54KB 3.46KB
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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 571.09KB 136.86KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 230.46KB 63.94KB
fields (index.js) 261.51KB 66.22KB
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.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 39.47KB 11.25KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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.04KB 3.92KB
plugin-calendar (index.js) 53.17KB 15.46KB
plugin-charts (index.js) 83.53KB 22.84KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 142.17KB 38.28KB
plugin-designer (index.js) 231.28KB 48.76KB
plugin-detail (index.js) 245.43KB 64.53KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.73KB 44.50KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 233.13KB 63.89KB
plugin-kanban (index.js) 49.51KB 15.54KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 4.14KB 1.96KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 5.07KB 2.39KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
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: 4d08cf8755a2764a64d5a0522f954e7badec1435
Local-runs: none

Review of PR #11510 for card #11505 (triage ruling A 5960494324, claim 5961122544, dev report 5961966360). Inputs: the card body and its four comments, the PR body, its 16-file list and net diff against main, the head's check-runs, and every pending .changeset/*.md entry at the head read as text (2680 entries, listed from the head's git tree, fetched one by one). Nothing built, run or re-run; one file at the head was read where a judgment below needed it (action-button.tsx, tombstone.zod.ts, the parity script, the workflows).

① Derived judgments

Each accept-set or public-surface change the diff implies, named right or wrong against ruling A:

  1. GridSchema zod face, both doors. smColumns / mdColumns / lgColumns / xlColumns are declared through a local retiredGridColumnsKey(key, breakpoint) over the house retirementTombstone (z.never({ error }).optional() in tombstone.zod.ts). Tolerant face (safeValidateSchema, what objectui validate runs): any value rode BaseSchema.passthrough() before, refused at the key's own path now. Strict face: unrecognized_keys at the node root before, invalid_type at the key with the RETIRED message now. The tolerant accept set narrows; the strict one is unchanged; nothing widens. The message names the key, the breakpoint and the columns: { BP: N } member to write, with the xs: C / xs: 2 migration. Right (ruling pin "the flat keys are refused on both faces"); the bare ADR-0049 spelling is the form the same file's existing tombstone already uses.
  2. GridSchema TypeScript face. Four ?: never tombstones with RETIRED docblocks and @deprecated, the objectui#11465 form. Needed, not decorative: the dev's ablation (TS face at base, tsc -p tsconfig.test.json exit 2, 10 errors) shows BaseSchema's index signature would otherwise type the keys any. Right.
  3. grid registration inputs. The four flat inputs removed; columns (closed enum of 1 to 12 plus the object arm), gap, className, children remain. A published registration surface narrows. Right (ruling A prong 1).
  4. grid registration defaultProps. columns: 1, mdColumns: 2, lgColumns: 4 becomes columns: { xs: 1, md: 2, lg: 4 }, the seed ruling A names verbatim; the pin measures the same class string, grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4, on base and head (ruling pin 2). Right. The seed's columns changes shape, number to object; the dev's census finds no reader of a registration's defaultProps (PageDesigner seeds from its own DEFAULT_PALETTE; zero getConfig / getMeta hits in plugin-designer, app-shell, apps/console) and no corpus document authoring a flat key. Accepted as measured.
  5. Renderer grid.tsx. The four reads and the GridSchema & { smColumns?… } prop-type intersection are gone; the mobile-first ramp's condition reduces to a bare numeric columns with baseCols above 1. Sound: the sm/md/lg/xl === undefined clauses were satisfiable only by the flat reads inside the numeric branch, so dropping them moves no rendering. A flat key that reaches the renderer unvalidated draws nothing and is never forwarded; the objectui#4787 whitelist canary keeps the four keys as unread keys, and its case 2 now expects md:grid-cols-4, the bare count's ramp. Right.
  6. SDUI manifest (validateTree over the live registry). A flat key now draws the unknown-prop warning any undeclared prop draws, where smColumns: 13 drew invalid-enum while the input was registered. This is the one tier that softens, and it is inherent to retiring rather than declaring (route B would have kept the enum); the two zod faces, the authoring gate, refuse at every value. Right, and both the new entry and the 11491 note say it.
  7. Ledger. REGISTERED_NOT_DECLARED in layout-spacing-sets-11474.test.tsx is empty and typed readonly string[]; the population row holds it empty both ways (ruling pin 1). Right.
  8. Docs. grid.mdx and schema-reference.md each gain one sentence naming the retirement and the object form; the schema-catalog test docblock now says columns alone. Right.
  9. Pins. packages/types/src/__tests__/grid-flat-columns-retired-11505.test.ts (type-level Equal rows and one @ts-expect-error per key; runtime refusal at the key's path on strict, tolerant and mirror over seven former values; the prescribed documents parse; lit controls) and packages/components/src/__tests__/grid-flat-columns-retired-11505.test.tsx (one column input, object seed, seed classes, read-by-nothing over five shapes, each prescribed object draws what its flat form drew, manifest unknown-prop per key, ramp control). Reverse verification: 27 red / 26 green with the three source files at base, the green rows being the controls meant to hold there. Right.
  10. Gate families on the head. Changeset Bump Policy, Changeset Declaration, Changeset Claim Re-read, Changeset Fixed Group Check, Changeset Overwrite Report, Doc Snippet Type Check, Doc Component Type Check, Doc Example Id Check, Governed Surface Queue Guard, Spec Main Shape Gate, Lint, Type Check, Build and E2E, Test (dist pins), Live E2E: every one success. Nothing red.

② Semver level

  • The diff publishes from @object-ui/types (layout.ts, zod/layout.zod.ts) and @object-ui/components (renderers/layout/grid.tsx); examples/, scripts/ and content/docs publish nothing. The new entry .changeset/11505-grid-flat-columns-retired.md is minor for exactly those two, carries a Breaking banner and the FROM → TO migration ({ "columns": 1, "mdColumns": 2, "lgColumns": 4 } → { "columns": { "xs": 1, "md": 2, "lg": 4 } }; { "smColumns": 3 } → { "columns": { "xs": 2, "sm": 3 } }). minor is this repo's level for a breaking change in the fixed group (check-changeset-no-major). Matches the diff.
  • Clause-②: no (narrowing) on the PR body and the claim: four registration inputs leave, the tolerant face starts refusing four keys it passed, the TS face closes them; nothing widens, the strict face only changes its refusal code. Right.
  • Pending entries whose standing reading this change makes false, judged from the head's 2680 entries:
    • 11491-grid-columns-set.md: "the grid registration's columns, smColumns, mdColumns, lgColumns and xlColumns inputs change … validateTree now answers smColumns: 13 with invalid-enum" is now false. Dated note 2026-10-02, append-only (+2/−0), house form, names what ships. Right.
    • 7097-grid-2xl-breakpoint.md: "the designer's flat smColumns…xlColumns channel are unchanged" is now false. Dated note, same form. Right.
    • 8516-8556-mirror-partial-record-narrowing.md (rider 5960370004): Path: columns → xxl, Code: invalid_key is stale since objectui#11073 (zod 4.6). The dated note attributes the change to objectui#11073 as the rider asked, records the present answer (unrecognized_keys at columns) and keeps the verdict. Append-only, house form. Right.
    • 11441-known-types-retired-layout-keys.md (the optional rider): the migration line's gap gains the objectui#11474 note mirroring its sibling 11441-retire-nav-responsive-grid-layout.md; every ResponsiveGrid step (0 to 6 and 8) is one of grid's ten. Append-only, house form. Right.
    • The rest were searched by content, not by name: the four key names (case-insensitive), "flat column / channel / key", validateTree, invalid-enum, unknown-prop, GridSchema, and grid beside inputs / defaultProps / seed / designer / registration. The only entries naming the four keys are the two annotated above, the new entry and grid-dom-attribute-whitelist-4787.md. Left alone and still true as written: grid-dom-attribute-whitelist-4787.md (mdcolumns / smcolumns are the leak measurement taken at that change; "the grid's own vocabulary was always read off schema" still holds), 7735-zod-mirrors-stop-authoring-defaults.md (let baseCols = 2 is still the renderer's fallback), 8505-grid-columns-breakpoint-narrowing.md (the six keys of the object), 11441-retire-nav-responsive-grid-layout.md, 11474-layout-spacing-sets.md (gap only), 7580-responsive-vocabulary-rehome.md, 9910-children-slot-declare-and-pin.md, 6740-flex-is-container.md, 3965-mint-box-neutral-container.md, layout-renderers-dom-props-5574.md, dom-leak-sweep-components-5574.md, 8284-content-channel-per-component.md, 8912-grid-children-items.md, 10959-unknown-prop-parser-tier-clause.md. No pending reading is left false without a note.

③ Boundary flags

  • open_questions: none in the dev report. Nothing to escalate.
  • Deviation 1, a Node v22.23.3 tarball on PATH from the run's scratch directory (the container's Node is below jsdom's floor): environment only, nothing system-wide, nothing in the diff. Answered.
  • Deviation 2, the doc-gate closure build moved to background by the harness and waited on to exit: process only; Doc Snippet Type Check on the head is success. Answered.
  • Deviation 3, scripts/check-component-surface-parity.mjs, one comment line outside the claim's file surface: the comment illustrated schemaTypeNameOf's intersection arm with GridSchema & { smColumns?: number }, the exact shape this diff deletes from grid.tsx. Leaving it would plant a citation of a shape that no longer exists. The replacement, UIActionSchema & { actionType?: string }, is live at the head (action-button.tsx, the registered component's schema parameter). Comment only, no behaviour, declared in the report as the claim's "explain in the report" asks, the script's own suite in the dev's radius, and Lint on the head is green. In bounds.
  • check:sdui-registration-pins NOT MEASURED locally (no console build on disk): the gate pins the registration KEYS a sideEffects array keeps at bundle time; this diff edits no sideEffects array and grid stays registered, so its derived set cannot move. It is wired to performance-budget.yml behind a path filter; no run of it appears on this head and none is a required context. Build and E2E, which builds the console, is success. Answered.
  • Out-of-scope carrier the dev noted and did not file: objectstack's tracked sdui.manifest.json still lists the four flat inputs on grid, re-recorded by the next objectui console-pin bump. Correct disposition; nothing for this record.
  • Check-runs on this head, read 2026-10-02T21:56Z: 37 completed success, 3 skipped (the coverage shards and dependabot, by design), 0 red; still in_progress: Test (shard 1/8), Test (shard 4/8). Those two bear on the pins' execution, not on the contract judged here, and the other six shards, Type Check and Test (dist pins) are green; the dev's run of the same suites at this head reported 9123 + 3612 + 3486 passed. The landing waits on every check green regardless (Prime Directive 14). No governed path is in the file list (Governed Surface Queue Guard success).

Implemented-by: claude/issue-11505-grid-flat-columns-retired
Reviewed-by: session_01XvhGmGAP79ZB8swnkapxPC

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 2, 2026 21:58
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 2, 2026 21:58
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 2, 2026
Merged via the queue into main with commit 2d576e4 Oct 2, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11505-grid-flat-columns-retired branch October 2, 2026 22:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants