Skip to content

fix(types): a gate-wrapped arm waives objectName when dataSource.object binds the node (objectui#11117) - #11312

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-11117-datasource-objectname-waiver
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-11117-datasource-objectname-waiver

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #11117
Clause-②: yes (narrowing): a widening on six arms (a non-empty dataSource.object counts as a record source), plus one narrowing on the object-map bag arm (an empty dataSource.object no longer counts)

What this does. The tolerant face (safeValidateSchema, which objectui validate runs) now accepts a node bound through dataSource.object without an objectName on every gate-wrapped arm that has a record-source requirement: object-grid, list-view, object-kanban, object-calendar, object-gantt and object-map. The binding counts only when dataSource.object is a non-empty string, as the spec gate's strName and the runtime's isElementDataSourceConfig read it. A node with neither is still refused, and dataSource: { object: '' } supplies nothing. Dispatched by the domain:spec @ objectui seat, session session_01VhxTqosz7wn54ahqyxgERT.

The change

  • One refinement. requireRecordSource(type, rungs) in packages/types/src/zod/objectql.zod.ts now takes each arm's own rung list and counts the binding as the last rung, using dataSourceSuppliesObject. That predicate is the one element:number's spec waiver already used; it is now exported from public-blocks.zod.ts as an internal helper, so there is one copy, and it is not re-exported from index.zod.ts.
    • It is installed with when: () => true, so the refusal is reported beside any other issue on the node. On object-grid and list-view the required member used to behave that way, and the refinement keeps it.
    • object-kanban's hand-copied requireKanbanRecordSource is gone. The kanban arm now calls the shared helper with bind / data / objectName.
  • Where the refusal lands. On object-grid and list-view, objectName is optional as a member and the refinement requires it unless a binding is present. The refusal is reported at objectName, the path the required member used, so objectui validate still prints Path: objectName (validate-union-arm-selection.test.ts stays green). The issue is now custom with params.code = 'RECORD_SOURCE_REQUIRED'. The four ladder arms still report at the root.
  • Each arm's objectName describe names the waiver.
  • ObjectViewSchema.table rebuilds the grid shape onto BaseSchema before its .omit(). zod 4 throws on .omit() of a refined object, and the grid now carries a refinement. The slot is unchanged: same keys, same .passthrough(), no record-source check.
  • Docs, in content/docs/utilities/data-objectstack.mdx.
    • The "Narrowing a saved view" fence now writes its filter as the spec's rule array, [{ "field": "total", "operator": "greater_than", "value": 100 }].
    • The ElementDataSource excerpt types filter as ViewFilterRule[], and its fragment marker gives the new reason.
  • Docs elsewhere. The data-binding guide (content/docs/guide/data-source.md) and content/docs/api/schema-reference.md say a bound node needs no objectName.
  • Changeset. .changeset/11117-datasource-objectname-waiver.md bumps @object-ui/types minor (a widening).

Premises, measured on origin/main 1ccb5ba7d against the built @object-ui/types dist

The probe ran every documented binding through AnyComponentSchema (tolerant) and StrictAnyComponentSchema.

  • H1 holds, and the form half is already answered. objectui#10859 batch 4 moved object-form onto ObjectFormBlockSchema. { "type": "object-form", "dataSource": { "object": "user" } } already parses on both faces, and the mode refusal belonged to the flat arm, which no longer judges authored documents.
    • The spec's object-form row declares objectName optional, so the bag arm has nothing to waive. A form node with neither key also parses, as the spec row allows; the renderer's inline customFields path is one reason a node can lack both. ⛔ This change does not narrow that.
    • The same holds for object-metric, object-timeline and object-master-detail-form: their rows require no object key.
  • H2 holds. object-kanban's "has no record source" was the same gap: dataSource.object was missing from its ladder. Calendar, gantt and map refused their bound nodes the same way. All three registrations are gate-wrapped, and their mapping lands binding.object on objectName.
  • H3 holds. On the base, the "Narrowing a saved view" fence was refused twice: objectName invalid_type, and dataSource.filter invalid_type for the record form. Both are gone.
  • After, on the head. Every json binding fence on both pages parses on both faces.
    • The new pin reads the fences off disk.
    • The probe also kept the old record-form document: it is still refused, at dataSource.filter only, which is the spec's own refusal.

The TypeScript twin of ObjectGridSchema keeps objectName: string

  • Why not optional. Making it optional reddens five translateOptions(schema.objectName, …) read sites in @object-ui/plugin-grid's ObjectGrid.tsx. I measured this with a source-mapped tsc program over core, plugin-grid, plugin-list, plugin-view, plugin-calendar, plugin-gantt, plugin-dashboard, plugin-designer and app-shell. Changing those sites is renderer typing, outside this card's file surface.
  • How the difference is recorded. The declaration's doc says why it stays string. The mirror's wider objectName is a CONCRETE entry in zod-mirror-parity.test.ts's WiderThanDeclared ledger, with its WIDER_ARMS row and the header figures. The ledger reddens when the two faces agree again.
  • ListViewSchema is derived from its mirror, so its objectName is now optional. The same consumer program reported no error from that.

Texts this change made false

Corrected here.

  • The object-grid objectName row and the kanban record-source note in schema-reference.md.
  • The kanban groupBy docblock in objectql.ts, which said the documented { type, dataSource } fragment is still refused. Also the kanban TS docblock and the objectql.zod.ts docblocks that named requireKanbanRecordSource.
  • Dated, append-only **Correction, 2026-10-01** notes on four pending changesets: objectui#6939, objectui#7313, objectui#7780 and objectui#8990. Their front matter is byte-identical.

Corrected in round 2 (2666d824b).

  • The objectName input descriptions on the object-map, object-gantt and object-calendar registrations, and on view:calendar, which shares the calendar list, now end with: "…refuses a block that declares none of them, unless the node's dataSource.object names the object, which then lands on this key."
    • No input name, type, required flag or shape moved.
    • registry-inputs-spec-parity.test.ts ran read-only and passed 230/230 untouched.
  • The plugin-gantt and plugin-kanban READMEs, and the comments next to the three registrations, carry the same correction.
  • The pending objectui#7470 and objectui#10392 changesets get dated, append-only corrections.
  • .changeset/11117-registration-objectname-description.md bumps the three plugins as a patch.

CI repair (round 2). scripts/markdown-test-inputs.mjs ADJUDICATED now registers element-data-source-objectname-waiver-11117.test.ts as reading content/docs/utilities/data-objectstack.mdx and content/docs/guide/data-source.md.

  • markdown-test-inputs.test.ts was red at 993a0c85c (1 failed / 24 passed) and is 25/25 at 2666d824b.
  • --audit reports 108 candidate test files, all adjudicated.

Round 2 gates, at 2666d824b:

  • The doc-snippet build closure built (35/35 turbo tasks). check:doc-snippets then passed with 698 of 698 compiled blocks and 0 failed. This replaces the round-1 NOT MEASURED.
  • check:readme-exports passed.
  • type-check passed for plugin-map, plugin-gantt and plugin-calendar.
  • A 34-file vitest union passed, 1674 tests: every adjudicated reader of the five edited markdown files, the ledger script's tests, and the description pins.
  • check:new-line-citations, check:control-bytes, check:changeset-claims, check-changeset-presence and check-changeset-no-major all exited 0.

Round 3 (01b9fb478), from contract record 5923725485. Two one-clause corrections:

  • content/docs/guide/data-source.md now says the validator refuses one of the six blocks only when it declares no other record source and names its object in neither place. The binding is "a" record source, not "the" one.
  • The @object-ui/types changeset now says the base refused the documented object-grid, list-view and object-kanban bindings, not every documented binding.

Gates at 01b9fb478:

  • the 11117 pin: 47/47;
  • markdown-test-inputs.test.ts: 25/25, and --audit is clean;
  • the 19-file union of the edited files' readers: 819/819;
  • check:doc-snippets: 698/698 compiled, 0 failed;
  • check:doc-fences, check:changeset-claims, check-changeset-presence, check:new-line-citations and check:control-bytes: all exit 0.

Round 4: merge of main (80ec249bc). The merge brings in objectui#10859 batch 5, under which an authored object-map is judged by its properties-bag arm, ObjectMapBlockSchema. That arm already counted a dataSource binding, by its presence alone.

  • Conflict in objectql.zod.ts. Batch 5's at: 'properties' mode and this PR's rung list, binding predicate and when: () => true are now ONE helper, requireRecordSource(type, rungs, at).
  • The one narrowing. The bag arm now counts only a non-empty dataSource.object, like every other arm and like the runtime. So { "type": "object-map", "dataSource": { "object": "" } } with no bag rung is refused with RECORD_SOURCE_REQUIRED. It used to parse, and it rendered no records.
  • Conflict in plugin-map/src/index.tsx. Main's text is taken. Its objectName input description already names the node's dataSource binding, so this PR no longer edits that file.
  • Population. Still six arms. object-map is now judged on its bag arm, which keeps a record-source rule read in properties. The flat ObjectMapSchema mirror is no longer an arm of the face, and it keeps the same rule on the node.
  • Pins. The 11117 pin's object-map row moves to ObjectMapBlockSchema, and the flat mirror gets its own case. Batch 5's flat-key pin (object-map-properties-bag-10859-b5.test.ts) now expects the root RECORD_SOURCE_REQUIRED issue beside its flat-key refusals.
  • Changesets. Both 11117 changesets are corrected for the bag arm and its one narrowing, and plugin-map leaves the registration changeset.
  • Gates at 80ec249bc:
    • @object-ui/types: build, type-check, and 306 files / 7,734 tests, all passing.
    • plugin-map, plugin-gantt and plugin-calendar: type-check passing.
    • A 122-file union of consumers, readers and pins: 2,656 tests passing. It includes the record-source pins and a read-only run of registry-inputs-spec-parity.test.ts (230/230).
    • markdown-test-inputs: 25/25, and --audit is clean.
    • check:doc-snippets: 698/698.
    • check:changeset-claims, check-changeset-presence, check-changeset-no-major, check:new-line-citations and check:control-bytes: all exit 0.

Round 5 (2a237f0ec): a CI repair. The round-4 merge resolution escaped the backticks in requireRecordSource's two binding strings. Those strings are single-quoted, so the escapes are useless, and no-useless-escape failed Lint with 6 errors.

  • The escapes are dropped. The string values are byte-identical.
  • pnpm --filter @object-ui/types run lint reports 0 errors. eslint --format json over all 12 TS/JS files in this diff also reports 0 errors.
  • @object-ui/types type-check passed. The 11117 pin and the batch-5 bag pin passed 93/93.

The census fixtures (24 in 18 files, from PR objectui#11286's list): judged, then handed back as a worklist

What I recommend. Convert 21 fixtures and keep 3. The conversion is separable from the validator change and spans 11 packages and 15 dom-test files. Each file asserts the merged filter the gate produces, so its expected values may need re-spelling. Nothing here converts a fixture.

Keep (3). Each pins how a renderer handles a stored legacy shape:

  • record-picker-malformed-filter-10789.test.tsx: the $regex record, and its well-formed record control.
  • ElementDataSourceGate.malformedFilter-10789.test.tsx: the $gt record control.

Convert (21). Each fixture's subject is the authored binding, and it reaches the gate as one; convert it to [{ field, operator, value }]:

  • record-picker-element-data-source (2)
  • ObjectChart.elementDataSource
  • ObjectPivot.elementDataSource
  • RecordRelatedListRenderer.elementDataSource
  • record-related-list.propsRefusal-9963: the _fullBinding type control
  • EmbeddableForm.elementDataSource
  • LineItemsPanel.elementDataSource
  • MasterDetailForm.elementDataSource
  • ObjectGantt.elementDataSource
  • ObjectGrid.elementDataSource (2)
  • ObjectKanban.elementDataSource (2)
  • ListView.elementDataSource
  • ListView.sharedGate
  • ObjectMap.elementDataSource
  • ObjectTimeline.elementDataSource (2)
  • ElementDataSourceGate.test (2). Once it is converted, that file should carry one explicitly named stored-shape (tuple array) case, so the gate keeps coverage of a shape a renderer may still receive.

Tests and gates, at head 993a0c85c

  • @object-ui/types.
    • pnpm --filter @object-ui/types build passes, with dist completeness: 1 package(s) complete.
    • type-check echoes tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json and passes.
    • pnpm exec vitest run packages/types/: 302 test files / 7649 tests passed.
  • Consumer suites, through the vitest source aliases. I ran every test file outside packages/types that reads safeValidateSchema, the strict face, or one of the six mirrors: 96 files across cli, core, sdui-parser, the plugin packages, app-shell, apps/console, examples/schema-catalog and scripts. All passed, in two runs: 70 files / 1330 tests, and 26 files / 257 tests.
  • Gates. These all exited 0:
    • check:spec-symbols, check:new-line-citations (0 new), check:control-bytes
    • check:doc-types, check:doc-fences, check:doc-example-ids
    • check:handler-key-reads, check:element-data-source-declaration, check:component-surface-parity, check:test-path-roots
    • check:changeset-claims (report-only; record-source paragraphs read and corrected above), check:pending-changeset-literals
    • check-changeset-presence and check-changeset-no-major
  • eslint. --no-inline-config over the 8 changed TypeScript files: 8 files in the JSON report, 0 errors. The 38 warnings are all no-explicit-any on unchanged lines. The config has no type-aware rules, so this diff cannot move a verdict on an untouched file.
  • Ablations. I ran three against committed ca5c505ba, each via ablation-replace.mjs with the anchor proven 1 to 0 on disk. The pins import ../zod/… source, so no dist is involved. Each run was restored, and the blob matched HEAD with git diff HEAD empty.
    1. Delete the binding rung (if (dataSourceSuppliesObject(node)) return;): 14 failed / 33 passed. The failures were the six arms on both faces, plus both docs pages.
    2. Accept an empty name (typeof object === 'string'): 6 failed, one EMPTY case per arm.
    3. Drop when: () => true: 7 failed. Six were the "not a record" cases; the seventh was the grid case that reports the missing record source beside a columns error.

NOT MEASURED: check:doc-snippets (exit 2, precondition: its 34 packages are unbuilt). Separately, I tested the edited fragment marker against the gate's own marker and verification-claim regexes: it matches, its reason is 556 characters, and it makes no claim.

Acceptance notes

  • object-chart. Its registration is gate-wrapped, but the strict face still refuses dataSource by name. That is the deferral the TS twin's dataSource docblock records, outside this card's population. objectName is already optional there.
  • ObjectViewSchema and ObjectTreeSchema. They refuse a bound node without objectName, and that is correct: neither registration is gate-wrapped.
  • The TypeScript twin of ObjectGridSchema keeps objectName: string (seat decision B, round 2). Open PR objectui#11306, since merged as 582edef1c, edited the same TS region and plugin-grid, and no TS-typed producer of a bound grid was found. The mirror's wider objectName is recorded as a CONCRETE WiderThanDeclared entry in zod-mirror-parity.test.ts, which goes red when the declaration becomes optional.
  • The H4 fixture worklist stays as written here. It is not carded.

Generated by Claude Code

claude added 5 commits October 1, 2026 01:29
…bject` binds the node (objectui#11117)

One shared refinement, `requireRecordSource(type, rungs)`, now carries the
record-source requirement of every gate-wrapped arm that has one, with the
node's `dataSource.object` counted as a rung on the predicate the spec gate
and the runtime use (`dataSourceSuppliesObject`, exported from
`public-blocks.zod.ts` as the one copy):

- object-grid and list-view: `objectName` is optional as a member and
  required by the refinement unless the binding names the object;
- object-map / object-gantt / object-calendar: their ladder gains the binding;
- object-kanban: its hand-copied ladder is replaced by the shared helper with
  its own rung list.

An empty `dataSource.object` supplies nothing, and a node with neither key is
still refused. `ObjectViewSchema.table` rebuilds the grid shape instead of
`.omit()`ing the now-refined grid. The TS twin of `ObjectGridSchema` makes
`objectName` optional to stay in parity with its mirror.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…nd the documented bindings (objectui#11117)

- New pin file: per arm (object-grid, list-view, object-kanban,
  object-calendar, object-gantt, object-map) the binding parses on the
  member, the tolerant face and the strict face; a node with neither key is
  refused at the root keyed RECORD_SOURCE_REQUIRED; an empty or non-record
  binding supplies nothing; a wrong-typed objectName beside a binding is
  still the member's refusal. Every json binding fence in
  content/docs/utilities/data-objectstack.mdx and content/docs/guide/data-source.md
  is read off disk and parsed on the tolerant face.
- The kanban and calendar record-source pins take the message that names
  the binding; the kanban pin that held the documented fragment refused on
  the record-source rule now holds it parsing.
- Docs: the "Narrowing a saved view" fence takes the spec's rule-array
  filter, the ElementDataSource excerpt types filter as ViewFilterRule[],
  and the data-binding guide says a bound node needs no objectName.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…ledger the mirror's waiver (objectui#11117)

Making `ObjectGridSchema.objectName` optional on the TypeScript face reddens
five `translateOptions(schema.objectName, ...)` read sites in
`@object-ui/plugin-grid` (measured with a source-mapped tsc program over the
consumer packages), a renderer-typing change outside this card's file
surface. The declaration stays `string`, its doc says why, and the mirror's
wider `objectName` is recorded in the parity test's `WiderThanDeclared`
ledger (CONCRETE, one arm), which reddens when the faces agree again.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…texts it makes false (objectui#11117)

- `.changeset/11117-datasource-objectname-waiver.md`: `@object-ui/types` minor (a widening).
- Dated, append-only corrections on the pending changesets of objectui#6939, #7313, #7780
  and #8990, whose bodies say a bound node with none of the rungs is refused, or name the
  retired `requireKanbanRecordSource`; front matter byte-identical.
- `content/docs/api/schema-reference.md`: the object-grid `objectName` row and the kanban
  record-source note name the binding.
- `content/docs/guide/data-source.md`: the bound-node sentence moves to its own paragraph,
  after the line-items note it had split, and names the six blocks.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…ung (objectui#11117)

On `object-grid` and `list-view` the record-source refusal now sits at
`objectName`, the path the required member used to report at, instead of
the root. `objectui validate` locates a refusal by path, and its
arm-selection pin (`validate-union-arm-selection.test.ts`) reads
`Path: objectName` for a grid that names no object. The four ladder arms
keep the root, where no single key is at fault.

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

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 56 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/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/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/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/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/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/6470-gantt-dependency-field-deprecated.md

  • names packages/plugin-gantt/README.md → packages/plugin-gantt/README.md — edited by this change

    packages/plugin-gantt/README.md's ObjectGanttSchema example authored the singular; it was the only in-repo site that did, and it now authors the plural with the alias named as legacy. No runtime code, fixture, example app or catalog schema authored it.

.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/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/6940-rowactions-boolean-mirror.md

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

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

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

  • names 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/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/7323-adapter-factory-return.md

  • names content/docs/utilities/data-objectstack.mdx → content/docs/utilities/data-objectstack.mdx — edited by this change

    The README's note saying the page could not yet teach the factory's shape is removed, and the four sections built on the adapter-only members (Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues) now continue from Basic Setup's createObjectStackAdapter(…) call instead of declaring the class by hand. The docs-site page content/docs/utilities/data-objectstack.mdx is corrected the same way: its prose, its factory signature fragment and its "hold the class type to reach these" section described the old narrow return, and its Mutations and Troubleshooting examples told the reader to construct the class by hand to reach members the factory now declares. src/adapterFactoryReturn.types.test.ts pins the card's TS2339 reproduction inverted, with two controls: the adapter-only members stay absent from DataSource (fires on option B), and the widened return stays assignable to DataSource (swappability kept).

.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/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/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/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/8338-retire-toast-action.md

  • names handler-keys-json-refusal-6124.test.ts → packages/types/src/__tests__/handler-keys-json-refusal-6124.test.ts — edited by this change

    Not handlerKeyRefusal(). action is ⛔ not a handler key: it is a VALUE key whose NESTED member was a function. So it takes the ADR-0049 retirement helper (invalid_type, and ?: never on the declaration) and not objectui#6124's named-refusal arm (custom, with a TS twin that stays callable for a runtime slot). The two helpers are pinned apart in handler-keys-json-refusal-6124.test.ts, whose census counts 45 runtime slots + 22 retired on* sites — a non-handler key has no seat in it.

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

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

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

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

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

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.1 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-Den40Nwg.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.17KB 136.43KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
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.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.95KB 11.48KB
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.90KB 15.35KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.36KB 37.31KB
plugin-designer (index.js) 216.45KB 44.60KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.75KB 44.64KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

…description names the dataSource.object binding (objectui#11117)

Round 2 of objectui#11117. The registrations' `objectName` input description
said the schema refuses a block that declares none of `data`, `staticData`
and `objectName`; since this PR the schema also accepts a block whose
`dataSource.object` names the object. The sentence now says so, on
`object-map`, `object-gantt` and `object-calendar` (`view:calendar` shares
the calendar list). No input name, type, `required` flag or shape moved.

The adjacent source comments that cite the refinement, and the
`plugin-gantt` / `plugin-kanban` README sentences, carry the same
correction, and the pending objectui#7470 / #10392 changesets get dated,
append-only corrections. Changeset: the three plugins, patch.

`scripts/markdown-test-inputs.mjs` adjudicates the round-1 pin
`element-data-source-objectname-waiver-11117.test.ts`, which reads the
binding fences of `data-objectstack.mdx` and `guide/data-source.md`; its
absence from `ADJUDICATED` reddened `markdown-test-inputs.test.ts` in CI.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.4 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-CGsTKvxc.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.82KB 136.59KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.95KB 11.48KB
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.98KB 15.39KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.45KB 44.60KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.58KB 7.85KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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: 2666d824b1386013d621eeb4d50a38c94d985736
Local-runs: none

Rendered 2026-10-01T02:47Z by the at-tier reviewer run inside the domain:spec @ objectui seat's session. Inputs: card #11117 (body and all seven comments), PR #11312 (body, the 25-file list, the net diff against main at 582edef1c), objectui AGENTS.md at origin/main, the installed spec 17.5.0 at objectstack 0f6dcac5e9 (page.zod.ts, component.zod.ts, view.zod.ts, shared/enums.zod.ts, and the props gate validate-component-props.ts in @objectstack/lint), files at the head and base refs read with git show / git grep (no checkout), and the head's check-runs, read last. Nothing was built, run or re-run. The PR body and the seat's ACCEPT 5923546311 were read as claims and re-derived from source.

Gates on this head (read 2026-10-01T02:44Z): 43 check-runs, 40 success, 3 skipped (dependabot and the two coverage rows), 0 in progress, 0 failed. Type Check, Lint, Doc Snippet Type Check, Test (shard 1/8) through Test (shard 8/8), Test (dist pins), Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Spec Main Shape Gate and Governed Surface Queue Guard are all success. These conclusions are the gate verdicts.

① Derived judgments

The mechanism, read from the head. One module-private requireRecordSource(type, rungs) in objectql.zod.ts, installed with an always-true when option on six arms: object-grid and list-view with the one rung objectName (member now z.string().optional()); object-map / object-gantt / object-calendar with data / staticData / objectName; object-kanban with bind / data / objectName, its hand copy requireKanbanRecordSource deleted. The binding rung is dataSourceSuppliesObject from public-blocks.zod.ts: a record dataSource whose object is a NON-EMPTY string, the one copy element:number's waiver already read. Right against the three authorities the card names: the spec props gate's strName (a string of positive length) and suppliedByDataSource (waives only an OMITTED object: "Only no key, or undefined, is waived"); the runtime's isElementDataSourceConfig (object.length positive), which decides whether ElementDataSourceGate lands anything; and the gate's default mapping object: objectKey = 'objectName', with no override in any of the six plugins' sources. All six registrations are elementDataSourceBlock-wrapped (verified: plugin-grid, plugin-list ListViewBlock, plugin-kanban, plugin-calendar, plugin-gantt, plugin-map).

Both faces, all six arms. The strict face is derived by cloneWithDef(schema, { shape, catchall: z.never() }), which keeps def.checks (the module's own docblock refuses the z.object(shape) rebuild precisely because it drops refinements). So the refinement runs on StrictAnyComponentSchema exactly as on AnyComponentSchema, and the accept-set change is the same on both. Per arm, the document that NEWLY parses, with no objectName and no other rung:

  • object-grid: { "type": "object-grid", "dataSource": { "object": "product", "limit": 20 } }, and the "Narrowing a saved view" node (whose fence also needed the filter rule-array conversion, see docs below). Right: spec ObjectGridPropsSchema.objectName is optional "because the component-level dataSource binding can supply the object instead".
  • list-view: { "type": "list-view", "dataSource": { "object": "user", "view": "active", "limit": 50 } }. Right: the spec has NO ComponentPropsMap row for list-view (verified in component.zod.ts), so its props gate judges nothing there and PageComponentSchema.dataSource accepts the node.
  • object-kanban: the documented board { "type": "object-kanban", "dataSource": { "object": "task", "filter": [ one rule ] } }, until this head refused RECORD_SOURCE_REQUIRED at the root. Right: spec row objectName optional.
  • object-calendar, object-gantt, object-map: { "type": …, "dataSource": { "object": "account" } }, not on the two docs pages but in the population. Right: each spec row's objectName is optional with the same sentence.

Refusals the card insists on, still held (pinned on the member and the tolerant face in element-data-source-objectname-waiver-11117.test.ts; on the strict face by the construction above; a strict-side refusal pin is absent, noted, not required):

  • neither key: refused, custom with params.code = 'RECORD_SOURCE_REQUIRED', at objectName on grid and list-view (the path the required member reported at, so objectui validate still prints Path: objectName), at the root on the four ladder arms;
  • dataSource: { object: '' }: refused. The spec's ElementDataSourceSchema.object is z.string() and admits the empty string, so this refusal is objectui's record-source rule, agreeing with the runtime and the lint gate;
  • dataSource: 'account': refused at dataSource and on the record source;
  • objectName: 7 beside a binding: the member's invalid_type; the waiver covers an OMITTED key only, as the lint gate's does;
  • nothing narrowed: objectName alone, an empty one included, still parses on all six. On grid and list-view the base member was z.string() required, so every previously accepted document carries it and the refinement returns early; on the four ladder arms the refinement only gained a rung. A monotone widening.

Issue-shape change, disclosed: on grid and list-view the neither-key refusal is now a custom issue keyed RECORD_SOURCE_REQUIRED where it was invalid_type. Named in the changeset. A consumer keying on the code invalid_type at objectName would stop matching; validate-union-arm-selection keys on the path and is green in CI.

Public-surface changes, each named:

  • @object-ui/types/zod: ObjectGridSchema.objectName and ListViewSchema.objectName optional as members, plus the refinement on six arms; the six objectName .describe() strings name the waiver and RECORD_SOURCE_REQUIRED, published metadata, read true. Right.
  • dataSourceSuppliesObject becomes export function in public-blocks.zod.ts. index.zod.ts re-exports that module through a NAMED list (verified) that does not include it, so it is NOT on the package's public entry. Right, internal.
  • TypeScript ListViewSchema (the type): ListViewAuthored maps over ListViewInferred, which is z.input of the mirror, so objectName is now string | undefined on a published TS face; a producer gains, a consumer reading it as string narrows. Type Check is success on the head, so every in-repo reader compiles. Disclosed in the changeset. Right.
  • TypeScript ObjectGridSchema.objectName: string unchanged; the mirror is now wider; see ③ (ledger).
  • ObjectViewSchema.table: rebuilt as BaseSchema.extend(ObjectGridSchema.shape).omit(…).extend(OBJECT_VIEW_TABLE_WITHHELD).partial() because zod 4 throws on .omit() of a refined object. The base ObjectGridSchema ended at }) with no check or transform, so .shape loses nothing; both are BaseSchema heirs (same passthrough); objectName was omitted before and is omitted now. Accept set unchanged, pinned (extra: 1 rides through, columns: 5 is refused at table.columns). Right. No other .pick / .omit / .extend of the six refined arms exists in the head tree (grep over non-markdown sources).
  • Registration inputs objectName descriptions on object-map, object-gantt, object-calendar and calendar (registered with namespace: 'view', i.e. view:calendar, sharing OBJECT_CALENDAR_INPUTS): the appended clause "unless the node's dataSource.object names the object, which then lands on this key" is TRUE after this head (gate default landing objectName; "names" matches the non-empty rule). No name, type, required or shape moved. Right.

Docs faces (AGENTS.md #2):

  • schema-reference.md: the grid objectName row and the kanban record-source note are both true ("omitting all of bind / data / objectName with no binding is still refused" is the exact rule).
  • data-objectstack.mdx: the "Narrowing a saved view" fence moves to [{ "field": "total", "operator": "greater_than", "value": 100 }]. Right: spec 17.5.0 ElementDataSourceSchema.filter is z.array(ViewFilterRuleSchema) and greater_than is in VIEW_FILTER_OPERATORS; the old record form stays refused at dataSource.filter, the spec's own refusal. The excerpt's filter?: ViewFilterRule[] and the marker's "exported from @objectstack/spec/ui" are right (export type ViewFilterRule in view.zod.ts, re-exported by ui/index.ts, ./ui subpath in the spec's package.json). object: string "(required)" stays right.
  • guide/data-source.md: "a node bound this way needs no objectName of its own" and the six-arm list are right; "safeValidateSchema, which objectui validate runs" is right (commands/validate.ts calls safeValidateSchema, which is AnyComponentSchema.safeParse). One clause is wrong as literally written: "still refuses one of those nodes that names its object in neither place". True for object-grid and list-view, whose only rung is objectName; but a staticData-only map, gantt or calendar, or a bind-only or data-only board, names its object in neither place and parses. Over-strict, teaches no invalid document, moves no accept set. Correct to "declares no other record source and names its object in neither place" at the next touch of the page.

Changesets, which publish verbatim; each sentence judged.

  • 11117-datasource-objectname-waiver.md (@object-ui/types: minor): every sentence true except one overstatement, "refused every per-element binding the docs teach": the page's object-form fence already parsed on this PR's base (the bag arm since objectui#10859 batch 4), and the guide's element:number fence already had its waiver; the enumeration that follows (grid, list-view, kanban) is the accurate list. Correct "every" to "the grid, list-view and kanban bindings" at the next touch. The before-state on the six blocks, the widening paragraph, all four refusal bullets, the one-predicate sentence, the two TS-twin sentences and the docs sentence are true.
  • 11117-registration-objectname-description.md (plugin-map, plugin-gantt, plugin-calendar: patch): true in every sentence, view:calendar sharing the list and "No input's name, type, required flag or shape changed" included.
  • Six appended notes, all append-only with front matter byte-identical to the base (the diff is pure additions): 10392 (the exception now holds on all three blocks) true; 6939 (map/gantt: "none of the three" is narrower; the message names the binding) true; 7313 (calendar) true; 7470 (map/gantt input description) true; 7780 ("Two statements above no longer hold": the still-refused fragment, and the board-specific predicate; ladder and presence rule otherwise unchanged) true; 8990 ("The second bullet above" is indeed the second bullet, and it no longer holds) true.

② Semver level

Clause-②: yes on the PR body's second line, matching triage 5893778824 and the claim 5922753752. The diff publishes: a widening of the accept set on both zod faces of six arms; a custom-for-invalid_type issue-shape change on two; objectName optional on the TypeScript ListViewSchema; description-only metadata on three plugins. @object-ui/types: minor is the right level and the maximum one: AGENTS.md's 版本号策略 forbids major in this fixed group and marks objectui's own breaking changes minor too; Changeset Bump Policy, Changeset Fixed Group Check and Changeset Declaration are success. The three plugin patch entries match description-only text. No skip-changeset. plugin-kanban is README-only (*.md, exempt from the presence rule) and declares nothing, which is right. Level: right.

③ Boundary flags

Dev deviations (os-dev-report 5923387834):

  1. File surface extended (schema-reference.md, four changeset notes, the parity ledger, a comment in handler-keys-json-refusal-6124.test.ts): answered by the seat's file-surface amendment; each is text this change made false or the ledger the change must register in.
  2. Registration inputs rows, stop-and-report, then round 2 edited descriptions only: answered and verified; no name, type, required flag or shape moved; registry-inputs-spec-parity.test.ts is not in the diff.
  3. ObjectViewSchema.table construction: answered above; accept set unchanged, pinned.
  4. H4 census worklist (21 to convert, 3 to keep) handed back; the seat: not carded. Answered by the seat. Reviewer's note: the fixtures reach no public door (ElementDataSourceGate never validates the node), so this is hygiene; but a worklist that lives only in a PR body has no carrier after merge, so the seat should either accept that it lapses or hold it somewhere trackable. Not a verdict matter.
  5. First commit made the grid TS twin optional, reverted after measurement: answered; the head carries objectName: string.

open_questions[0] (A: widen the TS twin in a follow-up; B: keep it required, the ledger entry stands; the dev recommended A; the seat decided B). Judged for the soundness of the RECORD, not the choice. The parity instrument's WiderThanDeclaredKeys compares the mirror's INPUT side (so .optional() shows) against the declaration; string | undefined is not assignable to string, so the pair measures WIDER on objectName, and assertionWiderMatchesLedger, a compile-time assertion that pnpm --filter @object-ui/types type-check reads, requires exactly this entry for objectql.zod.ts#ObjectGridSchema. Type Check is success on the head, so the entry is both necessary and sufficient. CONCRETE is the ledger's own definition of this case ("an author can write the spelling, safeParse returns green, and tsc refuses it"). The entry's docblock states which face is wrong (the declaration, consistent with the spec row) and why it stays, which is what the ledger's rule 3 asks when the mirror is NOT narrowed; it leaves by reddening when the declaration goes optional; the header figures are AST-pinned at test time. A sound record. Residual, stated plainly: a TS-typed producer of a bound object-grid is still refused by tsc for the missing member; that is the recorded debt, not a hidden one.

out_of_scope_findings: [0] registration descriptions, resolved in round 2. [1] H4 worklist, see deviation 4. [2] object-chart's strict face still refuses dataSource by name: a pre-existing deferral recorded in that TS twin's dataSource docblock under objectui#11070, outside this card's population (objectName already optional there); not a widening or narrowing of this head. Noted; it keeps no carrier from this card.

Card pointers: 5905810503 (record-form filter, the second refusal reason): answered; the fence and the excerpt are converted, and the record form stays refused at dataSource.filter, the spec's own door. 5921004645 (serial on objectui#10859 batch 4): discharged; object-form is judged by the bag arm, its spec row's objectName is optional, nothing waived or narrowed there. 5921421452 (the census): judged fixture by fixture, handed back. Triage 5893778824: met on every point; the waiver applies exactly on a present non-empty dataSource.object as the spec gate reads it; one shared refinement, the kanban hand copy removed; a neither-key node refused; not a .passthrough() flip; not docs-only; the documented bindings pinned from disk on the tolerant face; kanban's "no record source" was the same gap and is fixed; the form's mode refusal belonged to the retired flat arm and is reported rather than touched.

Region: main carries PR #11306 (582edef1c, this PR's base), and the seat reports a clean merge-tree against today's main; CI ran against the base and the queue re-tests the merged result.

Implemented-by: claude/issue-11117-datasource-objectname-waiver
Reviewed-by: session_01VhxTqosz7wn54ahqyxgERT

VERDICT: PASS

The accept-set change is right on both faces for all six arms, every refusal the card insists on holds, the semver level matches what the diff publishes, and every flag is answered. Two docs-clause defects are named above (the guide's "names its object in neither place" clause, over-strict for the four ladder arms; the changeset's "every per-element binding the docs teach", overstated ahead of its own enumeration); neither moves an accept set, a version or a pin, and each is a one-clause correction.

…at was refused (objectui#11117)

Round 3, from the at-tier contract record on 2666d82.

- `content/docs/guide/data-source.md`: the validator refuses one of the six
  blocks only when it declares no other record source and names its object
  in neither place; a `staticData`-only map, gantt or calendar, or a
  `bind`- or `data`-only board, parses. The binding counts as "a" record
  source, not "the" one.
- `.changeset/11117-datasource-objectname-waiver.md`: the base refused the
  documented `object-grid`, `list-view` and `object-kanban` bindings, not
  every documented binding; the `object-form` fence already parsed on the
  bag arm and `element:number` already had its waiver.

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 01b9fb4788cffc486e7ee1343bd0827d4970576f
Local-runs: none

Rendered 2026-10-01T03:03Z by the at-tier reviewer run inside the domain:spec @ objectui seat's session, on the round-3 head. Inputs: card #11117 (body and all seven comments; none added since the ACCEPT 5923546311), PR #11312 (body with its "Round 3" section, the 25-file list, the net diff against main at 582edef1c, and git diff between the parent head 2666d824b1386013d621eeb4d50a38c94d985736 and this head: ONE commit, 2 files, +8/−7), the same governing texts the parent record used, files at the refs read with git show (no checkout), and this head's check-runs, read last. Nothing was built, run or re-run. The PR body's "Round 3" gate readings (the 11117 pin 47/47, markdown-test-inputs 25/25, a 19-file union 819/819, check:doc-snippets 698/698) were read as claims.

Carried forward by reference. The at-tier record 5923725485 on the parent head 2666d824b (PASS) judged the whole diff. The round-3 delta touches no zod, TypeScript, plugin, script or test file, so every judgment there stands unchanged by the delta and is adopted here: ① the mechanism and its three authorities, the identical widening on both faces for all six arms, the four refusals held, nothing narrowed, the issue-shape change, every public-surface item (ListViewSchema TS optional, dataSourceSuppliesObject internal, ObjectViewSchema.table unchanged, the registration descriptions true), schema-reference.md, data-objectstack.mdx, the registration changeset, the six appended notes; ② the level; ③ every flag and the ledger judgment. Only the two faces the delta edits are re-judged below, each paragraph read whole.

Gates on this head (read 2026-10-01T03:02Z, about ten minutes after the push, which the PR’s updated_at places at 2026-10-01T02:52Z): 0 check-runs and 0 Actions workflow runs exist on this head. Polled twelve times over six minutes; none appeared. The only commit status is Vercel’s "Canceled by Ignored Build Step", which is not a repo gate. Recorded as not yet reported, not as verdicts. This record adopts no gate conclusion for this head: the derived gate families are answered only by the runs that eventually complete on it, and the enqueue waits on them as on any head. For scale: on the parent 2666d824b all 43 check-runs completed green (40 success, 3 skipped), and the delta edits two prose files that reach no typed or compiled face, so the only families it can move are the doc and changeset gates (markdown-test-inputs, doc fences, changeset claims, line citations, control bytes), which the PR body claims green locally. ⚠️ For the seat: a push that created no workflow run leaves every required context absent on this head; confirm the trigger (or re-push) before enqueue.

① Derived judgments

The delta, in full (01b9fb478, "docs(types): the bound-node sentence and the changeset say exactly what was refused"; its parent is 2666d824b; the PR's commit count is 7):

  1. content/docs/guide/data-source.md, the bound-node paragraph, read whole: "Because the binding lands on objectName, a node bound this way needs no objectName of its own. The schema validator (safeValidateSchema, which objectui validate runs) counts the binding as a record source of list-view, object-grid, object-kanban, object-calendar, object-gantt and object-map, and still refuses one of those nodes that declares no other record source and names its object in neither place. A binding with an empty object names nothing." Judged against the six arms on the head's requireRecordSource:

    • object-grid and list-view (one rung, objectName): the only "other record source" is objectName itself, so the clause collapses to "neither key", which is refused at objectName. RIGHT.
    • object-map, object-gantt, object-calendar (data / staticData / objectName) and object-kanban (bind / data / objectName): a node with none of its rungs and no binding is refused at the root; a staticData-only map, gantt or calendar, or a bind-only or data-only board, declares another record source, and the sentence no longer says it is refused. The clause defect named in 5923725485 is closed. RIGHT.
    • "a record source" rather than "the": right, the binding is one rung beside the arm's own.
    • "A binding with an empty object names nothing": right. dataSourceSuppliesObject is the non-empty-string predicate, as the lint gate's strName and the runtime's isElementDataSourceConfig are; with nothing else declared, that node is refused.
    • The sentence states a sufficient condition for refusal, not the whole refusal set (a list-view carrying data and no objectName is still refused, since data is not a rung on that arm); it asserts nothing false about such a node. As a docs face (AGENTS.md Add automated testing infrastructure and CI/CD workflows #2): TRUE.
    • The paragraph is prose, not a json fence; the 11117 pin's documentedBindings() reads fences only, so the pin's input is unchanged and the markdown-test-inputs adjudication of this page stands.
  2. .changeset/11117-datasource-objectname-waiver.md, the first body paragraph, read whole: "safeValidateSchema (and objectui validate, which runs it) refused the documented object-grid, list-view and object-kanban bindings — { "type": "object-grid", "dataSource": { "object": "product", "limit": 20 } }, the same on list-view, and object-kanban's documented board — although each of those blocks renders it: their registrations are wrapped in ElementDataSourceGate, which lands the binding's object on objectName before the renderer reads the node, and @objectstack/spec accepts the node as written. object-grid and list-view required objectName, and the record-source rule of object-kanban, object-calendar, object-gantt and object-map had no rung for the binding." Judged:

    • The refused documented set on this PR's base was exactly grid, list-view and kanban: the page's object-form fence already parsed on the bag arm, and the guide's element:number fence already carried its waiver. The "every" overstatement named in 5923725485 is closed. RIGHT.
    • "renders it": the three registrations are elementDataSourceBlock-wrapped and the gate's default landing is objectName. Right. "@objectstack/spec accepts the node as written": PageComponentSchema.dataSource by reference, objectName optional on the grid and kanban rows, no list-view row. Right.
    • The closing sentence names the cause per arm correctly on the base (z.string() required on grid and list-view; the four ladders' refinement had no binding rung). RIGHT.
    • The front matter ('@object-ui/types': minor) and every other sentence of the file are byte-identical to the parent head; they publish verbatim and were read true in 5923725485.

No accept-set or public-surface change is implied by the delta: no schema, type, .describe() string, registration, script or test moved. The six arms' accept sets on both faces are exactly those judged at 2666d824b.

② Semver level

Unchanged by the delta: Clause-②: yes is still the PR body's second line; @object-ui/types: minor (right, and the maximum under AGENTS.md's 版本号策略), the three plugin patch entries, no skip-changeset. The delta edits one changeset's prose only; its front matter is untouched. Level: right.

③ Boundary flags

  • The two docs-clause defects 5923725485 named are the only round-3 items, and both are closed by the delta as read above. No new dev flag, deviation or open_questions entry was raised for round 3 (no new os-dev-report, no new card comment; the PR body's "Round 3" section claims only the two corrections and its gate readings).
  • All five deviations, open_questions[0] (seat decision B; the WiderThanDeclared entry judged a sound record), the three out_of_scope_findings and the card pointers stand exactly as judged in 5923725485; nothing in the delta touches them.
  • Region: the delta touches two files already in the PR's set; the clean merge-tree the seat reported at 2666d824b was not re-measured here, and the queue re-tests the merged result.

Implemented-by: claude/issue-11117-datasource-objectname-waiver
Reviewed-by: session_01VhxTqosz7wn54ahqyxgERT

VERDICT: PASS

The round-3 delta is two prose corrections, each now true against all six arms; it moves no accept set, no public surface and no version, and the parent head's judgments carry forward unchanged.

…r (objectui#11117)

Brings in objectui#10859 batch 5 (an authored object-map is judged by its
`properties`-bag arm, `ObjectMapBlockSchema`) and the other merges since
`1ccb5ba7d`.

Conflicts, resolved by measurement:

- `packages/types/src/zod/objectql.zod.ts`: batch 5 gave `requireRecordSource`
  an `at: 'node' | 'properties'` mode for the bag arm; this PR gave it a
  per-arm rung list, the `dataSourceSuppliesObject` binding rung and
  `when: () => true`. The merged helper is ONE function taking both:
  `requireRecordSource(type, rungs, at)`. The bag arm now counts a NON-EMPTY
  `dataSource.object` (batch 5 counted presence), so
  `{ type: 'object-map', dataSource: { object: '' } }` with no bag rung is
  refused, as on every other arm and as at runtime. In bag mode the message
  names the node's `dataSource` binding.
- `packages/plugin-map/src/index.tsx`: batch 5's text is taken. Its
  `objectName` input description already names the node's `dataSource`
  binding, so this PR no longer edits that file.

Follow-ups in this merge:

- `object-map-properties-bag-10859-b5.test.ts`: the flat node now also
  carries the root `RECORD_SOURCE_REQUIRED` issue beside its flat-key
  refusals (`when: () => true`).
- The 11117 pin judges `object-map` on `ObjectMapBlockSchema`, with
  `objectName` written in the bag, and pins the flat mirror separately.
- Both 11117 changesets are corrected: plugin-map leaves the round-2 one,
  and the round-1 one names the object-map bag arm and its one narrowing.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.7 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-BWSj1IOX.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) 571.60KB 136.88KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.95KB 11.48KB
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.98KB 15.39KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.45KB 44.60KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

…s binding strings (objectui#11117)

The merge resolution (80ec249) spelled the two `binding` strings as
single-quoted literals with `\`` escapes, which `no-useless-escape` refuses
(6 errors, the `Lint` check). The escapes go; the string values are
byte-identical, so every message is unchanged.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.7 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-BWSj1IOX.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) 571.60KB 136.88KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.95KB 11.48KB
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.98KB 15.39KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.45KB 44.60KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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: 2a237f0ecc3b01ca63374ae3299a8b4b76b61dd3
Local-runs: none

Rendered 2026-10-01T03:47Z by the at-tier reviewer run inside the domain:spec @ objectui seat's session, on the round-5 head, which is the round-4 merge plus one two-line lint repair. No record was posted on the merge head 80ec249bc itself (its Lint was red). Inputs: card #11117 (body and all eight comments, the ACCEPT 5924129161 included), the cross-seat pointer 5924134305 on objectui#10859, PR #11312 (body with its "Round 4" and "Round 5" sections and its Clause line, the 25-file list, the net diff against the merge's main parent cd5b19a7e), the three-way separation of the merge (git diff from 01b9fb478 to cd5b19a7e for main's side, from 01b9fb478 to 80ec249bc for incoming plus resolution, and a per-file context-free comparison of this PR's old net patch against its new one), the same governing texts as the earlier records (objectui AGENTS.md, the installed spec 17.5.0 at objectstack 0f6dcac5e9, the lint gate's validate-component-props.ts), files at every ref read with git show / git grep (no checkout), the Clause line read with the fleet's one reader (readClause2Line in clause2-line.mjs), and this head's check-runs, read last. The check-runs on 80ec249bc were also read, to explain its reds. Nothing was built, run or re-run; the two string literals the lint repair touches were compared by evaluating them off the two files' text. The PR body, the ACCEPT and the pointer were read as claims and re-derived from source.

Carried forward by reference, by measurement. For every file of this PR, its old net patch (582edef1c to 01b9fb478) was compared context-free (+/- lines only) against its new net patch (cd5b19a7e to this head). IDENTICAL, so every judgment in 5923725485 and 5923885275 on them stands unchanged by the merge: the appended notes on .changeset/10392, 6939, 7313, 7470, 7780 and 8990; content/docs/guide/data-source.md; content/docs/utilities/data-objectstack.mdx; plugin-calendar/src/index.tsx; plugin-gantt/README.md; plugin-gantt/src/index.tsx; plugin-kanban/README.md; handler-keys-json-refusal-6124.test.ts; object-calendar-record-source-7313.test.ts; object-kanban-record-source-7780.test.ts; zod-mirror-parity.test.ts (the WiderThanDeclared entry, judged a sound record); scripts/markdown-test-inputs.mjs; and public-blocks.zod.ts (identical lines, context shifted). For objectql.ts and schema-reference.md the new patch's lines are a strict subset of the old two-dot patch's, the difference being PR #11306's showFilters text that a two-dot diff against a base the branch had not yet contained mis-attributed, so this PR's own hunks there are unchanged too. What the merge moved is re-judged below: objectql.zod.ts, the 11117 pin, batch 5's pin, both 11117 changesets, and plugin-map/src/index.tsx (which left the diff).

Gates on this head (read 2026-10-01T03:46Z, after a bounded wait for the twelve runs still in progress at the first read): 43 check-runs, 40 success, 3 skipped (dependabot and the two coverage rows), 0 in progress, 0 failed. Lint is green, so the round-5 repair held; Type Check is green, so the WiderThanDeclared entry is still exactly what the parity instrument requires on the merged face; Test (shard 1/8) through Test (shard 8/8) and Test (dist pins) are green, the 11117 pin, batch 5’s edited pin and its message assertions included; Doc Snippet Type Check, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Spec Main Shape Gate, Build Docs and Governed Surface Queue Guard are green. These conclusions are the gate verdicts; nothing was run locally.

① Derived judgments

Round 5, in full. One commit, 2a237f0ec, single parent 80ec249bc (no main merge). One file, two lines: the two binding literals in requireRecordSource lose their \`` escapes inside single quotes. Evaluated off both files, the values are byte-identical: "name the object in the node's dataSource binding (dataSource.object)" and "name the object in dataSource.object". So every message is unchanged and no accept set moves. On 80ec249, Lintwas red with exactly sixno-useless-escapeannotations on those two lines (read off the check-run), andTestwas red because its eight shards were CANCELLED when the round-5 push superseded the run (the aggregator reads each shard, so cancelled is not a pass); neither is a defect of the merged contract.git merge-treeof this head againstorigin/main 0c6f9bbis clean; the PR readsmergeable: true, state behind`. Right.

Round 4, the merge (80ec249bc, parents 01b9fb478 and cd5b19a7e), judged in full.

  1. One helper. requireRecordSource(type, rungs, at = 'node') merges batch 5's at: 'properties' mode (the rungs are read in the properties bag) with this PR's per-arm rung list, the dataSourceSuppliesObject(node) binding rung (read on the NODE in both modes, which is where the binding lives), and the always-true when option. The issue path is the one rung (['objectName'], or ['properties', 'objectName'] in bag mode) on a one-rung arm and the root on a ladder; in bag mode the message spells the rungs properties.KEY and names "the node's dataSource binding (dataSource.object)". Main's helper (batch 5) took (type, at), tested the bag-mode binding by schema.dataSource !== undefined, had no when, and left kanban its hand copy. Call sites on the merged face: object-grid and list-view with ['objectName']; the flat ObjectMapSchema, ObjectGanttSchema and ObjectCalendarSchema with data / staticData / objectName; ObjectKanbanSchema with bind / data / objectName; ObjectMapBlockSchema with the three rungs and 'properties'. One function, no hand copy. Right.

    The six-arm population on the merged face. AnyComponentSchema's object-map arm is ObjectMapBlockSchema, reached through ObjectQLPublicBlockComponentSchema; batch 5 took the flat ObjectMapSchema out of ObjectQLComponentSchema, so it is no longer an arm of either face (it stays exported as the node ObjectMap reads after the hoist, and keeps the node-mode rule). The six arms are therefore object-grid, list-view, object-kanban, object-calendar, object-gantt and object-map (bag). On each, the bound node { type, dataSource: { object: "account" } } with no other rung parses on the member, on the tolerant face and on the strict face (the strict clone keeps def.checks, and ObjectMapBlockSchema is a BaseSchema heir closed by .superRefine() like the others); a node with neither is refused custom / RECORD_SOURCE_REQUIRED at objectName on grid and list-view and at the root on the other four, the bag arm included (its three rungs sit in properties, so no single key is at fault); an empty object is refused on all six; a non-record dataSource is refused at the key and on the record source; a wrong-typed objectName beside a binding (properties.objectName on the bag arm) is the member's invalid_type; objectName alone, empty included, parses (in the bag on the map). The flat ObjectMapSchema is pinned separately: bound parses, neither refused at the root, empty refused. All of this is pinned at this head in element-data-source-objectname-waiver-11117.test.ts, whose object-map row moved to ObjectMapBlockSchema with withObjectName / OBJECT_NAME_PATH writing the bag spelling, plus the new flat-mirror case; the context-free comparison shows those as the pin's only changes. Right. One nuance, disclosed by the changeset: on the bag arm the bound node already parsed on the merge's main parent (batch 5 counted a binding by presence), so relative to that parent this PR widens five arms and the flat mirror, and relative to the last release all six.

  2. The new narrowing, on batch 5's bag arm. On the main parent the bag mode returned early on schema.dataSource !== undefined, so { "type": "object-map", "dataSource": { "object": "" } } with no properties.data, properties.staticData or properties.objectName parsed: the spec's ElementDataSourceSchema.object is z.string() and admits the empty string at the member, and the presence test asked nothing more. On the merged face dataSourceSuppliesObject reads the name and refuses it, so that node is refused RECORD_SOURCE_REQUIRED at the root. Judged against the three authorities: triage 5893778824 waives objectName "EXACTLY when dataSource.object is a present string, as the spec's props gate does", and the spec gate's strName returns undefined for an empty string (a string of positive length only); the runtime's isElementDataSourceConfig returns false for an empty name, so ElementDataSourceGate lands nothing and the map, with no bag rung, has no record source and draws no records. The narrowing brings the bag arm to triage's reading, to the other five arms, and to the runtime; it is also what makes this PR's own published sentences true on that arm (the guide's "A binding with an empty object names nothing", the changeset's "dataSource: { object: '' } supplies nothing"). It is the ONLY accept-set narrowing: every other document the bag arm now additionally refuses at the root (dataSource: "x", dataSource: { object: 7 }, the flat node) was already refused at a member, and only its issue set grows (the always-true when). RIGHT, and correctly named as a narrowing rather than folded into the widening.

  3. The Clause line and the level. The PR body's second line is Clause-②: yes (narrowing): a widening on six arms (a non-empty dataSource.object counts as a record source), plus one narrowing on the object-map bag arm (an empty dataSource.object no longer counts). readClause2Line returns kind: declared, value: yes, arm: narrowing: a legal spelling, and the prose names both directions. The level is judged in ② below.

  4. Batch 5's pin, object-map-properties-bag-10859-b5.test.ts. The flat node's issue keys go from ['map', 'objectName'] to ['', 'map', 'objectName'], with the root issue's params.code asserted RECORD_SOURCE_REQUIRED. Cause: the always-true when runs the refinement beside the flat-key refusals, and the flat node's bag holds no source and it has no binding. The edit records exactly that, keeps the flat keys the only KEYED issues, and leaves batch 5's message assertions untouched (the bag-mode message's tail changed from "or bind the node's dataSource" to the binding sentence above; the pointer states batch 5's assertions pass as written, which the test shards decide). A file-surface amendment accepted by the seat, with the cross-seat pointer 5924134305 on objectui#10859 informing the owning lane. Right.

  5. plugin-map/src/index.tsx has left the diff. Main's text (batch 5's) is taken: its objectName input reads "The record source is one of data, staticData and objectName, or the node's dataSource binding; the object-map schema refuses a block that declares none of them". True on the merged face in the gate's reading of a binding, which is one that names an object; not this PR's text. The registration changeset now lists @object-ui/plugin-gantt and @object-ui/plugin-calendar only (verified), with plugin-map gone from its front matter and title; its body is true in every sentence, including the added parenthetical that object-map's input already names the binding since batch 5, view:calendar sharing the calendar's list (registered as calendar with namespace: 'view'), "No input's name, type, required flag or shape changed", and the two READMEs carrying the correction (both still in the diff, patches identical). With no plugin-map source edit left, no plugin-map changeset entry is owed. Right.

  6. Both 11117 changesets, each sentence. 11117-datasource-objectname-waiver.md (@object-ui/types: minor): the before-state sentence now names "object-kanban, object-calendar and object-gantt, and ... the flat object-map mirror" as the arms whose rule had no binding rung, with the parenthetical that the authored object-map has been judged by its bag arm since batch 5, "which already counted a binding", true on the main parent; "Nothing that validated before is refused, except the one object-map shape named last" true; the four refusal bullets true on all six arms; the narrowing bullet true clause by clause (presence-counting on the parent, the exact document, "rendered no records" per the runtime predicate, the same predicate now, a named binding still parses); "One refinement ... reading the rungs on the node or, for the authored object-map, in its properties bag" true; the TS-twin and docs sentences unchanged and true. 11117-registration-objectname-description.md: true throughout, as read in 5. Batch 5's own 10859-object-map-properties-bag.md is left untouched, and rightly: no sentence of it becomes false ("or the node's dataSource binding" and "A node bound only through dataSource ... now parses" both read on a binding that names an object), and the narrowing is stated in this PR's entry in the same release.

No other public surface moved in the merge: index.zod.ts, README.md under zod/, and every other file the merge brought in are main's own changes, outside this PR's net diff.

② Semver level

Clause-②: yes (narrowing), read by the fleet's one reader as value yes, arm narrowing, matching the diff: a widening on six arms plus one narrowing on the object-map bag arm. @object-ui/types: minor still holds and is still the maximum: AGENTS.md's 版本号策略 forbids major anywhere in this fixed group (check-changeset-no-major, the Changeset Bump Policy check) and says objectui's own breaking changes are marked minor with the breaking semantics stated in the body (「在正文里写清 breaking 语义即可」). The body states it: a bold "One narrowing, on the object-map bag arm" bullet naming the exact document, why it parsed, why it rendered nothing, and what refuses it now. Batch 5's entry used a "BREAKING (authoring)" banner for its own narrowing; this entry's bold bullet meets the rule without one, and no script or workflow requires a banner (grep over scripts/ and .github/workflows/ for "narrowing" finds no such gate). The two plugin patch entries (gantt, calendar) match description-only text. No skip-changeset. Level: right.

③ Boundary flags

  • Every flag judged in 5923725485 stands (five deviations, open_questions[0] with seat decision B and the sound ledger entry, the three out_of_scope_findings, the card pointers, triage 5893778824 met), and the two docs-clause defects closed in round 3 stay closed; the files they live in are unchanged by the merge (measured above).
  • Round 4 raised three items, each answered: (a) the file-surface amendment for batch 5's pin, accepted with the cross-seat pointer 5924134305 on objectui#10859 (the domain:ui lane's card); (b) the narrowing, decided by the seat and consistent with triage, the spec gate and the runtime, stated in the Clause line and the changeset, and leaving batch 5's changeset true; (c) plugin-map/src/index.tsx dropped from the diff with the registration changeset corrected.
  • Round 5 raised none: a CI repair with byte-identical values.
  • The reds on 80ec249bc are accounted for: Lint by the repair, Test by cancellation on supersession.
  • Region: merge-tree of this head against origin/main 0c6f9bbd7 is clean; CI ran on the merged result's own head, and the queue re-tests the merged result.

Implemented-by: claude/issue-11117-datasource-objectname-waiver
Reviewed-by: session_01VhxTqosz7wn54ahqyxgERT

VERDICT: PASS

On the merged face the accept-set change is right on both faces for all six arms, including the bag arm object-map is now judged by; the one narrowing is the triage's own reading and the runtime's, correctly declared; every refusal the card insists on holds; the level is right; every flag is answered; and the round-5 delta changes nothing but two escape characters.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 03:48
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 0e6e76b Oct 1, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11117-datasource-objectname-waiver branch October 1, 2026 04:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants