Uh oh!
There was an error while loading. Please reload this page.
feat(spec): retire the themes carrier key and ThemeSchema — app.branding is the one colour surface (#10485, ADR-0049) - #10695
Conversation
…R-0049) Ruled B (退役授权面, 2026-08-21): strict-delete the stack themes key with a guidance prescription, delete ui/theme.zod.ts whole, drop the ingest mapping and the PLURAL_TO_SINGULAR fold (so /meta/theme gets the #8421 refusal), register six retired-def entries and the D3 semantic entry stack-themes-carrier-retired, pin the refusal, re-base docs and the showcase on app.branding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
Specimen swaps (theme -> webhook) in metadata-protocol, objectql, rest and runtime meta-door tests; new #10485 refusal pins; stack-collection-maps waivers updated; PROTOCOL_MAP row removed; Iso pin count 839 -> 834. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
…nt paragraph (#10485) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
…tire-themes-carrier # Conflicts: # content/docs/ui/index.mdx
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
📓 Docs Drift CheckThis PR changes 4 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 135 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e8f5c35fb09c0fb479c101c9eef7ad74eb860128 && git checkout e8f5c35fb09c0fb479c101c9eef7ad74eb860128
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5f2e54cc66330cbc53a17f6e3746acdfcdc14704 90dc771f2593e1d32d73397e587b95371230ba13 && git checkout -B drift-repro 5f2e54cc66330cbc53a17f6e3746acdfcdc14704 && git merge --no-ff 90dc771f2593e1d32d73397e587b95371230ba13
node scripts/docs-audit/affected-docs.mjs --json 5f2e54cc66330cbc53a17f6e3746acdfcdc14704
|
…section (10 of 16) (#10485) check:quick-reference-counts [row]+[total] remedies: references/ui/theme.mdx was deleted with the retired surface, so the row goes (retired, not moved) and M drops 17 -> 16, N 11 -> 10. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
Fixes#10485
What this is
ADR-0049 enforce-or-remove retirement of the
defineStack({ themes })carrier key and theThemeSchemaauthoring surface, per the maintainer ruling recorded on the issue (2026-08-21, verbatim, untranslated):The measured defect: authored themes were parsed strictly, ingested and stored — and then read by nothing. No framework package consumed
.themesor storedthemeitems,themewas never a registered metadata type, no first-party app mounted the spec-aware provider, and nothing selected an active theme. Every gate was green and the console never changed. Clause-②: this PR removes previously-acceptedthemesdeclarations from the acceptance set (contract-review tier; not queued by the implementing seat).Removal shape (per the
spec-property-retirementplaybook)ObjectStackDefinitionSchemais astrictObject(An unknown top-level stack key is named but still not rejected — and the diagnostic is not a warning, soobjectstack validate --strictcannot catch it either (17.0.0 GA) #8687) and the stack root is not a walked def (verified: no:themesrow exists anywhere in the authorable-surface baseline), so the route is strict delete +guidanceentry, not aretiredKey()tombstone. The unknown-key rejection carries the prescription: removal citation (defineStack({ themes })is parsed, ingested and never applied — the last hop from a storedthemeitem to the theme engine does not exist #10485, ADR-0049), why it was inert, and theapp.branding.primaryColor/accentColorreplacement. No rename suggestion (finding-7 discipline).ui/theme.zod.tsdeleted whole —ThemeSchema,ColorPaletteSchema,TypographySchema,BorderRadiusSchema,ShadowSchema,ThemeModeSchema,defineTheme, and the inferred types. Orphaned value schemas leave with their one consumer (refactor(spec)!: remove the plugin sandboxing / integrity / approval config that never existed (#3896 follow-up) #3950).ui/Theme,ui/ThemeMode,ui/ColorPalette,ui/Typography,ui/BorderRadius,ui/Shadow) + the D3 semantic entrystack-themes-carrier-retired, registered under protocol 18 (launch-window convention — v17.0.0 was cut; theadditionalTypesonMetadataPluginConfigis authorable, documented as THE way a plugin declares a metadata type, and has no reader — a plugin cannot declare a kind at all #8586/[finding] TheApiKeyreference table documents better-auth's apiKey-plugin schema — a plugin this platform does not load and a shapesys_api_keydoes not have #8715 precedent). Semantic rather than a D2 conversion, on the D2 lossless-only scope guard: a stack may declare N themes and M apps, so which palette entry becomes which app'sbranding.primaryColoris a judgment a transform cannot make; auto-deleting the whole authored artifact would silently discard salvageable content. (Secondary, recorded in the entry file: a full-carrierstripKeysconversion also cannot coexist with the publishedtheme-inert-token-scales-removed(主题引擎发出的 9 组 CSS 变量零消费方(--font-size-*/--z-*/--duration-*…):ADR-0049 该判去留 #5021) fixture under the fixture-disjointness replay contract — that fixture necessarily authorsthemes.)themes: 'theme'fold leftPLURAL_TO_SINGULAR, so the regenerated URL-spelling contract dropsthemeandPUT /meta/theme/:namenow earns the meta-unknown-type-namespace: a/metatype name that is not a plural of anything still mints a namespace —PUT /meta/fieldz/xanswers 200 #8421 unrecognised-type refusal (ADR-0112 envelope, nothing stored) — not a regression to the pre-themeandanalytics_cubehave strict stack schemas but noUNREGISTERED_KIND_SCHEMASbinding —PUT /meta/theme/:namestill accepts any JSON (the two doors #6245 left open) #10194 store-anything branch. TheUNREGISTERED_KIND_SCHEMAS.themebinding is removed with it. Legacy storedthemerows: reads answer, DELETE works (stored-namespace exemption),applyConversionsToStoredItempasses them through unchanged.ARTIFACT_FIELD_TO_TYPE.themesrow removed (packages/metadata/src/plugin.ts).ThemeEngine/ThemeContextand their unit tests (objectui untouched);app.brandingend to end.Pin tests
stack-top-level-strict.test.ts— a stack declaringthemes(a body that parsed green on 17.0) is refused at the key withunrecognized_keysat the root path, message carrying#10485+app.branding+primaryColor, and no "Did you mean" rename; positive control: the prescribedapp.brandingreplacement parses green; no theme export survives on./ui(with anti-vacuity probe).protocol.unrecognised-meta-type.test.ts—PUT /meta/themerefused with{ code: 'INVALID_REQUEST', status: 400 }and zero rows stored; the ACCEPTED-side specimen for "URL-map-only plugin kind" movedtheme→webhook(fixture triage: the class survives, the specimen retired).metadata-url-spelling.test.ts—theme/themesrefused by name; the five surviving plugin kinds pinned by name.protocol.meta-types-mint-door-agreement.test.ts—themereclassified from class 2 (url-map-only, creatable) to class 3 (withdrawn: advertisedfalse, mint refused), withwebhooktaking the end-to-end write slot.Docs
content/docs/ui/theming.mdxnever shipped (withheld from PR #10483 per the correction on the issue) — nothing to delete or redirect. This PR makesapp.brandingthe one documented colour surface:widget-contract.mdx's Theme section now teachesapp.branding(and the still-true token facts — bare-HSL-triple values, base-stylesheet-owned shadcn token set — were salvaged intoreact-pages.mdx, the optional half of the correction). Getting-started/ui index prose scrubbed;references/ui/theme.mdxdeleted bygen:docs.skills/**touch — line readouts (clause-mandated)skills/objectstack-platform/SKILL.mdskills/objectstack-ui/references/_index.md(generated bygen:skill-refs)skills/objectstack-platform(all .md)skills/objectstack-ui(all .md)Net shrink; no expansion.⚠️
skills/**is a governed surface (Prime Directive #14): human merge only — do not queue, arm, or flip out of draft.File-surface note (declared expansion over the claim comment)
The claimed surface named stack.zod.ts, theme.zod.ts + ui barrel, the ingest row, ADR-0087 registry, changeset, pin tests and docs pointers. The retirement's measured consumer sweep additionally required:
shared/metadata-collection.zod.ts(map rows),kernel/metadata-type-schemas.ts(door binding),meta-spelling/*(contract prose + regenerated data),clistats/explain rows,examples/app-showcase(config + theme sources + coverage/tour prose),scripts/check-stack-collection-maps.mjs(waiver rows in both directions), fourmetadata-protocol/objectql/rest/runtimeprotocol-test retriages,qa/downstream-contractfixture row,type-alias-convention.pin.test.ts(Iso700–704 + count 839→834),PROTOCOL_MAP.md, the strictness-ledger row (converted to a retirement paragraph, thenotification.zod.tsprecedent),skills/objectstack-platform/SKILL.md, and the generated baselines (authorable-surface −57 rows / manifest −6 defs / api-surface / export-origins / spec-changes / upgrade-guide / strictness-ledger counts / meta-url-spelling / reference docs / skill refs). All are the same removal; no new verification surface.packages/spec/src/contracts/metadata-service.ts(H17 trigger of hold #8722) is not touched.Out of scope, filed
contributes.themesdeclares a theme-contribution channel nothing reads — same declared-but-unenforced shape as the retired stackthemescarrier #10627 — plugin-manifestcontributes.themesdeclares a theme-contribution channel nothing reads (separate declared surface, same ADR-0049 shape).ThemeComponentSchemadead surface — separate card per the ruling (objectui repo).Verification
Consumer-suite readouts below were measured at
2db934d61;origin/mainwas then merged (PRs #10643/#10644 —ui/i18n.zod.ts/data/object.zod.ts, verified disjoint from this diff:git diff origin/main...HEADon those two paths is empty) via thescripts/pm/os-regen-merge.shdiscipline (merge committed first, artifacts regenerated on the merged tree). Final union at head846b572aa(worktree clean):check:generated→✓ All 14 generated artifacts are up to dateand the full spec suite →Test Files 415 passed (415)·Tests 11049 passed (11049), plus the light-gate set re-run green at the same head (stack-collection-maps, changeset trio + adr-0087-registration, skills-token-ratchet, nul-bytes, merge-driver, affected-docs, cross-package-test-inputs). Each verdict line quoted is the gate's own printed verdict; exit codes captured before any pipe.Tests — every package whose tests/fixtures referenced
themes/ThemeSchema/defineTheme, full suites, direction: downstream consumers of@objectstack/spec(built from the rebuiltdist):@objectstack/specTest Files 415 passed (415)·Tests 11049 passed (11049)(final head)@objectstack/metadataTest Files 31 passed (31)·Tests 609 passed (609)@objectstack/metadata-protocolTest Files 127 passed, 2 skipped (129)·Tests 1741 passed, 10 skipped (1751)@objectstack/downstream-contractTest Files 2 passed (2)·Tests 19 passed (19)@objectstack/objectqlTest Files 225 passed (225)·Tests 3984 passed (3984)@objectstack/cliTest Files 139 passed (139)·Tests 1547 passed (1547)@objectstack/restTest Files 132 passed (132)@objectstack/runtimeTest Files 178 passed (178)Reverse verification (measured direction, not assumed): the consumer suites were run against the rebuilt spec
distBEFORE retriage —themesaves that previously pinnedsuccess: truefailed with the exact new verdict (Error: [invalid_request] 'theme' is not a metadata type …atrefuseUnmintableMetaType), i.e. the retirement reached consumers through the built contract, not through test edits; the pins were then retriaged to record the new contract (specimen swapstheme→webhook, plus new#10485refusal pins). Direction observed: new refusals appeared where acceptance was pinned — diagnostics increased, the expected direction for an accept-set narrowing.Typecheck:
@objectstack/spec(withcheck:test-typecheck✓ after removing the Iso700–704 pins),cli,objectql,rest,runtime,downstream-contract,@objectstack/example-showcase— alltsc --noEmitclean after building each dependency closure.Gates:
check:generatedgreen across all 14 artifacts (authorable-surface/manifest deletions self-proved:6 schema(s) left the published set … each declared (#4725),5 baseline deletion(s) … carry their own proof (#4650)) ·check:stack-collection-maps✓ (7 enumerations reconciled against 31 declared collections) ·check:variant-docs✓ (18 discriminated union(s) — 8 governed, 10 exempt) ·check:liveness✓ ·check:empty-state✓ ·check:strictness-ledger✓ (row → retirement paragraph; counts regenerated) ·check:doc-formula-expressions✓ ·check:durability-log-level✓ ·check:slot-lookup✓ ·check:spec-parsed-alias✓ ·check:test-source-alias✓ ·check:type-source-resolution✓ ·check:merge-driver✓ ·check:cross-package-test-inputs✓ ·docs-audit/check-affected-docs✓ ·check:nul-bytes✓ · changeset trio (no-major,empty,adr-0087-registration) ✓ ·check:skills-token-ratchet✓ ·check:engine-double-contract✓ ·check:where-matcher✓ ·check:query-options-erasure✓ ·check:type-check-coverage✓ ·check:type-check-debt✓ after full workspace build (lock VERDICTcommand-exit 0).Gate list re-derived from the actual diff with
node scripts/pm/dispatch-gates.mjs(no paths — merge-base derivation); it added the changeset trio,check:skills-token-ratchet,check-affected-docs, and the test-convention family (query-options-erasure,type-check-coverage,type-check-debt,engine-double-contract,where-matcher) beyond the dispatch list — all run, readouts above.Generated by Claude Code
Generated by Claude Code