`packages/components/src/SchemaRenderer.tsx` was a 28-line component sharing an
export name with the real renderer in `packages/react/src/SchemaRenderer.tsx`,
reachable from nothing: absent from the barrel, imported by no file in the repo
in any form, and behind no subpath in the package's `exports` map.
It was never in the runtime bundle, so this is not a behaviour change. Its only
shipped footprint was a stray types-only `dist/SchemaRenderer.d.ts` with no
runtime module behind it and no specifier that resolves to it.
Deleting beat keeping because the copy is a trap: it evaluates no predicate.
Of the real renderer's six visibility legs it consults only `hidden`, by bare
truthiness rather than evaluation, so a node declaring `hiddenOn` is never
hidden; it reads neither enablement leg; and it spreads `{...schema}` raw, so
`disabled` would reach the widget unevaluated — the inverse of the real
renderer's evaluate-strip-forward contract.
Fixes#7319
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Fixes#7319
Deletes
packages/components/src/SchemaRenderer.tsx, a 28-line component sharing an export name with the real renderer inpackages/react/src/SchemaRenderer.tsx. Diff is 2 files: the deletion and a changeset.Clause-②: no
Derived from the diff against the criterion does this change contract accept/reject behaviour, or widen/narrow the published surface? Neither. The file is not in the runtime bundle, and no package specifier resolved to it before this change or after. Evidence below.
needs:contract-reviewis therefore not hung on this PR or on the card.Which SchemaRenderer this is, and the probe that separates them
SchemaRendereris a name that also exists live one package over, so a word-count probe is worthless here and nearly stopped this change for the wrong reason. Underpackages/components/srcthe bare word occurs 312 times. Only 2 of those are this file (its own declaration and its self-recursive call); the other 310 live in 101 other files, 55 of which import the name from@object-ui/reactand 0 of which import it from any relative path.The form that decides liveness is the declaration, not the word:
packages/componentspackages/react(control)SchemaRendererdeclarations in srcfrom '../SchemaRenderer'importersEvery control fired in the same run as its zero.
Nothing could reach it — four independent paths, each with a control
packages/components/src/index.tsis prose at:54.dist/index.d.tscontains 0 occurrences of it, against 24 export lines in that same file.exportsmap has exactly two entries,.and./style.css— no wildcard subpath, so no deep import resolves to it.@/*tosrc/*, but 0 files inpackages/components/srcuse that form and 0 files repo-wide import@/…SchemaRenderer. Control: the same grep shape finds 63 files repo-wide that do use@/.index.tsover 210 non-test source files reaches 207; this file was not among them.Also checked and clear: dynamic
import()/React.lazy(every hit resolves to the react renderer's own tests), generated files andscripts/(0 hits naming the components copy), the package README (0), and tests underpackages/components(0 relative importers; the 236 hits there all import from@object-ui/react).The triage stop-condition: confirmed, with one correction
Triage asked whoever took this to settle the "evaluates no gate at all" claim first, because it decides tidy-up versus removed hazard. Confirmed in substance — it is a trap. It evaluates no predicate at all. Of the real renderer's six visibility legs (
visibleWhen/visible/visibleOn/visibility/hidden/hiddenOn) it consults exactly one,hidden, and by bare truthiness rather than evaluation; it reads neither enablement leg (disabled/disabledOn); and it spreads{...schema}raw, sodisabledwould reach the widget unevaluated — the precise inverse of the real renderer's evaluate, strip, forward-the-verdict contract.Two precision corrections to the card, both narrowing rather than refuting it:
hiddenbeingboolean | string. On today'smain,BaseSchema.hiddenishidden?: boolean(packages/types/src/base.ts:328) and the string sibling ishiddenOn. The trap survives the correction and gets worse in one direction: the copy ignoreshiddenOnentirely, so a node declaring it is never hidden.Reverse verification: no behavioural pin, and that is a measured claim
There is nothing behavioural to pin, so this PR invents no pin. That is demonstrated rather than asserted, by mutating in both directions with the result proven on disk each way:
.d.tshasDeclaredVisibilityGate)893387f3matchingorigin/mainThe mutation was confirmed on disk before the rebuild by blob hash equality plus a marker count of 1, not by an editor's exit code; the restore was proven by state (
git diff HEADempty,git status --porcelainempty), not by exit code, under atrap … EXIT INT TERMusing absolute paths.What that table shows: the body never enters the runtime bundle in either direction, so no test could distinguish before from after — any pin would have been vacuous. The file's entire shipped footprint was one stray types-only
dist/SchemaRenderer.d.ts(143 bytes) with nodist/SchemaRenderer.jsbehind it and no specifier reaching it. That is a change to the shipped file list, not to the published surface, which is why the changeset is a patch and why Clause-② isno.Verification
Run on
746f6f818, the tree this PR proposes. Suites from the repo root per objectui#3378.vitest run --project unit --project dom packages/components/— 220 test files passed (220), 2021 tests passed (2021), 0 failed.pnpm --filter @object-ui/components type-check— clean.pnpm --filter @object-ui/components lint—932 problems (0 errors, 932 warnings); all pre-existing style warnings, no errors.check:readme-exports—✅ check-readme-exports: OK (… 0 unbuilt …).the population COLLAPSED -- this run proves nothingand28 unbuilt; that is a could-not-run, not a failure of this diff (0 of its findings namedpackages/components, 0 namedSchemaRenderer). It was converted into a real reading by building all 44 workspace targets, after which it judges 421 self-imports and 52 keys, against floors of 25/2/20.check:dist-completeness—✓ dist completeness: 12 package(s) complete (1608 emitted files verified); 2 type-check-only, on that same fully built tree.check:control-bytes—✅ check-control-bytes: OK (scanned 6194 tracked text file(s); skipped 85 binary).check:phantom-depsexit 0 — the deleted file imported@object-ui/core, which 235 other files in the package (92 non-test) still import, so the dependency is not orphaned.check:self-import,check:esm-specifiers,check:node-esm-load,check:entry-guard,check:side-effects-array,check:eager-closure— all exit 0.The sweep triage asked about
Triage flagged that two unreferenced duplicates in one package in one day is a pattern and asked whether a sweep is warranted. Measured rather than guessed, and the answer is yes, but for a smaller set than it looks:
use-sync-external-storeshims — are not dead: they are reached throughvite.config.tsalias entries, which an import-graph walk cannot see. That leaves one genuine orphan,packages/components/src/ui/toast.tsx(0 exports fromui/index.ts, 0 importers; control: the livesonnerimplementation has 16).ui/chart.tsxis reachable from the barrel —index.ts:90 export * from './ui'reachesui/index.ts:20 export * from './chart'. Its liveness picture differs from this card's, so it is not a second instance of this shape; that card stands on its own mechanism and stays open.check:*scripts detects an unreferenced source file, which is why both cards were found by hand. Filed separately as a finding rather than fixed here.🤖 Generated with Claude Code
Generated by Claude Code
Generated by Claude Code