feat(types): export ObjectTreeSchema from the root barrel (objectui#9550) - #9685
Conversation
…#9550)
`ObjectTreeSchema` was the one arm of `ObjectQLComponentSchema` that the
root barrel of `@object-ui/types` did not name, so no TypeScript consumer
could import it: the declaration lives in `objectql.ts`, the union applies
it, and the zod barrel re-exports it, while the package publishes no
`./objectql` subpath to reach around the barrel.
The repair is one name added to the existing explicit named re-export
list. The declaration is not moved or edited - it is held by another open
pull request and only published from here.
`object-tree-root-barrel-9550.test.ts` pins the repair on both halves: the
type-level legs (enforced by `tsc -p tsconfig.test.json`) assert the name
resolves off the root barrel and is the SAME declaration as the
`Extract<ObjectQLComponentSchema, { type: 'object-tree' }>` spelling a
consumer was forced into, and the source legs (enforced under vitest)
assert the list stays explicit rather than becoming a wildcard and that
the declaration stays in `objectql.ts`.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L
|
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
|
At-tier contract review record — PR #9685 / card objectui#9550 — FAILAdopted VERBATIM by the Tier, from the reviewer's OWN transcript (⛔ not Isolation: the reviewer got the card, existing rulings and the PR only — ⛔ never the dispatch order, ⛔ never this seat's conclusions, ⛔ never the implementer's report as an input. VERDICT: FAIL Served-tier: CONTRACT_REVIEW_TIER What I measured
What I checked and found1. Does the diff do what the card asks — no less, no more? The card (as graded in comment 5710385032, "修法边界: 补一行导出") asks for option 1, one name on the root barrel. The product change is exactly that one line, in the right clause, in union-arm order. Option 3 (a gate) was not built. Option 2's unmeasured item ( 2. Published surface. Measured, not assumed (rows 3–4): all eleven present arms are exported by a single type-only 3. Frozen files. 4. Is the pin real? Yes, and I proved it rather than trusting the PR body: with the one line reverted, the runtime leg 5. Changeset. Right package ( 6. Does anything this merge makes false go unrepaired? Yes — one live-reading claim. Of the 16 files naming
On merge every sentence of that paragraph inverts, nothing re-derives it (AGENTS.md #9), and the triage ruling on this very card named this exact site as the thing to close in the same stroke. The PR knows this (its acceptance note 2) and leaves it, citing the Claim's file fence and the absence of an open-PR intersection for Blocking items
Non-blocking observations (not gating)
Scratchpad: the review's own directory (clone at head, ⭐ — Record posted by PM Generated by Claude Code |
…ng it (objectui#9550) `ObjectTree` typed its node by narrowing the published union on the `object-tree` tag and binding that to a module-local alias. The spelling was forced, not chosen: the name it wanted was declared in `@object-ui/types` and carried by the zod barrel, but the root barrel did not name it, so there was nothing to import. The preceding commit put the name on that barrel; this one consumes it at both read sites, `ObjectTreeProps.schema` and `getTreeConfig`. The type does not move. `ObjectTree.schemaTyped-8655.test.ts` asserts `ObjectTreeProps['schema']` invariantly equal to the narrowing, independently of this branch, and it stays green. What goes away is a derived restatement of a published type - the per-consumer second authority an unimportable type mints, which is the class objectui#6349 is burning down. The docblock's justification paragraph goes with it: it gave as its MEASURED reason for the narrowing that the barrel omits the name so it "cannot be imported today", and that sentence is false now. The objectui#8651 shadowing warning beside it is KEPT and re-pointed at the import - a module-local type wearing a published type's name is the two-layers-one-word trap, and that is more load-bearing once the published name is importable, not less. The block now annotates the import rather than a declaration that no longer exists. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L
Patch round — commit
|
| gate | exit |
|---|---|
pnpm --filter @object-ui/plugin-tree run type-check |
0 (both tsc legs) |
pnpm --filter @object-ui/plugin-tree run lint |
0 (0 errors) |
pnpm exec vitest run on ObjectTree.schemaTyped-8655.test.ts |
0 — 17 tests, non-zero pass count |
pnpm exec vitest run packages/plugin-tree/ packages/types/ |
0 — 215 files, 4757 tests |
pnpm exec turbo run build --filter=!@object-ui/site |
0 — 43/43 |
check:readme-exports · check:dist-completeness · changeset:check · check-changeset-presence · check-changeset-overwrite · check:changeset-claims · check:pending-changeset-literals · check:spec-symbols · check:component-surface-parity · check:unreferenced-sources · check:self-import · check:entry-guard · check:control-bytes · check:test-path-roots · check:new-line-citations |
0 |
Path filtering only; no -t regex was used anywhere, so the objectui#9660 false-green shape is out of reach.
Two readings for whoever picks this up
needs:contract-reviewis on this PR now (it was absent when the PR was opened and this seat does not hang or clear it). The card carrier should be checked too — objectui's own half-state patrol documents this as a dual-carrier gate.mergeable_statereadsbehind. Not a conflict, and the merge queue rebuilds on current main anyway; flagged rather than acted on, and if a sync is wanted the compliant route in this repo is a merge, never a rebase plus force-push.
No README change is owed: packages/plugin-tree/README.md teaches an AUTHOR how to write the node through the published union, which is still exactly true, and packages/types/README.md does not enumerate exports.
Generated by Claude Code
✅ 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
|
At-tier contract review record — PR #9685 @ head
|
| # | probe | reading | source / control |
|---|---|---|---|
| 1 | ObjectTreeSchema in packages/types/src/index.ts |
base 0, head 1 | git show the path piped to grep -c; control ObjectGridSchema reads 2 at base in the same file |
| 2 | union arms of ObjectQLComponentSchema in objectql.ts at head |
12 arms | listed verbatim: Grid, Form, View, Map, Tree, Gantt, Calendar, Kanban, Chart, Gallery, DataTable, ListView |
| 3 | each arm's presence in the export type { … } from './objectql.js' clause at base |
11 of 12 read 1, ObjectTreeSchema 0, absent-token control ZZZ_ABSENT 0 |
clause extracted by awk; per-name grep -c |
| 4 | export form of the clause at head | export type { … } from './objectql.js' — one clause (closing line is the file's only one from that module; the other such line is an import type); no export { / export * from that module |
type-only, matches all 11 siblings exactly |
| 5 | frozen files in the diff | git diff --name-only base head over the two frozen paths → empty |
control: git ls-tree HEAD lists both files, so the empty result is a reading |
| 6 | ObjectTreeSchema in zod/index.zod.ts at head |
1 (a named export line, between ObjectMapConfigSchema and ObjectGanttSchema) |
control ObjectGridSchema 1 |
| 7 | package.json exports keys |
twelve keys, no ./objectql |
parsed with node, not grepped |
| 8 | ObjectQLComponentSchema / ObjectTreeSchema in ObjectTree.tsx at head |
0 / 6 | import line has no alias |
| 9 | vitest, the two pins at head | 2 files, 24 passed (7 + 17) | own clone |
| 10 | tsc -p tsconfig.test.json in packages/types at head |
exit 0, 0 lines | — |
| 11 | ablation: delete the one barrel line | mutated blob f540b80e = the base blob in the diff header; vitest exit 1, `1 failed |
6 passed`, b1 only; tsc exit 2, 8 errors all in the pin: TS2724 on the import, TS2322 on the six a1 members and on a2; no other file reds |
| 12 | tsc --noEmit and tsc -p tsconfig.test.json in packages/plugin-tree at head (dependency closure built, 12 turbo tasks) |
exit 0 / exit 0 | — |
| 13 | control on the cross-package pin: retype the prop to a different published type already in scope | tsc exit 2, _PropIsThePublishedNode reds with TS2344 at its own row, plus downstream TS2353/TS2559 |
restore byte-identical |
| 14 | whole packages/types/ + packages/plugin-tree/ vitest |
215 files / 4757 tests passed, exit 0 | — |
| 15 | CI wiring | ci.yml type-check job runs pnpm type-check (turbo → each package's type-check, whose third leg in types and second in plugin-tree is tsc -p tsconfig.test.json); test job runs the sharded root vitest whose unit project includes the packages' test globs; the "Decide whether this change needs a full run" step diffs base to head excluding only markdown, content/, docs/, apps/site/ and .changeset/ — this diff has three TypeScript files, so the full run fires |
read from .github/workflows/ci.yml, vitest.config.mts, both package.json scripts, both tsconfig.test.json |
| 16 | check-changeset-claims.mjs base-to-head |
exit 0, and its own output states it does not cover "a claim about anything it does not spell as a file name in backticks: a symbol"; the only backticked file in the 8655 changeset is ObjectTree.schemaTyped-8655.test.ts, untouched here |
lit: the gate ran and printed its coverage statement |
| 17 | pnpm changeset:check, check-changeset-presence.mjs |
0 / 0 ("3 source file(s) of 2 released package(s) changed … declares 2 changeset(s)") | — |
| 18 | line-number citations added by the diff | none | — |
| 19 | .changeset/ literals in the new test |
0 | control: packages/cli/src/__tests__/app-generator.test.ts does name one, so the grep fires |
| 20 | BreadcrumbSchema on the root barrel (card option 2, flagged NOT MEASURED) |
present, one line in the ./navigation.js clause; declared in navigation.ts |
clean, nothing to repair |
| 21 | drift since base | origin/main is 5 commits ahead; none touches index.ts, ObjectTree.tsx, objectql.ts or objectql.zod.ts; ObjectTreeSchema in origin/main index.ts still 0 |
git fetch origin main in my clone |
| 22 | PR #9540's changed files | 4: a 9309 changeset, object-gallery-filter-9309.test.ts, objectql.ts (hunks at an ObjectChartSchema docblock and ObjectGallerySchema.filter), zod/objectql.zod.ts |
disjoint from this diff; the b4 regex anchored on the ObjectTreeSchema interface declaration is untouched by those hunks |
| 23 | tree-wide search at head for claims that depend on the prior state | two hits asserting the prior state — see Blocking 1 and 2; every other hit re-read and still true | git grep over 5a82fd9e9 |
What I checked and what I found
1. Does the diff do what the card asks as ruled — no less, no more? The ruling is the grading comment's 修法边界: put the name on the barrel like its siblings (clause ② surface addition, not accept-set widening), AND retire the objectui#8655 hand-copy in plugin-tree by replacing it with an import, else 「这张卡看起来已经修好了」 while the second authority survives. Both halves are in this head: the barrel line, and ObjectTree.tsx now types the prop and getTreeConfig's parameter with a type-only named import (reading 8). Option 3 (a completeness gate) is correctly not taken and the test docblock says so in as many words. Option 2 is closed by measurement (reading 20). Nothing beyond the ruling is in the diff. Pass on this point.
2. The published surface. Readings 3, 4, 6: all 11 sibling arms are on one explicit type-only re-export clause; the new name is on the same clause in union-arm order; the zod barrel already carried it. This completes an already-published family with measured pull (the #8655 idiom), which is the "completing, not widening" side of 创业阶段不扩散. Pass.
3. Hard constraint. Reading 5: neither frozen file is in the diff; b4 pins that the declaration stays in objectql.ts and that index.ts declares nothing by that name. PR #9540's own hunks (reading 22) do not reach the declaration b4 matches. Pass.
4. Are the pins real? Yes, by my own ablation (reading 11): the vitest half reds on exactly b1, the type half reds on the import and on a1/a2, nothing else in the package moves, and the mutated blob is provably the base blob. The type-level legs are erased at runtime and would be silent under pnpm test alone — but tsc -p tsconfig.test.json is a leg of both packages' type-check scripts, and CI's type-check job runs pnpm type-check for any PR touching non-ignored paths (reading 15). The cross-package pin _PropIsThePublishedNode is live against the head renderer (reading 13), which is what makes the plugin-tree swap type-invariant by measurement, not by argument. Pass.
5. Changesets. @object-ui/types: minor matches both prior root-barrel additions (7697-combobox-option-root-barrel.md, 9406-types-root-barrel-two-names.md, both minor); @object-ui/plugin-tree: patch is right for a type-invariant swap; no major (AGENTS.md rule, gate exit 0). Every factual claim in both bodies checked against the tree: 12 arms / 11 exported / 1 missing (reading 3); zod re-export and objectui#7917 (reading 6, the 7917 changeset exists); no ./objectql subpath and the exports map pinned by package-exports-manifest.test.ts (reading 7; that test's header says so); the #8655 idiom (the removed hunk); a2/b3/b4 pin what the types body says they pin; the 8655 test asserts the equality invariantly and independently through the prop type (its Equal helper is the invariant form, reading 13 proves it can fail); the deleted paragraph did say "cannot be imported today"; the #8651 warning is kept. Pass, with one nuance under Non-blocking.
6. Does anything this merge makes false go unrepaired? Yes — three items, Blocking 1–3. Reading 23 found two in the tree; the PR body is the third.
7. Is the edited docblock itself true? Yes. Each sentence of the relocated block in ObjectTree.tsx was checked: "objectui#9550 is what put the name on that barrel" (this diff); "the interface whose own docblock calls itself the 'Object Tree (tree-grid) Component Schema'" (verbatim in objectql.ts); views.ts names it "the node an author writes" (carried over verbatim from the old block); "the same derivation ResolvedTreeConfig below already uses against TreeViewConfig" (ResolvedTreeConfig is defined below the import from a Pick of TreeViewConfig); "Never re-declared LOCALLY under this name, and not aliased on the way in" (reading 8: no alias, no local declaration); the #8651 sentence is verbatim from the old block and #8651's body does record a local CalendarSchema in ObjectCalendar.tsx beside the published one. The re-pointed leading clause was necessary — the old "Deliberately NOT spelled ObjectTreeSchema locally" would itself have gone false — and the replacement is true. Pass.
Blocking items
Blocking 1 — pending changeset .changeset/8655-object-tree-props-schema-typed.md (present at base, unreleased, @object-ui/plugin-tree: minor) publishes two sentences this head falsifies. It will land in the same plugin-tree CHANGELOG release as this PR's own 9550-plugin-tree-import-object-tree-schema.md, describing shipped code that will not exist:
- "The type is DERIVED, not written out" — naming the union-narrowing spelling that union's own docblock teaches — at this head the prop is the named import.
- "…rather than a named import because the TS barrel does not export
ObjectTreeSchema— the zod barrel does, its nine siblings are all on the TS one, and repairing that omission is a published-surface addition on another package, so it is reported rather than smuggled in here." — the barrel exports it at this head.
This is the WENT-FALSE shape check-changeset-claims.mjs itself defines (objectui#7721, #8617), and reading 16 shows why the gate stayed silent: the changeset names the state by symbol, not by a backticked file this diff touches. Precedent for the remedy is on main: commit c9f9baedf (PR #9495) corrected three falsified sentences in the pre-existing 6854-layout-renderer-retired-onclick.md in place, prose only, frontmatter byte-identical, after a round-1 contract-review FAIL on exactly this point; and check-changeset-claims.mjs's "The correction path" section says "correcting the body is the answer, and it is precedented rather than forbidden", noting check-changeset-overwrite.mjs will report it as its legitimate case 2. Action: in this PR, rewrite those two sentences to the past tense and point forward — e.g. "…was DERIVED … because, when this landed, the TS barrel did not export ObjectTreeSchema; objectui#9550 put the name on that barrel in this same release and this package now imports it (see that entry)." Frontmatter untouched. The claim comment's file fence needs amending to admit this file, as it was amended in round 1.
Blocking 2 — packages/plugin-tree/src/ObjectTree.schemaTyped-8655.test.ts carries a docblock that now asserts the opposite of the renderer it describes. Directly above its module-local union-narrowing alias the comment reads, verbatim: "The node the checker now sees at every read, spelled the way the renderer spells it." At this head the renderer spells it ObjectTreeSchema (reading 8). The row comment "① — the prop is the published node, derived off the union, and it is not any" likewise describes the retired declaration. This is the same class as this PR's round-1 FAIL (a plugin-tree docblock justifying the old spelling by a state the merge removes) and as PR #9495 round-1 defect 1 (a test comment above the block the branch changed). The pin itself is correct and must not change — the alias is now the valuable thing: an independent spelling that proves the named import IS the union arm from the consumer side (readings 12–13). Action: reword the two comments to say exactly that — the alias is a deliberate second spelling kept as the probe, not "the way the renderer spells it" — leaving _PropIsThePublishedNode and the rest of the file byte-for-byte. Fence amendment as above.
Blocking 3 — the PR body, which this repo squash-merges as the permanent commit message (AGENTS.md: 「本仓 PR 一律 --squash 入队合并」, and the recorded incident of a squash message describing a different diff), states figures that are false at this head.
- The "What changed" heading and its table say three files; the head touches five.
- 维护者速读: the rollback sentence says three files and one line of product code — five files, and
ObjectTree.tsxcarries product-code changes; the release-impact sentence omits the@object-ui/plugin-treepatch.
The later "SUPERSEDED BY PATCH ROUND 1" note is true, which makes the body internally contradictory rather than merely stale. PR feat(types): refuseshortcutby name on app action items (objectui#7719) #9495 round 1 treated false figures in the body as FAIL items 4–5; the same standard applies. Action (seat write, not the dev's): correct the heading and table to the five files, correct the 速读 rollback and release-impact sentences, and either refresh or explicitly label the "Gates" table as the round-1 reading.
Non-blocking observations (not gating)
- The "Gates" table in the body is the round-0 reading (types-only narrowing, 199 files / 4663 tests); it is not false but it is not this head's reading either (mine: 215 / 4757 across both packages, reading 14). Label it or refresh it while fixing Blocking 3.
- The types changeset says "The one hand-written copy this gap had already minted is retired in the same change." The 8655 test also holds a narrowing alias, minted by the same gap; once Blocking 2 re-describes it as the deliberate probe, "the one" is accurate for the renderer and I would leave the sentence alone.
- The 8655 changeset's "its nine siblings" was born wrong (the union has 11 sibling arms). Fold a correction into Blocking 1 if convenient; not gating on its own.
mergeable_state: behind— five commits onmainsince base, none touching the four files of interest, andObjectTreeSchemais still absent frommain'sindex.ts(reading 21), so no duplicate-export or conflict risk; the queue rebuilds on currentmain.- objectui#9687 (filed for the plugin-tree docblock under the old fence) is still open and is superseded by this head; close it as such at landing, as the seat's rework note already plans.
packages/plugin-tree/README.mdstill teaches the union narrowing for authors; that remains true andcheck:readme-exportshas nothing to say. Teaching the direct named import there is an improvement for whoever next edits it, not a defect.- The new pin's regex extraction is robust against the one hazard it names (the
import typeline from the same module) because the brace class cannot span clauses and the length assertion guards the count; the name parser drops commentary lines. Verified against the actual clause text. - Serial-constraint note for the ledger: PR fix(types): type ObjectGallerySchema.filter as its documented destination #9540's file list (reading 22) confirms it is disjoint from this diff at file granularity.
Scratchpad artefacts (logs of every run above) are under this review's own directory; the clone there is at 5a82fd9e with a clean working tree apart from build outputs.
⭐ Extract type applications, its <sha> placeholder, its indexed-prop spelling and its glob patterns are rendered as descriptions of the same things. ⛔ No finding, count, verdict or ranking was altered.
⭐ Blocking 3 is already CLOSED by the seat (it is a seat write, as the record says): the PR body now reads five files, the rollback and release-impact sentences name both packages, and the Gates table is explicitly labelled a round-0 reading. Footer count verified back at 1.
— Record posted by PM domain:spec @ objectui · session session_01UanLVj6xvbS6puBCewLr8L · 2026-09-17T12:25Z
Generated by Claude Code
…ased (objectui#9550) Three prose sites asserted the pre-merge tree. One class, one layer out from the one the previous commit closed - and the dangerous half is that two of them publish VERBATIM into a CHANGELOG at the next release, so they would have shipped as a description of code that never existed that way. `.changeset/8655-object-tree-props-schema-typed.md` is UNRELEASED and lands in the same `@object-ui/plugin-tree` release as this branch's own entry. Two of its sentences are falsified by this head: that the type is DERIVED by union-narrowing, and that this is so BECAUSE the TS barrel does not export `ObjectTreeSchema`. Both are now past tense and point forward at the entry that took the repair. Its "nine siblings" was born wrong - the union declares twelve arms, so eleven are siblings - and is corrected in the same pass. Frontmatter is byte-identical; `check-changeset-overwrite.mjs` prints the base and current declarations side by side and they match, which is that gate's case 2. `ObjectTree.schemaTyped-8655.test.ts` kept two comments describing the retired declaration. ⛔ The pin itself is UNCHANGED - proven, not asserted: masking comments with this repo's own `js-comment-mask.mjs` leaves 202 code lines hashing identically before and after, with a lit control showing the comparison does move when one token of code changes. That module-local narrowing is now the VALUABLE half: an independently written second spelling whose agreement with the renderer's named import is what makes the swap provably safe, so both comments now say it is a deliberate probe kept on purpose. This branch's own entry carried the same defect in miniature and is corrected with them: one present-tense clause about readers of a file that no longer reads that way, and a generic written in the bare angle-bracket form that GitHub's body sanitizer deletes - respelled in the spaced form the sibling changeset already uses, since this text publishes to a rendered page. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L
✅ 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
|
|
| head | round | result | duration |
|---|---|---|---|
bdb519c5 |
0 | success | 1181s |
5a82fd9e |
1 | success | 1192s |
a8cfe1ce |
2 | cancelled | 1217s |
The job's bound is timeout-minutes: 20 (1200s), at .github/workflows/ci.yml:752. ⇒ the shard was already at 99.3% of its ceiling on the previous head and crossed it on this one.
⭐ And round 2's diff cannot have caused it: this round changed one changeset paragraph and two code comments. ⛔ No test was added, renamed or made slower. A prose-only diff does not add 25 seconds of test execution.
⭐ This is a shape this repository has already recorded against itself: ci.yml:614-641 documents an incident where 「the job's timeout-minutes: 20 fired at 20m02s. The job went cancelled」 and states in as many words that raising the timeout is not the fix.
Why the existing fix is ⛔ not being ported
The fix exists: PR objectui#9584 (open) reshards Test 4 → 8 and makes an aggregator the required context. ⛔ It is not portable into this PR. It changes .github/workflows/ci.yml, lint.yml, three scripts and eight test files, and — decisively — a shard-matrix change must update the merge queue's required-check set in the same act, or the queue hangs silently. Porting that into a packages/types barrel-export PR would widen it into CI infrastructure and risk the queue for every other PR. ⇒ this is the case where 「port a fix that exists」 does not apply, and saying so is the required alternative.
What happens instead — ⛔ no re-run spent
This PR reads mergeable_state: behind and must take a base merge before it can enqueue. That merge produces a new head and a fresh CI run as ordinary required work, ⛔ not as a kick. ⇒ the one permitted re-run stays unspent, and the shard gets another attempt on the head that actually enqueues.
ℹ️ For the lane ledger: the sibling seat on objectui#9663 already lost a queue slot to this same shard being cancelled at ~20 minutes. That is two cards in one day.
Generated by Claude Code
At-tier contract review record — PR #9685 @ head
|
Re-run spent on
|
Fixes #9550
ObjectTreeSchemais now a named export of the@object-ui/typesroot barrel.WARNING — angle-bracket-shaped tokens are spelled out in WORDS throughout this body: GitHub's body sanitizer deletes them, including inside backticks and fenced blocks (AGENTS.md, the section on GitHub rewriting the bytes you write into an issue or PR body). So "an EXTRACT of
ObjectQLComponentSchemanarrowed to theobject-treetag", never the literal TypeScript spelling — a table written to show that idiom would otherwise render as if it showed nothing.What changed — seven files after patch round 2, one of them a single line of product code
packages/types/src/index.tsexport typelist that re-exports from./objectql.js, placed in union-arm order betweenObjectMapConfigandObjectGanttSchemapackages/types/src/__tests__/object-tree-root-barrel-9550.test.ts.changeset/9550-object-tree-root-barrel.md@object-ui/typesminorpackages/plugin-tree/src/ObjectTree.tsxObjectTreeSchemaat both read sites (ObjectTreeProps.schema,getTreeConfig); the now-false justification paragraph deleted; the objectui#8651 shadowing warning KEPT, its ruling sentence verbatim and its leading clause re-pointed (a byte-for-byte keep would itself have gone false).changeset/9550-plugin-tree-import-object-tree-schema.md@object-ui/plugin-treepatch (new file).changeset/8655-object-tree-props-schema-typed.mdpackages/plugin-tree/src/ObjectTree.schemaTyped-8655.test.tsPremise re-verified on this branch point, not relayed from the card
Base
72f55c9ec1. Occurrence counts inpackages/types/src/index.tsBEFORE the repair, each zero standing beside controls that read non-zero under the same query:ObjectTreeSchemaObjectGridSchema(control)ListViewSchema(control)BreadcrumbSchema(control)ZZZ_ABSENT_CONTROL(absent-token control)The declaration reads 2 in
objectql.tsand the zod barrel reads 1, so the instrument was lit in both directions. The card's second premise also holds: the package'sexportsmap declares.,./base,./complex,./data,./data-display,./feedback,./form,./internal/retired-field-keys,./layout,./navigation,./overlayand./zod— there is no./objectqlsubpath, so the root barrel was the only route and it did not carry the name.One more measurement worth having: nothing in this workspace star-re-exports
@object-ui/types(export *from that specifier: zero hits across every.ts/.tsx). That is what makes this addition unable to collide with any downstream barrel — the failure mode an additive export can otherwise have.The file fence held — nothing under another PR's hold was touched
packages/types/src/objectql.tsandpackages/types/src/zod/objectql.zod.tsare held by open PR objectui#9540 at a frozen head. Neither is in this diff. TheObjectTreeSchemadeclaration already existed inobjectql.ts; this PR publishes it from the barrel and does not edit, move or re-declare it. Leg b4 of the pin asserts exactly that:objectql.tsstill declares the interface andindex.tsdeclares nothing by that name.The pin, and the reverse verification that proves it can fail
The repair has two enforcement halves and the pin covers both, because a pin that only covered one would be green on half a regression.
pnpm test. Their enforcement istsc -p tsconfig.test.json, the third leg of this package'stype-checkscript, which CI runs as its own job. a2 is the interesting one: it asserts the imported name and the EXTRACT idiom are the SAME declaration, using the house invariant type-equality helper rather than assignability. That idiom is the measured "before" — the seat delivering objectui#8655 had to write it because the name could not be imported.dist/read: this repo's per-PRtestjob runspnpm testwith no build step ahead of it, so a test needing a freshdist/would be vacuously absent-or-red on a cold cache.package-exports-manifest.test.tsandcombobox-option-root-barrel-7697.test.tsrecord that same constraint for this package; the new file follows them rather than re-litigating it.Reverse verification (run from the committed fix, restored afterwards; the direction was predicted as RED before the run and RED is what it read):
1 failed / 6 passed, b1 failing with "expected [...] to include 'ObjectTreeSchema'"tsc -p tsconfig.test.jsonObjectTreeSchema") plus the a1/a2 equality assertions invertinggit checkout HEADon the path,git diff HEADempty, blob hash byte-identical to the HEAD blobOnly 1 of the 7 tests reds under mutation, which is the expected shape and not a weakness: b2/b3/b4 pin properties the mutation does not move, and a1/a2/a3 are erased at runtime — they red in the tsc column instead.
Gates — exit codes captured to disk before any pipe
@object-ui/typesonly: 199 files / 4663 tests). Patch round 1 re-ran the owed gates across both packages — 215 files / 4757 tests, all green — so this table is a true record of round 0 and ⛔ not a reading of the current head.pnpm --filter @object-ui/types run lintpnpm --filter @object-ui/types run type-checkpnpm exec vitest run packages/types/pnpm exec vitest runon the new test filepnpm exec turbo run build --filter=!@object-ui/sitepnpm changeset:checknode scripts/check-changeset-presence.mjspnpm check:changeset-claimspnpm check:pending-changeset-literalspnpm check:readme-exportspnpm check:dist-completenesspnpm check:component-surface-paritypnpm check:entry-guardpnpm check:unreferenced-sourcespnpm check:spec-symbolspnpm check:self-importpnpm check:control-bytespnpm check:test-path-rootspnpm check:new-line-citationspnpm check:esm-specifierspnpm check:node-esm-loadpnpm check:published-distpnpm check:published-tsconfig-excludeTwo readings that deserve their names rather than a bare number:
check:readme-exportsexits 1 on an unbuilt tree — "type entry./dist/index.d.tsis not on disk, runpnpm buildfirst", 36 of 40 packages unbuilt in a fresh worktree. That is PRECONDITION NOT MET, not a finding: it namespackages/data-objectstackand reports 0 fabricated names. After the full build it exits 0 with 542 self-imports judged real. The pre-build 1 is recorded here so nobody re-derives it and reads it as a regression.pnpm lintandpnpm type-checkat the repo root areturbo runacross every package — a whole-farm run that belongs to CI. They were narrowed to@object-ui/types, declared: the diff touches one package; no workspace package star-re-exports@object-ui/types(measured zero, above), so no other package's resolution can change; and the change is purely additive to a type-only re-export list, which cannot remove or retype anything a consumer already resolved. The full workspace build ran anyway and is green, which exercises every package's owntscemit.scripts/pm/check-half-states.mjsis a scheduled board patrol, not a per-PR gate on this diff (itspull_requestleg fires only for changes to the patrol itself). Not run; not owed.Acceptance notes — two things for the reviewer; ⭐ the second one IS now repaired here (patch round 1)
BreadcrumbSchema's TS-side status, which the card flagged as NOT MEASURED, is now MEASURED: it is CLEAN. The name is on the root barrel's./navigation.jsre-export clause, declared innavigation.ts, withBreadcrumbItemandNavigationSchemaas lit controls on the same list and an absent token reading false. So the card's option 2 finds nothing to repair on the TypeScript side — objectui#7917's other name never had this gap. Nothing filed, because there is nothing to file.⭐ SUPERSEDED BY PATCH ROUND 1 — the stale comment IS repaired in this PR.⚠️ The text below is kept as the record of what this note said when the PR was opened; ⛔ do not read it as the current state.
What changed and why: the at-tier contract review returned FAIL on exactly this point, and it was right. The triage grading on objectui#9550 had already ordered the repair in its 修法边界 section — 「decision(plugin-tree):
ObjectTreePropsdeclaresschema: any, so two cast reads are structurally UNANSWERABLE — the one class-(d) half with no carrier among the seven (objectui#8327) #8655 那份手抄件在补导出之后应当删掉并改为 import,⛔ 否则第二权威照旧存在,而这张卡看起来已经修好了」 — and the dispatching seat's order failed to carry that line and then fencedplugin-treeout explicitly. The narrowing was the seat's error, ⛔ not the implementer's. The fence was amended and the repair taken in this PR:packages/plugin-tree/src/ObjectTree.tsxnow takes a type-only named import ofObjectTreeSchemafrom@object-ui/typesat both read sites, the module-local narrowing alias is gone (ObjectQLComponentSchemaoccurrences in that file: 0), the false justification paragraph is deleted, and the still-true objectui#8651 shadowing warning is kept — with its ruling sentence verbatim and its leading clause re-pointed, because a byte-for-byte keep would itself have become false once the file imports that name. A@object-ui/plugin-treepatch changeset rides along.Original note 2, as written when this PR was opened — ⛔ no longer true
A comment in
packages/plugin-tree/src/ObjectTree.tsxgoes stale the moment this merges, and it is deliberately NOT repaired here. Its docblock states, as a measured reason for choosing the narrowing idiom, that the TS barrel "omits it ... So the name cannot be imported today." After this PR that sentence is false. The triage seat asked for the hand-written spelling to be replaced by an import once the export landed. It is out of this PR's declared file surface and out of the dispatch's ruling zone, which fixed the ask at the one name — so it is reported rather than taken unilaterally. It needs its own card.ℹ️ objectui#9687 was filed by the seat for this repair while the fence was believed correct; it is superseded and will be closed as a duplicate once this PR lands.
Not done here, on purpose: the card's option 3, a gate over barrel completeness for every declared node schema. That is a wider design with its own review, and a gate arriving unrequested inside a one-symbol repair is scope taken unilaterally. objectui#9526 is the sibling card in the same family; if that gate is ever built, this pin becomes one of its regression cases rather than its substitute.
Governed surface: this diff touches none of them (
AGENTS.md,CLAUDE.md,.claude/,skills/,docs/adr/), so it does not need the governed-surface approval before it is readied. It DOES carry clause 2 — a symbol added to a published barrel — so an at-tier contract review runs first.维护者速读(草稿)
改了什么。
@object-ui/types这个包的「总目录」(根 barrel)原本漏掉了一个名字:ObjectTreeSchema—— 树形表格这个组件的类型定义。它本身一直存在、一直被联合类型用着、zod 那一面也导出了,唯独 TypeScript 这一面没往外摆,所以谁都 import 不到。这次就是补上这一行,外加一个防止它再次消失的测试和一份 changeset。为什么改。 缺口不是「少一行」这么静态:objectui#8655 那一轮因为导不到这个名字,只能在插件里手写一份等价的类型表达式。一个导不出来的类型,每被需要一次就会被手抄一次,而手抄件今天对、明天就开始漂 —— 这正是 objectui#6349 那张 burn-down 卡在清的形状。补上导出,是把「第二权威」的源头关掉。
风险与代价(含回滚)。 风险接近于零,方向是纯新增:没有任何东西被删除、改类型或收窄,原来的写法照旧可用;全仓没有任何包用
export *转发这个包,所以新增的名字不可能和别人撞名。发版影响是@object-ui/types走一个 minor、@object-ui/plugin-tree走一个 patch(按本仓约定,破坏性变更也不写 major)。回滚就是回退本 PR:七个文件(两轮补丁后),产品代码是packages/types的一行导出加plugin-tree里把本地别名换成具名 import 的类型替换(该替换经实测为类型不变式:_PropIsThePublishedNode这条跨包 pin 断言两种拼法同一,且把它改成别的已发布类型会让tsc变红)。没有数据迁移、没有运行时行为改变。发版影响是@object-ui/types一个 minor 加@object-ui/plugin-tree一个 patch。席位意见。 (留空,待受权席位填写)
你要做的。 只有一件:确认「往已发布的包里新增一个公开名字」这件事本身可以接受(条款②的 at-tier 契约评审)。⭐ 补丁轮更新:开 PR 时 Acceptance notes 第 2 条说
plugin-tree里那段会因本次合并变成假话的注释「不在本 PR 修」—— 那条已作废。达档契约复核判 FAIL,而且判得对:分诊在卡上本来就写明了「补导出之后应当把那份手抄件改成 import,否则第二权威照旧存在,而这张卡看起来已经修好了」,是派发席漏带了这句并把plugin-tree围了出去。围栏已修订,该修复已在本 PR 内完成,并随附一个@object-ui/plugin-tree的 patch changeset。⇒ 合并后不会留下假话,也不需要再另开卡。Generated by Claude Code