…odes` is the only inline spelling (objectui#6951 B1)
`TreeViewSchema` declared two spellings for its one inline-nodes slot:
`nodes` (read second, after a `bind`-resolved value) and `data` (read
third), both declared by objectui#6150; `data` was required until
objectui#6939 / PR #7533 made it optional. Maintainer ruling B1
(2026-09-04): retire `data`; `nodes` stays optional; no presence refinement
(a `bind`-only tree-view is a legal, rendering document).
`BaseSchema` declares `data?: any` / `z.any().optional()`, so deleting the
member would ADMIT the key unvalidated; the retirement is a tombstone on
the extended schema that shadows the base member, and the base-vs-extended
contrast is pinned on both faces.
- `data?: never` on the interface, `retirementTombstone()` named refusal on
the Zod mirror (message names `nodes` and `bind`); `nodes` docblocks and
describe rewritten without the three-limb narrative
- tree-view.tsx reads `boundData || schema.nodes || []`
- packages/types/examples/data-display-examples.json and the
content/docs/api/schema-reference.md fence spell `nodes`; docs row
replaced by a retirement note
- PR #7533's pins that asserted `data` accepted flipped to refusal in place;
the catalog mirror test's data-spelling probes flipped; #6150 control
document moves to `nodes: []`
- new pin tree-view-data-retired-6951: both faces refuse, the union entry
carries the guidance, BaseSchema alone accepts the same document (both
faces), `nodes`-only / `bind`-only / bare document parse, TreeNode.data
control, tree-scoped catalog absence, renderer read set off disk, tsc legs
- changeset (types/components minor)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BAZFhALsQsGqxui8sNqM8s
Fixes #6951
Part of the A1 + B1 ruling on that card; the A half (
TextSchema.value) is PR #7761, which carries the closing line for its folded member card and only refs this card. This is the B half — the card closes when both are onmain, and this PR is the one that closes it.needs:contract-reviewon purpose. This narrows a published validator's accept set (Clause-② yes); the dispatching seat reviews at tier before it lands. Please do not mark ready or enable auto-merge.The ruling
Maintainer ruling B1 (2026-09-04, director record on #6951, batch #25), verbatim as recorded: "B1: retire
data,nodesis the only inline spelling … So B1 as recorded = retiredata;nodesstays optional; no refinement." The batch text's 「nodes 必填」 was swapped by the director under the 2026-09-03 seat discipline because abind-only tree-view is a legal, rendering document (packages/components/src/renderers/__tests__/shadowed-renderer-behaviour.test.tsx,{ type: 'tree-view', bind: 'treeNodes' }) andbindis the FIRST source the renderer reads. ⛔nodesis NOT made required here and no two-limb presence refinement is added; both are pinned as legal documents.Premise re-measured on the current
mainbefore editingOn
origin/main=3e377c93(cut),53ded82band2760075f(merged before opening, in that order):packages/types/src/data-display.ts:1289data?: TreeNode[](optional since PR #7533) and:1305nodes?: TreeNode[]insideTreeViewSchema(:1270);packages/types/src/zod/data-display.zod.ts:338data: z.array(TreeNodeSchema).optional()/:340nodes: … .optional();packages/components/src/renderers/data-display/tree-view.tsx:105const rawNodes = boundData || schema.nodes || schema.data || [];.datawas NOT already a tombstone — the retirement is a tombstone on an existing optional member, as the ruling read it. (data-display.ts:1439/data-display.zod.ts:399carry a same-file precedent:ChartDataSeries.data, tombstoned by objectui#6896.)The corpus (rewrite list, with the denominator — the ruling has no majority clause for B): the same walker as PR #7761 (object literal whose OWN
typekey istree-view), overexamples/,apps/, theexamples/directories underpackages/,content/docs/**(939 files): 10tree-viewnodes — 6nodes-only, 3data-only, 1 both. Of thedata-bearing four:packages/types/examples/data-display-examples.json#examples.tree-viewand thecontent/docs/api/schema-reference.md:512JSON fence authordataand are rewritten tonodes; the other two are probes insideexamples/schema-catalog/test/tree-view-nodes-mirror-6939.test.tsx(data: 'not-an-array'and a both-spellings carrier), flipped below. The six onnodes: the fourcomponents-data-display-tree-view/*catalog entries, the nested tree incomponents-complex-resizable/editor-interface.json, and anodes-only probe in that same test. In-repo tests separately (2349 files): 9tree-viewliterals — the #7533 pin (NODES_ONLY_DOCUMENT+ twoBaseSchemacontrols ondata, which stay as controls), the #6150 control document (data: []→nodes: []), and the three mirror-test probes. Package sources (packages/*/src, 1684 files): 0 tree-view literals.skills/**: no tree-view fence authors either key. Grep cross-check:"type": "tree-view"under the four roots — 7 files on base, the twodatasites now 0.The retirement — both faces, and the read
packages/types/src/data-display.ts,TreeViewSchemablock only):data?: neverwith the house-form@deprecatedRETIRED docblock pointing atnodesand notingbindstays the first-read source;nodes's docblock no longer narrates the three-limb read.TreeNode.data?: any(the per-node payload,:1265) andChartDataSeries.dataare other members — untouched, the former pinned as the control.packages/types/src/zod/data-display.zod.ts,TreeViewSchemaconst only):data: retirementTombstone('RETIRED (objectui#6951) —datais no longer part of TreeViewSchema; writenodes(or bind the tree withbind). …');nodes's describe rewritten without the three-limb narrative.tree-view.tsx:105):boundData || schema.nodes || schema.data || []→boundData || schema.nodes || []. Nothing else in the file.BaseSchema.datatrap, measured (the PM's mechanism assumption):BaseSchemadeclaresdata?: any/z.any().optional()and is.passthrough(), so a DELETED member would ADMIT an authoreddataunvalidated through the base (PR fix(types): tree-view mirror stops requiring the limb it reads third (group 2 of objectui#6939) #7533's body explains this; its pin measured it). The tombstone on the extended schema SHADOWS the base member, and the new pin proves it rather than assumes it: on the same document{ type: 'tree-view', data: [...] },BaseSchema.safeParsesucceeds with the array surviving whileTreeViewSchema.safeParserefuses at pathdata, andsafeValidateSchema(theAnyComponentSchemaunion entry) refuses with the guidance inside theinvalid_unionenvelope; on the TS face the same literal is a legalBaseSchemaand an@ts-expect-erroronTreeViewSchema(a declarednevermember wins over both the inheritedanyand the index signature).SchemaNodeSchemaare validated againstBaseSchemaCore, not the union — the same reach every tombstone in this package has.Corpus rewrite
packages/types/examples/data-display-examples.json#examples.tree-view:data→nodes(targeted key rename; the file is notJSON.stringify-canonical, so no re-serialisation; verified by parse: keystype, id, nodes, multiSelect, showLines). A different region from PR fix(types): lift the flex example's direction/gap out of the inertpropsenvelope #7729'scompositeExample.content/docs/api/schema-reference.md:517:"data": [→"nodes": [in theTreeViewSchemafence.content/docs/components/data-display/tree-view.mdx: thedata?: TreeNode[]; // Tree data (alias for nodes)row removed,nodesrow reworded, a retirement note after the schema fence quoting the refusal and stating thatbindis unchanged,nodesstays optional, andTreeNode.datais a different member.Pins
tree-view-data-optional-6939.test.ts): the type-level pin becomes anEqualofTsTreeViewSchema['data']withundefined(still red if the member is DELETED — it would resolve to the inheritedany); "the accept set only WIDENED" → everydatadocument refused at pathdata; "element-level enforcement" → refused at the KEY with the guidance (the tombstone, not element validation); the passthrough control's carrier moves fromdata: []tonodes: []; the read-site pins now assertboundData || schema.nodes || [], noschema.datain the file, andbindread ahead ofnodes. The relaxation's still-true pins —nodes-only legal, a bare{ type: 'tree-view' }legal (no refinement),BaseSchemaalone admits both refused values — stay, with a header note.tree-view-nodes-mirror-6939.test.tsx): "correcting it todatachanges no pixel" → spelling itdatanow draws an EMPTY tree (title still on screen, every root label gone — the enforce-or-remove half measured through the DOM); "both spellings validate" → onlynodesvalidates,datarefused by name on the direct schema and on the union door; the DECLARED-keys carrier isnodes-only.content/docs/componentspages #6150 (undeclared-but-consumed-keys-6150.test.ts):TREE_CONTROLis{ type: 'tree-view', nodes: [] }and thenodescase's read text isboundData || schema.nodes || [].packages/types/src/__tests__/tree-view-data-retired-6951.test.ts(18 tests): zod half — a well-formed array and[]refused at pathdatawithinvalid_type/expected: never, message namesnodesandbindand equals the.describe(), the union door refuses with the guidance and accepts thenodes-only andbind-only documents, BASE CONTROL (BaseSchemaaccepts the same document), key still DECLARED; inside the boundary —nodes-only parses and survives,bind-only parses (pinned explicitly as the document a two-limb refinement would have refused), bare{ type: 'tree-view' }parses, wrong-typednodesstill refused,TreeNode.datacontrol accepted, undeclared key rides passthrough; corpus — tree-scoped walk of every JSON underexamples/schema-catalog/src/schemasandpackages/types/examples(six tree-view nodes; floor 6) asserting none spellsdataand each parses; renderer — read set off disk (boundData || schema.nodes || [], noschema.data,bindread beforenodes); TS half —@ts-expect-erroron the member type, the fresh literal (with theBaseSchemabase control compiling beside it) and a widened value;nodes,bindandTreeNode.datacompile.?: never/retirementTombstone()pair does not drift;tree-view's only ledger entry staysRuntimeOnlyDeclared → onNodeClick. ⛔MIRRORS/EXCLUSIONSuntouched; PR test(types): watch the mirror-wider-than-declared direction with a fifth parity ledger #7758'sWiderThanDeclaredregion is not needed here.Verification (exit codes captured before any pipe; heavy runs through the shared verify lock; every reading on the head named)
pnpm --filter '@object-ui/components^...' --filter @object-ui/types build(lock)3e377c93+diffVERDICT command-exit 0 · held the lock 28s(dist verified on disk for types/core/react/react-runtime/sdui-parser/i18n)pnpm exec vitest run --maxWorkers=2 packages/types/ packages/components/ examples/schema-catalog/test/tree-view-nodes-mirror-6939.test.tsx examples/schema-catalog/test/safe-validate-corpus-6318.test.ts scripts/__tests__/check-doc-component-types.test.ts scripts/__tests__/doc-version-claims.test.ts(lock)db4c848e(pre-merge)Test Files 2 failed, 357 passed (359) · Tests 2 failed, 4371 passed (4373)— both failures my own: the new pin's non-vacuity floor (7 written, 6 measured) and the mirror probe reading the guidance off the union envelope instead of the arm; fixed in80aae7cbpnpm --filter @object-ui/types build && pnpm exec vitest run --maxWorkers=2 packages/types/ …tree-view-nodes-mirror-6939… …safe-validate-corpus-6318… …shadowed-renderer-behaviour… …skill-guide-data-table-binding…(lock; after mergingorigin/main=53ded82b)80aae7cbTest Files 126 passed (126) · Tests 2147 passed (2147) · VERDICT command-exit 0 · held the lock 26spnpm --filter @object-ui/types type-check·pnpm --filter @object-ui/components type-check80aae7cb, re-run onaf691923pnpm exec vitest run --maxWorkers=2 packages/types/ …tree-view-nodes-mirror-6939… …shadowed-renderer-behaviour…(lock; after mergingorigin/main=2760075f, so PR #7758's fifth parity ledger runs against this tombstone)af691923(final head)Test Files 124 passed (124) · Tests 2126 passed (2126) · VERDICT command-exit 0 · held the lock 20spnpm --filter @object-ui/types lint·@object-ui/components lint·@object-ui/example-schema-catalog lint(eacheslint .)db4c848e0 errorseach (268 / 925 pre-existing warnings, none on a touched line)node scripts/check-changeset-presence.mjs80aae7cb, re-run onaf691923✅ 6 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/6951-tree-view-data-retired.mdnode scripts/check-changeset-fixed.mjs·check-changeset-no-major.mjs·check-changeset-overwrite.mjsdb4c848e✅ All workspace packages are in the changeset fixed group·✅ No changeset declares a major bump·✅ No pre-existing changeset was modified or deletednode scripts/check-control-bytes.mjs·pnpm check:spec-symbols80aae7cb✅ check-control-bytes: OK (scanned 6326 tracked text file(s))·✅ spec member citations: no comment cites a key its spec symbol does not declarepnpm check:doc-types·pnpm check:doc-fences·node scripts/check-doc-links.mjsdb4c848e✅ Every documented component type is registered·✅ check:doc-fences …·Links are valid across 17 scan rootspnpm check:unreferenced-sources·check:i18n-keys·check:i18n-drift·check:element-data-source-declaration·check:action-forward-parity·check:designer-field-key-parity·check:phantom-deps·check:self-import·check:side-effects-array·check:entry-guard·check:icon-record-names·check:skills-paths·check:shell-escape-residue·check:docs-route-closuredb4c848elint.yml/ci.ymlfamilies reading a touched path; derived by hand from.github/workflows/*)node scripts/pm/check-governed-merges.mjs --test …(objectstack, final 11-path list — unchanged by the second merge)80aae7cb0 of 11 path(s) hit the register … NOT governedNarrowing declared: the full
packages/components/suite ran green in the pre-merge lock run (db4c848e); the post-merge run re-ranpackages/types/(where the merged commits — PR #7737 chart-series, #7739 chatbot — land) plus the four tree-view-relevant files, becausegit diff --stat 3e377c93 origin/main -- packages/components examples/schema-catalogis EMPTY — none of the merged commits (#7754, #7757, #7758) touches either tree; the onepackages/typeschange among them is #7758's parity-ledger test, which the post-mergepackages/types/run covers. CI runs the farm.NOT MEASURED (precondition exits, not verdicts):
pnpm check:doc-snippets/pnpm check:skill-examples(26-package build closure, not built on this shared box) — this diff touches nots/tsxfence anywhere (the two doc edits are ajsonfence and aplaintextfence, which those gates do not compile) and noskills/**file, and the census found no doc or skill fence authoringtree-view.dataon a typed literal, so the tombstone cannot move either gate's verdict;pnpm check:readme-exports/check:sdui-registration-pins(needapp-shelldist / a console build). CI runs all four on the farm.Ablation (red-first; each leg mutate → prove on disk by blob hash and anchor counts → run →
git checkout HEAD -- ABSOLUTE PATH→ prove restored by blob hash == HEAD blob;trapon EXIT INT TERM; finalgit diff HEADempty)databack toz.array(TreeNodeSchema).optional(); anchorsdata: retirementTombstone(in theTreeViewSchemaregion 1→0, marker 0→1): the pin file → exit 1, 6 failed / 12 passed — both refusal cases, the guidance case, thesafeValidateSchemacase, the BASE CONTROL (the base and the extended schema now AGREE, which is the failure) and the DECLARED case; every inside-the-boundary, corpus, renderer and TS-erased leg stays green.TreeViewSchema.data?: never→data?: TreeNode[], block-anchored so the pre-existingChartDataSeries.datatombstone in the same file stays put; anchorsdata?: never2→1, marker 0→1):tsc -p tsconfig.test.json→ exit 2 —TS2578 Unused '@ts-expect-error'on exactly the three directives (:303,:323,:343),TS2344on the flipped fix(types): tree-view mirror stops requiring the limb it reads third (group 2 of objectui#6939) #7533 type pin (tree-view-data-optional-6939.test.ts:96), andzod-mirror-parity.test.ts(1524): TS2322 '"data-display.zod.ts#TreeViewSchema"' is not assignable to type 'never'— the existing ledger catching the one-face drift. (A first attempt anchored on the bare member text did NOT land — the anchor matched twice — and was reported by the script's on-disk check rather than read as a pass; the re-run above is the reading.)boundData || schema.nodes || []→boundData || schema.nodes || schema.data || []; anchors 1→0 / 0→1): pin +tree-view-data-optional-6939+undeclared-but-consumed-keys-6150+ the catalog mirror test → exit 1, 7 failed / 121 passed — the off-disk read-set pin, the flipped fix(types): tree-view mirror stops requiring the limb it reads third (group 2 of objectui#6939) #7533 read pin, finding(types): 13 top-level schema keys that component renderers genuinely READ are declared by no shipped type — measured across all 76content/docs/componentspages #6150's read-site pin, and the four "spelling itdatanow draws an EMPTY tree" cases (the tree draws again once the limb is read — the DOM half of enforce-or-remove going red-first).git diff HEADempty,git statusclean.ADR-0087 disposition marker — the gate's own reading
Same reading as PR #7761, re-taken on this tree: grep of
scripts/check-changeset-*.mjs(4 scripts),scripts/__tests__/*,.github/workflows/*and.changeset/README.mdforadr-0087/dispositionfinds no gate that reads a changeset or PR-body marker (hits: a prose entry inscripts/__tests__/doc-version-claims.test.ts, a comment inchangelog.yml); objectstack'scheck:adr-0087-registrationhas no objectui counterpart. The four changeset gates ran green. No marker was added; if the reviewing seat wants one, say which gate should read it.Deviations (declared, not silent)
3e377c93andorigin/mainwas merged twice before opening per the PM's notices —53ded82b(carrying finding(types): serieschartTypeis undeclared and SILENTLY STRIPPED - the normalizer reads it first, measured not live, alias-refusal owed #7694 / PR feat(types): refusechartTypeon a chart series by name, pointing attype#7737 and finding(types/plugin-chatbot):chatbot-floatingdeclares two keys nothing reads -displayMode(designer control included) andFloatingChatbotConfig.triggerIcon#7654 / PR feat(types)!: retire ChatbotSchema.displayMode as an ADR-0049 tombstone; remove the Display Mode control and seed (objectui#7654) #7739 indata-display.*andcomplex.*), then2760075f(PR test(types): watch the mirror-wider-than-declared direction with a fifth parity ledger #7758's parity ledger; this PR adds no ledger row and does not touch that file).examples/schema-catalog/test/tree-view-nodes-mirror-6939.test.tsxflips and theundeclared-but-consumed-keys-6150.test.tscontrol document are outside the claim's literal file list but inside "existing pins that assertdataaccepted flipped" — they were the pins asserting it.check:doc-snippets/check:skill-examples/check:readme-exports/check:sdui-registration-pinsNOT MEASURED locally (above), with the reason the first two cannot be moved by this diff.Out of scope — observed, not touched
packages/types/src/__tests__/tree-view-data-optional-6939.test.tskeeps its original header prose describing the three-limb read as history (a note at the top records the retirement);examples/schema-catalog/test/tree-view-nodes-mirror-6939.test.tsx:10-14likewise.skills/counter-example prose ontext.value) are on PR feat(types)!: retire TextSchema.value as an ADR-0049 tombstone —contentis the one spelling (objectui#6951 A1 / objectui#7016) #7761's report, not here.Session:
https://claude.ai/code/session_01BAZFhALsQsGqxui8sNqM8s🤖 Generated with Claude Code
https://claude.ai/code/session_01BAZFhALsQsGqxui8sNqM8s
Generated by Claude Code