Closes the recipe→theme direction of the soma→eidos CSS-var drift stumble. New
check in recipe-css-contract.test.ts: every NO-FALLBACK `var(--x)` in a
component recipe must resolve to a token declared somewhere in the eidos CSS
tree (foundation + shared layers + the component's own self-declarations). A
`var(--x, default)` stays exempt — those are runtime-optional by construction
(provider/floating vars whose fallback IS the default), matching the existing
`collectOwnPublicVariables` rule.
This is the exact class that silently broke the Knob (`--color-neutral-content`,
`--state-hover`, `--color-neutral-bg` resolved to nothing, only visible in the
render). The guard immediately caught 3 MORE real phantoms — fixed, not silenced
(silencing would be the anti-pattern of a guard exempting the gap it exists to
catch):
- card-group: `--color-content-default` → `--color-content-primary`
- link-preview: `--leading-body` → `--leading-normal`
- textarea: `--font-family-body` → `--font-family-primary`
Full eidos suite 290/290. The provider→recipe direction (renaming a
provider-written `--knob-angle` breaking the recipe) remains open as option A
(morfo `cssVars` declaration or a grep cross-check) — noted in STUMBLES.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El Knob del ejercicio **ya está construido de verdad** en las 5 capas
El Knob del ejercicio **ya está construido de verdad** en las 5 capas
(morfo/langs/sema/soma/eidos, `c13cad74`) con demo (`8dfd3505`) — lo que valida
(morfo/langs/sema/soma/eidos, `c13cad74`) con demo (`8dfd3505`) — lo que valida
el ejercicio: 8 de los 9 tropiezos se cerraron; solo queda **#7** (contrato
el ejercicio: 8 de 9 cerrados; de **#7** (contrato CSS-vars soma→eidos) se cerró
CSS-vars soma→eidos), que es diseño abierto.
la dirección recipe→tema con un guard (opción C, cazó 3 fantasmas reales), y
queda solo el sub-contrato provider→recipe (opción A, opcional).
| # | Tropiezo | Estado | Cómo |
| # | Tropiezo | Estado | Cómo |
|---|---|---|---|
|---|---|---|---|
@ -20,7 +21,7 @@ CSS-vars soma→eidos), que es diseño abierto.
| 4 | Doctrina `trigger()` continuo sin cerrar | ✅ RESUELTO | sección `## Continuous components` en `sema.md` (`1da36ca6`), fact-check adversarial |
| 4 | Doctrina `trigger()` continuo sin cerrar | ✅ RESUELTO | sección `## Continuous components` en `sema.md` (`1da36ca6`), fact-check adversarial |
| 5 | Falta condición `part-absent` | ✅ RESUELTO | `{ when: 'part-absent', part }` en morfo (`11504043`); el morfo del Knob la usa |
| 5 | Falta condición `part-absent` | ✅ RESUELTO | `{ when: 'part-absent', part }` en morfo (`11504043`); el morfo del Knob la usa |
| 6 | Formato de `langs/components/{kebab}.ts` | ✅ RESUELTO | forma `LangNode` (`{ key: { es, en } }`, anidable, `satisfies`) documentada en morfo/soma/checklist |
| 6 | Formato de `langs/components/{kebab}.ts` | ✅ RESUELTO | forma `LangNode` (`{ key: { es, en } }`, anidable, `satisfies`) documentada en morfo/soma/checklist |
| 7 | CSS-vars del provider sin contrato | ⏳ PENDIENTE | **diseño abierto** — ver abajo |
| 7 | CSS-vars del provider sin contrato | 🟡 CASI | dirección **recipe→tema** guardada (opción C: guard de fantasmas de tema en `recipe-css-contract` — pilló 3 bugs reales); queda solo el sub-contrato **provider→recipe** (opción A, opcional) |
| 8 | Docs imprescindibles fuera del paquete | ✅ RESUELTO | `component-audit.md §0` lista el paquete mínimo como archivos exactos |
| 8 | Docs imprescindibles fuera del paquete | ✅ RESUELTO | `component-audit.md §0` lista el paquete mínimo como archivos exactos |
| 9 | Fricciones menores (docs) | ✅ RESUELTO | `### Authoring notes` en `soma.md` §6: `state<T>()` vs `$state`, `role` opcional en Provider, `Without<>`/`PrimitiveDivAttributes`, ownership de pointermove/up del gesture |
| 9 | Fricciones menores (docs) | ✅ RESUELTO | `### Authoring notes` en `soma.md` §6: `state<T>()` vs `$state`, `role` opcional en Provider, `Without<>`/`PrimitiveDivAttributes`, ownership de pointermove/up del gesture |
@ -118,6 +119,17 @@ selectors vía `style` no, pero soma+eidos ya son 2).
> en el morfo validada contra escrituras del provider + lecturas del recipe, o
> en el morfo validada contra escrituras del provider + lecturas del recipe, o
> un cross-check de `eidos-lint` de vars escritas-por-provider vs consumidas.
> un cross-check de `eidos-lint` de vars escritas-por-provider vs consumidas.
> Decidir antes de implementar.
> Decidir antes de implementar.
>
> **Cerrada la dirección recipe→tema (opción C, 2026-07-03).** Guard nuevo en
> `recipe-css-contract.test.ts`: toda `var(--x)`**sin fallback** de un recipe
> debe resolver a un token declarado en algún sitio del árbol CSS de eidos
> (foundation + capas + auto-declaraciones del componente); las `var(--x,
> default)` son runtime-opcionales (vars provider/floating con default) y quedan
> exentas por construcción. El guard cazó **3 fantasmas reales** además de los
> del Knob — `card-group --color-content-default`→`-primary`, `link-preview