Skip to content

feat(types): declare node-level responsiveStyles on flex, object-grid and object-chart, by reference to the spec (objectui#10872, batch 9) - #11273

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-10872-envelope-responsive-styles-flat-arms
Sep 30, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-10872-envelope-responsive-styles-flat-arms

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #10872
Clause-②: yes

Batch 9 of objectui#10872. It declares the node-level responsiveStyles that @objectstack/spec's PageComponentSchema declares on every page component on the three arms outside the public-block set that a producer writes it on: flex, object-grid and object-chart. The declaration is by reference to the spec, from batch 8's fragment. Claim 5918816527, seat domain:ui#1, session https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ.

Clause-② yes (widening). The strict authoring face (StrictAnyComponentSchema) now accepts a spec-valid node-level responsiveStyles on FlexSchema, ObjectGridSchema and ObjectChartSchema. Before, it refused the key by name. Changeset: .changeset/10872-flat-arm-responsive-styles.md, '@object-ui/types': minor. Unlike batch 8's arms, these three were already published, so the changeset spells out the narrowings too (see Acceptance notes).

What changed

  • zod. FlexSchema (layout.zod.ts), ObjectGridSchema and ObjectChartSchema (objectql.zod.ts) spread the same fragment the 30 public-block arms spread. The member is ONE object on all 33 arms: the spec's ResponsiveStylesSchema through the objectui#8317 boundary. BaseSchema is not touched, and neither is any other arm.
  • The fragment is renamed from PUBLIC_BLOCK_ENVELOPE to NODE_ENVELOPE. It stays in public-blocks.zod.ts, because that module imports only base.zod.ts, imported-defaults.ts and tombstone.zod.ts, so neither import makes a cycle. With flex spreading it, the old name would tell a reader that flex is a public block. The rename is mechanical: 30 occurrences in public-blocks.zod.ts, 5 in objectql.zod.ts, plus one census row key, one test comment and one README line. On-disk counts were 0 old / 38 new after the rename.
  • TypeScript twins. FlexSchema (layout.ts), ObjectGridSchema and ObjectChartSchema (objectql.ts) declare responsiveStyles?: ResponsiveStyles, the spec's type by reference (type-only import, as dataSource?: ElementDataSource already is). It is not on FlexLayoutProps, so stack does not gain it.
  • object-view's table slot does not gain the key. The slot is built from ObjectGridSchema's shape, so the zod change alone would have made table.responsiveStyles accepted and judged, where nothing compiles it: ObjectView draws its grid as a component. The slot now refuses it by name, as it refuses the other node-level keys (objectui#10976). The reason is its own, not the shared node-level one, because that one points the author at the object-view node, whose arm does not declare the key either.
  • Pins. New: packages/types/src/__tests__/flat-arm-responsive-styles-10872.test.ts, with 37 runtime rows and 3 type-level assertion groups. Forced edits to existing pins:
    • batch 8's pin: its boundary control moves from flex to grid;
    • object-view-slot-key-lists.test.ts: the grid's member count goes from 63 to 64, and responsiveStyles joins the nodeLevel withheld set;
    • stack-schema-emitted-members.test.ts: stack declares exactly flex's members less the named responsiveStyles;
    • census text in zod-mirror-parity.test.ts and imported-defaults-8317.test.ts: the row key follows the rename, and the reason names the three arms. The census reads presence, not words.
  • One zod README.md line plus the rename in the existing line, and one changeset.

The order's mechanism assumptions, measured

  1. The pull, re-driven. objectui 54a78308a3 was read from its tracked files. objectstack aaad682dbc (main at 2026-09-30T19:33Z) was read from a depth-1 scratch clone; the shared checkout was not fetched or edited. The instrument is scripts/body-dialect-census.mjs (scanNodes, keepFencedCodeOnly, bucketOf) under an uncommitted scratch driver. It counts NODES carrying a node-level responsiveStyles, and it reads the arm population at run time from AnyComponentSchema (189 literals, 30 of them public-block literals).

    • Non-test producers on arms outside the public-block set: flex 16 (showcase command-center.page.ts 8 and styling-gallery.page.ts 7, plus skill rules/pages.md 1), object-chart 1 and object-grid 1 (both in command-center.page.ts). Every other non-public arm: 0. objectui non-test: 0.
    • Lit control: the detector fires on the test buckets, on the same types (objectstack flex 11; objectui flex 1, object-chart 1).
    • Confirmed, and the population is exactly these three arms.
  2. The faces at base. For all three arms, the strict face refused a valid map as unrecognized_keys naming responsiveStyles. The tolerant face ACCEPTED and kept 7 and { md: … } unjudged. grid, stack and container read the same. Confirmed.

  3. Reuse the fragment. There is no cycle, so the fragment was not moved. It was renamed because the name would lie (see What changed). There is no second copy of the member: the pin asserts identity with PageSectionBlockSchema.shape.responsiveStyles.

  4. Flat arms and the TypeScript twins. Measured with the zod member alone, before any TypeScript edit, tsc -p tsconfig.test.json failed on two census assertions:

    • assertionMirroredUndeclaredMatchesLedger named exactly the three pairs (layout.zod.ts#FlexSchema, objectql.zod.ts#ObjectChartSchema, objectql.zod.ts#ObjectGridSchema);
    • assertionMirroredUndeclaredRecordsEveryKey named responsiveStyles.

    Two edits turn it green: three new MirroredUndeclared rows, or the declaration on the twins. That ledger is a floor that "can only shrink", so the declaration was taken, by reference to the spec's type, as the order's item 4 says. It is part of the same widening. Two more consumers read the twins through the compiler and were forced as listed above.

  5. BaseSchema stays untouched. It is not in the diff; the pin asserts that BaseSchema.shape lacks the key and that every other arm refuses it. There is no needs_decision.

Evidence

  • Red first, at base 54a78308a3 with the pin added and the source unmodified. pnpm exec vitest run --maxWorkers=2 …/flat-arm-responsive-styles-10872.test.ts gave Tests 17 failed | 20 passed (37). The 17 reds:

    • 3 member-identity rows, the spec-map row and the arm-population row;
    • 3 valid-node rows (strict face unrecognized_keys);
    • 9 invalid-value rows (tolerant face accepted, expected the spec's issue).

    The 20 controls (population, lit control, grid / stack / container, and the other five envelope keys) were green. pnpm --filter @object-ui/types type-check at base gave 8 errors: TS2339 on the three zod shapes, and TS2344 Type 'false' does not satisfy the constraint 'true' on the three TypeScript-twin assertions.

  • Ablation, at c4edd1d106 with the implementation committed first. It ran three single-arm legs through objectstack's scripts/ablation-replace.mjs in WRAP mode, plus a shell trap restoring both zod files by absolute path. Each leg deleted one arm's spread, and each anchor hit exactly once:

    • flex leg: blob 797a9c0b4e63 to 6011a4533631, Tests 7 failed | 30 passed (37);
    • object-grid leg: 2aa44c282213 to 1e00d88afe2c, 6 failed | 31 passed;
    • object-chart leg: 2aa44c282213 to 673ec48085df, 6 failed | 31 passed.

    The union of the three is exactly the 17 red-first rows. Every restore read ok restored: blob == HEAD … git diff HEAD is empty, and the final check read both blobs equal to HEAD with git diff HEAD at 0 bytes. The pin imports ../zod/index.zod.js from source, so there is no dist leg.

  • Reverse type check, through the rebuilt dist. A scratch consumer in packages/components resolved @object-ui/types to packages/types/dist/index.d.ts (--traceResolution). It got exactly 3 errors, one per bad line: TS2353 'md' does not exist, TS2559 for a number, and TS2322 for a boolean style value. The three valid lines got none. The scratch files were deleted.

  • Runtime probe (scratch, deleted). Each type was mounted through the real SchemaRenderer and registry, and each emitted a style[data-os-scope] with the base block and the @media override:

    • flex: the scope class is on the flex root div;
    • object-grid: on the grid's container div, which holds the rows;
    • object-chart: on the chart container ([data-slot="chart"]), which holds the SVG.

    In each control without the key, 0 style tags were emitted. Declared is enforced on these arms.

  • Final head c31dff6c1f, clean tree, one os-verify-lock hold per step, every VERDICT command-exit 0:

    • pnpm --filter @object-ui/types build gave dist completeness: 1 package(s) complete;
    • pnpm --filter @object-ui/types type-check passed, and tsc -p tsconfig.test.json --listFilesOnly lists both 10872 pins;
    • pnpm exec vitest run packages/types/ gave Test Files 296 passed (296) and Tests 7412 passed (7412);
    • pnpm exec vitest run packages/cli/ gave 23 passed (23) and 335 passed (335); the registered-types ratchet is unchanged;
    • the arm-list consumer sweep ran 162 test files outside packages/types and packages/cli: every file naming the zod node union, the strict face, safeValidateSchema, the @object-ui/types/zod entry, an arm .shape, or FlexSchema / StackSchema / ObjectGridSchema / ObjectChartSchema / ObjectViewSchema / responsiveStyles / the fragment. It gave 79 passed with 1537 tests and 83 passed with 1196 tests;
    • the 24 scripts/__tests__ files that read .changeset/ gave 24 passed (24) and 914 passed (914).

Gates at c31dff6c1f

objectui has no scripts/pm/dispatch-gates.mjs. The families were derived by hand from the root package.json for this diff's paths (packages/types/src/**, one README, one changeset).

  • pnpm check:handler-key-reads: exit 0, OK 140 arm(s), 212 registration(s).
  • pnpm check:spec-symbols: exit 0.
  • pnpm check:new-line-citations: exit 0, VERDICT new-cross-file-line-citations: 0 new citation(s).
  • node scripts/check-changeset-presence.mjs: exit 0, 12 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s).
  • pnpm changeset:check: exit 0, No changeset declares a major bump.
  • pnpm check:control-bytes: exit 0. A grep -P control-byte scan of the 13 changed files found 0.
  • Exit 0 each: check:changeset-claims (report-only), check:pending-changeset-literals, check:component-surface-parity (report-only; no row names these keys), check:test-path-roots, check:element-data-source-declaration, check:esm-specifiers, and markdown-test-inputs --audit.
  • check-governed-queue-guard.mjs --test on the 13 paths: NOT GOVERNED.
  • ESLint on the 11 changed TS files, --no-inline-config --format json: 11 files, 0 errors. All 37 warnings are no-explicit-any on lines this diff does not add (checked line by line against the diff). --print-config shows no project / projectService, so linting is not type-aware and an untouched file's verdict cannot move. Repo-wide pnpm lint is CI's.
  • NOT MEASURED: check:readme-exports. Its population collapsed (packagesRead: found 3, floor is 25) because the other packages' dist is not built in this worktree. That is a prerequisite, not a verdict. The README lines edited here carry no fenced block.

Acceptance notes

  • The narrowings on already-published arms, disclosed in the changeset. The tolerant face now refuses a spec-refused responsiveStyles value on the three arms, and table.responsiveStyles by name. The three TypeScript interfaces type the key as the spec's type, where it was any through the index signature. Level: minor, as the ruling set it; check-changeset-no-major forbids major.
  • The 18 producer nodes still do not validate on objectui's faces after this batch, for a reason outside this card's key. The showcase writes them in the spec's page-component form, with props under properties. The three flat arms do not declare properties, so the strict face also refuses them by name. On the tolerant face the object-grid and object-chart nodes are refused too (objectName required; no chart family), because the arms read those keys at node level. SchemaRenderer hoists properties and renders the nodes, and the spec accepts them. This is reported to the seat as a finding, and is relevant to PR objectui#11069's un-park census.
  • object-chart's scope class lands on the chart container, not the ObjectChart root. A first probe on a node with no series binding drew the explanatory placeholder, and that placeholder did not carry the class.
  • Downstream consumers' type-check is declared to CI's Type Check. Outside packages/types, no TypeScript source writes responsiveStyles on a node typed as one of these interfaces: the only hits are comments, identity-probe fixtures and SchemaRenderer's own read. The one derived type, the table slot's Pick, is pinned by object-view-slot-key-lists.
  • origin/main moved to f4ed2387e9 (objectui#10188, objectui#11163) with zero file overlap with this branch, so it was not merged; the queue rebuilds on main.

Deviations

  • The fragment was renamed without being moved. The order tied the rename to a move, but the name would lie either way.
  • The PR's reach is wider than the claim's file surface: public-blocks.zod.ts (rename only), layout.ts / objectql.ts (the twins, forced by the census), and two existing type-reading pins (forced). Each is listed above with its reason.

Generated by Claude Code

…-grid` and `object-chart`, by reference to the spec (objectui#10872, batch 9)

The three arms outside the public-block set whose nodes a producer writes
`responsiveStyles` on spread batch 8's shared fragment, so the member is
the same object on all 33 arms. The fragment is renamed from
`PUBLIC_BLOCK_ENVELOPE` to `NODE_ENVELOPE`, because it is no longer the
public blocks' alone. The published TypeScript twins declare the key as
the spec's `ResponsiveStyles`, which the mirror-parity census requires
for a mirrored key. `BaseSchema` and every other arm are unchanged.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
… and the imported-defaults note (objectui#10872)

Text only. The census reads the row's presence, not its words.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
…eStyles` (objectui#10872, batch 9)

The slot takes `ObjectGridSchema`'s members, so declaring the key on the
grid put it in the slot too, where nothing compiles it: the view draws
its grid as a component. The slot now refuses `table.responsiveStyles`
by name on both faces, as it refuses the other node-level keys
(objectui#10976), with a reason of its own. The two member pins that
read the TypeScript twins name the one new member: the grid's count, and
the one member `flex` declares and `stack` does not.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
…n, not a bare `any` (objectui#10872)

`stack` declares no `responsiveStyles`, so its member resolves through the
index signature: the exact shape the three twins had before this batch.
No explicit `any` is left in the file.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation package: types tests labels Sep 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 54 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/10872-container-children-channel.md

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

    Also in this change, with no behaviour change: objectql.zod.ts's two public-block arms (object-metric, object-master-detail-form) build their properties member with the same propsBag helper as the other public-block arms, instead of a byte copy of it. The member's description text is unchanged.

.changeset/10976-table-slot-relay.md

  • names packages/types/src/__tests__/object-view-slot-key-lists.test.ts → packages/types/src/__tests__/object-view-slot-key-lists.test.ts — edited by this change

    The slot's key list and the withheld set are pinned against drift by packages/types/src/__tests__/object-view-slot-key-lists.test.ts, which also requires the validator to refuse every withheld key by name, so the two faces refuse the same keys.

.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/5903-objectgantt-declared-keys.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.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.

  • 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.

  • names packages/types/src/__tests__/zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⚠️ The JUSTIFICATION for that parity clause was retired (objectui#9743); the clause itself stands and did not move. As first written it credited the zod-mirror-parity ratchet with a zero-drift reading for this pair — but at that time the ratchet measured three directions and was structurally blind to the MIRRORED-but-undeclared one (objectui#9711), so a zero from it recorded that it had not looked in that direction, not that nothing was there. objectui#9725 landed the fourth direction, and it covers this pair BY NAME: packages/types/src/__tests__/zod-mirror-parity.test.ts registers objectql.zod.ts#ObjectGanttSchema in both its mirror map and its declaration map; assertionMirroredUndeclaredMatchesLedger requires every registered pair's mirrored-but-undeclared key set to equal that pair's MirroredUndeclared ledger entry — never for a pair the ledger does not name — and assertionNoVacuousMirroredUndeclaredMeasurement refuses a measurement that has degenerated to any. ⛔ Read this pair's verdict off that reconciliation, which re-derives it on every run, rather than off any figure written here; when this paragraph was authored, on 2026-09-18, it required no MirroredUndeclared entry for the pair.

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

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Where the non-pair is recorded now. zod-mirror-parity.test.ts keys its existing EXCLUSIONS entry — the mechanism that accounts for every exported const with no TypeScript declaration to mirror, each with its stated reason — to ClassNameStylePropsSchema. Named for its own two keys, the const leaves no like-named declaration for a name-derived pairing to reach for.

.changeset/6051-gantt-flat-config-declared-keys.md

  • names packages/types/src/__tests__/zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⚠️ The JUSTIFICATION for that parity clause was retired (objectui#9743) — the same retirement objectui#5903's entry carries, for the same clause and the same reason; the clause itself stands and did not move. As first written it credited the zod-mirror-parity ratchet with a zero-drift reading for this pair, at a time when that ratchet measured three directions and was structurally blind to the MIRRORED-but-undeclared one (objectui#9711), so a zero from it recorded that it had not looked in that direction. objectui#9725 landed the fourth direction, and it covers this pair BY NAME: packages/types/src/__tests__/zod-mirror-parity.test.ts registers objectql.zod.ts#ObjectGanttSchema in both its mirror map and its declaration map; assertionMirroredUndeclaredMatchesLedger reconciles every registered pair's mirrored-but-undeclared key set against that pair's MirroredUndeclared ledger entry — never for a pair the ledger does not name — and assertionNoVacuousMirroredUndeclaredMeasurement refuses a measurement that has degenerated to any. ⛔ Read this pair's verdict off that reconciliation, which re-derives it on every run, rather than off any figure written here; when this paragraph was authored, on 2026-09-18, it required no MirroredUndeclared entry for the pair.

.changeset/6150-undeclared-but-consumed-keys.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⭐ AMENDED, and the amendment ships in this same release. The TreeViewSchema slice (604476d97) gave the key a zod arm after all — a NAMED REFUSAL (handlerKeyRefusal(key, 'runtime-slot', label)), never a shape — because "no mirror entry" is not neutral under BaseSchema.passthrough(): it meant an authored { "type": "tree-view", "onNodeClick": { "action": "toast" } } parsed GREEN, survived the parse, and reached a call site that expects a function. ⇒ the three clauses this bullet used to carry are no longer true of the code shipping beside it. The key is now a MEMBER of TreeViewSchema.shape and an authored value is refused BY NAME at path onNodeClick; it has LEFT zod-mirror-parity.test.ts's RuntimeOnlyDeclared for that file's KnownDrift; and it is no longer "the first pair to sit there without also sitting in UnmirroredDeclared" — draining it emptied that difference, so RuntimeOnlyDeclared is now a SUBSET of UnmirroredDeclared and the union of the two equals UnmirroredDeclared itself. ⛔ objectui#6152's ruling is untouched by any of this: what the key still does not have, and never will, is a z.function() shape — no serialized document could satisfy one.

.changeset/6151-stack-schema-omit-collapse.md

  • names packages/types/src/__tests__/stack-schema-emitted-members.test.ts → packages/types/src/__tests__/stack-schema-emitted-members.test.ts — edited by this change

    Guarded by packages/types/src/__tests__/stack-schema-emitted-members.test.ts, which asserts against the EMITTED declaration rather than the source. That distinction is the whole point: a source-level assertion passes on the broken code, because the index signature answers for the missing key with any. The guard emits declarations with the package's own tsconfig and asserts (1) StackSchema declares exactly what FlexSchema declares, and (2) no member of the LayoutSchema union has lost any of BaseSchema's named members — so the next heritage clause that collapses under the index signature reds for the whole class, not just for this one interface.

.changeset/6152-object-form-unmirrored-members.md

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

    ObjectFormSchema in @object-ui/types (objectql.ts) declared a set of members that its zod mirror in @object-ui/types/zod had never heard of. Every one of them is read by the object-form renderer (ObjectForm in @object-ui/plugin-form). The two published faces answered differently:

.changeset/6175-column-state-persistence.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Nothing is retired. Both spellings remain declared on DataTableSchema; onColumnReorder stays declared and stays unwired, exactly as the RuntimeOnlyDeclared ledger in zod-mirror-parity.test.ts records it. Which of the two survives is a declared-surface ruling that stays open and is deliberately not settled here.

.changeset/6269-objectview-slot-key-lists.md

  • names packages/types/src/__tests__/object-view-slot-key-lists.test.ts → packages/types/src/__tests__/object-view-slot-key-lists.test.ts — edited by this change

    Each Omit is now a Partial<PickANGLE-BRACKETS(…)> over an explicit key list — at this change 59 keys for table, 64 for form, i.e. every declared member minus the identity keys the view itself fixes. Pick with literal keys never computes keyof T, so it cannot collapse the same way. The key lists are pinned against silent drift by packages/types/src/__tests__/object-view-slot-key-lists.test.ts, which recomputes each source schema's declared members through the TypeScript checker and, at this change, requires set equality; a member added to ObjectGridSchema and not to the list turns it red.

.changeset/6639-objectgrid-title-mirrored.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The gain is the typed refusal: the mirror's .passthrough() base was already admitting any title unexamined, and it now enforces the declared string. zod-mirror-parity.test.ts's UnmirroredDeclared ledger records the key as worked off — the ledger's first shrink by repair (97 + 1 mirrored + 23 reclassified is what the seeded "121" now means).

.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-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/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.

.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 objectql.ts → packages/types/src/objectql.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.

  • 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/7344-handler-string-any-mirrors.md

.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 objectql.ts → packages/types/src/objectql.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).

  • 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/7632-shared-record-source-config.md

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

    That ladder is published contract on both faces — packages/types/src/objectql.ts and its zod mirror both ship .describe() strings naming getDataConfig's order (77cb489b4, maintainer ruling 2026-09-02), pinned by objectql-record-source-refinement-6939.test.ts — and it was hand-copied into five plugin components with no gate holding them together. A change to the ruled order had five edit sites and nothing that noticed a missed one; that is the AGENTS.md #0.1 drift class.

.changeset/7654-floating-chatbot-trigger-icon-tombstone.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Every other tombstone in this package pairs ?: never with a retirementTombstone() refusal on the Zod twin. There is no twin here to carry one: FloatingChatbotConfig has no Zod mirror at all, and floatingConfig sits in the UnmirroredDeclared ledger (zod-mirror-parity.test.ts, complex.zod.ts#ChatbotSchema). BaseSchema is .passthrough(), so the whole floatingConfig object rides through unvalidated — before this change and after it. Minting a mirror to host a refusal would be the declared-but-UNMIRRORED axis (objectui#6152), a different defect: a key can be mirrored and inert, or unmirrored and live, and fixing one says nothing about the other. This change does not widen into it.

.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/7804-objectql-handler-key-arms.md

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

    The four plain objectql.ts node faces declare the nine handler keys their registered renderers read (the objectql.ts slice): ObjectFormSchema.onCancel / .onError / .onOpenChange / .onStepChange / .onSuccess, ObjectGallerySchema.onCardClick / .onRowClick, ObjectGridSchema.onNavigate and ObjectViewSchema.onNavigate.

.changeset/7804-tree-view-handler-slot.md

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

    'runtime-slot' and not 'retired', measured at this key's own channel. 'retired' publishes "no renderer reads this key, so nothing could ever run it" — true of the two siblings already tombstoned on this arm (onSelectChange, onExpandChange) and flatly false here, since the read is live and INVOKED. ⚠️ No in-repo host builds a tree-view node carrying the key: the channel is wired end to end and only the supplier is absent, which is the same shape as ObjectFormSchema.onStepChange in this card's objectql.ts slice and is not evidence of a dead read. The TypeScript declaration is unchanged and still callable, so a programmatic host supplies it exactly as before.

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The pair moves from zod-mirror-parity.test.ts's RuntimeOnlyDeclared to its KnownDrift, which empties the former of the one entry the latter did not also hold — so the two unmirrored ledgers are now in a containment relation, and the cross-ledger figure that recorded their difference states the containment instead.

.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/7963-alert-dialog-footer-keys-retired.md

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

    Nothing else moves. These spellings are overloaded across the tree and every other owner is a live key on a different declaration — FormSchema.cancelLabel, objectql.ts's confirmLabel, plugin-designer's ConfirmDialog React props, plugin-grid's def.confirmLabel, and plugin-form's ModalForm / DrawerForm, which build a local cancelLabel from schema.cancelText. None is an AlertDialogSchema; none is touched, and a pin asserts it. No fixture, catalog schema, example app or doc fence authored any of the three on an alert-dialog node, so no shipped document is stranded.

.changeset/8338-retire-toast-action.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The parity ledgers drain with it, every figure re-derived by zod-mirror-parity.test.ts's own AST and mirror instruments rather than stepped by hand: KnownDrift 42 entries / 64 keys → 41 / 63 (the entry's whole content, so the entry went too — the ledger's first loss by RETIRING a key rather than by moving either face toward the other), and WiderThanDeclared 23 / 36 / 47 arms, split 6 / 30 / 0 / 11 → 22 / 35 / 45, split 6 / 29 / 0 / 10. The pair itself stays registered, so EXPECTED_MIRROR_PAIRS does not move.

.changeset/8344-node-recursion-point-redirect.md

  • names zod/public-blocks.zod.ts → packages/types/src/zod/public-blocks.zod.ts — edited by this change

    ⚠️ Dated note, 2026-09-28 — a second category module — objectui#10872. Later still in this same release zod/public-blocks.zod.ts joins them as well and imports zod/base.zod.ts too, so both counts in the mechanical note above are two higher, not the one the objectui#10859 note says; the reason the arm cannot be an import is unchanged. The rest of this entry, and that note, are kept as their readings.

.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/8516-8556-mirror-partial-record-narrowing.md

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

    | key | mirror was | mirror is | | :-- | :--------- | :-------- | | GridSchema.columns (zod/layout.zod.ts) | z.record(z.string(), z.number()) | a PARTIAL record over the six breakpoints | | ReportComponentSchema.exportConfigs (zod/reports.zod.ts) | z.record(z.string(), ReportExportConfigSchema) | a PARTIAL record over ReportExportFormat |

.changeset/8572-chatbot-body-retired.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Why this key and not another. It was the ONE place in this vocabulary where body did not mean "what goes inside this component": zod-mirror-parity.test.ts carried the pair under KnownDrift as "two different meanings of one key", and the same collision was the whole reason chatbot was the single arm of the component union whose output was not assignable to SchemaNode. Both ledger rows move with this change, and the two pins that recorded the old state are INVERTED rather than deleted (see below).

.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/8767-object-grid-refuses-string-sort.md

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

    Migration. Write the array: sort: [{ field: 'name', order: 'desc' }]. Both keys are required. SortConfig.order carries no ? in @object-ui/types (packages/types/src/objectql.ts) and no .optional() in its zod mirror, and the protocol's own reusable SortItemSchema requires order as well — measured: that schema refuses [{ field: 'name' }] with invalid_value at 0.order. Do not omit it: this block's array arm interpolates whatever is present, so an omitted order lowers to $orderby: 'name undefined' today. That is pre-existing behaviour on the arm this change does not touch, and it is filed as a successor card rather than widened into here.

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

  • names packages/types/src/objectql.ts → packages/types/src/objectql.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.
  • 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 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/8885-object-chart-drilldown-title-compareto.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.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.

  • 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 objectql.ts → packages/types/src/objectql.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.

  • 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/objectql.ts → packages/types/src/objectql.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.

  • 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/9092-inline-locale-declared-face.md

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

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)
  • 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/9309-object-gallery-filter-destination-typed.md

  • names objectql.ts → packages/types/src/objectql.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).

  • 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/9511-record-id-is-a-string.md

  • names objectql.ts → packages/types/src/objectql.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.

  • 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.

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⭐ The declaration alone would not have been enough, and this is the reusable part. A TypeScript declaration does not run at parse time. The hand-written zod mirror is the only face in this repository that can refuse an authored number, so narrowing the declaration without the mirror would have shipped declared !== enforced on a published surface — and zod-mirror-parity.test.ts would have stayed GREEN through it, because that instrument asserts a mirror accepts everything its declaration declares and a mirror left WIDER passes. Both faces moved together for that reason.

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

  • names objectql.ts → packages/types/src/objectql.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).

  • 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/9550-object-tree-root-barrel.md

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

    ObjectQLComponentSchema declares the node types an ObjectQL block may be. Every one of its arms was a named export of this package's root barrel except ObjectTreeSchema, which was declared in objectql.ts, applied by the union, and re-exported by the ./zod barrel (objectui#7917) — while no TypeScript consumer could name it. There is no ./objectql subpath to reach around the barrel: the package's exports map is pinned by packages/types/src/__tests__/package-exports-manifest.test.ts, and the root barrel was the only route to this type.

.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.)

  • names objectql.ts → packages/types/src/objectql.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/9628-kanban-column-collapsed-honoured.md

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

    The key was declared on both published faces of the object-kanban arm — the lane element of ObjectKanbanSchema (objectql.ts and its Zod mirror) and the runtime lane KanbanColumn (complex.ts and its mirror) — and read by KanbanEnhanced alone, a module no production source imports. An authored { "id": "todo", "title": "To Do", "collapsed": true } therefore parsed green on both faces and reached a board that did nothing with it: KanbanImpl's only collapse is the SWIMLANE row's, held in viewer state under objectui:kanban-collapsed:ANGLE-BRACKETS(swimlaneField) and never keyed to a lane's declared value. That is the ADR-0049 declared-but-unhonoured shape.

.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 zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Who is NOT affected: every correctly typed document, and every document that never wrote these keys — absent stays valid on all nine. No renderer changed. The parity ledger (zod-mirror-parity.test.ts) records the move: UnmirroredDeclared 14 entries / 96 keys to 14 / 87, the ObjectViewSchema entry re-derived into the SPEC-DERIVED half because the mirror now references the spec in code.

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 f61dab169 (merge-base with origin/main): 12 file(s) changed outside .changeset/, read against 1842 pending declaration(s) that publish a body (2454 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3583.6 KB 3607.4 KB
Main entry chunk (gzip) 149.9 KB 350 KB
Entry file index-D6RKF3JJ.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.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) 570.05KB 136.37KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 228.86KB 63.53KB
fields (index.js) 261.11KB 66.26KB
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) 41.23KB 11.53KB
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.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 138.72KB 37.05KB
plugin-designer (index.js) 216.32KB 44.56KB
plugin-detail (index.js) 244.72KB 64.45KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.61KB 44.59KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.25KB 63.50KB
plugin-kanban (index.js) 48.46KB 15.12KB
plugin-list (index.js) 116.42KB 28.89KB
plugin-map (index.js) 23.50KB 7.82KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
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.55KB 39.23KB
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 (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
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) 19.82KB 7.15KB
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: c31dff6c1f16a78d9256b21167364da2432cc345
Local-runs: none

PR objectui#11273 (batch 9 of card objectui#10872), judged against the card's body and its 54 comments, the PR body and file list, the net diff against main at the head (merge base 54a78308a3, batch 8's merge; four commits; 13 files, +498 / −55), and the head's 43 check-runs. Spec source read at objectstack origin/main 31c39964fc (@objectstack/spec 17.5.0, the version packages/types/package.json pins as ^17.5.0): packages/spec/src/ui/page.zod.ts (PageComponentSchema.responsiveStyles, :344; .properties, :324), responsive.zod.ts (ResponsiveStylesSchema, :99; export type ResponsiveStyles, :148) and component.zod.ts (ComponentPropsMap, :5632). Governing text: batch-9 claim 5918816527 (Clause-②: yes; surface layout.zod.ts / objectql.zod.ts reusing batch 8's fragment, forced census rows, pins, README line, one changeset; not BaseSchema, not the other five keys, not PR objectui#11069's files), batch 8's ACCEPT 5918461519 ("whether that is BaseSchema or a fragment on the named arms is that batch's measured choice"), partial-landing note 5918807919, batch 7's ACCEPT 5916807221 (the ruling: additive, Clause-② yes (widening), minor; and the definition of the "refusal half"), release 5863830858 ("additive, when a producer needs them"), triage 5906817482; dev report 5919939389.

① Derived judgments

Every accept-set and public-surface change the diff implies, each judged:

  1. The strict face widens on exactly three arms — right. ...NODE_ENVELOPE is spread into FlexSchema (layout.zod.ts :338), ObjectGridSchema (objectql.zod.ts :298) and ObjectChartSchema (:2939). The fragment is the one batch 8 declared (public-blocks.zod.ts :293, stripImportedDefaults(SpecResponsiveStylesSchema).optional()), so the member is ONE ZodOptional instance on all 33 arms; the pin asserts arm.shape.responsiveStyles is PageSectionBlockSchema.shape.responsiveStyles by identity and its inner shape equals the spec's key by key. base.zod.ts is not in the diff, the pin asserts BaseSchema.shape lacks the key, and the arm list is read off the live union at run time with grid / stack / container as strict-face controls, so an arm that gains the member silently turns the pin red. The widening reaches depth through flex's own children recursion (the pin's nested child). No other arm and no other envelope key moves; dataSource stays where objectui#11070 put it (object-grid only), held in a named set.

  2. The tolerant face narrows on the same three arms for spec-refused values — right and disclosed. safeValidateSchema used to keep 7 or { md: … } unjudged on these arms; now both faces return the spec's own issue list (the pin compares code, path and message with PageComponentSchema.safeParse's verdict, read at run time). Unlike batch 8's arms, these three ARE in the published 17.6.0, so this is a narrowing on shipped arms; the changeset says so, and its renderer sentence is now exact where batch 8's was overstated: SchemaRenderer applied nothing for a number or a spec-less map, emitted a non-CSS value inside a valid bucket verbatim, and applied the valid buckets of a mixed map. Level settled in ②.

  3. object-view's table slot refuses table.responsiveStyles by name on both faces — right, and it IS a narrowing of the tolerant face against main. The slot is ObjectGridSchema.omit({ type, objectName }).extend(OBJECT_VIEW_TABLE_WITHHELD).partial() (objectql.zod.ts :869), so every grid member the withheld list does not name rides into the slot: without the new row the slot would have gained the key. On main, BaseSchema being .passthrough(), table.responsiveStyles was kept unexamined; at the head it is retirementTombstone (z.never({ error }).optional(), tombstone.zod.ts :64), which refuses a present key on the tolerant face and, through the strict rebuild, on the strict face. Declared = enforced holds: ObjectView hands its grid only the relay keys, the TS slot is a Partial of a Pick over ObjectGridSchema by ObjectGridSlotKey (34 named keys, objectql.ts :2170, no responsiveStyles), and plugin-view's TableRelay is the same shape, so nothing compiles a map written there. The changeset's last bullet discloses it ("The tolerant face used to keep it unjudged, and the TypeScript slot never declared it"). The own reason is right: TABLE_KEY_NODE_LEVEL ends "A node-level key belongs on the object-view node itself", and ObjectViewSchema does not declare responsiveStyles (no producer), so the shared text would steer the author into a strict-face unrecognized_keys. One nuance, not blocking: SchemaRenderer reads the key off every node (packages/react/src/SchemaRenderer.tsx :1942), so on the object-view NODE it would render today while the strict face refuses it; "Delete it" is the remedy that holds on both faces, and the seat may want the message to say the object-view arm leaves the key undeclared too.

  4. The TypeScript twins declare the key as the spec's type — right, and inside the order. FlexSchema (layout.ts :718), ObjectGridSchema (objectql.ts :788) and ObjectChartSchema (:4957) declare responsiveStyles?: SpecResponsiveStyles, a type-only import from @objectstack/spec/ui on the ElementDataSource precedent. The mirror-parity census forced it (the dev measured assertionMirroredUndeclaredMatchesLedger naming the three pairs with the zod member alone; that ledger is a floor that only shrinks), which is the case the dispatch order anticipated. It is a public-surface narrowing for a TS consumer that wrote a non-spec value through BaseSchema's index signature, disclosed in the changeset. It is placed on FlexSchema, not on FlexLayoutProps, so StackSchema does not inherit it. The only derived types are the two explicit Pick lists in item 3, unaffected. CI Type Check: success.

  5. stack stays undeclared — right, and pinned three ways. No producer: this review's own census of objectstack origin/main finds no stack (or grid / container) node carrying the key. Pinned by the new pin's strict-face control on stack, by its type-level assertionUndeclaredTwinIsRefused (Equal on the twin resolves through the index signature to any and is refused), and by stack-schema-emitted-members.test.ts, whose FLEX_ONLY = ['responsiveStyles'] is named so a second difference reddens it.

  6. The rename PUBLIC_BLOCK_ENVELOPE → NODE_ENVELOPE, in place — internal, no semver effect. The constant is a module export of public-blocks.zod.ts consumed by objectql.zod.ts and now layout.zod.ts; index.zod.ts's export block from ./public-blocks.zod.js (:421-450) is a named list that omits it, index.zod.ts has no export *, index.ts re-exports no zod value, and the package exports map publishes ./zod as dist/zod/index.zod.js alone, with no deep path. It has never shipped: it arrived at 54a78308a3 today, packages/types/package.json reads 17.6.0 at both the merge base and main, and .changeset/10872-envelope-responsive-styles.md is still pending on main. No cycle: public-blocks.zod.ts imports base.zod.ts, imported-defaults.ts, tombstone.zod.ts and the spec, nothing from layout.zod.ts or objectql.zod.ts. The claim allowed a move if a boundary required it; a text-only rename because the old name would call flex a public block is a lesser edit, ratified. Nit: the PR body's "0 old / 38 new" is a dated reading; at the head the old name survives 3 times as history (README :243, the docblock :260, the parity comment :4345) and the new one appears 47 times.

  7. The producer pull — sound, and re-read here. Independently of the dev's census, this review attributed every responsiveStyles site in objectstack origin/main 31c39964fc to its node type: examples/app-showcase/src/ui/pages/command-center.page.ts carries 8 flex sites (the head / panel / band helpers, cc_root, cc_titlebar, cc_rule, the bar and the spacer), 1 object-chart (the chart helper) and 1 object-grid (cc_queue_g); styling-gallery.page.ts carries 7 flex sites; skills/objectstack-ui/rules/pages.md teaches 1 flex; every other typed site on those lines is a public-block arm (element:text, element:button, object-metric), and the only other non-test hits in the repo are lint source, the spec's authorable-surface JSON and changelog text. That is the dev's flex 16 / object-chart 1 / object-grid 1 exactly, with every other non-public arm at 0. Under the card's rule ("additive, when a producer needs them"), one lit site is a producer; the population is these three arms and no other. Weakness, not a defect: object-grid and object-chart rest on one site each.

  8. Forced pin edits — exactly forced. object-view-slot-key-lists.test.ts pins the grid's member count (63 → 64) and holds the zod withheld list to the TS Pick, so the new row had to join nodeLevel; stack-schema-emitted-members.test.ts asserted stack equals flex; batch 8's pin used flex as its boundary control, which now declares the key (moved to grid); zod-mirror-parity.test.ts's EXCLUSIONS is closed over export const names (index.zod.ts :634-636), so the rename forces the key public-blocks.zod.ts#NODE_ENVELOPE; the imported-defaults-8317 edit is comment text. The README hunk is one rewritten line (the rename, with its history) and one added line naming the three arms. Doc Snippet Type Check, Internal Docs Link Check, README Export Check: success.

  9. Merge state. The branch was not merged with main, which has since moved five commits to 18d1a0abd3 (objectui#11258, feat(plugin-detail): record:related_list reads its declared actions; host-bridge actions stay the default (objectui#11163) #11263, feat(types,core,fields,app-shell,plugins)!: reference is the only relational-target spelling objectui writes or reads; reference_to retires (objectui#11070, round 4) #11264, fix(app-shell): a Studio switch inside the autosave debounce never writes the previous item's buffer into the one just opened (objectui#11232) #11265, fix(types): objectui validate judges every node inside a page container's properties.children and items[].children, at the positions the platform walks (objectui#11223) #11267). A test merge of the head onto 18d1a0abd3 is conflict-free. The one file both sides touch is packages/types/src/zod/README.md, in disjoint regions (:243 here, :323 in fix(types): objectui validate judges every node inside a page container's properties.children and items[].children, at the positions the platform walks (objectui#11223) #11267). The queue rebuilds on main, as the dev says.

② Semver level

  • Changeset .changeset/10872-flat-arm-responsive-styles.md: '@object-ui/types': minor, Clause-②: yes (widening). PR body and claim 5918816527: Clause-②: yes. Changeset Declaration, Changeset Claim Re-read, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report: all success on the head. check-changeset-presence counts 12 source files of 1 released package against this one changeset.
  • minor is right, and the line is the ruling's own spelling. (i) ACCEPT 5916807221 set "Clause-② yes (widening) and minor" for the envelope-key batch, and 5918461519 carried it to this batch under meta-rule ①. (ii) The narrowings in ① 2, 3 and 4 do not move the level: check-changeset-no-major (Changeset Bump Policy) forbids major, and objectui's rule ships a narrowing as minor with the break spelled out in the body (batches 5, 6 and 8 on this card). (iii) The body spells all three out under "Narrowing, on already-published arms", and, unlike batch 8, says plainly that these arms were published before the change.
  • Two text nits, ratified as read, neither moving a verdict: the Clause-② line names the widening only, as batch 8's did; and the new pin's docblock (flat-arm-responsive-styles-10872.test.ts :33) calls the fragment PAGE_COMPONENT_ENVELOPE, a name no zod module carries (it collides with an unrelated local Set in list-view-spec-parity.test.ts). Comment only; the code imports and pins NODE_ENVELOPE's member by identity. Fix it in the landing or a later text round.

③ Boundary flags

Dev report 5919939389: open_questions: []; two out_of_scope_findings; seven deviations. Each answered or escalated:

  1. Finding 1 (class b), the properties bag on the three flat arms — a DISTINCT gap, not the remainder's "flat-props refusal half"; escalated to the seat for a carrier. Not blocking this PR. The remainder the partial-landing note 5918807919 names is defined by batch 7's ACCEPT 5916807221 as the "Refusal half (flat row keys on the 29 public-block arm types)": a NARROWING on the bag arms, after objectui#11183 moves the taught nodes, refusing a spec-row key written flat. The finding is the inverse on the three FLAT arms: a node written in the spec's page-component form is refused because the arms declare no properties bag. Verified at the head: FlexSchema, ObjectGridSchema and ObjectChartSchema declare no properties; BaseSchema is .passthrough(); the spec's PageComponentSchema.properties is z.record(z.string(), z.unknown()).optional().default({}); SchemaRenderer hoists properties onto the node (:147-166, :718). So the strict face refuses properties by name on all three, and the tolerant face refuses the showcase's object-grid node at objectName and its object-chart node with the no-chart-family issue, because those arms read the props at node level, while a flex node passes it. That is a WIDENING and a dialect question (which spelling objectui's own non-spec arms accept), and it is not homogeneous: the spec's ComponentPropsMap has an object-grid row (:5632), is "deliberately absent" for object-chart (:5630) and has none for flex. Triage 5906817482's "no second dialect" cuts the other way here, so it needs a ruling, not a mechanical fold. What the seat must weigh: this batch does exactly what the ruling granted, but the claim's sequencing goal ("lands before PR objectui#11069 switches objectui validate to the strict face", so the 18 producer nodes pass) is NOT reached by it: those nodes are still refused by the strict face at properties, and two of them by the tolerant face too, as they were before this batch. Carrier: a card (class b, measured through safeValidateSchema, the function objectui validate calls; the CLI binary was not run), or an explicit seat note that widens this card's remainder and routes the flat-vs-bag question where the action:* fork went; and objectui#11069's un-park census for sequencing (5916790889), which should count the 18.
  2. Finding 2 (object-chart's scope class lands on [data-slot="chart"], and the no-series placeholder carries no class) — an observation with no public-door reading; SchemaRenderer adds the scope class to the node's className, and where a component puts that class is the component's. Acceptance notes.
  3. Deviation: the rename without a move — answered in ① 6.
  4. Deviation: surface wider than the claim's list — public-blocks.zod.ts (rename only, no accept-set change), layout.ts / objectql.ts (① 4, forced by the census as the order anticipated), the three existing pins (① 8, forced), the table-slot refusal (① 3, the smaller change that keeps this diff's accept-set change to the three claimed arms). Each inside the claim's intent and the order; answered.
  5. Deviation: the own reason on the table slot — answered in ① 3.
  6. Deviation: the ablation as three single-arm legs — the union of the legs equals the 17 red-first rows, and each leg's restore is proven by blob; a method this review cannot repeat, accepted on the report.
  7. Deviation: objectstack read from a depth-1 scratch clone at aaad682dbc — a read-only method; this review re-read the same producer files at 31c39964fc and agrees (① 7).
  8. Deviation: main not merged — ① 9; the queue rebuilds on main. Attribution trailers — process, outside this record's scope.
  9. Seat 2's objectui#11223 (claim 5917605842): its PR objectui#11267 merged at 2026-09-30T21:25:04Z as 7d074baaec, after this head, so this PR is the second lander and has nothing to do beyond the queue's merge of main. This diff touches none of fix(types): objectui validate judges every node inside a page container's properties.children and items[].children, at the positions the platform walks (objectui#11223) #11267's surface (index.zod.ts, strict-authoring-face.ts, nested-component-walk.ts are untouched here), and fix(types): objectui validate judges every node inside a page container's properties.children and items[].children, at the positions the platform walks (objectui#11223) #11267 touches none of this diff's files except zod/README.md, in a different region; the test merge is clean. fix(types): objectui validate judges every node inside a page container's properties.children and items[].children, at the positions the platform walks (objectui#11223) #11267's walk reads the node union by reference (AnyComponentSchema gains a .check(nestedComponentJudgment(…)) and descends only into the page containers' properties.children / items[].children), so the three arms' new member rides through it unchanged, and the new pin's armsByLiteral reads .options off a union that still carries them. The merged head's Test shards are the confirmation.
  10. Check-runs on c31dff6c1f, read 2026-09-30T21:32Z: 43; 40 success, 3 skipped by design (Test (coverage), its matrix row, dependabot), 0 failure, 0 in progress. Earlier reads in this review (at 2026-09-30T21:16Z and 2026-09-30T21:29Z) had Test shards, Type Check and Spec Main Shape Gate in progress; every one has since concluded success, so nothing is left unconcluded. Success includes Type Check, Lint, all eight Test shards and the Test aggregate, Test (dist pins), Spec Main Shape Gate, Build Docs, Build & E2E, README Export Check, Governed Surface Queue Guard, Line Citation Gate and the five changeset gates.
  11. Governed: none of the 13 paths (Governed Surface Queue Guard: success). Closing keywords: Part of #10872 only; the card stays open for record:line_items, the flat-props refusal half (after objectui#11183), the held arms, and now the carrier in item 1. Scope against claim 5918816527: no BaseSchema, no other envelope key, no PR objectui#11069 file, no packages/cli/**, no ratchet; the ratchet is unmoved. The PR is a draft, behind main; the seat's ready-and-queue lands it on the rebuilt head.

Implemented-by: claude/issue-10872-envelope-responsive-styles-flat-arms
Reviewed-by: session_0122Knsowci76D2rBWReCzzZ

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 21:40
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit f3135a4 Sep 30, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10872-envelope-responsive-styles-flat-arms branch September 30, 2026 21:55
huangyiirene pushed a commit that referenced this pull request Sep 30, 2026
Carries main through PR #11267, #11268, #11269, #11271, #11273 and #11274.
No conflicts.

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: types tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants