Uh oh!
There was an error while loading. Please reload this page.
refactor(types)!: retire ThemeComponentSchema — the type: 'theme' component kind no renderer implements - #5650
Merged
Conversation
…mponent kind no renderer implements (#5489) `packages/types/src/theme.ts` declared a theme-manager COMPONENT — `type: 'theme'` carrying `themes[]`, `activeTheme`, `allowSwitching`, `persistPreference`, `storageKey` — and `zod/theme.zod.ts` published the matching Zod object as a member of `ThemeUnionSchema`, hence of `AnyComponentSchema`. Nothing rendered it: the literal is absent from every `ComponentRegistry.register(...)` / `registerLazy(...)` site in `packages/*/src` and from both `PROTOCOL_COMPONENTS` and `PALETTE_PLACEHOLDER_BLOCKS`, so it did not even resolve to a placeholder. A page declaring one got the "Unknown component type" panel, never a theme manager. Removed under the maintainer ruling of 2026-08-21 on objectstack#10485 (option B). The theme SYSTEM is explicitly retained by that same ruling and is untouched here: `Theme`, `ThemeDefinitionSchema`, `ThemeModeSchema`, `ThemeEngine` and `ThemeProvider` all stand. - types + zod declaration, `ThemeComponentSchemaType`, both export barrels - `ThemeUnionSchema` loses its `'theme'` member - the acceptance test that pinned the dead shape as VALID becomes a retirement pin proving it REFUSED, with the theme document it carried as the control leg - `content/docs/guide/schema-overview.md` stops teaching the removed type; the `check-doc-component-types` exemption it needed goes with it, and the theme-schema.mdx exemption is re-pointed at the retirement and at #5648
Contributor
✅ 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
|
os-sales
marked this pull request as ready for review
August 22, 2026 01:08
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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.
Fixes#5489
Retires
ThemeComponentSchema(type: 'theme') from@object-ui/types— a component kind no renderer ever implemented. Under the maintainer ruling of 2026-08-21 on objectstack#10485, option B (verbatim, untranslated):The theme system is untouched.
ThemeEngine(packages/core/src/theme/ThemeEngine.ts),ThemeContext/ThemeProvider(packages/react/src/context/ThemeContext.tsx) and their unit tests are explicitly retained by that ruling and are not in this diff.Theme,ThemeDefinitionSchemaandThemeModeSchemaalso stand. Retiring the dead component kind is not retiring theming.Consumer sweep — the search terms, and the positive control for each
The card records an explicit confidence gap: no in-repo consumer was found, but out-of-repo consumers of the published types package are not measurable from here. A zero-hit sweep is only evidence if the command is proven able to hit, so every term below is paired with a control run through the identical pipeline.
--include='*.ts' --include='*.tsx'plus.js/.mjs/.json/.md/.mdxwhere noted;node_modulesanddist/excluded)grep -rn "ThemeComponentSchema" .packages/types(2 declaration sites, 2 export barrels, its own unit test, prose in 1 doc page) + 2 gate-script exemption strings. No consumer.grep -rn "ThemeComponentSchemaType" .zod/index.zod.ts; zero consumers.grep -rn "ThemeUnionSchema"packages/typesgrep -rn "AnyComponentSchema" packages/ apps/ examples/ e2e/ scripts/ | grep -v '^packages/types/'BaseSchema→ 131grep -rhoE "ComponentRegistry\.(register|registerLazy)\(\s*'[a-z0-9-]+'" packages/*/src | sed -E "s/.*'([a-z0-9-]+)'/\1/" | sort -u | grep -cx 'theme'grep -cx 'tooltip'→ 1awk '/PROTOCOL_COMPONENTS = \[/,/^\];/' packages/components/src/renderers/placeholders.tsx | grep -c "'theme'"grep -c "'ai:input'"→ 1grep -rn '"type": *"theme"' .(JSON/TS/MD fixtures repo-wide)"type": "form"→ 98grep -rn "export \* from '@object-ui/types" packages/ apps/grep -rn "ThemeComponentSchema"over the siblingobjectstackcheckoutConclusion:
'theme'is registered by nothing — not by a renderer, and not even by the placeholder registry, so it did not resolve to a placeholder either. A page declaring one got the registry's "Unknown component type" panel (OBJUI-001), never a theme manager. Declared-but-unenforced, the ADR-0078 class. No in-repo consumer exists, so stop-and-report condition 1 did not trigger. Out-of-repo consumers of the published@object-ui/typesremain unmeasurable from this repository — that gap is unchanged by this PR, and is the reason the ruling, not this PR, is the authority for the removal.ADR-0087 registry entry: NO, none is owed
Checked rather than assumed, in both directions:
docs/adr/holds the 0001…0059-era ADRs, andfind . -name '*0087*'outside.gitreturns nothing. The only two mentions of the number in the whole repo are a code comment inpackages/types/src/ui-action.ts:444naming a spec-side conversion, and onepackages/types/CHANGELOG.mdline.docs/adr/0087-metadata-protocol-upgrade-contract.md) and its registries are@objectstack/specartefacts:packages/spec/spec-changes.json,packages/spec/src/migrations/. Its stated Consumers are@objectstack/spec,@objectstack/cli, the runtime metadata loader,@objectstack/mcp,@objectstack/create-objectstackand the Release workflow. objectui is not among them, and@object-ui/typesis not the metadata protocol — it is objectui's TypeScript/Zod surface for UI components.themenode —grep -rn "'theme'" packages/spec/src/ui/in objectstack returns 1 hit, a field namedthemein a view test, while the control'form'returns 57. Nothing on the protocol side references the kind being removed.The changeset is this removal's release-notes input, per the repo's own contract.
Sequencing / shared artefacts
Independent of the objectstack-side retirement, as the card predicted: different repo, different package, no shared gate and no shared generated artefact. Measured, not assumed — see the spec-vocabulary cross-check above, and
check:spec-symbolsbelow, which passes with the removal in place.What changed
packages/types/src/theme.ts— interface removed, replaced by a tombstone carrying the ruling verbatim and the unregistered-kind measurement.packages/types/src/zod/theme.zod.ts— the Zod object, its'theme'membership inThemeUnionSchema, andThemeComponentSchemaType.packages/types/src/index.ts,packages/types/src/zod/index.zod.ts— both export barrels.packages/types/src/__tests__/phase2-schemas.test.ts— the acceptance test that pinned the dead shape as valid becomes a retirement pin proving it refused by bothThemeUnionSchemaandAnyComponentSchema, with the theme document it carried (ThemeDefinitionSchema.safeParse) as the control leg that makes the refusal attributable to the retired wrapper rather than to a broken fixture..changeset/retire-theme-component-schema-5489.md—@object-ui/types: minor(nevermajor; fixed group).Bounded in-place fixes, named here with their evidence
Two follow-on edits are the same defect class as the removal (references to the retired kind), mechanically determined by it, and in the same gate family. Naming them is the condition for making them here rather than filing them:
content/docs/guide/schema-overview.mdimported and annotatedThemeComponentSchemaintypescript-fenced blocks (animport typenaming it from@object-ui/types, and twoconst theme: ThemeComponentSchema = { … }declarations), plus a comparison-table row and two prose bullets. Left alone it would teach a symbol this PR deletes. The theming section now describes the retained surface — a theme document handed toThemeProvider— with notype:literal in any fence. The page is inUNGATED_DOCS, so it is not compiled; the ledger conditions are re-derived per run and all still hold (document exists;typescriptfences 8 → 7, still ≥ 1, andtypescriptis inTS_FENCE_LANGUAGES; reason string still over the minimum).scripts/check-doc-component-types.mjs— that page'sthemeexemption would have failed asstale-exemptionthe moment the literal left its code blocks (the gate re-derives every entry and only checks that the file still spells the value, confirmed by readinganalyze()), so it is deleted. Thecontent/docs/core/theme-schema.mdxentry stays live — that page still spells the literal — but its reason namedpackages/types/src/theme.tsas the declaration site, which is now false; it is re-pointed at the retirement and at the rewrite card.scripts/check-doc-snippet-types.mjs's ledger prose for schema-overview.md gets one appended clause for the same reason. Gate verdict after both edits:✅ Every documented component type is registered.Verification — all of it on the final commit
b06fdf397Genuine, not the zero-match trap:
packages/types/package.jsondeclares@objectstack/specandzodonly — no workspace dependency exists to build.Gates, each verdict quoted from the gate's own output (exit codes captured before any pipe):
check-doc-component-types✅ Every documented component type is registered.(183 docs, 1053 code blocks, 886typeliterals, 743 registered / 143 exempted)check-changeset-presence✅ 5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)check-changeset-no-major✅ No changeset declares a 'major' bump.check-changeset-fixed✅ All workspace packages are in the changeset fixed group.check-control-bytes✅ check-control-bytes: OK (scanned 4696 tracked text file(s); skipped 85 binary).check-spec-symbol-derivation✅ spec symbol derivation: 1290 files scanned against 4912 spec export namescheck-package-self-import✅ No package names itself inside its own src/.check-lint-coverage✅ lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).check-type-check-coverage✅ type-check coverage: 45/46 via 'type-check', 0 known-brokencheck-doc-linksLinks are valid across 13 scan roots.Ablation — the retirement pin proven able to fail, both legs
The test resolves
'../zod/index.zod'as a relative source import, and the root vitest config additionally aliases@object-ui/types→packages/types/src; nothing in this path readsdist/, so this ablation is source-level and the rebuild-then-re-hash step does not apply here. The script carried atraponEXIT INT TERMthat rangit checkouton both mutated files, so a cap-kill mid-mutation could cost at most one reading and never leave a mutated tree behind. Each leg proved its mutation on disk with anchored counts before the run was believed — an editor's exit code is not evidence.type: z.literal('theme')member toThemeUnionSchema. On-disk proof: injected marker → 2,grep -cxF " ABLATION_ThemeComponentSchema,"→ 1. Result:Tests 1 failed | 30 passed,AssertionError: expected true to be falseon the refusal assertion. Predicted red, observed red.typography.fontSizeback into the fixture, leaving the union alone. On-disk proof:grep -cxF " fontSize: 16,"→ 1. Result:Tests 1 failed | 30 passed,AssertionError: expected false to be true— the opposite direction and the other assertion, which is what shows the control is scoped to the same subject and can fail independently.git checkout-ed; markers re-measured at 0 and 0,git status --porcelainempty for both, re-runTests 31 passed (31).Declared narrowings — measurement, not omission
Each is a repo-scale run CI performs in full regardless; each is declared with its own evidence rather than silently skipped.
pnpm lint(turbo run lint, 46 packages). Narrowed to the 7 changed.ts/.mjsfiles. (a) The population comes from eslint's own flat config, not a guess:eslint.config.jsmatches**/*.{ts,tsx}withjs.configs.recommended+tseslint.configs.recommended. (b) File count read from--format json: 7 files linted, 0 errors, 10 warnings. (c) Invariance for untouched files: the config declares noparserOptions.projectand noprojectService, so linting is not type-aware and no edit here can move any untouched file's verdict. The 10 warnings are pre-existing and unchanged — the base revision of that same file, linted through the identical command, also reports 10 warnings, 0 errors. CI sets no--max-warnings.check-published-dist-toolingwas killed by the container's 10-minute foreground cap (exit 143) at 9m42s with 15 of 26 turbo builds done and no finding emitted — it builds the entire workspace by design (its own header cites the 2026-08-16 ruling on objectui#4846 that assigns exactly this cost to CI). It inspects every package'sdist/for tooling material and does not read this diff, which adds no test or bench file to anydist/. CI owns it.check-doc-snippet-typesis a known-broken gauge (exits 1 onmain) and compiles a scoped package set. Not run; its ledger conditions were instead re-derived by hand fromanalyze()and all hold — see the bounded-fix note above. CI owns it, as it already does.Out-of-scope findings, filed unassigned, not touched here
ThemeSwitcherSchema/ThemePreviewSchemaare the same dead component kind #5489 retires —theme-switcherandtheme-previeware registered by nothing either #5647 —ThemeSwitcherSchema/ThemePreviewSchemaare unregistered in exactly the same way (theme-switcher→ 0,theme-preview→ 0, same pipeline, controltooltip→ 1), but the ruling did not name them. After this PRThemeUnionSchemaconsists of precisely those two unimplemented kinds. Extending the retirement to them would need its own ruling.content/docs/core/theme-schema.mdxteaches a schema that does not exist — six independent falsehoods, now including thetype: 'theme'kind #5489 retires #5648 —content/docs/core/theme-schema.mdxteaches a schema that does not exist: six independent falsehoods, five of them predating this card. It is the reason that page's gate exemption is re-pointed rather than deleted here; deleting the three entries is part of that rewrite.Generated by Claude Code