Repository navigation
test(schema-catalog): ratchet deprecated component types, and report why the div sweep cannot run as ruled - #6732
Merged
Conversation
Every catalog entry is an exemplar — the corpus a human copies from and that few-shot retrieval hands a generating model — so a fixture authoring a type the engine deprecates teaches the deprecated spelling at corpus scale. Nothing stopped the next one: both gates that touch component types ask only whether the type RESOLVES (catalog-gallery-render asserts ComponentRegistry.get(type) is truthy; check-doc-component-types walks content/docs and not examples/**), and the deprecation is not declared anywhere machine-readable at all. Freezes the measured stock (25 files / 80 nodes on 9486ac6) and refuses growth in either direction: a new file, a baselined file gaining nodes, or removals left un-deducted from the table. `span` is zero-tolerance — it has no stock to drain. `components-basic-div/` is exempt because it IS the migration guidance for this deprecation, shipping use-card-instead/flex-layout/grid-layout beside the deprecated demonstrations. The stock is frozen rather than swept because the replacements the deprecation notice names are not drop-in; the measurement is recorded in the file's docblock. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
os-sales
marked this pull request as ready for review
August 29, 2026 04:14
This was referenced Aug 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #3965
Authored by the
domain:uiexecution seat, sessionsession_01CRJge11jso9TpXRWFt1Z49(recorded here in prose because a body edit rewrites the footer to its bare form).Lands the closing lint the 2026-08-29 ruling ordered, in ratchet form, and reports the measurement that says the sweep half cannot run as specified. #3965 remains open as the worklist carrier.
Both of the card's headline numbers are wrong on today's ref
The card's own query was
grep -rl '"type": "div"' examples/schema-catalog/src/schemas | wc -l, so the 48 counted FILES. Re-counted in that unit onorigin/main@9486ac672:Positive control in the identical query shape:
"type": "card"= 93 nodes / 53 files, the same 93 measured on 08-10, 08-18 and 08-28. So thedivdrop is a real migration (#4003 moved 106 nodes), not a broken pattern.The card's stated symptom is also already gone. It says "a deprecation warning per thumbnail". Rendering all 431 entries through the real
SchemaRendererwith aconsole.warnspy active from the first entry:One notice for the whole corpus —⚠️ The first run of this census read
DivRendererreports once per module load (#3998) and exemptskind:'html'nodes by provenance (#4000). The other three warns (react-i18next, avisibleWhenpredicate, an expression failure) are the positive control that the spy was capturing, so1is a measurement rather than a silent spy.0, because an earlier test in the same file had already latched the once-per-module-load guard; the number above is from an isolated file. A census that shares a module instance with any otherdivrender is not measuring the corpus.Where the deprecation is declared, and why no fixture was swept
packages/components/src/renderers/basic/div.tsx, as aconsole.warnstring literal. It namescard/flex/container/stack/grid. None of them is a drop-in, measured through the realSchemaRendereron identical authored input{ className: 'p-4', children: [...] }:(Angle brackets are spelled out as
TAG[classes]below — this body is stored through a sanitizer that eats short tag-shaped fragments, backticks included.)divdivp-4— the authored value, verbatimcontainerdivw-full max-w-xl mx-auto sm:p-3 md:p-4 p-4flexdivflex flex-row justify-start items-start gap-1.5 sm:gap-2 p-4stackdivflex flex-col justify-start items-stretch gap-1.5 sm:gap-2 p-4griddivgrid grid-cols-2 gap-4 p-4carddivrounded-lg border bg-card text-card-foreground shadow-sm p-4CardContentelement,p-6 pt-0sectionsectionp-4— the authored value, verbatimdivis class-transparent; every replacement injects layout. Notecontainer:twMergedoes not strip the injectedsm:p-3 md:p-4, because those are different responsive variants from the authoredp-4— the box keeps the authored padding at base and silently gets smaller padding fromsmup. And none of the 85 nodes authors a single layout prop (the only keys across all 85 aretype,className,children,body) or carries a flex/grid display class, so there is no authored intent to carry across — the injection is pure addition.The second difference is not cosmetic.
container/flex/stack/gridrenderschema.childrenonly;divrendersschema.children || schema.body. Four of the 85 nodes authorbody. Measured on the realbasic-sidebar.json, swapping only itsdiv:The element count is unchanged at 21, so the loss is invisible to any element-count check, and
catalog-gallery-render'sdrewSomethingcontrol still passes because the sidebar beside it draws. That is the silent catalog-wide regression a find-and-replace would have shipped — worse than the warning it removes, exactly as the dispatch warned.section/article/asideare the one class-transparent family (semantic.tsxrendersTagwithclassNameand injects nothing, and readschildren || body), so they are technically one-to-one — but they teach landmark elements as generic boxes across 27 exemplars, which is negative on the AI-authoring axis this card exists to protect. Three parties have now independently recommended against it (the 2026-08-09 implementing agent, the PM, and the 2026-08-28 dev); this makes four.No fixture was touched. Under the 2026-08-25 escape clause the residue is excluded, and the 2026-08-29 ruling's own acceptance criterion — "browser spot-verification that renders are unchanged" — is the thing that cannot be met.
What this PR does land
A catalog-scoped ratchet,
examples/schema-catalog/test/deprecated-component-types.test.ts, delivering the ruling's stated purpose ("so the stock never regrows") in the only form that can ship before the sweep.Nothing stopped the 49th before this. Both gates that touch component types ask only whether the type resolves, never whether it is deprecated —
catalog-gallery-renderassertsComponentRegistry.get(type)is truthy (green today with all 85 deprecated nodes present), andcheck-doc-component-types.mjswalkscontent/docsand notexamples/**. It is worse than the usual declared-but-unenforced shape: the deprecation is not declared anywhere machine-readable —RegistryComponentMetaExtrashas nodeprecatedfield, so the only statements of it are theconsole.warnliteral and the label'Container (Deprecated)'. The gate therefore mirrors the list by hand and pins its own premise.spanis zero-tolerance — 0 nodes in this corpus at every ref measured, so nothing to grandfather.components-basic-div/(2 files / 5 nodes) is exempt, not baselined: that category is the migration guidance for this deprecation, shippinguse-card-instead/flex-layout/grid-layoutbesidenested-divs/custom-card. Sweeping it would delete the before-and-after. Same class asdiv.mdx's deliberate legacy examples. The exemption is checked for non-vacuity so it cannot become a hole nothing uses.div.tsx/span.tsxstill declare their notices, so the hand-kept mirror dies loudly rather than outliving the deprecation.The hand-kept mirror is a symptom, not a design choice: #6674 tracks giving the registry a machine-readable
deprecatedflag. If that lands,DEPRECATED_TYPEShere derives from the registry instead of being mirrored, and the premise pin can be deleted.27 files / 85 nodes = 25/80 baselined + 2/5 exempt.
Verification
Every arm goes RED against unfixed source — committed first, each mutation proven on disk before reading any result, each restore pinned to
HEADand proven byte-identical:divspanappearsspanis zero-tolerance…div.tsxRestore proof:
git diff HEADempty,git status --porcelainempty, and HEAD-blob hash equality for both mutated files (empty hash treated as failure, not "nothing to compare"). Gate re-run on the restored tree:Tests 7 passed (7), exit 0.Also run, exit code captured before any pipe:
pnpm exec vitest run examples/schema-catalog/—Test Files 15 passed (15),Tests 1853 passed (1853), exit 0.pnpm exec eslint --no-inline-config --format jsonon the changed file — 1 file, 0 errors, 0 warnings. Declared narrowing: the repo-widepnpm lintwas not run. The population is read from eslint's own config, the file count off its--format jsonoutput, and the config is not type-aware (noproject:/projectServiceineslint.config.js), so this diff cannot move the verdict on any untouched file. CI runs the full farm regardless.regenerate:check—src/index.ts is up to date (431 entries), exit 0.check-changeset-presence.mjs— its own verdict: "No source of a released package changed in this range, so no changeset is owed."check-changeset-no-major.mjs— "No changeset declares amajorbump." A changeset is included anyway, in the empty-frontmatter form AGENTS.md blesses for test-only changes, so "no release" is declared rather than inferred.pnpm --filter @object-ui/example-schema-catalog run type-check(objectui spells it with the hyphen) — exit 0, zeroerror TSlines, afterpnpm --workspace-concurrency=2 --filter '@object-ui/example-schema-catalog^...' build(exit 0). Run before that build it exits 2 with 37 errors, 0 of them naming this PR's file — every one isTS2307/TS2882"cannot find module@object-ui/*" in files this PR does not touch, i.e. the unbuilt siblingdist/*.d.tsclosure of a fresh worktree. Recorded because it is the trap, not a finding. The new file is genuinely in the program rather than silently excluded:tsc -p tsconfig.test.json --listFileshit = 1.What is not checked by anything
Thumbnail appearance. That is precisely why the renderer comparison above is the gate here and not the test suite — and why no fixture was swept.
Generated by Claude Code