Repository navigation
fix(types): a bind-only list is accepted; ListSchema.items is optional with an at-least-one-of bind / items refinement (objectui#11405) - #11407
Conversation
…ional with an at-least-one-of `bind` / `items` rule The `list` renderer reads `useDataScope(schema.bind)` first and falls back to `schema.items`, so a bind-only list draws its entries from the host scope. Both published faces required `items` and refused that shape with `invalid_type` at `items`. `items` is now optional on the TypeScript and zod faces, and the zod arm ends in `listHasAnEntrySource`, which refuses a node with neither key once, at the node, keyed `LIST_ENTRIES_REQUIRED`. The strict face carries the check. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
… `bind` stays declared once `base-bind-declared.test.ts` scans for a second `bind?:` declaration outside `BaseSchema` (objectui#6357); the refinement's destructuring annotation matched it. The refinement now reads the raw node as a plain record. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
…e two skill guides `list-bind-only-11405.test.ts` reads its fences off `skills/objectui/guides/data-integration.md` and `schema-expressions.md`, so the markdown-test-inputs ledger records it: an edit to either guide is an edit to that test's input, and CI's markdown fast path must not skip it. 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
|
Contract reviewServed-tier: Isolated at-tier reviewer, written 2026-10-01T23:57Z. Inputs: card #11405 (body and comments ① Derived judgmentsJudged against triage's ruling
② Semver level
③ Boundary flags
Check-runs on the head (their conclusions are the gate verdicts; read last, polled until none was pending): 43 runs, 40 success, 3 skipped by design ( Implemented-by: VERDICT: PASS |
Fixes #11405
Clause-②: yes — the
listarm of@object-ui/typesonly widens. A bind-onlylistgoes from refused to accepted; alistwith neither key was refused before (atitems) and is refused now (at the node). Nothing went from accepted to refused.Dispatched run for the
domain:specseat (objectui#10217), sessionhttps://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT, claim comment 5942476028, triage ruling 5941529511. Draft, left for the seat; the at-tier contract-review record is owed on the landing head before enqueue.What changes
packages/types/src/zod/data-display.zod.ts):ListSchema.itemsis.optional(), and the arm ends in.superRefine(listHasAnEntrySource, …)with awhenthat always answers true, so the rule is reported beside any other issue on the node. A node with neitherbindnoritemsis refused once, at the node:code: custom,params.code: LIST_ENTRIES_REQUIRED, a message naming both keys. The rule is at least one, not exactly one, becauseitemsis the renderer's fallback besidebind. Presence is!== undefined, as inrequireRecordSource. The strict face carries the check, because its clone keepsdef.checks.packages/types/src/data-display.ts):items?: ListItem[]. The docblock names the read site and the mirror refinement, in the shapeObjectKanbanSchema.objectNameuses (objectui#7780).bindstays declared once, onBaseSchema.packages/types/src/__tests__/list-bind-only-11405.test.ts.content/docs/components/data-display/list.mdxdocumentsbind/ optionalitemsand the at-least-one rule..changeset/11405-list-bind-only.md,@object-ui/typesminor.scripts/markdown-test-inputs.mjsgains one ADJUDICATED row for the new pin. The pin reads the two guides, and the ledger audit (scripts/__tests__/markdown-test-inputs.test.ts) fails on an unregistered reader. This row is outside the claim's declared file surface; see Deviations.⛔ Not edited:
skills/**(the four fences become valid as they stand),list.tsx, and every other arm.Zone 2 measurements
7728c67c8(rebuilt dist).{ type: list, bind: customerNames }is refused withinvalid_typeatitemsby the arm,safeValidateSchemaandStrictAnyComponentSchema. Alistwith neither key was refused the same way, atitems.superRefineon the arm keeps it a validDataDisplaySchema/AnyComponentSchemamember: the module loads,DataDisplaySchema._zod.propValues.typestill holdslist, and a wrong literal is stillinvalid_unionattype. The issue lands at the node:[]at the root,children.0when nested. Precedent:requireRecordSourcealready sits on discriminated arms. TS union spelling, measured with a probe: it refuses a literal with neither key, but the TS2322 names onlybindas missing. The reader's type forschema.itemsis identical to the optional member's (ListItem[]or undefined). An interface can no longer extend it (TS2312), and a type alias drops declaration merging. So it was not taken: no reader gains, and three precedent arms (object-kanban,object-map,object-tree) keep the optional member plus a mirror refinement.ListSchema['items']list.tsx'sArray.isArray(schema.items) ? schema.items : [], guarded.@object-ui/componentstype-check is green against the rebuilt types. Nothing to retype.list.tsx'sitemsinput carries norequired. The console parity guard has nolistrow, andcheck:component-surface-paritycompares key sets only. The registration is left alone.scanSkillFences(the marker gate's own reader) and judged throughsafeValidateSchemaand the strict face. Four marked fences author a bind-onlylist. The three taught as valid pass. The fence the guide marks as wrong is refused only atchildren, never atitemsand never on the new rule.zod-mirror-parityrow opens; that file is untouched. Ablation M3 below shows the TS face moving alone reddens the parity ratchet.Verification (landing head
8b317ec61)pnpm --filter @object-ui/types build: exit 0.pnpm --filter @object-ui/types type-check(three legs, includingtsc -p tsconfig.test.json, whose--listFilesOnlyincludes the new pin): exit 0.pnpm exec vitest run packages/types/:Test Files 321 passed (321),Tests 8560 passed (8560).pnpm --filter '@object-ui/components^...' build, thenpnpm --filter @object-ui/components type-check: exit 0.list-title-declared-11347,list-item-content-channel-9590,shadowed-renderer-behaviour,bind-base-prop-parser-tier-11008,skill-guide-data-table-binding),markdown-test-inputs,check-skill-examples, and the changeset-script suites that name the ledger. Result:Test Files 15 passed (15),Tests 494 passed (494).check:control-bytes,check:new-line-citations,check:test-path-roots,check:vi-mock-specifiers,check:phantom-deps,check:self-import,check:unreferenced-sources,check:spec-symbols,check:changeset-claims,check:pending-changeset-literals,check-changeset-no-major,check-changeset-presence,check:doc-types,check:doc-fences,check:doc-example-ids,check-doc-links,check-doc-expression-carriage,check:doc-example-readers,check:component-surface-parity --type list(report-only),check:prompt-keys,check:pre-install-import-graph,check-lint-coverage,check-type-check-coverage,markdown-test-inputs --audit, andcheck-governed-queue-guard --testover the six changed paths (NOT GOVERNED).check:doc-snippetsandcheck:doc-examplesneed the whole-workspace build (check:doc-examplesexited 2, could-not-run). In their place, thelist.mdxts fence was compiled--strictagainst the rebuilt@object-ui/typesdist (exit 0), with a failing control in the same harness (TS2322, exit 2). CI builds their closure.data-display.zod.ts,data-display.ts, the pin, andmarkdown-test-inputs.mjs. The.mdxand the changeset are reported as "no matching configuration". The count of 4 comes from--format json. Result: 0 errors.data-display.tshas 30no-explicit-anywarnings, the same 30 as its base blob.eslint.config.jsenables no type-aware linting (noparserOptions.project/projectService), so this diff cannot move any untouched file's verdict.Ablation (fix committed first, each leg through
ablation-replace, restored from HEAD and proven blob-equal)itemsrequired again: 11 failed, 1 passed of 12. Only the counter-probe stays green, which is correct.itemsrequired again:tsc -p tsconfig.test.jsonexit 2. TS2741 at the pin's bind-only literal, plus thezod-mirror-parityratchet (TS2322 ondata-display.zod.ts#ListSchemaanditems).Acceptance notes
object-kanban.content/docs/guide/expressions.md("Scoped Data", "Index in Loops") teaches alistwithitemsas a string, anitemTemplateand per-itemitem/indexexpressions. The publishedskills/objectuiexpressions guide refutes that shape.safeValidateSchemarefuses it atitems, and the strict face addsunrecognized_keysforitemTemplate.check-doc-expression-carriagealready lists both sites, and its header points to that family's open cards. Noted, not filed. Untouched here.tree-viewis the otheruseDataScopereader in@object-ui/components. Measured: it already accepts a bind-only node, so the same defect is not present there.Deviations
scripts/markdown-test-inputs.mjs: one ledger row, outside the claim's file surface. It is required because the claimed pin reads the guides off disk, andmarkdown-test-inputs.test.tsfails on an unregistered reader. The script's own suite and its importers ran green (above).Generated by Claude Code