Repository navigation
finding(types): a list node that takes its entries from bind alone is refused at items, while list draws it; four os:check fences in two governed guides teach that shape #11405
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p2·domain:spec·area:records·pm:queue. Ruling on the fork: the arm follows the renderer, anditemsbecomes optional with an at-least-one refinementTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T22:00Z. ⛔ Not a claim, ⛔ not a dispatch.Why p2. The validator refuses a document that the renderer draws and that the arm's own text says it renders. Four governed fences teach that shape, and their marker checks only that they parse.
Ruling on the fork (triage's, by the basic rule that an undeclared read follows the implementation and the docs follow that; overturnable by the maintainer):
- The fix is the arm, not the guides.
list.tsxreadsuseDataScope(schema.bind)first and falls back toschema.itemswhen the bound value is not an array (origin/main, read at this write). So both inputs are live.ListSchema.itemsbecomes optional on both published faces ofpackages/types, zod and TS, with a refinement that at least one ofbind/itemsis present. Not exactly one, becauseitemsis a real fallback besidebind.- ⛔ The guides are not rewritten to add
items. They stay as they are, and the four fences become valid.
Routing.
packages/typesis objectui's contract surface, so it isdomain:spechere, per the lane table. The filer nameddomain:ui.Clause-②. Making a published type's member optional is a change to that face. The claim declares it, with the matching changeset.
Pins: a bind-only
listis accepted; an items-only one is accepted; alistwith neither is refused at the node; the four fences passsafeValidateSchema.
Generated by Claude Code
- addedarea:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneand removed
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 4 (this card as triaged:
ListSchema.itemsoptional on both published faces, with an at-least-one-ofbind/itemsrefinement)
Session:session_01VhxTqosz7wn54ahqyxgERT
Account:os-litant(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-11405-list-bind-only
Worktree:objectui-issue-11405
Domain:domain:spec
Seat:domain:spec#1(objectui#10217)
File surface: thelistarm only, per triage's ruling5941529511(the arm follows the renderer, and the guides stay as they are).packages/types:ListSchema.itemsbecomes optional on both published faces: zod inzod/data-display.zod.tsand TS indata-display.ts. A refinement requires at least one ofbind/items, ⛔ not exactly one, becauseitemsis the renderer's fallback besidebind. The strict face follows.- Pins:
- a bind-only
listis accepted; - an items-only
listis accepted; - a
listwith neither is refused at the node; - the four
os:checkfences inskills/objectui/guides/data-integration.mdandschema-expressions.mdpasssafeValidateSchema, read from the guides, ⛔ not edited.
- a bind-only
- Readers of
ListSchema['items']that assumed it present: typed in place, region-local, only where the optional member makes them fail to compile. - Plus: docs (AGENTS.md Add automated testing infrastructure and CI/CD workflows #2) and one changeset per released package changed.
⛔ No edit to
skills/**: the governed guides stay as they are, and their fences become valid. ⛔ No renderer behaviour change inlist.tsx. Stop on breach; explain in the report.
Container & model:S,mode:subagent,model: opus(TIER_DEFAULT; no path-derived mandate)
Clause-②: yes
Why yes: a required member of a published type becomes optional, and the validator starts accepting a bind-onlylistit refused. The at-tier contract record is owed on the landing head before enqueue (objectstack#21187 ruling A: a contract face).
Thread-read: 5941529511
Serial constraints cleared: read 2026-10-01T23:11Z at objectuiorigin/main7728c67c8, over all open PRs' file lists and every openpm:dispatchedclaim. The seat runs serially under the maintainer's word at 2026-10-01T23:08Z, 「继续上班,只处理 p0 p1 p2任务,串行派发」, so this is the only card in flight.data-display.ts/zod/data-display.zod.ts: no open PR edits them.zod-mirror-parity.test.ts: PR objectui#11406 (objectui#11276, another seat) edits itsEXCLUSIONSblock. This card opens no parity row if both faces move together. If one opens, edit only that row, and whichever PR lands second mergesmain.
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 11405, "status": "done", "branch": "claude/issue-11405-list-bind-only", "pr": "https://github.com/objectstack-ai/objectui/pull/11407", "session": "session_01VhxTqosz7wn54ahqyxgERT", "premise_still_valid": true, "summary": "ListSchema.items is optional on both published faces of @object-ui/types. The zod arm ends in .superRefine(listHasAnEntrySource) with a when-always-true option. A list with neither bind nor items is refused once, at the node (code custom, params.code LIST_ENTRIES_REQUIRED, message naming both keys). Bind-only, items-only and both are accepted through the arm, safeValidateSchema and the strict face. Zone 2 readings: H1 was confirmed on 7728c67c8 (bind-only and neither were both refused with invalid_type at items, on all three faces). H2: the refinement keeps the arm a valid discriminated-union member, and a TS union spelling was probed and not taken: the reader type is identical, TS2312 blocks extends, its error names only bind, and three precedent arms keep optional plus a mirror refinement. H3: a type-checker census over 5787 files finds one reader, list.tsx, which is Array.isArray-guarded. H4: nothing reads required from the registration, so it is untouched. H5: the four guide fences are read off disk with scanSkillFences; three pass and the one marked wrong is refused only at children. H6: no parity row is needed; the parity ratchet fires if the TS face moves alone. The pin, the list.mdx docs, a minor changeset and one ledger row in scripts/markdown-test-inputs.mjs (see deviations) land with it. skills/** and list.tsx are untouched.", "tests": "Final union on landing head 8b317ec61, under os-verify-lock: types build exit 0; `pnpm --filter @object-ui/types type-check` exit 0 (three legs; tsc -p tsconfig.test.json --listFilesOnly includes the new pin); `pnpm exec vitest run packages/types/` Test Files 321 passed (321), Tests 8560 passed (8560). Then `pnpm --filter '@object-ui/components^...' build` exit 0 and `pnpm --filter @object-ui/components type-check` exit 0, against a dist grep-confirmed to carry `items?: ListItem[]` and LIST_ENTRIES_REQUIRED. Consumer and ledger suites, 15 files (components list and bind tests, skill-guide-data-table-binding, markdown-test-inputs, check-skill-examples, and the changeset-script suites that name the ledger): Test Files 15 passed (15), Tests 494 passed (494). An earlier full types run at 24c2c1e1e was red, 1 failed of 8560: base-bind-declared's single-bind-declaration scan matched the refinement's `bind?:` destructuring annotation. db0a0537a fixed it by reading the node as a plain record. Ablation used objectstack's ablation-replace in WRAP mode, with fix committed and anchors hit x1 then x0, and blob restored equal to HEAD with git diff HEAD empty. It ran at 24c2c1e1e; db0a0537a and 8b317ec61 touch none of the three anchors. M1 (zod items required): 11 failed, 1 passed of 12, with only the counter-probe green. M2 (refinement removed): 5 failed, 7 passed, the neither, nested and beside-a-fault rows. The first M2 attempt was a no-op: the replacement count did not rise, so the tool refused before the command ran. It was re-run with a --delete anchor. M3 (TS items required): tsc -p tsconfig.test.json exit 2 with TS2741 at the pin's BIND_ONLY literal, plus the zod-mirror-parity ratchet (TS2322 on data-display.zod.ts#ListSchema and items). Control on unmutated HEAD: 12/12 passed, tsc exit 0. No dist build was needed, because the pin imports ../zod/index.zod and ../data-display from src.", "mcp_calls": "0", "api_writes": "3 — every one a relay dispatch POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectui/pulls (#11407, draft forced; read-back 9052 bytes identical); (2) label-write --assign os-litant → POST /repos/objectstack-ai/objectui/issues/11407/assignees (read-back MATCHES); (3) post-stamped os-dev-report → POST /repos/objectstack-ai/objectui/issues/11405/comments. git push x4 to claude/issue-11405-list-bind-only (empty probe, then three commits) is not REST.", "open_questions": [], "out_of_scope_findings": [ "class: c · reach: public door — safeValidateSchema on both list fences in content/docs/guide/expressions.md ('Scoped Data', 'Index in Loops') answers invalid_type at items, and StrictAnyComponentSchema adds unrecognized_keys for itemTemplate. The page teaches items as the string ${users}, an itemTemplate and per-item ${item.*} / ${index}, which the published skills/objectui expressions guide refutes ('No per-item template iteration'). · evidence: scripts/check-doc-expression-carriage.mjs already lists both sites (list.items ${users}) in its report-only census, and its header names the family's open cards objectui#7440, #7444, #7838; I did not verify which one owns this page. Family member: merge it into that family's closing card, ⛔ no single-point card. · dedupe words: expressions.md itemTemplate; list items ${users}; Scoped Data Index in Loops; per-item template", "carrier: none needed — tree-view, the other useDataScope reader in @object-ui/components, was measured: { type: tree-view, bind: rows } is already accepted on both faces, so the same defect is absent there · noted, not filed" ], "files_changed": [ ".changeset/11405-list-bind-only.md (+22/-0)", "content/docs/components/data-display/list.mdx (+18/-1)", "packages/types/src/__tests__/list-bind-only-11405.test.ts (+179/-0)", "packages/types/src/data-display.ts (+18/-3)", "packages/types/src/zod/data-display.zod.ts (+41/-2)", "scripts/markdown-test-inputs.mjs (+13/-0)" ], "gates": { "head": "8b317ec61 (gate family run at this sha; hand-derived because dispatch-gates.mjs derives nothing for objectui)", "passed_exit_0": [ "check:control-bytes", "check:new-line-citations (0 new citations)", "check:test-path-roots", "check:vi-mock-specifiers", "check:phantom-deps", "check:self-import", "check:unreferenced-sources", "check:spec-symbols", "check:changeset-claims (13 pending changesets name a touched file; each list-related paragraph read, none made false)", "check:pending-changeset-literals", "check-changeset-no-major", "check-changeset-presence (1 released package, 1 changeset)", "check:doc-types", "check:doc-fences", "check:doc-example-ids", "check-doc-links", "check-doc-expression-carriage (report-only)", "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 (110 candidates, all adjudicated)", "check-governed-queue-guard --test over the 6 paths: NOT GOVERNED" ], "not_measured": [ "check:doc-examples — exit 2 (could not run: needs the whole-workspace dist build); check:doc-snippets — same build closure, not run. Narrowed substitute: the list.mdx ts fence compiled --strict against the rebuilt @object-ui/types dist, exit 0, with a failing control in the same harness (TS2322, exit 2). CI builds their closure." ], "lint": "narrowed and proven. (1) Population from eslint's own config: 4 changed files are linted; the .mdx and changeset report 'no matching configuration'. (2) Count 4 from --format json; 0 errors. data-display.ts has 30 no-explicit-any warnings, the same 30 as its base blob through --stdin. (3) eslint.config.js has no parserOptions.project or projectService, so no type-aware linting, and the diff cannot move an untouched file's verdict.", "ci": "in_progress — not awaited, per the dispatch contract" }, "line_budget": "n/a — no skills/** or ratcheted ledger with a ceiling touched; diff +291/-6 over 6 files against base 7728c67c8", "deviations": [ "File surface: scripts/markdown-test-inputs.mjs gains one ADJUDICATED row registering the new pin as a reader of skills/objectui/guides/data-integration.md and schema-expressions.md. The row is outside the claim's declared surface. It is required: the claimed pin reads the guides off disk (H5), `markdown-test-inputs.mjs --audit` answered 'unadjudicated-test' for it, and scripts/__tests__/markdown-test-inputs.test.ts asserts auditTree(...) equals [] on the live tree. The seat owes the claim's file-surface addition. The script's own suite and its importers ran green.", "PR body amendment (seat to write if wanted): its Ablation section names no sha. The legs ran at 24c2c1e1e, and the later commits db0a0537a and 8b317ec61 touch none of the three anchors.", "Clause-② line: copied as `Clause-②: yes` with em-dash reasoning and no parenthetical arm. Measured: the change only widens, because neither was already refused before, at items." ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsACCEPT: report
5942839913, PR #11407 at8b317ec61, ready for the at-tier contract review (Fixes #11405). From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01VhxTqosz7wn54ahqyxgERT, 2026-10-01T23:46Z. The seat runs serially, so this PR's review is the one subagent in flight.Checked against the PR, not the report's prose
check-governed-mergesreads NOT governed (0 of 6 paths). Size: +291 / -6.git merge-treeagainstmain7728c67c8is clean.- The PR opens with
Fixes #11405/Clause-②: yes. That is right on the diff's face:- a bind-only
listgoes from refused to accepted; - a
listwith neither key was refused before, atitems, and is refused now, at the node; - nothing goes from accepted to refused.
- a bind-only
- The changeset is
@object-ui/typesminor. - Triage's ruling
5941529511is executed as written:itemsis optional on both faces, with an at-least-one refinement (listHasAnEntrySource,!== undefined,when: () => true,params.codeLIST_ENTRIES_REQUIRED);skills/**andlist.tsxare untouched;- the four guide fences are read off disk.
Deviation accepted:
scripts/markdown-test-inputs.mjsgains one adjudicated row, outside the claim's surface. It is forced: the pin reads the governed guides off disk, andmarkdown-test-inputs.test.tsasserts that the live tree's audit is empty. That path is not governed.Prose faces, judged by this seat (objectstack
main8dea55d31,contract-review.md:.changesetand docs prose are the ACCEPT's). Each sentence was read against the diff:.changeset/11405-list-bind-only.md:- the renderer's read order (
list.tsx,useDataScope(schema.bind)first, thenArray.isArray(schema.items)); - the three faces' old refusal of a bind-only literal;
- all four rows of the before/after table;
- "at least one, not exactly one";
- presence
!== undefined, sobind: ''anditems: []count; - the node-level issue reported beside other issues (
when: () => true); - the TS reader
ListItem[] | undefined, whose one reader isArray.isArray-guarded; - "the TypeScript face cannot express the at-least-one rule".
All hold.
- the renderer's read order (
content/docs/components/data-display/list.mdx:- the illustrative interface's
bind?/items?lines; - the at-least-one paragraph;
- the bind-only example;
- the
LIST_ENTRIES_REQUIREDrefusal; - the TS-face caveat.
All hold. The snippet's pre-existingitems?: SchemaNode[](the real member type isListItem[]) predates this PR and is noted, not owed.
- the illustrative interface's
Carried, not filed: the dev's class-(c) finding.
content/docs/guide/expressions.mdteacheslist.items: '${users}'with anitemTemplate, which the validator refuses and the published skills guide refutes. It belongs to the family thatscripts/check-doc-expression-carriage.mjsalready censuses (objectui#7440 / #7444 / #7838). ⛔ No single-point card.objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsLanded: PR objectui#11407 merged through the queue as
9547063da(Fixes #11405). From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01VhxTqosz7wn54ahqyxgERT, 2026-10-02T00:14Z.Verification
- Merge content matches the PR: the merge commit's
git patch-id --stableequals the PR's net diff (080ff8c82558on both sides). - Reviewed head is the landed head: the at-tier record
5942964923(PASS) names8b317ec61, and nothing was pushed after it. Its scope was the contract faces only (objectstackmain8dea55d31,contract-review.md). The changeset and docs prose were judged in the ACCEPT5942857572. - CI on that head: 40 success and 3 skipped by design.
Delivered
- A bind-only
listis accepted.ListSchema.itemsis optional on both faces of@object-ui/types, and the zod arm requires at least one ofbind/items, per triage's ruling5941529511. - A
listwith neither key is refused at the node, withparams.codeLIST_ENTRIES_REQUIRED. - Nothing that was accepted is now refused.
- The four
os:checkfences in the governedskills/objectuiguides passsafeValidateSchemaas written. The pin reads them off disk. - Changeset:
minor.
Carried, not filed:
content/docs/guide/expressions.mdteacheslist.items: '${users}'with anitemTemplate. It belongs to the familycheck-doc-expression-carriagealready censuses (objectui#7440 / #7444 / #7838).The leftover
pm:dispatchedis stripped in the same act.- Merge content matches the PR: the merge commit's
- added a commit that references this issue
on Oct 7, 2026
Filing-gate category: ① a defect, class (a)/(b): the validator refuses a document that the renderer draws, and that the arm's own docs say it renders.
reach:a public door, measured throughsafeValidateSchema, the functionobjectui validatecalls (on the built@object-ui/typesdist; the CLI binary was not run on these fences). Filed by objectui'sdomain:uiseat 2 (session_01JG2jy8a9su7ia4Hx7zxv42, seat post #9771) from #10859's governed-guide dev report (comment5940650071,out_of_scope_findings[0], PR #11404). The count of four fences and the grading here come from the at-tier contract review5940930445③, which corrected the dev's count of two and class (c). Reader who acts: objectui triage first (grade and route), then thedomain:uiseat. ⛔ Not graded here.Dedupe: the 1000 most recently updated objectui issues and PRs, open and closed (down to #2443, updated since 2026-09-25T10:59Z), were listed via REST and grepped locally for a
listnode near bothitemsandbind. That gave 1 hit: PR #11404 itself, the source of this finding. As a control, 6 items nameListSchema/ListItemSchema(#11360, #10907, #9590, #10714, #10879, #10822, all closed), so the grep reaches the arm's cards. None is aboutitemsbeing required besidebind. A semantic search over objectui issues for the same words gave 4 results, none on point.Measured (objectui
main6aa029b63f, as read by the dev and the review)safeValidateSchema({ "type": "list", "bind": "customerNames" })is refused withinvalid_typeatitems. The strict face agrees.ListSchema.itemsis required on both published faces: zoditems: z.array(ListItemSchema)(packages/types/src/zod/data-display.zod.ts), and TSitems: ListItem[].useDataScope(schema.bind)first andschema.itemssecond (list.tsx). The arm's own tombstone text listsbindamong what it renders. So a bind-onlylistdraws one entry per element of the bound array, andobjectui validaterefuses it.os:check-marked fences in two governed guides author alistnode withbindand noitems:skills/objectui/guides/data-integration.md, under "Viabind+useDataScope";skills/objectui/guides/schema-expressions.md: the jsoncustomerNamesexample, the jsonc{ "type": "list", "bind": "rows" }one-liner, and the deliberately wrong jsonc "Renders two EMPTY li" example (which is wrong for itschildrenand its expression, not for the missingitems).os:checkmarker asserts JSON parse only (check-skill-examples.mjs), so no gate goes red.The fork (for triage)
The review names the likely fix surface as the arm:
itemsoptional, with a one-of-bind/itemsrefinement inpackages/types(zod and TS faces). That follows the basic rule (an undeclared read follows the implementation, and the docs follow it). The other reading is that the guides teach a shape objectui should refuse, which would make the fix a governedskills/**PR. That would contradict the renderer's documentedbindread. ⛔ Not decided here.domain:uiseat 2 · findingGenerated by Claude Code