46 KiB
CONTINUE — eje Theming «theme-reach» (handoff, act. 2026-08-21)
Estado: EN EJECUCIÓN — vocabulario CERRADO, bloque F2-A CERRADO y con
REVISIÓN ADVERSARIAL PASADA (2026-08-21: 34 hipótesis · 26 refutadas · 8
reales, las de código arregladas con 0 diffs). El guard R-5.4 existe:
npm run theming:sentinel -- <c> <url> + ledger
scripts/theming-sentinel-exceptions.ts (doctrina en recipe-contract §4).
Alcance global 43 % (era 33 % al abrir el eje, 37 % al empezar el día 20).
15 componentes con contrato, 49 sin él, 7 al 100 %.
LO PRIMERO AL ENTRAR (cinco minutos, en este orden)
-
Comprueba que el suelo no se ha movido:
node --import tsx/esm scripts/theming-census.ts # 162 · 5.190 · 2.102 (43 %) · 49 sin contrato node --import tsx/esm scripts/theming-census.ts --names # DESVIADAS 0 — la gramática se mantieneSi el primero no cuadra, alguien tocó recetas: regenera el informe (
--report) antes de nada. Si el segundo no da 0 desviadas, alguien escribió un nombre fuera de la gramática y R-5.3 debería haberlo cazado — mira por qué no. -
Arranca el dev server ANTES de medir nada — sin él las sondas no valen:
preview_startcon la configdevde.claude/launch.json(puerto 5180). Las sondas se ejecutan connode, no contsx, desde la raíz. -
Lee el veredicto §5 de la ficha del componente que toques (
docs/audit/theming/{c}.md). La §4 generada NO se sigue a ciegas: en este bloque, tres veredictos de ocho tenían un error de lectura que sólo apareció al medir (ver «Lo que este bloque enseñó»).
QUÉ HACER AHORA — dos opciones, y la recomendación
A · Revisión adversarial del bloque F2-A — HECHA (2026-08-21). Molde
Sidebar: worktree con SOLO el CSS revertido a la base del bloque (DOM de demo
constante — prueba más dura que la del autor), sonda ×2 para el suelo de
ruido, 0 diffs en los ocho. Lo que sobrevivió y lo que no, en el registro de
abajo; los 4 hallazgos de código quedaron arreglados (dos retiradas de
declaración muerta, un literal tokenizado, y el ledger del guard). Los que
mueven píxel esperan firma (ver «PENDIENTE DE FIRMA»).
B · Diseño de la capa calendar-surface (firma 1 del acta). Lo más
rentable en cifras: desbloquea range-calendar (98 knobs) + month-grid (61)
year-grid(61) + mediadate-range-picker— unos 220 knobs, el salto de 43 % a ~48 %. El diseño se PRESENTA al autor antes de escribir una línea (nombre, ejes, qué posee la capa; moldelib/list-surface.cssy sus cuatro reglas). No es un backfill más: es una capa nueva.
C · Seguir con la cola por alcance, que tras F2-A queda así:
| componente | knobs | nota |
|---|---|---|
field-langs |
41 | espera el mandato Field (firma 2) para la mitad |
grid-list |
32 | mismo patrón que tree-grid/table (fila + capa de estado) |
textarea |
28 | familia campo |
tree-view |
28 | hermano de tree-grid; su ficha ya avisa del forward de paleta |
code-block |
26 | 24 globales, cero privados — el más mecánico de los que quedan |
card-group |
24 | |
spinner |
23 | 15 privados |
picker-shell |
21 | es CAPA de los pickers: mirar antes qué posee y qué presta |
EL PROTOCOLO, EN CORTO (el largo está en PLAN-theming.md §7)
Un componente = un commit. Por componente:
- Leer entero: receta, su bloque en
recipes/base.ts, README de eidos, morfo, y el veredicto §5 de su ficha. - Sonda ANTES (
__theming-probe.ts {c} antes.json <url>). - Escribir el contrato en
recipes/base.ts— fusionando en el bloque existente si ya lo hay (los forwards de paleta THM-2 ya ocupan bloque en muchos componentes; un segundo bloque con el mismo nombre se descarta EN SILENCIO). - La receta consume los nombres resueltos; retirar los bloques
[data-size]del CSS — los emite el TSC, y dejarlos vivos hace que el diff dé 0 por la ruta vieja. npm run generate:eidos-css, sonda DESPUÉS, diff = 0.- Guard R-5.4 (
npm run theming:sentinel -- {c} <url>) y adjudicar cada «no effect» uno a uno EN EL LEDGER (scripts/theming-sentinel-exceptions.ts, una razón medida por token) — el guard falla con un muerto sin adjudicar, y avisa de excepciones STALE. - Guards: censo
--only {c}·component-audit --only {c}·eidos-lint {c}·vitest run src/uix/eidos(comparar POR FICHERO: el rojo conocido esskin-media-player) ·rtl:check·docs:check·npm run checkatribuido POR FICHERO (73 errores globales son de otras sesiones). - README del componente con su sección «Talla y tema».
- Tab
Tokensen la demo — una línea:<TokensPanel component="x" stage={stageRef ?? undefined} />+ su botón. - Commit con las cifras y los artefactos en el mensaje.
LO QUE ESTE BLOQUE ENSEÑÓ (léelo antes de tocar nada)
Sobre los veredictos. Tres de ocho tenían un error de lectura que sólo
apareció midiendo: el título de feed escala por aria-level, no por
talla; --gp-current-gradient lo estampa soma, no el wrapper de eidos; y
el trigger de gradient-picker no había que tokenizarlo sino borrarlo. El
veredicto orienta; la medición decide.
Un token que no mueve nada es un token que miente. Ocurrió tres veces:
table.selected-row-fg y la tinta de listbox (el arquetipo las gana por
especificidad) y el cromo ENTERO del trigger de gradient-picker (36 de 45
tokens sin efecto — lo pinta popover.css, que gana 0,2,0 contra 0,1,0). En
los tres casos la acción correcta fue retirar la declaración muerta, y en
los tres retirarla dio 0 diffs, que es la prueba de que estaba muerta.
Un alias puro no se renombra: se borra. Los dieciséis --_mp-* de
media-player eran alias de su propia fuente; el veredicto pedía renombrarlos y
lo que procedía era eliminar la capa entera.
Lo que la capa posee, el consumidor no lo acuña. listbox se queda en 68 %
a propósito: su ritmo de fila es de list-surface. La métrica penaliza hacer lo
correcto — registrado en next-features.md §13.
Antes de acuñar un hover-*, mide contra la capa de estado. En listbox el
highlighted pinta DOS veces (plano + velo); se dejó sin token para no
bendecir un duplicado condenado.
La sonda miente de tres maneras distintas, y todas costaron tiempo aquí:
congela transition pero nunca animation; el estado hover se mide
después de forzar tallas, así que arrastra la última; y un empate de
especificidad resuelto por orden de carga hace que tree-grid dé 0 ó 3
diffs según la recarga. Ante un diff inesperado, lo primero es correr la
sonda dos veces sobre el MISMO código. La revisión adversarial encontró y
arregló la causa del caso feed (2026-08-21): no era una animación sin
localizar sino la demo aún CARGANDO — el auto load-more mantiene [data-busy]
~3 s y la firma sema commit-settle anima box-shadow sobre el nodo medido.
Sonda y guard esperan ahora a que [data-busy] caiga; con eso, dos corridas
del mismo código dan 0 diffs. Y el «falso negativo sin causa» de
command.input-border también tenía causa: el centinela ABRÍA haciendo clic
en el input, y un clic de ratón sobre un input de texto SÍ casa
:focus-visible — la regla de foco pintaba el borde y el token de reposo
leía muerto. El instrumento viejo acumulaba 22 falsos negativos de 26;
las tres causas están arregladas en theming-sentinel.ts.
LAS INCIDENCIAS NO SE ARREGLAN AQUÍ
Todo lo que este eje destapa y no le toca arreglar está en
docs/next-features.md §12 (el contrato de cascada
del velo de estado: 131 declaraciones-atajo en 45 componentes, la prop
hoverable que no suprime nada, el empate por orden de carga, la banda que
mata el hover, el token de tinta que no puede ganar al arquetipo, el thumb de
scroll-area sin velo) y §13 (huecos de instrumento y de demo, más los
cuatro que salieron de gradient-picker y listbox).
Regla: si una incidencia mueve píxel o toca morfo, se MIDE, se anota ahí y se sigue. No se arregla dentro del commit del componente.
PENDIENTE DE FIRMA (nada bloquea la cola)
- Diseño de
calendar-surface— la firma 1 aprobó el PATRÓN, no un diseño concreto; ese se presenta. scroll-area.thumb-bg-hover— único knob de la firma 3 sin resolver: su thumb llevaarchetype: 'thumb', que no recibe velo. Cambiarlo es morfo.- Extender la clase
systemdel censo a las capas compartidas, como se hizo con--style-*para los primitivos tipográficos (§13 del registro). El— FIRMADO Y EJECUTADO 2026-08-21:stripeddetableinerte con RowDetail:nth-child(even of [data-table-row])con:where(:not([data-selected])), mismo (0,6,0) para no matar el hover. 12 diffs exactos (2 filas × 6 estados); selected y hover verificados.- El arquetipo
itemdetablevive en la fila Y en la celda — el velo cae en la celda bajo el puntero (elementsFromPoint, 2026-08-21). La decisión de morfo delhoverable(§12.2) debe contemplar los dos portadores. - Que soma lea de un token el gap entre diapositivas de
carousel— hoy es canal de valor inline y el tríoitem-gap*se retiró muerto; darle superficie de tema toca soma (§13).
REGISTRO DE LA SESIÓN DEL 2026-08-20/21 (histórico — no es la agenda)
EJECUTADO 2026-08-21 — revisión adversarial de F2-A (§7.7) + guard R-5.4
Molde Sidebar, medición propia contra worktree con solo el CSS revertido a la
base del bloque. 34 hipótesis · 26 refutadas · 8 reales. Refutado: el
«default idéntico» aguanta en los ocho (0 diffs; los 3 de tree-grid y los 5 de
feed son del instrumento, idénticos corriendo dos veces el mismo código), 0
tokens huérfanos de 336, el bundle ES 1:1 con los primitivos, el «verificado a
mano» de gradient-picker era verdad. Los 8 reales: (1) el re-point de
--command-radius en el Dialog era declaración muerta con comentario falso —
retirado, 0 diffs; (2) el 2.25 vertical del carrusel iba a pelo y
active-indicator-scale solo alcanzaba la horizontal — tokenizado, 0 diffs;
(3) el trío item-gap* de carousel no podía ganar al gap INLINE de soma
— retirado, 0 diffs (canal de valor, como --gp-current-gradient); (4) el
striped de table es inerte con RowDetail (parity de :nth-of-type) —
PENDIENTE DE FIRMA; (5-7) el instrumento tenía 22 falsos negativos de 26
(clic-foco en el input · override solo en [data-{c}] · pseudos invisibles) y
la no-determinación de feed era la demo cargando (commit-settle sobre
[data-busy]) — las cuatro causas arregladas en theming-sentinel.ts y la
sonda; (8) tres cifras de especificidad del registro §12 estaban desviadas
en uno y el arquetipo de la fila de table vive TAMBIÉN en la celda —
corregidas. R-5.4 nace: npm run theming:sentinel -- <c> <url>, ledger
theming-sentinel-exceptions.ts con una razón medida por token (46
excepciones en los ocho, cinco entradas mías salieron STALE y las borró el
propio guard), doctrina en recipe-contract §4. Dos hallazgos los produjeron
los propios arreglos: el blur apaga focus-input-border (adjudicado) y la
espera de asentado desmonta el spinner de feed (adjudicado). Censo tras la
retirada: carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 %
intacto.
Lo que sigue es el detalle de cómo se llegó aquí: las firmas, el codemod, el guard, y el bloque componente a componente. Se conserva porque cada decisión lleva su porqué medido, pero la agenda es lo de arriba.
EJECUTADO 2026-08-20 — vocabulario de tokens: D-TH.6 (codemod) + R-5.3 (guard)
Por qué antes que nada (conversación 2026-08-20): el canon de nombres
existe (theming §6.7 slots + recipe-contract §1 dimensionales) pero no tiene
guard, y la medición del 2026-07-01 ya demostró que canon sin guard deriva
30-85 %. Está derivando: el backfill de ayer escribió la forma DOCUMENTADA
(modificador delante, 36 claves) mientras el catálogo habla al revés — cada
componente que se corrija sin firmar esto ensancha la brecha. El guard de
eventos (eidos-event-vocabulary.ts) es el precedente: nació de un
commit-resize muerto tres meses en una receta; R-4.4 es el otro (una regla de
nombres ya guardada, deuda muerta «en el mismo pass, sin allowlist»).
FIRMADO 2026-08-20 — el slot de tinta es fg; color como slot MUERE.
La sesión del 20 leyó lo que la del 19 no había leído (arts/color,
architecture/eidos.md entero, motion §17, §25/§38) y encontró que la
pregunta central no era «normalizar hacia lo documentado» sino una
CONTRADICCIÓN entre dos doctrinas: theming §6.7 r7 (fg) contra el principio
de plataforma del px/py («el token se llama como la propiedad»: color). El
autor adjudicó fg: el principio de plataforma gobierna los ejes
DIMENSIONALES, no la pareja bg/fg (si la gobernara, bg sería
background-color); color es la palabra más sobrecargada del sistema
(--color-*, data-color, 9 roles, arts/color); on-fg compone; Chakra
v3/Panda hablan la pareja.
Inventario CORREGIDO 2026-08-20 (instrumental: los scripts/__names-*.ts
de esta sesión — census, slots, owners, audit7, audit7b; el exacto y
regenerable lo produce --names en el paso 1). Sobre 3.333 claves públicas,
301 desviadas en 66 componentes — 193 con color como slot, 78 con el
modificador DETRÁS a nivel de receta, 30 con ambas. La semilla
__names-inventory.ts decía 370 porque contaba como desviadas 47 claves
{rol}-{slot-de-rol} que son CANÓNICAS (button.primary-solid-hover,
toggle.palette-hover… — COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN) — un codemod sobre las 370 las habría roto. Adopción viva:
196 -color contra 55 -fg (las fg son mayormente las del backfill de este
eje). Peores: tag-group 22 · toast 15 · field 13 · calendar 13 · tabs 12.
Tres familias, tres gramáticas (el guard codifica las tres): rol/paleta =
modificador DETRÁS (canon) · sistema (--state-hover, --opacity-hover) =
detrás, fuera del alcance del guard · recipe-level = §6.7 r7 (delante:
hover-bg, 13 claves conformes contra 22 bg-hover).
Falsos amigos (22) — FIRMADOS en el acta: *-focus-ring-color (3, espeja
la familia del SISTEMA) · aura.orb-color-* (6, color como sustantivo del
orbe) · gradient-builder.stop-color-* (4, stop-color ES la parte —
verificado: gradient-builder-stop-color.svelte + parts DOM) → se quedan ·
los 7 con modificador «fuera de vocabulario» resultaron ser DOS clases que el
morfo ya resuelve (valores de data-state/ejes declarados: active, partial,
read, failed; y current = aria, que entra al vocabulario universal) más
una de pseudo-partes (played/buffered) → se renombran (destinos en el
acta) · background.scrim-color-on-{dark,light} (2, chocaban con el prefijo
on-) → scrim-fg-over-{dark,light}.
Hovers neutros — firmado: NO se renombran, MIGRAN: §38 + R-4.3 deprecan el
hover neutro por componente (es la capa --state-*). dropdown-menu.item-bg-hover,
context-menu.item-bg-hover, scroll-area.track/thumb-bg-hover,
splitter.handle-bg-hover… la firma 3 del acta ordena su migración a la capa
de estado (captura + diff explicado por componente). El codemod los EXCLUYE y
el --names los lista aparte como cola de esa migración.
LOS CUATRO PASOS ESTÁN HECHOS (f09e04fab acta · bd916ef3f codemod ·
7781d6e4c guard + doctrina, sin pushear). Lo que queda del bloque, y sólo
esto:
- El tercer muro espera a la firma 3. El tipo en
defineRecipes(moldePhysicalAxisKey) está escrito y probado: dispara sobre 17 claves, que son los hovers neutros concoloren cola de migración. Meterlo hoy rompenpm run checka todo el mundo. Entra cuando la migración las vacíe — dos líneas: una ramaColorSlotKeyy las tres excepciones firmadas (*focus-ring-color,orb-color-*,stop-color-*) deletreadas. __names-inventory.tsquedó superado portheming-census --names(contaba 370 porque metía las 47 role-slot). No se borra sin que lo digas.
El registro de los pasos, para quien audite después:
Los pasos, en orden y con su gate (el paso 0 murió el 2026-08-20: D-TH.6
está firmada COMPLETA — ver §«Firmas del 2026-08-20»; la gramática ejecutable
es: slot fg · delante lo interactivo, detrás lo dimensional/contextual ·
modificador ∈ universal (+current) ∪ data-state/ejes del morfo · rol/paleta
detrás · hovers neutros excluidos · renombres nominales de los ex-falsos
amigos en el acta):
--namesen el censo: el inventario exacto por clase de desviación, regenerable, con las 47 role-slot reconocidas como canónicas, los falsos amigos resueltos por el acta (los 3 grupos que se quedan excluidos; los 4 renombrados contados como desviación con su destino) y los hovers neutros listados APARTE (van a la firma 3, no al codemod). Muta-prueba del instrumento: 3 desviadas conocidas salen, 3 role-slot NO salen.- Codemod value-preserving (molde: px/py del 2026-07-06 y su triple muro):
-color→-fg(196 + 49 mediales, menos los 3 grupos que el acta deja quedarse y los hovers neutros que migran aparte), los renombres nominales del acta (rating-group · chat-message · breadcrumb · waveform ·scrim-fg-over-*), y modificador detrás → delante a nivel receta (bg-hover→hover-bg, 22 claves); renombra la clave enrecipes/base.tsY todos los consumidores (var(--{c}-{clave})en css/svelte/ts/md — congit ls-files, no de memoria; los .md de HISTORIA — changelog, errores-toxico, PLAN-affix/background — NO se reescriben), regenera, y verifica: diff degenerated/= renombres 1:1, censo global IDÉNTICO (un rename no cambia alcance), sonda de computed en 3 componentes renombrados = 0 diffs. Un commit, script commiteado como registro. - Guard R-5.3 en
component-audit— de GRAMÁTICA, no de lista (a diferencia del de eventos, que valida pertenencia a un vocabulario cerrado), y POR FAMILIA:{rol|palette}-{slot-de-rol}con modificador detrás (COLOR_ROLE_SLOTS) ∪ recipe-level[variante-]?[modificador-]?[parte-]?[slot][-talla]?donde talla ∈ las 7 canónicas, modificador ∈ vocabulario cerrado y DELANTE, slot ∈ (§6.7 recipe-level ∪ ejes dimensionales de recipe-contract §1), y la PARTE contracompileMorfo(morfo).parts+ la allowlist eidos-only (el mismo criterio queeidos-lint). Severidaderrordirecto: tras el codemod no hay deuda que tolerar, y la rampawarnes como estos guards mueren. Muta-prueba obligatoria de tres caras antes de darlo por bueno:-bg-hoverrojo ·trigger-colorrojo ·primary-solid-hoverVERDE (memoriaa-guard-that-inspects-nothing-passes). - Docs en el mismo pass: recipe-contract §1 gana la fila del modificador
(hoy sólo la insinúa §6.7) y §4 la fila R-5.3; theming §6.7 nota fechada
con la firma de
fgy el acotamiento del principio de plataforma a los ejes dimensionales; T-TH.2 del plan se cierra.
Gates del bloque entero: censo global idéntico antes/después del codemod ·
suite eidos sin rojos nuevos · component:audit sin fallos nuevos fuera de
R-5.3 · docs:check 0 · rtl:check 0.
Después — por dónde seguir
node --import tsx/esm scripts/theming-census.ts— debe dar 162 recetas · 5.203 knobs · 1.841 públicos (37 %) · 56 sin contrato (antes del codemod; tras él, el MISMO alcance con nombres nuevos). Si no cuadra, alguien tocó recetas: regenerar el informe (--report) antes de nada.- Abrir la ficha del componente que toque y leer su §5 Veredicto: dice qué
de la propuesta es correcto, qué está mal y qué espera firma. Está verificado
contra el CSS; la §4 generada NO se sigue a ciegas. Ojo: los veredictos
se escribieron ANTES del acta del 2026-08-20 — sus «espera firma» se cruzan
con §«Firmas del 2026-08-20» (la mayoría ya están resueltos: calendar,
Field, hover→estado, D-TH.2) y sus nombres propuestos se pasan por la
gramática firmada (p. ej. el
guide-colorde la fila tree-grid de la cola esguide-fg). - Ejecutar con el protocolo §7 entero. Un componente = un commit.
PRIMERA POSICIÓN — repaso de los 7 ya corregidos contra la gramática nueva
Encargo del autor (2026-08-20): revisar lo ya ejecutado por si incumple las
reglas nuevas. Auditados los 7 con scripts/__names-audit7.ts y
__names-audit7b.ts (claves públicas + privados + consumo en css/README).
Resultado: 6 de 7 limpios, 1 hallazgo real.
| componente | claves | veredicto |
|---|---|---|
combobox |
106 | ✅ limpio — ya habla fg, sin modificador detrás, privados sanos |
natural-time-picker |
62 | ✅ limpio |
date-range-picker |
24 | ✅ limpio |
time-range-picker |
22 | ✅ limpio |
proof-of-human |
20 | ✅ limpio |
time-picker |
16 | ✅ limpio |
gradient-builder |
72 | ⚠ 1 corrección (abajo) |
Que los 6 salgan limpios NO es casualidad ni suerte: el backfill de este eje
venía escribiendo fg (55 de las ~55 claves -fg del catálogo son suyas),
que es exactamente lo que la firma ratificó. La firma valida el trabajo
hecho, no lo invalida.
La corrección — gradient-builder.checker-color → checker-fg (1 clave,
consumida 4 veces en gradient-builder.css:29-32). Es el color de los cuadros
del damero de transparencia: color ahí es slot, no sustantivo — cae de
lleno en la regla firmada. Las otras 4 claves que el audit marca
(stop-color-title-font-size, -font-weight, -fg, stop-color-sliders-gap)
son falso amigo verificado: stop-color es la PARTE
(gradient-builder-stop-color.svelte, [data-gradient-builder-stop-color-*]),
y una de ellas ya termina en -fg correctamente. No se tocan.
Ejecución: va DENTRO del codemod del paso 2 (misma mecánica, mismos gates), no como commit aparte — es una clave, y sacarla del codemod duplicaría la verificación. Si el codemod se retrasa por las firmas pendientes, se ejecuta sola con el protocolo §7.
BLOQUE F2-A — CERRADO 2026-08-21 (preparado el 20, ejecutado el 21)
Los ocho, con su commit y su alcance final:
| componente | antes → después | commit |
|---|---|---|
| pieza 0 (censo) | 37 % → 38 % | bd032534b |
command |
0 % → 98 % | 4ddea7fe2 |
table |
0 % → 86 % | 564133b48 |
media-player |
0 % → 88 % | b7a4cef08 |
tree-grid |
0 % → 95 % | 9748733ad |
gradient-picker |
0 % → 88 % | d177ee331 |
listbox |
0 % → 68 % | 3b325c439 |
carousel |
0 % → 77 % | 4cab77667 |
feed |
0 % → 94 % | 34a95a550 |
Global 37 % → 43 %; sin contrato 56 → 49; al 100 % 6 → 7. Más
90aa6dc5f (el TokensPanel que las demos enseñan) y 8324e2ced (el registro
de incidencias).
El plan del bloque, tal como se preparó:
El primer bloque del backfill bajo TODAS las firmas. Ocho componentes con veredicto §5 verificado + una pieza de instrumento que va primero. Suma 330 knobs hoy a 0 %; al cerrar, el global debe rondar 37 % → ~42 %. Cada componente: protocolo §7 entero, un componente = un commit, con la gramática firmada (¡el guard R-5.3 y el muro de tipos ya vigilan!).
Pieza 0 — el instrumento ANTES del bloque (D-TH.2-b, firmada). La familia
tipográfica B5 mide su superficie por --style-* como sistema transversal: el
censo añade --style-* a la clase system PARA los primitivos tipográficos
(heading, text, display, code, s-text…), con gate: las cifras de TODOS los
demás componentes quedan idénticas, y heading pasa de «36 privados» a su
verdad (consume la capa semántica). Va primero porque cambia el suelo del
censo — hacerla a mitad de bloque falsearía los antes/después.
| # | componente | knobs | defectos medidos (censo + ficha) | corrección firmada por el veredicto |
|---|---|---|---|---|
| 1 ✅ | command |
52 | SIN bloque en base.ts; 35 globales · 9 privados sin derivar · 6 literales |
contrato entero con talla resuelta (patrón Sidebar); radius con la adaptación al Dialog como CONTEXTO (lee un público de dialog, legítimo — no partir en dos); list-padding-inline + scrollbar-inset (patrón combobox); sonda con el palette ABIERTO |
| 2 | table |
46 | 23 privados · 14 globales · 5 literales | root-height-{k} es la fila → row-height-{k} al bundle; el prefijo root- sobra en el resto; talla |
| 3 | media-player |
45 | 37 globales · 8 literales; privados --_mp-* ABREVIADOS (§6 r5 los prohíbe) |
renombrar --_mp-* al nombre completo (⚠ prettier: la anotación /* literal: */ en la MISMA línea — trampa de natural-time-picker) + tokens; su acento theme-stable está FIRMADO en base.ts — no tocarlo; el ⚠ de bg son DOS knobs (superficie y velo) |
| 4 | tree-grid |
42 | 29 privados · 8 globales | root-bg-image son las guías de indentación: el knob es la tinta (guide-fg — era guide-color pre-acta), los 5 gradientes se quedan en la receta; talla |
| 5 | gradient-picker |
38 | privados --gp-* ABREVIADOS · 23 globales |
renombrar --gp-* + tokens; content-width es por talla (20/18/22rem), no un solo knob |
| 6 | listbox |
38 | 23 globales · 9 privados; una fila FALSA en la propuesta | tokens; retirar la fila font-size = var(--list-font-size): es público de la capa list-surface — un consumidor no acuña lo que la capa posee |
| 7 | carousel |
36 | 12 globales · 14 privados · 5 literales | tokens + talla; los indicator-width-{k} son geometría propia (rem), ni bundle ni desviación |
| 8 | feed |
33 | 16 globales · 13 privados | tokens + talla; el título es por talla y el feed tipa por debajo del 1:1 (md → sm), verbatim — no «corregirlo» al 1:1 |
Cada componente engancha su tab Tokens en el MISMO commit (firmado
2026-08-20, a raíz de que el autor viera que la demo de command no enseñaba su
superficie de tema — y resultó que ninguna lo hacía). Es una línea:
<TokensPanel component="x" stage={stageRef} /> más el botón del tab; el panel
(web/routes/uix/lib/TokensPanel.svelte) lee el contrato vivo con
eidos.getRecipeTokens(), así que no hay tabla que mantener. Un componente
temable cuya demo esconde sus tokens está a medio entregar.
Gates de cada componente (§7.4/7.5, sin excepciones): censo --only {c} al
100 % o excepción escrita · sonda antes/después = 0 diffs · centinela
por token nuevo · component:audit PASS (R-5.3 incluida) · eidos-lint 0
invalid · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 ·
check por fichero · README del componente con la tabla «Talla y tema»
(molde navigation-menu). Al cerrar el bloque: revisión adversarial §7.7
(molde Sidebar: refutar «idéntico», «alcanza», «píxel arriba» con medición
propia).
Al cerrar F2-A, lo desbloqueado por las firmas espera en este orden:
- Diseño de la capa
calendar-surface(firma 1) — se PRESENTA antes de escribirla (nombre, ejes, qué posee; moldelist-surfacey sus 4 reglas). Desbloquea los tres gordos del catálogo:range-calendar(98) +month-grid(61) +year-grid(61) y mediadate-range-picker— 220 knobs, el salto de ~42 % a ~47 %. - Mandato Field ejecutable (firma 2) —
field-langs(41) + las mitades detime-picker/time-range-picker/date-range-picker. - El resto del orden B1-B8 de D-TH.4 (
grid-list32 ·textarea28 · …).
AL TERMINAR EL EJE ENTERO — partir recipes/base.ts por componente
Firmado 2026-08-20, a raíz de la pregunta del autor «¿por qué todos los tokens viven en un fichero?». Va al final de TODO el themeable, no al cerrar un bloque: mientras quede un componente por tokenizar, ese fichero se toca en cada commit.
recipes/base.ts son 6.223 líneas y 128 componentes, y NO es un fichero de
tokens: es el valor del campo recipes de EidosConfig, serializable, que un
tema hidrata. Por eso los valores no pueden mudarse a components/{x}/ — se
intentó y se retiró (architecture/eidos.md: «The old tokens/components/*
were retired»). Lo que sí puede repartirse es la AUTORÍA:
// lib/recipes/table.ts
export const tableRecipe = { 'row-height-md': '…', … };
// lib/recipes/base.ts — el valor sigue siendo UNO
export const THEME_BASE_RECIPE_TOKENS = defineRecipes({ table: tableRecipe, … });
Verificado antes de proponerlo, no supuesto: el muro de tipos de
defineRecipes SIGUE disparando cuando las claves llegan por spread desde un
fichero suelto — probado con trigger-color (slot color) y padding-x (eje
físico), ambas dan error de compilación igual que inline. El único consumidor
sensible a la forma es eidos-purge.ts, que hace Object.keys() y es
indiferente. recipe-css-contract, el censo, R-5.3 y getRecipeTokens() leen
el objeto compuesto.
Los tres costes que paga el fichero único, todos medidos en la sesión del
2026-08-20: casi entra un SEGUNDO bloque table que el catálogo habría
descartado en silencio (la trampa está anotada abajo); una coma perdida
rompió el catálogo entero dos veces; y es el punto de conflicto de todas las
sesiones concurrentes. Repartido, un error afecta a un componente.
Por qué al final y no antes: son ~6.000 líneas movidas en el fichero que cada commit del backfill toca. Hacerlo con componentes pendientes garantiza conflicto con toda sesión que esté tokenizando; con el eje cerrado es un commit mecánico y aislado.
Gate: THEME_BASE_RECIPE_TOKENS idéntico antes/después (comparación
ESTRUCTURAL del objeto, no del fichero) · censo global idéntico ·
generated/base.css sin un solo cambio · el muro de tipos con su muta-prueba
(las dos claves malas siguen sin compilar).
FIRMA 3 en ejecución — hover → capa de estado (act. 2026-08-20)
El diagnóstico cambió al empezar. La firma decía «migrar los hovers neutros
a la capa de estado». Al hacerlo salió que el velo del sistema no llegaba a
ninguno, y no por el hover: por la regla BASE. archetypes.css pinta el velo
con :where(...), peso (0,0,0); cualquier receta que declare el ATAJO
background: sobre ese mismo nodo fija background-image: none con (0,1,0)
y lo cancela SIEMPRE — ni en hover ni nunca. El hover propio no era un
capricho: era la cicatriz de un velo que no llegaba.
Así que cada migración son DOS cambios: background: → background-color: en
la base, y fuera el hover propio.
| componente | commit | medido |
|---|---|---|
checkbox |
b35855c20 |
8 diffs, todos en hover: velo + fondo base (2.436 valores) |
splitter |
c66b73810 |
2 diffs en el asa (812 valores) |
switch |
3219a19c3 |
2 diffs en el track off (377); el swap del checked, verificado |
date-range-picker |
0c80a8efb |
sin medir el pintado — la demo no monta kind month/año |
Los 6 knobs de la cola eran 47 hasta que se midió la PROPIEDAD: el velo es
background-image, así que un hover de borde (22) o de tinta (19) no es la
capa de estado, sea cual sea su valor. Esos 41 eran deuda de nombre y se
renombraron (93017975c).
Las incidencias van al registro, no al eje
Todo lo que este eje destapa y NO le toca arreglar está registrado en
docs/next-features.md §12 y §13 — con su medición, no
como impresión:
- §12 · El contrato de cascada del velo de estado. Seis incidencias que son
la misma: nadie fijó cómo compone el velo de
archetypes.csscon las reglas de una receta. Incluye las 131 declaraciones-atajo en 45 componentes, la prophoverableque no suprime nada, el empate (0,5,0) que resuelve el orden de carga, la banda detree-gridque mata el hover, el token de tinta que no puede ganar al arquetipo, y el thumb descroll-areasin velo al que migrar. - §13 · Huecos de instrumento y de demo. Lo que hizo que una medición
mintiera o no existiera: falsos negativos del centinela, pseudo-elementos y
componentes compuestos que no ve, la sonda que no pasa el ratón por un
<tr>, y la lección general — una sonda sobre una parte que la demo no monta compara CERO valores y pasa.
Regla para las sesiones de este eje: si una incidencia mueve píxel o toca morfo, se MIDE, se anota ahí y se sigue; no se arregla dentro del commit del componente.
Lo que queda de la firma 3, y por qué no lo he hecho
scroll-area.thumb-bg-hover— pide DECISIÓN de morfo. El thumb llevaarchetype: 'thumb', que NO recibe velo. Migrarlo exige cambiar su arquetipo, y un arquetipo es morfo: se presenta, no se toca de oficio (§7.3-9). La alternativa es dejar su hover como excepción anotada.- El hallazgo sistémico: 141 declaraciones en 45 componentes cancelan el
velo del mismo modo, medido con
scripts/__statelayer-shorthand.ts(que comprueba contra el morfo que el atajo cae sobre el nodo VELADO, no sobre cualquiera). Peores: radio-group 12 · tag-group 8 · checkbox 7 · table 7 · combobox 6 · tabs 6. Esto explica la adopción 21/135 que midió la auditoría del 2026-07-01: el velo estaba escrito y estructuralmente derrotado. Es un barrido de catálogo que mueve píxel en 45 componentes: no entra en la firma 3, que se firmó sobre unos knobs concretos. - Hueco de demo:
date-range-pickerno exponekind='month'/'year'(norma N-6), así que la mitad de su API no se ve ni se mide.
Firmas del 2026-08-20 — el acta (las 14, «FIRMO TODAS»)
Presentadas con recomendación fundada y firmadas en bloque por el autor. El detalle razonado de cada una vive en la conversación del 2026-08-20 y en la fila correspondiente del plan §4; aquí el QUÉ ejecutable:
- Familia calendar = capa compartida (molde
lib/list-surface.css: attr de capa, un eje = token público + ranura privadavar(--_x, var(--x)), guardada porshared-layer-contract.test.ts+layer:check). NUNCA ~150 alias--calendar-*. Desbloquearange-calendar(98),month-grid(61),year-grid(61) y mediadate-range-picker. El diseño de la capa (nombre, ejes, qué posee) se presenta antes de escribirla. - Mandato Field: ejecutable. Los x-field/pickers componen el wrapper
Field; las alturas de segmento salen del eje size a nivel familia
(
--field-control-height-{k}); muere la re-duplicaciónheight-{xs..xl}por componente (theming-audit §2-B: computabanautodesde su nacimiento). - hover → capa de estado, donde reaparezca (empezando por los 3 knobs de
combobox). Mueve píxel hacia el canon §38/R-4.3 ⇒ cada migración lleva captura antes/después y su diff explicado en el commit. Desbloquea la poda D-2 (changelog §38 ◇: los--{x}-bg-hoverhuérfanos, incluidopagination.selected-bg-hover= capa sobre acento). - D-TH.6 completa — slot
fg+ modificador DELANTE con la regla: delante lo interactivo, detrás lo dimensional y contextual; modificador válido ∈ vocabulario universal (+current) ∪ valoresdata-state/ejes del morfo del componente;waveform.played/buffered= pseudo-partes;scrim-color-on-*→scrim-fg-over-{dark,light}; hovers neutros FUERA del codemod (migran por la firma 3). Renombres nominales de los ex-falsos amigos:item-fg/active-item-fg/partial-item-fg(rating-group),read-status-fg/failed-status-fg(chat-message),current-link-fg(breadcrumb),played-fg/buffered-fg(waveform). Se quedan:*-focus-ring-color(sistema),aura.orb-color-*(sustantivo),gradient-builder.stop-color-*(parte). - D-TH.1 R-5 dura · D-TH.3 tracks WIP en censo,
errorsólo fuera de carril · D-TH.5 el default no cambia (única excepción: la firma 3, con captura). - D-TH.2 con la opción (b) de
heading: la familia tipográfica B5 mide por--style-*como sistema transversal — no se acuñan--{c}-*tipográficos. La listaKNOB_PROPSes el perímetro; cambios = firma. - D-TH.4 con enmienda: la cola revisada de este CONTINUE prioriza sobre el orden de familias B1-B8.
- D-TH.7 con enmienda: R-5.3 a
errordirecto tras el codemod; R-5.1/5.2 esperan F3. - D-TH.8: panel de tokens en
/temas, en F4.
Lo ejecutado (censo antes → después)
Bloque del vocabulario, 2026-08-20 (tres commits, sin pushear):
| commit | qué |
|---|---|
f09e04fab |
el acta de las 14 firmas + la revisión de los 7 ya corregidos |
bd916ef3f |
codemod: 269 claves · 793 referencias · 101 ficheros · alcance IDÉNTICO · 0 diffs de computed |
7781d6e4c |
R-5.3 en error (muta-prueba de 3 caras) + recipe-contract §1/§4 + theming §6.7 + checklist |
Lo que el bloque enseñó (más caro de lo que parece):
- El plan del codemod se escribe ANTES de tocar nada. Releer el clasificador después da CERO desviaciones —ya están renombradas— y el verificador del diff pasa sobre el vacío. Costó dos vueltas.
- Un valor puede cambiar de texto sin cambiar de significado: 22 privados reapuntan a nombres nuevos. El gate normaliza por el plan antes de comparar, o los cuenta como cambio de valor y da rojo en falso.
- No pasar prettier sobre lo que el codemod tocó. El árbol tiene CRLF
(
autocrlf=true) y 88 de 101 ficheros ya salían «sucios» ANTES; un--writeen masa reformateó 300 ficheros de deriva ajena. Se midió lo que importaba —¿alarga el renombrado alguna línea?— y la respuesta fue no en todos, así que prettier no hacía falta. - Clasificar un hover por el NOMBRE es clasificarlo mal:
dropdown-menu.item-bg-hoverleevar(--color-primary-element)— es el palette swap con valencia de recipe-contract §2, no el hover bespoke que §38 deprecó. Lo decide el VALOR. Yo lo había listado mal en este mismo handoff.
| componente | alcance | commit |
|---|---|---|
gradient-builder (piloto) |
0 % → 80 % | 3cdb5b5f0 |
combobox |
0 % → 76 % | cef2d32f1 |
natural-time-picker |
0 % → 61 % | e774ca7e8 |
date-range-picker |
0 % → 42 % | 07778b597 |
time-range-picker |
0 % → 19 % | 68eb8af26 |
time-picker |
0 % → 18 % | e4fcdd62e |
proof-of-human |
0 % → 14 % | ae91573c3 |
Global del catálogo: 33 % → 37 % (1.621 → 1.841 knobs públicos).
Informe + instrumento: 2591ec2f0; revisión de las fichas 11-20: 3bfe0d413.
El instrumental (vive en scripts/, prefijo __ = temporal del eje)
node scripts/__theming-probe.ts <componente> <salida.json> # sonda antes/después
node scripts/__theming-diff.ts <antes.json> <después.json> # el gate: diff VACÍO
npm run theming:sentinel -- <componente> <url> # guard R-5.4: ¿alcanza cada token? (ya no es `__`)
node scripts/__shot.ts <componente> <salida.png> # captura 2× del stage
Se ejecutan con node, no con tsx (tsx inyecta helpers que rompen
page.evaluate) y desde la raíz del repo, contra el dev server en
localhost:5173. Los scripts/__dbg*.ts y __sentinel-targeted*.ts son de un
solo uso (verificaciones dirigidas de ayer); no se commitean.
Lo que el instrumento aprendió a fuerza de mentir
- La sonda congela
transition, NUNCAanimation: una propiedad transicionada devuelve su valor INICIAL justo tras escribir el token (un token vivo parecía muerto), pero congelaranimationimpide que Presence monte el panel — y entonces mides un popup que no existe. - Un token RESUELTO no se mueve desde
:root: se declara en[data-{c}]o en su parte, así que el tema mueve la coordenada. El centinela escribe en:rooty en el host, y aun así los resueltos salen «muertos» por diseño. - Un panel portalado no ve el ámbito del componente: sus tokens se emiten en
:root(o conparts), y un override scoped no lo alcanza. - La demo puede tapar el componente:
proof-of-humandeclara su propiomin-block-sizey fondo sobre el stage — ahí el centinela no sirve, y se dice. - Un valor centinela igual al real lee como «no efecto» (
9999pxcontra un--radius-fullque ya era 9999px).
Trampas que costaron un commit cada una (no repetirlas)
- Clave duplicada en
recipes/base.ts: añadir un bloque que ya existía lo descarta en silencio (gana la segunda). Guard antes de generar:grep -oE "^\t'?[a-z0-9-]+'?: \{" src/uix/eidos/lib/recipes/base.ts | sed "s/[\t':{ ]//g" | sort | uniq -d→ debe salir vacío. - Dejar vivos los bloques
[data-…][data-size='…']del CSS al emitir la cascada por el TSC: pisan los tokens nuevos con los valores viejos, y el diff de computed da 0 porque la ruta vieja sigue mandando. Lo cazó el centinela. - Correr
component:auditDESPUÉS deprettier, no antes: al renombrar un privado a su nombre completo, prettier partió tres declaraciones en varias líneas y separó la anotación/* literal: … */de su valor — R-2.1 la exige en la MISMA línea, así que el guard pasó a ver colores crudos donde antes veía excepciones anotadas. Se arregla con/* prettier-ignore */sobre la declaración: NO acortando el comentario ni tocando el valor. Regresión real ennatural-time-picker, cazada un día tarde por correr el audit antes del formateo. - Backticks en
node -e "…"desde bash: se ejecutan y vacían el texto. Para editar markdown con código, usar el editor, no el shell. prettier --writesobre un README no protege de que un Edit se coma un encabezado: comprobargrep -c "^## Gaps"después de insertar secciones.- Lo de ayer sigue vigente: medir el nodo que el usuario señala (píxel arriba),
el hover es del sistema, el default no cambia, rama compartida sin
stashni--amend,checkpor fichero.
Fuentes
- Plan y decisiones:
PLAN-theming.md· informe:docs/audit/theming/README.md. - Auditoría del SISTEMA (fase 1, cerrada):
theming-audit.md— §B familia calendar, §5.3-3 mandato de Field. - Doctrina:
docs/theming/reference.md(§5 talla, §6 nombres, §38 capa de estado) ·docs/canon/recipe-contract.md·docs/canon/tsc.md·docs/architecture/eidos.md(vertebración tipográfica, columnaunused) ·src/uix/eidos/components/README.md(capas compartidas). - Precedente de la forma por talla:
PLAN-sidebar.md§3 + F3 y el bloquesidebarderecipes/base.ts.