|
| 1 | +--- |
| 2 | +"@objectstack/spec": minor |
| 3 | +--- |
| 4 | + |
| 5 | +feat(spec): retire the `themes` carrier key and `ThemeSchema` — the authoring surface nothing ever applied (#10485, ADR-0049) |
| 6 | + |
| 7 | +**BREAKING** accept-set narrowing, landing after the v17.0.0 cut (the lockstep |
| 8 | +launch-window convention ships it as `minor`; the migration prescription is |
| 9 | +registered under protocol major 18, where `os migrate meta` users will look). |
| 10 | +Maintainer ruling 2026-08-21, recorded verbatim on #10485: 「B:退役授权面 — |
| 11 | +收掉 `themes` 载体键与 schema,`app.branding` 留作唯一颜色面;objectui 引擎代码 |
| 12 | +与单测保留。」 |
| 13 | + |
| 14 | +`defineStack({ themes })` was a real authoring surface — parsed strictly at the |
| 15 | +authoring gate, ingested and stored by artifact ingest |
| 16 | +(`ARTIFACT_FIELD_TO_TYPE`) — with ZERO consumers past that point, measured: |
| 17 | +no non-test read of `.themes` or of stored `theme` items anywhere in |
| 18 | +core/runtime/rest/services/plugins; `theme` never in `MetadataTypeSchema`, |
| 19 | +`DEFAULT_METADATA_TYPE_REGISTRY` or `BUILTIN_METADATA_TYPE_SCHEMAS`; the only |
| 20 | +mounted `ThemeProvider` is the app-shell chrome light/dark toggle (unrelated to |
| 21 | +`ThemeSchema`); and no stack- or app-level key ever selected an active theme. |
| 22 | +An author who wrote a theme shipped it through every green gate and the console |
| 23 | +looked exactly the same. |
| 24 | + |
| 25 | +**What is refused:** the top-level `themes:` key. `ObjectStackDefinitionSchema` |
| 26 | +is a `strictObject`, so the key is deleted from the shape and the unknown-key |
| 27 | +rejection carries the retirement prescription via the schema's `guidance` entry |
| 28 | +(removal citation, why it was inert, and the `app.branding` replacement). |
| 29 | +`ThemeSchema`, `ColorPaletteSchema`, `TypographySchema`, `BorderRadiusSchema`, |
| 30 | +`ShadowSchema`, `ThemeModeSchema`, `defineTheme` and the `Theme` / |
| 31 | +`ThemeParsed` / `ColorPalette` / `Typography` / `BorderRadius` / `Shadow` / |
| 32 | +`ThemeMode` types are removed from `@objectstack/spec` / `@objectstack/spec/ui` |
| 33 | +(orphaned value schemas leave with their one consumer, #3950). `PUT |
| 34 | +/api/v1/meta/theme/:name` now gets the #8421 unrecognised-type refusal — the |
| 35 | +`themes: 'theme'` fold left `PLURAL_TO_SINGULAR` and with it the generated |
| 36 | +URL-spelling contract — instead of the pre-#10194 store-anything branch. |
| 37 | + |
| 38 | +**What stays:** `app.branding.primaryColor` / `accentColor` — the one live |
| 39 | +colour surface (objectui's `AppShell` reads it and derives `--primary`, |
| 40 | +`--accent` and friends) — plus objectui's `ThemeEngine` / `ThemeContext` engine |
| 41 | +code and their unit tests, explicitly retained by the ruling. Legacy stored |
| 42 | +`theme` rows are untouched: reads still answer, DELETE still works, and |
| 43 | +`applyConversionsToStoredItem` passes them through unchanged. |
| 44 | + |
| 45 | +The retirement kit: |
| 46 | + |
| 47 | +- strict deletion + `guidance` prescription at the stack schema |
| 48 | + (`packages/spec/src/stack.zod.ts`); `packages/spec/src/ui/theme.zod.ts` |
| 49 | + deleted whole |
| 50 | +- ADR-0087 registration: retired-def entries `ui/Theme`, `ui/ThemeMode`, |
| 51 | + `ui/ColorPalette`, `ui/Typography`, `ui/BorderRadius`, `ui/Shadow` and the |
| 52 | + D3 **semantic** entry `stack-themes-carrier-retired` (protocol 18). Semantic |
| 53 | + rather than a D2 conversion on the lossless-only scope guard: a stack may |
| 54 | + declare N themes and M apps, so which palette entry becomes which app's |
| 55 | + `branding.primaryColor` is a judgment the transform cannot make — the entry |
| 56 | + prescribes the hand move instead of auto-deleting authored content |
| 57 | +- ingest mapping removed (`packages/metadata/src/plugin.ts`), CLI stats row |
| 58 | + removed, showcase example re-based on app branding |
| 59 | +- pin tests: `stack-top-level-strict.test.ts` (refusal carries `#10485` + |
| 60 | + `app.branding` + no rename suggestion; replacement parses green; no theme |
| 61 | + export survives on `./ui`) and `protocol.unrecognised-meta-type.test.ts` |
| 62 | + (`/meta/theme` refused with the ADR-0112 envelope, nothing stored) |
| 63 | +- generated baselines/docs follow the schema (`authorable-surface/`, |
| 64 | + `json-schema.manifest/`, api-surface, export-origins, meta-url-spelling, |
| 65 | + spec-changes, upgrade guide, reference docs, skill references) |
| 66 | + |
| 67 | +## FROM → TO |
| 68 | + |
| 69 | +```ts |
| 70 | +// before — parsed green, stored by artifact ingest, applied by NOTHING: |
| 71 | +defineStack({ |
| 72 | + themes: [{ name: 'corporate', label: 'Corporate', mode: 'light', |
| 73 | + colors: { primary: '#7C3AED' } }], |
| 74 | +}); |
| 75 | + |
| 76 | +// after — delete the key; colour the console where something reads it: |
| 77 | +defineApp({ |
| 78 | + name: 'my_app', |
| 79 | + label: 'My App', |
| 80 | + branding: { primaryColor: '#7C3AED', accentColor: '#06B6D4' }, |
| 81 | +}); |
| 82 | +// a custom CSS variable your own stylesheet consumed has no spec slot any |
| 83 | +// more — move it into your own CSS. |
| 84 | +``` |
| 85 | + |
| 86 | +<!-- adr-0087: registered stack-themes-carrier-retired --> |
0 commit comments