Repository navigation
fix(types): a gate-wrapped arm waives objectName when dataSource.object binds the node (objectui#11117) - #11312
Conversation
…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>
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…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>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Rendered 2026-10-01T02:47Z by the at-tier reviewer run inside the Gates on this head (read 2026-10-01T02:44Z): 43 check-runs, 40 ① Derived judgmentsThe mechanism, read from the head. One module-private Both faces, all six arms. The strict face is derived by
Refusals the card insists on, still held (pinned on the member and the tolerant face in
Issue-shape change, disclosed: on grid and list-view the neither-key refusal is now a Public-surface changes, each named:
Docs faces (AGENTS.md #2):
Changesets, which publish verbatim; each sentence judged.
② Semver level
③ Boundary flagsDev deviations (
Card pointers: Region: Implemented-by: 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>
Contract reviewServed-tier: Rendered 2026-10-01T03:03Z by the at-tier reviewer run inside the Carried forward by reference. The at-tier record Gates on this head (read 2026-10-01T03:02Z, about ten minutes after the push, which the PR’s ① Derived judgmentsThe delta, in full (
No accept-set or public-surface change is implied by the delta: no schema, type, ② Semver levelUnchanged by the delta: ③ Boundary flags
Implemented-by: 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>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…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>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Rendered 2026-10-01T03:47Z by the at-tier reviewer run inside the Carried forward by reference, by measurement. For every file of this PR, its old net patch ( 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 ① Derived judgmentsRound 5, in full. One commit, Round 4, the merge (
No other public surface moved in the merge: ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS On the merged face the accept-set change is right on both faces for all six arms, including the bag arm |
Fixes #11117
Clause-②: yes (narrowing): a widening on six arms (a non-empty
dataSource.objectcounts as a record source), plus one narrowing on theobject-mapbag arm (an emptydataSource.objectno longer counts)What this does. The tolerant face (
safeValidateSchema, whichobjectui validateruns) now accepts a node bound throughdataSource.objectwithout anobjectNameon every gate-wrapped arm that has a record-source requirement:object-grid,list-view,object-kanban,object-calendar,object-ganttandobject-map. The binding counts only whendataSource.objectis a non-empty string, as the spec gate'sstrNameand the runtime'sisElementDataSourceConfigread it. A node with neither is still refused, anddataSource: { object: '' }supplies nothing. Dispatched by thedomain:spec @ objectuiseat, sessionsession_01VhxTqosz7wn54ahqyxgERT.The change
requireRecordSource(type, rungs)inpackages/types/src/zod/objectql.zod.tsnow takes each arm's own rung list and counts the binding as the last rung, usingdataSourceSuppliesObject. That predicate is the oneelement:number's spec waiver already used; it is now exported frompublic-blocks.zod.tsas an internal helper, so there is one copy, and it is not re-exported fromindex.zod.ts.when: () => true, so the refusal is reported beside any other issue on the node. Onobject-gridandlist-viewthe required member used to behave that way, and the refinement keeps it.object-kanban's hand-copiedrequireKanbanRecordSourceis gone. The kanban arm now calls the shared helper withbind/data/objectName.object-gridandlist-view,objectNameis optional as a member and the refinement requires it unless a binding is present. The refusal is reported atobjectName, the path the required member used, soobjectui validatestill printsPath: objectName(validate-union-arm-selection.test.tsstays green). The issue is nowcustomwithparams.code = 'RECORD_SOURCE_REQUIRED'. The four ladder arms still report at the root.objectNamedescribe names the waiver.ObjectViewSchema.tablerebuilds the grid shape ontoBaseSchemabefore 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.content/docs/utilities/data-objectstack.mdx.filteras the spec's rule array,[{ "field": "total", "operator": "greater_than", "value": 100 }].ElementDataSourceexcerpt typesfilterasViewFilterRule[], and its fragment marker gives the new reason.content/docs/guide/data-source.md) andcontent/docs/api/schema-reference.mdsay a bound node needs noobjectName..changeset/11117-datasource-objectname-waiver.mdbumps@object-ui/typesminor (a widening).Premises, measured on
origin/main1ccb5ba7dagainst the built@object-ui/typesdistThe probe ran every documented binding through
AnyComponentSchema(tolerant) andStrictAnyComponentSchema.object-formontoObjectFormBlockSchema.{ "type": "object-form", "dataSource": { "object": "user" } }already parses on both faces, and themoderefusal belonged to the flat arm, which no longer judges authored documents.object-formrow declaresobjectNameoptional, so the bag arm has nothing to waive. A form node with neither key also parses, as the spec row allows; the renderer's inlinecustomFieldspath is one reason a node can lack both. ⛔ This change does not narrow that.object-metric,object-timelineandobject-master-detail-form: their rows require no object key.object-kanban's "has no record source" was the same gap:dataSource.objectwas missing from its ladder. Calendar, gantt and map refused their bound nodes the same way. All three registrations are gate-wrapped, and their mapping landsbinding.objectonobjectName.objectNameinvalid_type, anddataSource.filterinvalid_typefor the record form. Both are gone.dataSource.filteronly, which is the spec's own refusal.The TypeScript twin of
ObjectGridSchemakeepsobjectName: stringtranslateOptions(schema.objectName, …)read sites in@object-ui/plugin-grid'sObjectGrid.tsx. I measured this with a source-mappedtscprogram 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.string. The mirror's widerobjectNameis a CONCRETE entry inzod-mirror-parity.test.ts'sWiderThanDeclaredledger, with itsWIDER_ARMSrow and the header figures. The ledger reddens when the two faces agree again.ListViewSchemais derived from its mirror, so itsobjectNameis now optional. The same consumer program reported no error from that.Texts this change made false
Corrected here.
object-gridobjectNamerow and the kanban record-source note inschema-reference.md.groupBydocblock inobjectql.ts, which said the documented{ type, dataSource }fragment is still refused. Also the kanban TS docblock and theobjectql.zod.tsdocblocks that namedrequireKanbanRecordSource.**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).objectNameinput descriptions on theobject-map,object-ganttandobject-calendarregistrations, and onview:calendar, which shares the calendar list, now end with: "…refuses a block that declares none of them, unless the node'sdataSource.objectnames the object, which then lands on this key."requiredflag or shape moved.registry-inputs-spec-parity.test.tsran read-only and passed 230/230 untouched.plugin-ganttandplugin-kanbanREADMEs, and the comments next to the three registrations, carry the same correction..changeset/11117-registration-objectname-description.mdbumps the three plugins as a patch.CI repair (round 2).
scripts/markdown-test-inputs.mjsADJUDICATEDnow registerselement-data-source-objectname-waiver-11117.test.tsas readingcontent/docs/utilities/data-objectstack.mdxandcontent/docs/guide/data-source.md.markdown-test-inputs.test.tswas red at993a0c85c(1 failed / 24 passed) and is 25/25 at2666d824b.--auditreports 108 candidate test files, all adjudicated.Round 2 gates, at
2666d824b:check:doc-snippetsthen passed with 698 of 698 compiled blocks and 0 failed. This replaces the round-1 NOT MEASURED.check:readme-exportspassed.type-checkpassed for plugin-map, plugin-gantt and plugin-calendar.check:new-line-citations,check:control-bytes,check:changeset-claims,check-changeset-presenceandcheck-changeset-no-majorall exited 0.Round 3 (
01b9fb478), from contract record 5923725485. Two one-clause corrections:content/docs/guide/data-source.mdnow 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.@object-ui/typeschangeset now says the base refused the documentedobject-grid,list-viewandobject-kanbanbindings, not every documented binding.Gates at
01b9fb478:markdown-test-inputs.test.ts: 25/25, and--auditis clean;check:doc-snippets: 698/698 compiled, 0 failed;check:doc-fences,check:changeset-claims,check-changeset-presence,check:new-line-citationsandcheck:control-bytes: all exit 0.Round 4: merge of
main(80ec249bc). The merge brings in objectui#10859 batch 5, under which an authoredobject-mapis judged by itsproperties-bag arm,ObjectMapBlockSchema. That arm already counted adataSourcebinding, by its presence alone.objectql.zod.ts. Batch 5'sat: 'properties'mode and this PR's rung list, binding predicate andwhen: () => trueare now ONE helper,requireRecordSource(type, rungs, at).dataSource.object, like every other arm and like the runtime. So{ "type": "object-map", "dataSource": { "object": "" } }with no bag rung is refused withRECORD_SOURCE_REQUIRED. It used to parse, and it rendered no records.plugin-map/src/index.tsx. Main's text is taken. ItsobjectNameinput description already names the node'sdataSourcebinding, so this PR no longer edits that file.object-mapis now judged on its bag arm, which keeps a record-source rule read inproperties. The flatObjectMapSchemamirror is no longer an arm of the face, and it keeps the same rule on the node.object-maprow moves toObjectMapBlockSchema, 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 rootRECORD_SOURCE_REQUIREDissue beside its flat-key refusals.plugin-mapleaves the registration changeset.80ec249bc:@object-ui/types: build, type-check, and 306 files / 7,734 tests, all passing.registry-inputs-spec-parity.test.ts(230/230).markdown-test-inputs: 25/25, and--auditis clean.check:doc-snippets: 698/698.check:changeset-claims,check-changeset-presence,check-changeset-no-major,check:new-line-citationsandcheck:control-bytes: all exit 0.Round 5 (
2a237f0ec): a CI repair. The round-4 merge resolution escaped the backticks inrequireRecordSource's twobindingstrings. Those strings are single-quoted, so the escapes are useless, andno-useless-escapefailedLintwith 6 errors.pnpm --filter @object-ui/types run lintreports 0 errors. eslint--format jsonover all 12 TS/JS files in this diff also reports 0 errors.@object-ui/typestype-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$regexrecord, and its well-formed record control.ElementDataSourceGate.malformedFilter-10789.test.tsx: the$gtrecord 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.elementDataSourceObjectPivot.elementDataSourceRecordRelatedListRenderer.elementDataSourcerecord-related-list.propsRefusal-9963: the_fullBindingtype controlEmbeddableForm.elementDataSourceLineItemsPanel.elementDataSourceMasterDetailForm.elementDataSourceObjectGantt.elementDataSourceObjectGrid.elementDataSource(2)ObjectKanban.elementDataSource(2)ListView.elementDataSourceListView.sharedGateObjectMap.elementDataSourceObjectTimeline.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 buildpasses, withdist completeness: 1 package(s) complete.type-checkechoestsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.jsonand passes.pnpm exec vitest run packages/types/: 302 test files / 7649 tests passed.packages/typesthat readssafeValidateSchema, 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.check:spec-symbols,check:new-line-citations(0 new),check:control-bytescheck:doc-types,check:doc-fences,check:doc-example-idscheck:handler-key-reads,check:element-data-source-declaration,check:component-surface-parity,check:test-path-rootscheck:changeset-claims(report-only; record-source paragraphs read and corrected above),check:pending-changeset-literalscheck-changeset-presenceandcheck-changeset-no-major--no-inline-configover the 8 changed TypeScript files: 8 files in the JSON report, 0 errors. The 38 warnings are allno-explicit-anyon unchanged lines. The config has no type-aware rules, so this diff cannot move a verdict on an untouched file.ca5c505ba, each viaablation-replace.mjswith 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 withgit diff HEADempty.if (dataSourceSuppliesObject(node)) return;): 14 failed / 33 passed. The failures were the six arms on both faces, plus both docs pages.typeof object === 'string'): 6 failed, one EMPTY case per arm.when: () => true: 7 failed. Six were the "not a record" cases; the seventh was the grid case that reports the missing record source beside acolumnserror.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 refusesdataSourceby name. That is the deferral the TS twin'sdataSourcedocblock records, outside this card's population.objectNameis already optional there.ObjectViewSchemaandObjectTreeSchema. They refuse a bound node withoutobjectName, and that is correct: neither registration is gate-wrapped.ObjectGridSchemakeepsobjectName: string(seat decision B, round 2). Open PR objectui#11306, since merged as582edef1c, edited the same TS region and plugin-grid, and no TS-typed producer of a bound grid was found. The mirror's widerobjectNameis recorded as a CONCRETEWiderThanDeclaredentry inzod-mirror-parity.test.ts, which goes red when the declaration becomes optional.Generated by Claude Code