uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca
código y es la que importa.
1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré
como «declaraciones muertas» apoyándome en los precedentes de
`table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el
mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el
plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es
neutral — **fija el empate a favor del plano** y con él su 1.25 donde la
receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9
tiene pendiente para VEINTE componentes. Un empate por orden de carga se
registra, no se resuelve dentro de un commit de tokenización.
Y la medición era más débil de lo que el commit daba a entender: estable en
cinco recargas **en DEV**, sin verificar en un bundle de producción.
Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5
tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa
razón. §12.9 y el README dicen ahora que el par se deja declarado A
PROPÓSITO.
2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al
100 % o excepción escrita», y la válvula que `component-audit` lee es esa
marca (`readmeException`), no la prosa. Tenía la razón documentada en
veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2,
los 16 habrían dado error. Una marca por clase medida — forward de paleta
THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de
layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan.
3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por
grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el
protocolo lo pide por componente: una sesión nueva habría arrancado con el
mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la
revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8
firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus
seis lecciones, y actualizada la memoria del eje.
4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el
bloque, cada una nombrando la forma en que el instrumento mentía, más la
lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso
— `picker-shell` medía CERO y reportaba éxito.
5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`,
`virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md
(md→sm); los valores eran correctos (0 diffs), el texto no.
Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B
sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la
medición de quien los escribió, y el plan la exige antes de cerrar un bloque.
El handoff nombra las dos hipótesis concretas que hay que atacar.
Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves ·
`component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno
con un commit de esta sesión en su código) · R-5.4 verde en los 22 ·
suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero
limpio.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
# CONTINUE — eje Theming «theme-reach» (handoff, act. 2026-08-22)
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(theming): revisión adversarial de F2-B — 4 hipótesis, 3 refutadas, 1 token que mentía
El plan (§7.7) exige una revisión escéptica antes de cerrar un bloque, y F2-B
no la tenía. Molde F2-A: worktree en la base del bloque (`8400e5bdd`) con su
propio dev server, medición independiente.
**REFUTADA \u2014 «el default cambió en algún sitio que la demo no mira».** 77 casos
FORZADOS (variantes, formas, tallas y estados que las demos NO montan, en los
21 componentes) comparados base vs HEAD: **0 diferencias**. Era la sospecha más
fuerte, porque cuatro componentes miden UN nodo (`link`, `code`, `skeleton`,
`link-preview`) y sus tokens viven justo en las variantes que la demo no
renderiza.
**REFUTADA \u2014 «hay tokens huérfanos».** 339 públicos de los 21 bloques, 0 sin
consumo. El primer extractor daba «SIN BLOQUE» en los 21 \u2014 el mismo antipatrón
que audita este eje \u2014 y se rehízo contando llaves; muta-prueba: un token falso
inyectado sale detectado y el árbol se restaura.
**REFUTADA \u2014 «el gate de 0 diffs pasó sobre casi nada».** Contados los nodos que
ve cada sonda; los cuatro de un nodo se cubrieron forzando sus variantes arriba.
**HALLAZGO REAL \u2014 `drag-drop.preview-z` mentía, y lo escondía una adjudicación
mía sobre un nodo SINTÉTICO.** El ledger lo daba por vivo («forzado → alcanza,
100 → 4321»), pero ese nodo lo había creado yo con JS. El preview REAL que soma
monta durante un arrastre lleva `z-index: 9999` **INLINE**
(`drag-drop-provider.svelte.ts:663,671`): el token resuelve a 4321 y el nodo
computa 9999. Ninguna cascada puede ganarle. **Retirado con 0 diffs** contra el
snapshot original (1.827 valores × 7 estados), misma clase que el `item-gap*`
de carousel que retiró la revisión de F2-A. Era el único público que drag-drop
tenía antes del eje; su alcance se mantiene en 89 %.
**La lección que generaliza, y va al protocolo**: una adjudicación «forzado →
alcanza» vale lo que valga el nodo que se forzó \u2014 si lo creaste tú, no lleva lo
que soma le pone. Re-verificado el resto de la misma clase contra nodos reales:
`clipboard` (indicador tras un clic real) y `picker-shell` (pie real) coinciden
con lo adjudicado y no llevan inline; el inline de `skeleton-line` es de
`inline-size`, no del eje tokenizado.
DOS HALLAZGOS DE INSTRUMENTO en la propia revisión (→ §13):
- **El orden de carga NO es el mismo en dev que en producción.** Medido con
`npm run build` + preview y CDP: en dev la receta de `link-preview` va ANTES
del plano `overlay`; en producción queda EN MEDIO de sus dos reglas. El
ganador coincide por casualidad. La fragilidad de §12.9 queda medida en los
dos entornos, y confirma que restaurar el par fue lo correcto.
- **Un worktree cuyo `node_modules` es una junction fuera del root NO HIDRATA**:
Vite responde 403 a `@fs/…` y SvelteKit no arranca, así que la página SSR se
ve pero ningún componente monta. La primera pasada de esta revisión comparó
contra una base MUERTA y devolvió «77 casos, 1 diferencia» sin que nada lo
delatara. Se arregla con `server: { fs: { allow: [...] } }`; queda escrito en
el handoff junto a la regla: **comprueba que un componente monta antes de
comparar**.
Gates: censo global 50 % · `--names` DESVIADAS 0 · drag-drop 89 % ·
component:audit PASS · eidos-lint 0 invalid · R-5.4 verde · suite eidos sin
rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero limpio.
Worktree eliminado quitando la junction ANTES del `remove --force`, con el
`node_modules` real verificado intacto (memoria
`git-worktree-remove-recurses-into-junctions`).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
**Estado: EN EJECUCIÓN — F0 y F1 CERRADAS · F2 en curso, bloque F2-B CERRADO
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
con su revisión adversarial pasada (2026-08-22); la cola sigue abierta y
`text-gradient` ya está dentro (2026-08-22).** Alcance global **56 %** (33 % al abrir el eje, 45 % tras F2-A, 50 % tras F2-B,
uix(theming): el censo aprende que un privado DERIVADO sí alcanza — 50 % → 56 %
Seis puntos de alcance sin tocar una línea de componente: **la medición estaba
mal, no el código**.
EL DEFECTO. F0 encargaba «detección "privado que deriva de público" (leer
`--_c-x: var(--c-y)` en la misma receta y reclasificar)». Se implementó a
medias: el censo CALCULA `derives` y lo imprime en la columna «¿deriva de un
público?» de la §3 de cada ficha, pero **el contador nunca lo usó** — esos
knobs seguían puntuando como deuda. Medido: **240 privados en 59 componentes**
estaban en ese estado, es decir, haciendo exactamente lo que la doctrina
prescribe («privados sólo si derivan de públicos») y penalizados por ello.
Lo destapó tokenizar `avatar`: sus 17 privados YA leían públicos
(`--_avatar-size: var(--avatar-size-md)`), así que no había nada que tokenizar
— había que arreglar el instrumento.
LA CORRECCIÓN. Segunda pasada tras recoger privados y knobs: un knob que lee
`var(--_c-x)` pasa a `public` si ese privado alcanza un público. El cierre es
**TRANSITIVO** (`--_a: calc(var(--_b) * .3)` alcanza si `--_b` alcanza) con
conjunto de visitados para que un ciclo no lo cuelgue.
**Muta-prueba** (obligatoria, memoria `a-guard-that-inspects-nothing-passes`):
apuntar `--_avatar-radius` al primitivo crudo baja avatar de 75 % a 72 % y el
knob vuelve a `private`; revertirlo lo devuelve a 75 % y deja el árbol
idéntico.
EFECTO MEDIDO:
- Global **50 % → 56 %** · privados 625 → 341 · al 100 % 10 → 14 · <20 % 22 → 20.
- `avatar` 36 % → **75 %**, `picker-shell` 76 % → **95 %**, `menu-dial` 25 % →
38 %, y otras 51 fichas con cifras nuevas — **ninguna por cambio de código**.
- Los READMEs de `picker-shell` y `menu-dial` se actualizan con la cifra nueva
y con la razón, para que nadie crea que el componente cambió.
⚠ **Cualquier cifra anterior a este commit no es comparable con las de ahora.**
Queda escrito en el handoff.
Y un hueco de F0 que sale al hacerlo, registrado en §13: **el test del SUELO
que el plan pedía NO EXISTE** («alcance global ≥ el de hoy y literales ≤ 614 —
el número sube o el test falla»). Hoy nada impide que el alcance BAJE entre
sesiones: sólo se vería mirando la cifra a mano. Con el censo ya endurecido, es
el momento de escribirlo.
Gates: `component:audit` 162 PASS / 4 NEEDS-WORK (preexistentes) · `--names`
DESVIADAS 0 · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · el
censo compila.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
y ** +6 puntos al arreglar la MEDICIÓN**, no el código: ver abajo). Sin contrato
uix(chart): temable — 9 % → 49 %, y la ficha pedía 71 claves de las que 20 llevaban el mismo valor
22 claves nuevas fusionadas en el bloque existente (17 → 39). Censo chart
9 % → **49 %**; global **56 % → 57 %** (2.638 → 2.675 públicos; <20 % 19 → 18).
LO QUE DECIDIÓ LA MEDICIÓN, no la ficha:
Las 84 filas fuera de alcance colapsaban a **26 valores distintos** — 9×
`content-secondary`, 9× `content-primary`, 9× `100%`, 6× `font-size-sm`, 6×
`font-size-xs`, 6× `font-weight-medium`, 5× `surface-default`, 4× `radius-sm`.
Nombrar por parte habría dado cinco claves para el pelo que separa marcas y
nueve para la tinta de anotación: la inflación de `spinner` con otra cara.
**El texto no recibe tokens de chart.** Toma su ROL DE TAMAÑO de los estilos con
nombre del canon —`caption` (xs) para las anotaciones, `label` (sm) para el
texto de cromo— y su tinta y su peso de las ranuras del sistema. Acuñar
`--chart-label-font-size` sería tipografía por componente, prohibido el mismo
día para text-effects. Precedente de consumir la capa: `code-block`, `kbd`,
`announce`. Doce declaraciones cambian de `var(--font-size-xs|sm)` a
`var(--style-caption|label-font-size)`, mismo valor.
**La receta sólo posee la tinta de lo que NO es texto**, cinco claves y cada una
un concepto que el CSS repite: `axis-fg` (eje + rejilla smith + conector del
funnel), `grid-fg` (rejilla + radar), `separator-fg` (el pelo de superficie
entre marcas contiguas: porción, punto activo, punto smith, pétalo, segmento),
`smith-boundary-fg` y `track-bg` (raíl del bar-list y del gauge). Más 17 de
geometría de cromo (gap, los pares row/column-gap, swatch, track, bar-segment).
**Incumplimiento heredado, corregido**: el CSS leía la coordenada CRUDA
`var(--control-height-sm, var(--space-7))` —lo que `recipe-css-contract`
prohíbe— con un respaldo muerto. El contrato guarda el bundle
`var(--size-sm-control-height)`, 1:1 con él.
**Identidad fuera de contrato**: los nueve `100%` («ocupa a tu padre») y el
`min-inline-size: max-content` del tooltip, misma clase ya adjudicada en
`date-picker`. Por eso el techo es 49 %, y está escrito en el veredicto §5.
EL INSTRUMENTO ESTABA CIEGO DE TRES MANERAS, arregladas ANTES de medir:
1. **La superficie de chart vive en DIECISÉIS rutas de demo**, una por tipo:
`/chart` monta 58 nodos de los 302 de la página y el resto vive en rutas
hermanas. El guard gana `urls`: un token está VIVO si ALGUNA superficie del
componente lo sigue, y cada URL sólo re-prueba lo pendiente (el caso de una
sola ruta cuesta exactamente lo que costaba). De paso `picker-shell` recibe
la suya: su ruta propia es un 404 y se mide dentro del date-picker — pasa de
0/31 con seis sin adjudicar a 6/31 sin tener que saberlo.
2. **El guard no leía NINGUNA propiedad SVG.** Sin `fill` / `stroke` /
`strokeWidth` / `fillOpacity` / `r` / `rx`, doce de las 39 claves de un
componente que dibuja en SVG leían muertas estando vivas. Misma clase que el
hueco de `filter` / `backdrop-filter` de ayer.
3. **El valor centinela se elegía por SUBCADENA**: `slice-stroke-width` recibía
`rgb(1, 2, 3)` por contener «stroke» —inválido para una longitud— y leía
muerta. La dimensión se decide ahora por el FINAL del nombre.
Los 28 componentes con ledger re-verificados tras los tres cambios: cero
regresiones, cero STALE. (Ojo: doce a la vez contra el mismo dev server dan
falsos exit=1 por timeout; repetir a solas antes de creerse una regresión.)
Gates: sonda **0 diffs en 161.850 valores computados** (16 rutas × 7 estados,
892 nodos en reposo) · centinela **37/39**, con `transition-duration` y `-ease`
adjudicadas midiéndolas sin la congelación (0,12s → 11,5s · cubic-bezier →
steps(4)) · censo --only 49 % · --names 39/39 conformes · component-audit PASS ·
eidos-lint 0 invalid · suite eidos con el único rojo conocido
(`skin-media-player`) · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados.
Abre (→ next-features §13): el censo **penaliza consumir la capa tipográfica**
si no eres un primitivo —las doce declaraciones correctas siguen contando
`global`—, que es la nota de `listbox` con una segunda ocurrencia; y el número
del gauge no tiene estilo con nombre que lo represente (un readout de dato no es
un `h2`), así que sigue leyendo `--font-size-xl`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**24**; al 100 % **17** ; alcance < 20 % **18** . Ú ltimo: `chart` 9 % → 49 %.
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## LO PRIMERO AL ENTRAR (cinco minutos, en este orden)
1. **Comprueba que el suelo no se ha movido** :
```bash
uix(chart): temable — 9 % → 49 %, y la ficha pedía 71 claves de las que 20 llevaban el mismo valor
22 claves nuevas fusionadas en el bloque existente (17 → 39). Censo chart
9 % → **49 %**; global **56 % → 57 %** (2.638 → 2.675 públicos; <20 % 19 → 18).
LO QUE DECIDIÓ LA MEDICIÓN, no la ficha:
Las 84 filas fuera de alcance colapsaban a **26 valores distintos** — 9×
`content-secondary`, 9× `content-primary`, 9× `100%`, 6× `font-size-sm`, 6×
`font-size-xs`, 6× `font-weight-medium`, 5× `surface-default`, 4× `radius-sm`.
Nombrar por parte habría dado cinco claves para el pelo que separa marcas y
nueve para la tinta de anotación: la inflación de `spinner` con otra cara.
**El texto no recibe tokens de chart.** Toma su ROL DE TAMAÑO de los estilos con
nombre del canon —`caption` (xs) para las anotaciones, `label` (sm) para el
texto de cromo— y su tinta y su peso de las ranuras del sistema. Acuñar
`--chart-label-font-size` sería tipografía por componente, prohibido el mismo
día para text-effects. Precedente de consumir la capa: `code-block`, `kbd`,
`announce`. Doce declaraciones cambian de `var(--font-size-xs|sm)` a
`var(--style-caption|label-font-size)`, mismo valor.
**La receta sólo posee la tinta de lo que NO es texto**, cinco claves y cada una
un concepto que el CSS repite: `axis-fg` (eje + rejilla smith + conector del
funnel), `grid-fg` (rejilla + radar), `separator-fg` (el pelo de superficie
entre marcas contiguas: porción, punto activo, punto smith, pétalo, segmento),
`smith-boundary-fg` y `track-bg` (raíl del bar-list y del gauge). Más 17 de
geometría de cromo (gap, los pares row/column-gap, swatch, track, bar-segment).
**Incumplimiento heredado, corregido**: el CSS leía la coordenada CRUDA
`var(--control-height-sm, var(--space-7))` —lo que `recipe-css-contract`
prohíbe— con un respaldo muerto. El contrato guarda el bundle
`var(--size-sm-control-height)`, 1:1 con él.
**Identidad fuera de contrato**: los nueve `100%` («ocupa a tu padre») y el
`min-inline-size: max-content` del tooltip, misma clase ya adjudicada en
`date-picker`. Por eso el techo es 49 %, y está escrito en el veredicto §5.
EL INSTRUMENTO ESTABA CIEGO DE TRES MANERAS, arregladas ANTES de medir:
1. **La superficie de chart vive en DIECISÉIS rutas de demo**, una por tipo:
`/chart` monta 58 nodos de los 302 de la página y el resto vive en rutas
hermanas. El guard gana `urls`: un token está VIVO si ALGUNA superficie del
componente lo sigue, y cada URL sólo re-prueba lo pendiente (el caso de una
sola ruta cuesta exactamente lo que costaba). De paso `picker-shell` recibe
la suya: su ruta propia es un 404 y se mide dentro del date-picker — pasa de
0/31 con seis sin adjudicar a 6/31 sin tener que saberlo.
2. **El guard no leía NINGUNA propiedad SVG.** Sin `fill` / `stroke` /
`strokeWidth` / `fillOpacity` / `r` / `rx`, doce de las 39 claves de un
componente que dibuja en SVG leían muertas estando vivas. Misma clase que el
hueco de `filter` / `backdrop-filter` de ayer.
3. **El valor centinela se elegía por SUBCADENA**: `slice-stroke-width` recibía
`rgb(1, 2, 3)` por contener «stroke» —inválido para una longitud— y leía
muerta. La dimensión se decide ahora por el FINAL del nombre.
Los 28 componentes con ledger re-verificados tras los tres cambios: cero
regresiones, cero STALE. (Ojo: doce a la vez contra el mismo dev server dan
falsos exit=1 por timeout; repetir a solas antes de creerse una regresión.)
Gates: sonda **0 diffs en 161.850 valores computados** (16 rutas × 7 estados,
892 nodos en reposo) · centinela **37/39**, con `transition-duration` y `-ease`
adjudicadas midiéndolas sin la congelación (0,12s → 11,5s · cubic-bezier →
steps(4)) · censo --only 49 % · --names 39/39 conformes · component-audit PASS ·
eidos-lint 0 invalid · suite eidos con el único rojo conocido
(`skin-media-player`) · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados.
Abre (→ next-features §13): el censo **penaliza consumir la capa tipográfica**
si no eres un primitivo —las doce declaraciones correctas siguen contando
`global`—, que es la nota de `listbox` con una segunda ocurrencia; y el número
del gauge no tiene estilo con nombre que lo represente (un readout de dato no es
un `h2`), así que sigue leyendo `--font-size-xl`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
node --import tsx/esm scripts/theming-census.ts # 162 · 5.191 · 2.675 (57 %) · 24 sin contrato
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
node --import tsx/esm scripts/theming-census.ts --names # DESVIADAS 0 — la gramática se mantiene
```
uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca
código y es la que importa.
1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré
como «declaraciones muertas» apoyándome en los precedentes de
`table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el
mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el
plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es
neutral — **fija el empate a favor del plano** y con él su 1.25 donde la
receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9
tiene pendiente para VEINTE componentes. Un empate por orden de carga se
registra, no se resuelve dentro de un commit de tokenización.
Y la medición era más débil de lo que el commit daba a entender: estable en
cinco recargas **en DEV**, sin verificar en un bundle de producción.
Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5
tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa
razón. §12.9 y el README dicen ahora que el par se deja declarado A
PROPÓSITO.
2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al
100 % o excepción escrita», y la válvula que `component-audit` lee es esa
marca (`readmeException`), no la prosa. Tenía la razón documentada en
veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2,
los 16 habrían dado error. Una marca por clase medida — forward de paleta
THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de
layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan.
3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por
grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el
protocolo lo pide por componente: una sesión nueva habría arrancado con el
mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la
revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8
firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus
seis lecciones, y actualizada la memoria del eje.
4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el
bloque, cada una nombrando la forma en que el instrumento mentía, más la
lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso
— `picker-shell` medía CERO y reportaba éxito.
5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`,
`virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md
(md→sm); los valores eran correctos (0 diffs), el texto no.
Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B
sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la
medición de quien los escribió, y el plan la exige antes de cerrar un bloque.
El handoff nombra las dos hipótesis concretas que hay que atacar.
Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves ·
`component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno
con un commit de esta sesión en su código) · R-5.4 verde en los 22 ·
suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero
limpio.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
El segundo cubre componentes que `component:audit` NO ve (p. ej.
`picker-shell` , eidos-only sin morfo): córrelo siempre, no te fíes del audit
para la gramática.
2. **Arranca el dev server ANTES de medir nada** : `preview_start` con la config
`dev` de `.claude/launch.json` (puerto **5180** ). Las sondas se ejecutan con
`node` , no con `tsx` , desde la raíz, y **su default es 5173** : pásales
siempre la URL como tercer argumento.
3. **Lee el veredicto §5 de la ficha** (`docs/audit/theming/{c}.md`). Si está
vacío — lo están ** ~130 de 171** — lo escribes TÚ midiendo, y luego
ejecutas: ése fue el flujo de todo F2-B. La §4 generada **no se sigue a
ciegas**; ver «Lo que F2-B enseñó».
## QUÉ HACER AHORA
uix(theming): revisión adversarial de F2-B — 4 hipótesis, 3 refutadas, 1 token que mentía
El plan (§7.7) exige una revisión escéptica antes de cerrar un bloque, y F2-B
no la tenía. Molde F2-A: worktree en la base del bloque (`8400e5bdd`) con su
propio dev server, medición independiente.
**REFUTADA \u2014 «el default cambió en algún sitio que la demo no mira».** 77 casos
FORZADOS (variantes, formas, tallas y estados que las demos NO montan, en los
21 componentes) comparados base vs HEAD: **0 diferencias**. Era la sospecha más
fuerte, porque cuatro componentes miden UN nodo (`link`, `code`, `skeleton`,
`link-preview`) y sus tokens viven justo en las variantes que la demo no
renderiza.
**REFUTADA \u2014 «hay tokens huérfanos».** 339 públicos de los 21 bloques, 0 sin
consumo. El primer extractor daba «SIN BLOQUE» en los 21 \u2014 el mismo antipatrón
que audita este eje \u2014 y se rehízo contando llaves; muta-prueba: un token falso
inyectado sale detectado y el árbol se restaura.
**REFUTADA \u2014 «el gate de 0 diffs pasó sobre casi nada».** Contados los nodos que
ve cada sonda; los cuatro de un nodo se cubrieron forzando sus variantes arriba.
**HALLAZGO REAL \u2014 `drag-drop.preview-z` mentía, y lo escondía una adjudicación
mía sobre un nodo SINTÉTICO.** El ledger lo daba por vivo («forzado → alcanza,
100 → 4321»), pero ese nodo lo había creado yo con JS. El preview REAL que soma
monta durante un arrastre lleva `z-index: 9999` **INLINE**
(`drag-drop-provider.svelte.ts:663,671`): el token resuelve a 4321 y el nodo
computa 9999. Ninguna cascada puede ganarle. **Retirado con 0 diffs** contra el
snapshot original (1.827 valores × 7 estados), misma clase que el `item-gap*`
de carousel que retiró la revisión de F2-A. Era el único público que drag-drop
tenía antes del eje; su alcance se mantiene en 89 %.
**La lección que generaliza, y va al protocolo**: una adjudicación «forzado →
alcanza» vale lo que valga el nodo que se forzó \u2014 si lo creaste tú, no lleva lo
que soma le pone. Re-verificado el resto de la misma clase contra nodos reales:
`clipboard` (indicador tras un clic real) y `picker-shell` (pie real) coinciden
con lo adjudicado y no llevan inline; el inline de `skeleton-line` es de
`inline-size`, no del eje tokenizado.
DOS HALLAZGOS DE INSTRUMENTO en la propia revisión (→ §13):
- **El orden de carga NO es el mismo en dev que en producción.** Medido con
`npm run build` + preview y CDP: en dev la receta de `link-preview` va ANTES
del plano `overlay`; en producción queda EN MEDIO de sus dos reglas. El
ganador coincide por casualidad. La fragilidad de §12.9 queda medida en los
dos entornos, y confirma que restaurar el par fue lo correcto.
- **Un worktree cuyo `node_modules` es una junction fuera del root NO HIDRATA**:
Vite responde 403 a `@fs/…` y SvelteKit no arranca, así que la página SSR se
ve pero ningún componente monta. La primera pasada de esta revisión comparó
contra una base MUERTA y devolvió «77 casos, 1 diferencia» sin que nada lo
delatara. Se arregla con `server: { fs: { allow: [...] } }`; queda escrito en
el handoff junto a la regla: **comprueba que un componente monta antes de
comparar**.
Gates: censo global 50 % · `--names` DESVIADAS 0 · drag-drop 89 % ·
component:audit PASS · eidos-lint 0 invalid · R-5.4 verde · suite eidos sin
rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero limpio.
Worktree eliminado quitando la junction ANTES del `remove --force`, con el
`node_modules` real verificado intacto (memoria
`git-worktree-remove-recurses-into-junctions`).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
### 1. F2-B está CERRADO — su revisión adversarial (§7.7) se hizo el 2026-08-22
**4 hipótesis · 3 refutadas · 1 hallazgo real.** Detalle en `PLAN §8` . Lo que
hay que recordar de ella:
- **`drag-drop.preview-z` mentió y se retiró**: soma escribe su `z-index`
INLINE, y la adjudicación que lo daba por vivo se había medido sobre un nodo
creado a mano. **Una adjudicación «forzado → alcanza» vale lo que valga el
nodo que se forzó** — si lo creaste tú, no lleva lo que soma le pone.
- **El orden de carga NO es el mismo en dev que en producción** (medido con
build + CDP). El empate de §12.9 sigue abierto, ahora con las dos medidas.
- **Un worktree cuyo `node_modules` es una junction fuera del root no hidrata**:
Vite responde 403 a `@fs/…` . La primera pasada comparó contra una base muerta
sin que nada lo delatara — añade `server: { fs: { allow: [...] } }` a su
`vite.config.ts` y **comprueba que un componente monta antes de comparar** .
uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca
código y es la que importa.
1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré
como «declaraciones muertas» apoyándome en los precedentes de
`table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el
mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el
plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es
neutral — **fija el empate a favor del plano** y con él su 1.25 donde la
receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9
tiene pendiente para VEINTE componentes. Un empate por orden de carga se
registra, no se resuelve dentro de un commit de tokenización.
Y la medición era más débil de lo que el commit daba a entender: estable en
cinco recargas **en DEV**, sin verificar en un bundle de producción.
Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5
tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa
razón. §12.9 y el README dicen ahora que el par se deja declarado A
PROPÓSITO.
2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al
100 % o excepción escrita», y la válvula que `component-audit` lee es esa
marca (`readmeException`), no la prosa. Tenía la razón documentada en
veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2,
los 16 habrían dado error. Una marca por clase medida — forward de paleta
THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de
layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan.
3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por
grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el
protocolo lo pide por componente: una sesión nueva habría arrancado con el
mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la
revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8
firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus
seis lecciones, y actualizada la memoria del eje.
4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el
bloque, cada una nombrando la forma en que el instrumento mentía, más la
lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso
— `picker-shell` medía CERO y reportaba éxito.
5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`,
`virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md
(md→sm); los valores eran correctos (0 diffs), el texto no.
Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B
sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la
medición de quien los escribió, y el plan la exige antes de cerrar un bloque.
El handoff nombra las dos hipótesis concretas que hay que atacar.
Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves ·
`component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno
con un commit de esta sesión en su código) · R-5.4 verde en los 22 ·
suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero
limpio.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
uix(theming): el censo aprende que un privado DERIVADO sí alcanza — 50 % → 56 %
Seis puntos de alcance sin tocar una línea de componente: **la medición estaba
mal, no el código**.
EL DEFECTO. F0 encargaba «detección "privado que deriva de público" (leer
`--_c-x: var(--c-y)` en la misma receta y reclasificar)». Se implementó a
medias: el censo CALCULA `derives` y lo imprime en la columna «¿deriva de un
público?» de la §3 de cada ficha, pero **el contador nunca lo usó** — esos
knobs seguían puntuando como deuda. Medido: **240 privados en 59 componentes**
estaban en ese estado, es decir, haciendo exactamente lo que la doctrina
prescribe («privados sólo si derivan de públicos») y penalizados por ello.
Lo destapó tokenizar `avatar`: sus 17 privados YA leían públicos
(`--_avatar-size: var(--avatar-size-md)`), así que no había nada que tokenizar
— había que arreglar el instrumento.
LA CORRECCIÓN. Segunda pasada tras recoger privados y knobs: un knob que lee
`var(--_c-x)` pasa a `public` si ese privado alcanza un público. El cierre es
**TRANSITIVO** (`--_a: calc(var(--_b) * .3)` alcanza si `--_b` alcanza) con
conjunto de visitados para que un ciclo no lo cuelgue.
**Muta-prueba** (obligatoria, memoria `a-guard-that-inspects-nothing-passes`):
apuntar `--_avatar-radius` al primitivo crudo baja avatar de 75 % a 72 % y el
knob vuelve a `private`; revertirlo lo devuelve a 75 % y deja el árbol
idéntico.
EFECTO MEDIDO:
- Global **50 % → 56 %** · privados 625 → 341 · al 100 % 10 → 14 · <20 % 22 → 20.
- `avatar` 36 % → **75 %**, `picker-shell` 76 % → **95 %**, `menu-dial` 25 % →
38 %, y otras 51 fichas con cifras nuevas — **ninguna por cambio de código**.
- Los READMEs de `picker-shell` y `menu-dial` se actualizan con la cifra nueva
y con la razón, para que nadie crea que el componente cambió.
⚠ **Cualquier cifra anterior a este commit no es comparable con las de ahora.**
Queda escrito en el handoff.
Y un hueco de F0 que sale al hacerlo, registrado en §13: **el test del SUELO
que el plan pedía NO EXISTE** («alcance global ≥ el de hoy y literales ≤ 614 —
el número sube o el test falla»). Hoy nada impide que el alcance BAJE entre
sesiones: sólo se vería mirando la cifra a mano. Con el censo ya endurecido, es
el momento de escribirlo.
Gates: `component:audit` 162 PASS / 4 NEEDS-WORK (preexistentes) · `--names`
DESVIADAS 0 · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · el
censo compila.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
### 2. El censo cuenta distinto desde el 2026-08-22 — léelo antes de comparar cifras
**Un privado que DERIVA de un público ya cuenta como alcanzable.** Es lo que F0
encargaba («leer `--_c-x: var(--c-y)` y reclasificar») y sólo se había
implementado para el INFORME: la columna «¿deriva de un público?» de la §3 lo
decía, pero el contador lo seguía puntuando como deuda. Medido: **240 privados
en 59 componentes** estaban en ese estado, haciendo exactamente lo que la
doctrina prescribe y penalizados por ello.
El cierre es TRANSITIVO (`--_a: calc(var(--_b) * .3)` alcanza si `--_b`
alcanza) y tiene muta-prueba: apuntar un privado a un primitivo crudo baja la
cifra, revertirlo la sube. **Global 50 % → 56 %** , privados 625 → 341, al 100 %
10 → 14. `avatar` pasó de 36 % a 75 % y `picker-shell` de 76 % a 95 % **sin
tocar una línea de su código**.
Consecuencia: **cualquier cifra anterior al 2026-08-22 no es comparable con las
de ahora.** Las de este handoff ya son las nuevas.
fix(theming): el slot de tinta es `fg` — R-5.3 me cazó tres claves, y el handoff de mañana
Los guards globales, corridos antes de cerrar la sesión, encontraron una
regresión MÍA: `--names` daba **3 desviadas en 2 componentes** y
`component:audit` había pasado de 4 a **6 NEEDS-WORK**.
La causa: en `text-circular` y `text-focus` nombré tres claves con el slot
`color` (`color`, `border-color`, `glow-color`) cuando la gramática firmada el
2026-08-20 dice que **el slot de tinta es `fg` y `color` como slot MUERE**.
R-5.3 existe justamente para eso y funcionó — pero después de que yo lo
escribiera, porque el guard es de catálogo y yo verificaba `--only`.
Renombradas a `fg`, `border-fg` y `glow-fg`, con los destinos que el propio
censo indica en su salida `--names`. Value-preserving verificado: **0 diffs**
en text-circular (5.916 valores) y en text-focus (1.769), guards 5/6 y 8/8,
`--names` **DESVIADAS 0**, audit de vuelta a **162 PASS / 4 NEEDS-WORK** (los
cuatro preexistentes y ajenos al eje).
La lección para el protocolo, y va al handoff: **la propuesta §4 generada usa
el slot `color`**, así que es la cuarta manera en que falla — y como el paso 7
corre los guards con `--only`, la desviación de gramática sólo aparece al
correr `--names` GLOBAL. Ese comando ya estaba en el paso 1 del handoff; ahora
también se explica por qué no basta con el audit.
Y el HANDOFF para mañana: cifras nuevas (56 %, sin contrato 25, al 100 % 16),
la lista de los 24 componentes de F2-B con su alcance, las tres cosas que el
bloque enseñó y hay que tener delante al seguir (las cuatro maneras en que
falla la propuesta · declarar un público «de facto» puede MATAR una escala si
detrás hay un privado por talla · cuenta los nodos que ve la sonda antes de
creerte un gate verde), y la cola actualizada — con `mockup` marcado como SIN
DEMO (404) y `text-scramble` como «0 % estructural, escribe el veredicto, no
tokens».
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
### 3. Lo que dejó la sesión del 22 (24 componentes en F2-B, todos con 0 diffs)
`grid-list` 85 · `textarea` 80 · `tree-view` 92 · `code-block` 81 ·
`card-group` 90 · `spinner` 70 · `picker-shell` 95 · `link-preview` 100 ·
`drag-drop` 89 · `kbd` 86 · `link` 36 · `code` 100 · `virtual-list` 67 ·
`virtual-grid` 73 · `skeleton` 45 · `label` 100 · `announce` 88 ·
`clipboard` 63 · `scroll-frames` 33 · `mark` 60 · `menu-dial` 38 · `knob` 75 ·
`skin-media-player` 22 · `text-circular` 100 · `text-focus` 100. Más cinco
adjudicados como **0 % estructural** (`aspect-ratio`, `text-blur` , `cascade` ,
`motion` , `date-picker` ), la revisión adversarial §7.7, el arreglo del censo y
el test del suelo.
**Tres cosas que enseñó y conviene tener delante al seguir:**
1. **La propuesta §4 generada falla de cuatro maneras.** Hereda el
incumplimiento del CSS de partida (si ya violaba `recipe-css-contract` , lo
repite); infla hasta 4× (71 tokens para 23 knobs en `spinner` ); nombra por
el primer selector de la regla y no por el nodo pintado (llamaba `root-bg` a
una guía de indentación); y **usa el slot `color` donde la gramática firmada
exige `fg` ** — R-5.3 lo caza, pero después de escribirlo.
2. **Declarar un público «de facto» puede MATAR una escala.** Varios
componentes consumen `var(--c-x, <default>)` sin declarar `--c-x` . Si detrás
del fallback hay un privado por talla (`knob`), declarar el público lo mata:
el público va primero y gana siempre. Hay que subir la escala al TSC. Si no
lo hay (`text-circular`, `text-focus` ), declarar es seguro.
3. **Cuenta los nodos que ve la sonda antes de creerte un gate verde.** Cuatro
componentes miden UN nodo, `picker-shell` medía CERO. Y el centinela tiene
tres puntos ciegos ya medidos: congela transiciones, su valor grande se capa
contra `min()` , y no ve lo que la demo no monta.
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 3.bis Lo último ejecutado — `text-gradient` (2026-08-22)
**67 % → 100 %**, 7 claves. Detalle y porqués en `PLAN §8` y en el veredicto
§5 de su ficha. Lo que conviene tener delante al seguir con la cola:
- **La ficha §4 falla también contra el CATÁLOGO, no sólo contra la doctrina.**
Proponía conservar `weight` (77 claves del catálogo dicen `font-weight` ), y
marcaba `width` /`height` con «⚠ eje físico» cuando el `calc(100% - 2px)`
era UN knob —el trazo del marco— del que el hueco deriva. **Antes de acuñar
un nombre, cuéntalo en `base.ts` **: el catálogo es el que habla.
- **Un literal igual a un paso de escala no se congela: se nombra.**
`1px` → `var(--border-width)` , `blur(4px)` → `var(--blur-sm)` . Y ojo con
`blur` : es familia MÉTRICA del eje `scaling` (theming §23), así que un
literal es ciego al zoom global aunque el computed coincida a scaling 100.
- **Los dos instrumentos ya saben encender una VARIANTE.** La sonda tiene
`DEMO_VARIANTS` (chips que se re-pulsan tras CADA recarga — la sonda recarga
una vez por talla) y el centinela acepta `openWith` apuntando a un chip, no
sólo a un trigger de panel. Sin eso, esta demo daba 2 nodos y los tres tokens
del marco no se medían. **Cuenta los nodos antes de creerte el gate.**
docs(theming): las demos quedan FUERA del eje — sólo los componentes
Decisión del autor (2026-08-22): «las demos de los componentes no son
importantes y no deben formar parte del plan, solo los componentes». Y sobre la
escala: «las escalas tipográficas son las que están definidas en el módulo
tipografía, esos son los valores canónicos» — prohibido tocar sus valores.
Se retira del registro del eje (PLAN §8 · CONTINUE §3.bis · next-features §13)
todo lo que hablaba de demos o del harness del sitio: los escenarios de
text-gradient/text-blur, la tabla de tokens a mano de la pestaña API, las
pestañas Tokens que faltan, las 37 referencias `--uix-font-size-*`. Nada de eso
es trabajo del eje: la demo se toca sólo como INSTRUMENTO para que la sonda vea
los nodos, y lo mínimo.
Queda lo que sí es del componente: `text-focus.word-size` referencia
`--font-size-4xl`, un paso que la escala canónica (`xxs..xxxl`,
`lib/primitives/typography.ts`) no tiene y que entró como idioma Tailwind con el
import de julio. El módulo de tipografía no se toca; el fix va en el consumidor,
a un paso o estilo EXISTENTE, y espera firma porque `3rem` cae entre `xxl` y
`xxxl` y mueve el default.
docs:check 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **Las demos NO forman parte del eje** (autor, 2026-08-22): sólo los
componentes. Un hallazgo de demo o del harness del sitio no se registra ni se
mide; la demo se toca únicamente como INSTRUMENTO (encender una variante para
que la sonda vea los nodos), y lo mínimo.
uix(text-gradient): temable — 67 % → 100 %, y la ficha se equivocaba en las TRES
7 claves en una entrada NUEVA de `recipes/base.ts`. Censo 67 % → **100 %**
(10 knobs, 10 públicos, 0 literales). Global 2.634 → 2.638 públicos.
Tercero de la familia text-* con la forma de text-circular / text-focus: cuatro
públicos «de facto» consumidos con fallback y **ninguno declarado**, más tres
literales. Sin eje `size` ni privados propios, así que declarar no puede matar
una escala.
LO QUE LA PROPUESTA §4 NO ACERTABA — y se resolvió midiendo contra el CATÁLOGO,
no contra el CSS de partida:
- **`weight` era deriva de nombre.** El catálogo tiene 77 claves `font-weight`
contra 4 `weight` a secas — y dos de esas cuatro las escribí ayer en esta
misma familia. Pasa a `font-weight`, y su valor al paso de escala que ya
pintaba: `500` ES `var(--font-weight-medium)`.
- **El `calc(100% - 2px)` del hueco no son dos knobs de eje físico** (la ficha
los marcaba con ⚠ «pártelo en ejes lógicos») sino UNO: los 2 px son el trazo
del marco asomando a cada lado. El knob es `border-width` —con
`var(--border-width)`, el paso `thin`, no un literal `1px` igual a un paso de
la escala— y el hueco DERIVA de él.
- **`blur(4px)` va a la escala de blur.** `blur` es familia MÉTRICA del eje
`scaling` (theming §23): un literal es ciego al zoom global. `--blur-sm` es
`calc(4px * var(--scaling))` — el mismo píxel al 100 y el correcto al 90/110.
Precedente: `dialog`/`drawer.overlay-blur`, también backdrop-filter.
Y el shorthand físico `padding: var(--space-1) var(--space-2)` se parte en ejes
lógicos (`border-padding-block` / `-inline`), que es la normalización firmada el
2026-07-06; el público de facto `border-padding` era un knob con dos ejes dentro.
FUERA DEL CONTRATO POR NATURALEZA: `--text-gradient-duration` y
`--_text-gradient-play-direction` los escribe el wrapper INLINE desde
`animationSpeed` y `yoyo`. Ningún token gana a un estilo inline, así que
declararlos sería acuñar dos nombres que mienten — misma clase que los cuatro
ausentes de `knob`, el `item-gap` de `carousel` y el `preview-z` de `drag-drop`.
INSTRUMENTO — dos puntos ciegos arreglados ANTES de medir (la lección de F2-B
aplicada por adelantado, no después):
- La demo arranca con `showBorder=false`, así que sonda y centinela veían **2
nodos** y NINGUNO de los tres tokens del marco. La sonda gana un mapa
`DEMO_VARIANTS` (chips de variante re-pulsados tras CADA recarga — recarga una
vez por talla) y el centinela una entrada `openWith` que no abre un panel sino
que ENCIENDE UNA VARIANTE: uso nuevo del mismo gancho, sin tocar su lógica.
- La sonda no leía `backdropFilter`, que `KNOB_PROPS` del censo sí cuenta y el
centinela ya leía: un token de fondo podía moverse entre las dos corridas sin
que el diff lo viera. Con la propiedad y la variante: **4 nodos**.
Hover: 0 nodos medidos, y es correcto — las cuatro partes son `<span>` y el
único efecto de hover es `animation-play-state`, que no es knob.
LO QUE ABRE (→ next-features §13): **la escala de `font-size` no tiene
`2xl`/`3xl`/`4xl`** —es `xxs..xxxl`— y tres sitios los referencian: el token
`text-focus.word-size` que subí ayer (`var(--font-size-4xl, 3rem)`, pintando el
fallback: segundo fantasma de la clase `--radius-xs`, y en el contrato) y los
escenarios de las demos de `text-gradient` y `text-blur`, que sin fallback dejan
la declaración INVÁLIDA y enseñan un efecto tipográfico a 14 px. Y
`--uix-font-size-*` no existe: 37 referencias en el harness del sitio, cero
declaraciones. Ninguno se toca aquí: se miden, se anotan y van a su commit.
Demo: pestaña **Tokens** (`TokensPanel`, contrato vivo) y fuera la tabla de
tokens escrita a mano de la pestaña API — era una segunda fuente de verdad y
este renombrado la dejaba obsoleta al instante.
Gates: sonda antes/después **0 diffs** en 720 valores × 7 estados (4 nodos) ·
centinela **7/7 sin una sola excepción** · censo --only 100 % · --names 7/7
conformes · component-audit PASS · eidos-lint 0 invalid · suite eidos con el
único rojo conocido (`skin-media-player`, lint.test.ts) · rtl:check 0 ·
docs:check 0 · `check` con 72 errores globales y **0 en los ficheros tocados**.
Prettier: los seis ficheros editados ya estaban sucios en HEAD (el repo tiene
2.392), no se reformatean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(theming): text-scramble no tiene contrato que escribir — y el barrido de fantasmas, hecho
Sin cambios de código: su veredicto §5 y el registro.
Su 50 % no es ni deuda ni logro, es la suma de dos cosas que no son knobs:
- **Los dos literales son la técnica sr-only** (`width`/`height: 1px` en
`[data-text-scramble-sr]`), idéntica a `text-blur` y al resto del catálogo:
existe para que el lector de pantalla lea el texto entero mientras la versión
animada se parte en caracteres. Moverlos rompe la técnica.
- **Los dos «públicos» son tipografía que el componente no debería acuñar**
(`--text-scramble-font` / `-size`, con fallback y sin declarar): es la firma
1.bis pendiente. Escribirles contrato ahora sería acuñar exactamente lo que
esa firma propone retirar. Con esto, 1.bis son **7 claves en 4 de los 5**
componentes de la familia; `text-blur` es el único limpio: hereda.
Y el `font` apunta a un FANTASMA, medido en navegador: `var(--font-mono, …)` no
existe —el canónico es `--font-family-mono`— así que pinta `ui-monospace` e
**ignora la mono del tema** (`Azeret Mono`, que está ahí al lado).
EL BARRIDO MECÁNICO QUE PEDÍAN `--radius-xs` Y `--font-size-4xl`, HECHO:
5.793 custom properties declaradas en todo `src/uix/eidos` (fundación + capas +
recetas + `generated/`) contra los `var(--x, fallback)` de las recetas cuyo `--x`
sea vocabulario de SISTEMA → **9 referencias fantasma**. (Las que van sin
fallback ya las guarda `recipe-css-contract`; el hueco eran justo las que
«funcionan por accidente».) Tabla completa en next-features §13:
- `--color-content-tertiary` ×3 (float-panel, proof-of-human clock y
rotate-align) — **es el caso histórico** que `recipe-contract` §1 cita como
origen de su propia regla, y sigue vivo en tres recetas.
- `--style-{body,prose,label,caption}-font-variation-settings` ×4 (`text.css`) —
los estilos con nombre emiten 6 claves y ésta no está: **el eje de fuente
variable no llega nunca al primitivo `Text`**.
- `--font-family-sans` (palabras, track WIP) y `--font-mono` (text-scramble).
Ninguna se arregla aquí. El barrido debería ser un guard: misma lógica que el
test de fantasmas sin fallback que ya existe, con el filtro de vocabulario de
sistema encima.
Guards: censo `--only` sin cambio (50 %, es el veredicto lo que cambia) ·
component-audit PASS · docs:check 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 3.ter `text-scramble` ADJUDICADO (2026-08-22) — sin contrato, y por qué
Su 50 % no es deuda ni logro: **dos literales son la técnica sr-only** (como
`text-blur` ) y **los dos «públicos» son tipografía que no debería acuñar** , o
sea la firma 1.bis. Escribirle un contrato ahora sería acuñar justo lo que esa
firma propone retirar. Veredicto §5 escrito, cero cambios de código.
De paso salió el **barrido mecánico** que `--radius-xs` y `--font-size-4xl`
pedían: **9 referencias fantasma con fallback** en el catálogo (5.793 nombres
declarados). Tabla en next-features §13. La más grave no es de esta familia:
`--color-content-tertiary` sigue vivo en TRES recetas —es el caso histórico que
`recipe-contract` §1 cita como origen de la regla— y los cuatro
`--style-*-font-variation-settings` de `text.css` significan que **el eje de
fuente variable no llega nunca al primitivo `Text` **. Ninguna se arregla aquí.
uix(chart): temable — 9 % → 49 %, y la ficha pedía 71 claves de las que 20 llevaban el mismo valor
22 claves nuevas fusionadas en el bloque existente (17 → 39). Censo chart
9 % → **49 %**; global **56 % → 57 %** (2.638 → 2.675 públicos; <20 % 19 → 18).
LO QUE DECIDIÓ LA MEDICIÓN, no la ficha:
Las 84 filas fuera de alcance colapsaban a **26 valores distintos** — 9×
`content-secondary`, 9× `content-primary`, 9× `100%`, 6× `font-size-sm`, 6×
`font-size-xs`, 6× `font-weight-medium`, 5× `surface-default`, 4× `radius-sm`.
Nombrar por parte habría dado cinco claves para el pelo que separa marcas y
nueve para la tinta de anotación: la inflación de `spinner` con otra cara.
**El texto no recibe tokens de chart.** Toma su ROL DE TAMAÑO de los estilos con
nombre del canon —`caption` (xs) para las anotaciones, `label` (sm) para el
texto de cromo— y su tinta y su peso de las ranuras del sistema. Acuñar
`--chart-label-font-size` sería tipografía por componente, prohibido el mismo
día para text-effects. Precedente de consumir la capa: `code-block`, `kbd`,
`announce`. Doce declaraciones cambian de `var(--font-size-xs|sm)` a
`var(--style-caption|label-font-size)`, mismo valor.
**La receta sólo posee la tinta de lo que NO es texto**, cinco claves y cada una
un concepto que el CSS repite: `axis-fg` (eje + rejilla smith + conector del
funnel), `grid-fg` (rejilla + radar), `separator-fg` (el pelo de superficie
entre marcas contiguas: porción, punto activo, punto smith, pétalo, segmento),
`smith-boundary-fg` y `track-bg` (raíl del bar-list y del gauge). Más 17 de
geometría de cromo (gap, los pares row/column-gap, swatch, track, bar-segment).
**Incumplimiento heredado, corregido**: el CSS leía la coordenada CRUDA
`var(--control-height-sm, var(--space-7))` —lo que `recipe-css-contract`
prohíbe— con un respaldo muerto. El contrato guarda el bundle
`var(--size-sm-control-height)`, 1:1 con él.
**Identidad fuera de contrato**: los nueve `100%` («ocupa a tu padre») y el
`min-inline-size: max-content` del tooltip, misma clase ya adjudicada en
`date-picker`. Por eso el techo es 49 %, y está escrito en el veredicto §5.
EL INSTRUMENTO ESTABA CIEGO DE TRES MANERAS, arregladas ANTES de medir:
1. **La superficie de chart vive en DIECISÉIS rutas de demo**, una por tipo:
`/chart` monta 58 nodos de los 302 de la página y el resto vive en rutas
hermanas. El guard gana `urls`: un token está VIVO si ALGUNA superficie del
componente lo sigue, y cada URL sólo re-prueba lo pendiente (el caso de una
sola ruta cuesta exactamente lo que costaba). De paso `picker-shell` recibe
la suya: su ruta propia es un 404 y se mide dentro del date-picker — pasa de
0/31 con seis sin adjudicar a 6/31 sin tener que saberlo.
2. **El guard no leía NINGUNA propiedad SVG.** Sin `fill` / `stroke` /
`strokeWidth` / `fillOpacity` / `r` / `rx`, doce de las 39 claves de un
componente que dibuja en SVG leían muertas estando vivas. Misma clase que el
hueco de `filter` / `backdrop-filter` de ayer.
3. **El valor centinela se elegía por SUBCADENA**: `slice-stroke-width` recibía
`rgb(1, 2, 3)` por contener «stroke» —inválido para una longitud— y leía
muerta. La dimensión se decide ahora por el FINAL del nombre.
Los 28 componentes con ledger re-verificados tras los tres cambios: cero
regresiones, cero STALE. (Ojo: doce a la vez contra el mismo dev server dan
falsos exit=1 por timeout; repetir a solas antes de creerse una regresión.)
Gates: sonda **0 diffs en 161.850 valores computados** (16 rutas × 7 estados,
892 nodos en reposo) · centinela **37/39**, con `transition-duration` y `-ease`
adjudicadas midiéndolas sin la congelación (0,12s → 11,5s · cubic-bezier →
steps(4)) · censo --only 49 % · --names 39/39 conformes · component-audit PASS ·
eidos-lint 0 invalid · suite eidos con el único rojo conocido
(`skin-media-player`) · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados.
Abre (→ next-features §13): el censo **penaliza consumir la capa tipográfica**
si no eres un primitivo —las doce declaraciones correctas siguen contando
`global`—, que es la nota de `listbox` con una segunda ocurrencia; y el número
del gauge no tiene estilo con nombre que lo represente (un readout de dato no es
un `h2`), así que sigue leyendo `--font-size-xl`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 3.quater `chart` 9 % → 49 % (2026-08-22) — y el instrumento, tres veces ciego
Lo que hay que llevarse de aquí:
- **La ficha pedía 71 claves; ~20 habrían llevado el mismo valor.** Antes de
aceptar una propuesta larga, **agrupa las filas por VALOR** : las 84 de chart
colapsaban a 26. Nombrar por parte lo que es un concepto es la inflación de
`spinner` con otra cara.
- **El texto de un componente no recibe tokens propios**: rol de tamaño de los
estilos con nombre (`caption` / `label` ), tinta y peso de las ranuras del
sistema. Sólo se acuña la tinta de lo que NO es texto.
- **El guard ya sabe medir en VARIAS rutas** (`urls` en `COMPONENT_OVERRIDES` ):
un token vive si alguna superficie del componente lo sigue. Chart tiene 16;
`picker-shell` ya lleva la del date-picker (su ruta propia es 404).
- **El guard ya lee SVG** (`fill`, `stroke` , `strokeWidth` , `fillOpacity` , `r` ,
`rx` …) y **elige el valor centinela por el FINAL del nombre** , no por
subcadena — `slice-stroke-width` recibía un color por contener «stroke».
- Tras cualquier cambio del guard: **re-verificar los 28 componentes con
ledger** (se hizo: cero regresiones, cero STALE). Y ojo, correr doce a la vez
contra el mismo dev server da falsos exit=1 por timeout: repite el que falle
a solas antes de creerte una regresión.
fix(theming): el slot de tinta es `fg` — R-5.3 me cazó tres claves, y el handoff de mañana
Los guards globales, corridos antes de cerrar la sesión, encontraron una
regresión MÍA: `--names` daba **3 desviadas en 2 componentes** y
`component:audit` había pasado de 4 a **6 NEEDS-WORK**.
La causa: en `text-circular` y `text-focus` nombré tres claves con el slot
`color` (`color`, `border-color`, `glow-color`) cuando la gramática firmada el
2026-08-20 dice que **el slot de tinta es `fg` y `color` como slot MUERE**.
R-5.3 existe justamente para eso y funcionó — pero después de que yo lo
escribiera, porque el guard es de catálogo y yo verificaba `--only`.
Renombradas a `fg`, `border-fg` y `glow-fg`, con los destinos que el propio
censo indica en su salida `--names`. Value-preserving verificado: **0 diffs**
en text-circular (5.916 valores) y en text-focus (1.769), guards 5/6 y 8/8,
`--names` **DESVIADAS 0**, audit de vuelta a **162 PASS / 4 NEEDS-WORK** (los
cuatro preexistentes y ajenos al eje).
La lección para el protocolo, y va al handoff: **la propuesta §4 generada usa
el slot `color`**, así que es la cuarta manera en que falla — y como el paso 7
corre los guards con `--only`, la desviación de gramática sólo aparece al
correr `--names` GLOBAL. Ese comando ya estaba en el paso 1 del handoff; ahora
también se explica por qué no basta con el audit.
Y el HANDOFF para mañana: cifras nuevas (56 %, sin contrato 25, al 100 % 16),
la lista de los 24 componentes de F2-B con su alcance, las tres cosas que el
bloque enseñó y hay que tener delante al seguir (las cuatro maneras en que
falla la propuesta · declarar un público «de facto» puede MATAR una escala si
detrás hay un privado por talla · cuenta los nodos que ve la sonda antes de
creerte un gate verde), y la cola actualizada — con `mockup` marcado como SIN
DEMO (404) y `text-scramble` como «0 % estructural, escribe el veredicto, no
tokens».
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
### 4. La cola que queda, por alcance
uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca
código y es la que importa.
1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré
como «declaraciones muertas» apoyándome en los precedentes de
`table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el
mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el
plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es
neutral — **fija el empate a favor del plano** y con él su 1.25 donde la
receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9
tiene pendiente para VEINTE componentes. Un empate por orden de carga se
registra, no se resuelve dentro de un commit de tokenización.
Y la medición era más débil de lo que el commit daba a entender: estable en
cinco recargas **en DEV**, sin verificar en un bundle de producción.
Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5
tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa
razón. §12.9 y el README dicen ahora que el par se deja declarado A
PROPÓSITO.
2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al
100 % o excepción escrita», y la válvula que `component-audit` lee es esa
marca (`readmeException`), no la prosa. Tenía la razón documentada en
veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2,
los 16 habrían dado error. Una marca por clase medida — forward de paleta
THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de
layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan.
3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por
grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el
protocolo lo pide por componente: una sesión nueva habría arrancado con el
mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la
revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8
firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus
seis lecciones, y actualizada la memoria del eje.
4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el
bloque, cada una nombrando la forma en que el instrumento mentía, más la
lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso
— `picker-shell` medía CERO y reportaba éxito.
5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`,
`virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md
(md→sm); los valores eran correctos (0 diffs), el texto no.
Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B
sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la
medición de quien los escribió, y el plan la exige antes de cerrar un bloque.
El handoff nombra las dos hipótesis concretas que hay que atacar.
Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves ·
`component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno
con un commit de esta sesión en su código) · R-5.4 verde en los 22 ·
suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero
limpio.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
| componente | knobs | nota |
| --- | ---: | --- |
fix(theming): el slot de tinta es `fg` — R-5.3 me cazó tres claves, y el handoff de mañana
Los guards globales, corridos antes de cerrar la sesión, encontraron una
regresión MÍA: `--names` daba **3 desviadas en 2 componentes** y
`component:audit` había pasado de 4 a **6 NEEDS-WORK**.
La causa: en `text-circular` y `text-focus` nombré tres claves con el slot
`color` (`color`, `border-color`, `glow-color`) cuando la gramática firmada el
2026-08-20 dice que **el slot de tinta es `fg` y `color` como slot MUERE**.
R-5.3 existe justamente para eso y funcionó — pero después de que yo lo
escribiera, porque el guard es de catálogo y yo verificaba `--only`.
Renombradas a `fg`, `border-fg` y `glow-fg`, con los destinos que el propio
censo indica en su salida `--names`. Value-preserving verificado: **0 diffs**
en text-circular (5.916 valores) y en text-focus (1.769), guards 5/6 y 8/8,
`--names` **DESVIADAS 0**, audit de vuelta a **162 PASS / 4 NEEDS-WORK** (los
cuatro preexistentes y ajenos al eje).
La lección para el protocolo, y va al handoff: **la propuesta §4 generada usa
el slot `color`**, así que es la cuarta manera en que falla — y como el paso 7
corre los guards con `--only`, la desviación de gramática sólo aparece al
correr `--names` GLOBAL. Ese comando ya estaba en el paso 1 del handoff; ahora
también se explica por qué no basta con el audit.
Y el HANDOFF para mañana: cifras nuevas (56 %, sin contrato 25, al 100 % 16),
la lista de los 24 componentes de F2-B con su alcance, las tres cosas que el
bloque enseñó y hay que tener delante al seguir (las cuatro maneras en que
falla la propuesta · declarar un público «de facto» puede MATAR una escala si
detrás hay un privado por talla · cuenta los nodos que ve la sonda antes de
creerte un gate verde), y la cola actualizada — con `mockup` marcado como SIN
DEMO (404) y `text-scramble` como «0 % estructural, escribe el veredicto, no
tokens».
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
| `palabras` | 471 (22 %) | **el mayor del catálogo con diferencia** , sin tocar; D-TH.3 decide si entra |
uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca
código y es la que importa.
1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré
como «declaraciones muertas» apoyándome en los precedentes de
`table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el
mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el
plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es
neutral — **fija el empate a favor del plano** y con él su 1.25 donde la
receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9
tiene pendiente para VEINTE componentes. Un empate por orden de carga se
registra, no se resuelve dentro de un commit de tokenización.
Y la medición era más débil de lo que el commit daba a entender: estable en
cinco recargas **en DEV**, sin verificar en un bundle de producción.
Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5
tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa
razón. §12.9 y el README dicen ahora que el par se deja declarado A
PROPÓSITO.
2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al
100 % o excepción escrita», y la válvula que `component-audit` lee es esa
marca (`readmeException`), no la prosa. Tenía la razón documentada en
veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2,
los 16 habrían dado error. Una marca por clase medida — forward de paleta
THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de
layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan.
3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por
grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el
protocolo lo pide por componente: una sesión nueva habría arrancado con el
mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la
revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8
firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus
seis lecciones, y actualizada la memoria del eje.
4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el
bloque, cada una nombrando la forma en que el instrumento mentía, más la
lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso
— `picker-shell` medía CERO y reportaba éxito.
5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`,
`virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md
(md→sm); los valores eran correctos (0 diffs), el texto no.
Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B
sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la
medición de quien los escribió, y el plan la exige antes de cerrar un bloque.
El handoff nombra las dos hipótesis concretas que hay que atacar.
Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves ·
`component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno
con un commit de esta sesión en su código) · R-5.4 verde en los 22 ·
suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero
limpio.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
| `field-langs` | 41 | espera el **mandato Field** (firma 2), que NO es tokenización: manda que los x-field incorporen el wrapper Field — cambia composición y mueve píxel |
fix(theming): el slot de tinta es `fg` — R-5.3 me cazó tres claves, y el handoff de mañana
Los guards globales, corridos antes de cerrar la sesión, encontraron una
regresión MÍA: `--names` daba **3 desviadas en 2 componentes** y
`component:audit` había pasado de 4 a **6 NEEDS-WORK**.
La causa: en `text-circular` y `text-focus` nombré tres claves con el slot
`color` (`color`, `border-color`, `glow-color`) cuando la gramática firmada el
2026-08-20 dice que **el slot de tinta es `fg` y `color` como slot MUERE**.
R-5.3 existe justamente para eso y funcionó — pero después de que yo lo
escribiera, porque el guard es de catálogo y yo verificaba `--only`.
Renombradas a `fg`, `border-fg` y `glow-fg`, con los destinos que el propio
censo indica en su salida `--names`. Value-preserving verificado: **0 diffs**
en text-circular (5.916 valores) y en text-focus (1.769), guards 5/6 y 8/8,
`--names` **DESVIADAS 0**, audit de vuelta a **162 PASS / 4 NEEDS-WORK** (los
cuatro preexistentes y ajenos al eje).
La lección para el protocolo, y va al handoff: **la propuesta §4 generada usa
el slot `color`**, así que es la cuarta manera en que falla — y como el paso 7
corre los guards con `--only`, la desviación de gramática sólo aparece al
correr `--names` GLOBAL. Ese comando ya estaba en el paso 1 del handoff; ahora
también se explica por qué no basta con el audit.
Y el HANDOFF para mañana: cifras nuevas (56 %, sin contrato 25, al 100 % 16),
la lista de los 24 componentes de F2-B con su alcance, las tres cosas que el
bloque enseñó y hay que tener delante al seguir (las cuatro maneras en que
falla la propuesta · declarar un público «de facto» puede MATAR una escala si
detrás hay un privado por talla · cuenta los nodos que ve la sonda antes de
creerte un gate verde), y la cola actualizada — con `mockup` marcado como SIN
DEMO (404) y `text-scramble` como «0 % estructural, escribe el veredicto, no
tokens».
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
| `mockup` | 29 (38 %) | ⚠ **sin ruta de demo (404)** , como `picker-shell` : sin sonda ni guard. Y sale NEEDS-WORK en el audit, preexistente |
| `chronos` | 245 (1 %) | el segundo mayor; sin abrir |
uix(chart): temable — 9 % → 49 %, y la ficha pedía 71 claves de las que 20 llevaban el mismo valor
22 claves nuevas fusionadas en el bloque existente (17 → 39). Censo chart
9 % → **49 %**; global **56 % → 57 %** (2.638 → 2.675 públicos; <20 % 19 → 18).
LO QUE DECIDIÓ LA MEDICIÓN, no la ficha:
Las 84 filas fuera de alcance colapsaban a **26 valores distintos** — 9×
`content-secondary`, 9× `content-primary`, 9× `100%`, 6× `font-size-sm`, 6×
`font-size-xs`, 6× `font-weight-medium`, 5× `surface-default`, 4× `radius-sm`.
Nombrar por parte habría dado cinco claves para el pelo que separa marcas y
nueve para la tinta de anotación: la inflación de `spinner` con otra cara.
**El texto no recibe tokens de chart.** Toma su ROL DE TAMAÑO de los estilos con
nombre del canon —`caption` (xs) para las anotaciones, `label` (sm) para el
texto de cromo— y su tinta y su peso de las ranuras del sistema. Acuñar
`--chart-label-font-size` sería tipografía por componente, prohibido el mismo
día para text-effects. Precedente de consumir la capa: `code-block`, `kbd`,
`announce`. Doce declaraciones cambian de `var(--font-size-xs|sm)` a
`var(--style-caption|label-font-size)`, mismo valor.
**La receta sólo posee la tinta de lo que NO es texto**, cinco claves y cada una
un concepto que el CSS repite: `axis-fg` (eje + rejilla smith + conector del
funnel), `grid-fg` (rejilla + radar), `separator-fg` (el pelo de superficie
entre marcas contiguas: porción, punto activo, punto smith, pétalo, segmento),
`smith-boundary-fg` y `track-bg` (raíl del bar-list y del gauge). Más 17 de
geometría de cromo (gap, los pares row/column-gap, swatch, track, bar-segment).
**Incumplimiento heredado, corregido**: el CSS leía la coordenada CRUDA
`var(--control-height-sm, var(--space-7))` —lo que `recipe-css-contract`
prohíbe— con un respaldo muerto. El contrato guarda el bundle
`var(--size-sm-control-height)`, 1:1 con él.
**Identidad fuera de contrato**: los nueve `100%` («ocupa a tu padre») y el
`min-inline-size: max-content` del tooltip, misma clase ya adjudicada en
`date-picker`. Por eso el techo es 49 %, y está escrito en el veredicto §5.
EL INSTRUMENTO ESTABA CIEGO DE TRES MANERAS, arregladas ANTES de medir:
1. **La superficie de chart vive en DIECISÉIS rutas de demo**, una por tipo:
`/chart` monta 58 nodos de los 302 de la página y el resto vive en rutas
hermanas. El guard gana `urls`: un token está VIVO si ALGUNA superficie del
componente lo sigue, y cada URL sólo re-prueba lo pendiente (el caso de una
sola ruta cuesta exactamente lo que costaba). De paso `picker-shell` recibe
la suya: su ruta propia es un 404 y se mide dentro del date-picker — pasa de
0/31 con seis sin adjudicar a 6/31 sin tener que saberlo.
2. **El guard no leía NINGUNA propiedad SVG.** Sin `fill` / `stroke` /
`strokeWidth` / `fillOpacity` / `r` / `rx`, doce de las 39 claves de un
componente que dibuja en SVG leían muertas estando vivas. Misma clase que el
hueco de `filter` / `backdrop-filter` de ayer.
3. **El valor centinela se elegía por SUBCADENA**: `slice-stroke-width` recibía
`rgb(1, 2, 3)` por contener «stroke» —inválido para una longitud— y leía
muerta. La dimensión se decide ahora por el FINAL del nombre.
Los 28 componentes con ledger re-verificados tras los tres cambios: cero
regresiones, cero STALE. (Ojo: doce a la vez contra el mismo dev server dan
falsos exit=1 por timeout; repetir a solas antes de creerse una regresión.)
Gates: sonda **0 diffs en 161.850 valores computados** (16 rutas × 7 estados,
892 nodos en reposo) · centinela **37/39**, con `transition-duration` y `-ease`
adjudicadas midiéndolas sin la congelación (0,12s → 11,5s · cubic-bezier →
steps(4)) · censo --only 49 % · --names 39/39 conformes · component-audit PASS ·
eidos-lint 0 invalid · suite eidos con el único rojo conocido
(`skin-media-player`) · rtl:check 0 · docs:check 0 · `check` con 72 errores
globales y 0 en los ficheros tocados.
Abre (→ next-features §13): el censo **penaliza consumir la capa tipográfica**
si no eres un primitivo —las doce declaraciones correctas siguen contando
`global`—, que es la nota de `listbox` con una segunda ocurrencia; y el número
del gauge no tiene estilo con nombre que lo represente (un readout de dato no es
un `h2`), así que sigue leyendo `--font-size-xl`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| `proof-of-human` | 95 (15 %) | escena de tono fijo firmada; lo demás son hooks de clase (eje aparte) |
| `prose` | 74 (1 %) | su superficie es la capa tipográfica: mismo caso que chart, un piso más arriba |
fix(theming): el slot de tinta es `fg` — R-5.3 me cazó tres claves, y el handoff de mañana
Los guards globales, corridos antes de cerrar la sesión, encontraron una
regresión MÍA: `--names` daba **3 desviadas en 2 componentes** y
`component:audit` había pasado de 4 a **6 NEEDS-WORK**.
La causa: en `text-circular` y `text-focus` nombré tres claves con el slot
`color` (`color`, `border-color`, `glow-color`) cuando la gramática firmada el
2026-08-20 dice que **el slot de tinta es `fg` y `color` como slot MUERE**.
R-5.3 existe justamente para eso y funcionó — pero después de que yo lo
escribiera, porque el guard es de catálogo y yo verificaba `--only`.
Renombradas a `fg`, `border-fg` y `glow-fg`, con los destinos que el propio
censo indica en su salida `--names`. Value-preserving verificado: **0 diffs**
en text-circular (5.916 valores) y en text-focus (1.769), guards 5/6 y 8/8,
`--names` **DESVIADAS 0**, audit de vuelta a **162 PASS / 4 NEEDS-WORK** (los
cuatro preexistentes y ajenos al eje).
La lección para el protocolo, y va al handoff: **la propuesta §4 generada usa
el slot `color`**, así que es la cuarta manera en que falla — y como el paso 7
corre los guards con `--only`, la desviación de gramática sólo aparece al
correr `--names` GLOBAL. Ese comando ya estaba en el paso 1 del handoff; ahora
también se explica por qué no basta con el audit.
Y el HANDOFF para mañana: cifras nuevas (56 %, sin contrato 25, al 100 % 16),
la lista de los 24 componentes de F2-B con su alcance, las tres cosas que el
bloque enseñó y hay que tener delante al seguir (las cuatro maneras en que
falla la propuesta · declarar un público «de facto» puede MATAR una escala si
detrás hay un privado por talla · cuenta los nodos que ve la sonda antes de
creerte un gate verde), y la cola actualizada — con `mockup` marcado como SIN
DEMO (404) y `text-scramble` como «0 % estructural, escribe el veredicto, no
tokens».
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
| `result` · `banner` · … | varios | ver el censo, `reach < 20 %` = 19 |
`avatar` ya está al **75 %** y `knob` al 75 % (ambos hechos). `heading` /
`text` / `display` son primitivos tipográficos: su superficie es `--style-*` y
el censo los saca del ratio. `range-calendar` / `month-grid` / `year-grid`
están a 0 % **honesto** (consumen `calendar-surface` ).
uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca
código y es la que importa.
1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré
como «declaraciones muertas» apoyándome en los precedentes de
`table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el
mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el
plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es
neutral — **fija el empate a favor del plano** y con él su 1.25 donde la
receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9
tiene pendiente para VEINTE componentes. Un empate por orden de carga se
registra, no se resuelve dentro de un commit de tokenización.
Y la medición era más débil de lo que el commit daba a entender: estable en
cinco recargas **en DEV**, sin verificar en un bundle de producción.
Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5
tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa
razón. §12.9 y el README dicen ahora que el par se deja declarado A
PROPÓSITO.
2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al
100 % o excepción escrita», y la válvula que `component-audit` lee es esa
marca (`readmeException`), no la prosa. Tenía la razón documentada en
veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2,
los 16 habrían dado error. Una marca por clase medida — forward de paleta
THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de
layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan.
3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por
grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el
protocolo lo pide por componente: una sesión nueva habría arrancado con el
mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la
revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8
firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus
seis lecciones, y actualizada la memoria del eje.
4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el
bloque, cada una nombrando la forma en que el instrumento mentía, más la
lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso
— `picker-shell` medía CERO y reportaba éxito.
5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`,
`virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md
(md→sm); los valores eran correctos (0 diffs), el texto no.
Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B
sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la
medición de quien los escribió, y el plan la exige antes de cerrar un bloque.
El handoff nombra las dos hipótesis concretas que hay que atacar.
Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves ·
`component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno
con un commit de esta sesión en su código) · R-5.4 verde en los 22 ·
suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero
limpio.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
`heading` / `text` / `display` son primitivos tipográficos: su superficie es
`--style-*` y el censo ya los saca del ratio. `range-calendar` / `month-grid` /
`year-grid` están a 0 % **honesto** (consumen `calendar-surface` ).
`alert-dialog` , `css-field` , `mask-field` , `number-field` , `highlight` , `stack` ,
`wrap` , `auto-grid` tienen **cero knobs** : no hay nada que tokenizar.
fix(theming): el slot de tinta es `fg` — R-5.3 me cazó tres claves, y el handoff de mañana
Los guards globales, corridos antes de cerrar la sesión, encontraron una
regresión MÍA: `--names` daba **3 desviadas en 2 componentes** y
`component:audit` había pasado de 4 a **6 NEEDS-WORK**.
La causa: en `text-circular` y `text-focus` nombré tres claves con el slot
`color` (`color`, `border-color`, `glow-color`) cuando la gramática firmada el
2026-08-20 dice que **el slot de tinta es `fg` y `color` como slot MUERE**.
R-5.3 existe justamente para eso y funcionó — pero después de que yo lo
escribiera, porque el guard es de catálogo y yo verificaba `--only`.
Renombradas a `fg`, `border-fg` y `glow-fg`, con los destinos que el propio
censo indica en su salida `--names`. Value-preserving verificado: **0 diffs**
en text-circular (5.916 valores) y en text-focus (1.769), guards 5/6 y 8/8,
`--names` **DESVIADAS 0**, audit de vuelta a **162 PASS / 4 NEEDS-WORK** (los
cuatro preexistentes y ajenos al eje).
La lección para el protocolo, y va al handoff: **la propuesta §4 generada usa
el slot `color`**, así que es la cuarta manera en que falla — y como el paso 7
corre los guards con `--only`, la desviación de gramática sólo aparece al
correr `--names` GLOBAL. Ese comando ya estaba en el paso 1 del handoff; ahora
también se explica por qué no basta con el audit.
Y el HANDOFF para mañana: cifras nuevas (56 %, sin contrato 25, al 100 % 16),
la lista de los 24 componentes de F2-B con su alcance, las tres cosas que el
bloque enseñó y hay que tener delante al seguir (las cuatro maneras en que
falla la propuesta · declarar un público «de facto» puede MATAR una escala si
detrás hay un privado por talla · cuenta los nodos que ve la sonda antes de
creerte un gate verde), y la cola actualizada — con `mockup` marcado como SIN
DEMO (404) y `text-scramble` como «0 % estructural, escribe el veredicto, no
tokens».
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
### 5. Lo que espera TU FIRMA (todo medido, nada bloquea la cola)
uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca
código y es la que importa.
1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré
como «declaraciones muertas» apoyándome en los precedentes de
`table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el
mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el
plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es
neutral — **fija el empate a favor del plano** y con él su 1.25 donde la
receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9
tiene pendiente para VEINTE componentes. Un empate por orden de carga se
registra, no se resuelve dentro de un commit de tokenización.
Y la medición era más débil de lo que el commit daba a entender: estable en
cinco recargas **en DEV**, sin verificar en un bundle de producción.
Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5
tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa
razón. §12.9 y el README dicen ahora que el par se deja declarado A
PROPÓSITO.
2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al
100 % o excepción escrita», y la válvula que `component-audit` lee es esa
marca (`readmeException`), no la prosa. Tenía la razón documentada en
veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2,
los 16 habrían dado error. Una marca por clase medida — forward de paleta
THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de
layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan.
3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por
grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el
protocolo lo pide por componente: una sesión nueva habría arrancado con el
mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la
revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8
firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus
seis lecciones, y actualizada la memoria del eje.
4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el
bloque, cada una nombrando la forma en que el instrumento mentía, más la
lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso
— `picker-shell` medía CERO y reportaba éxito.
5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`,
`virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md
(md→sm); los valores eran correctos (0 diffs), el texto no.
Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B
sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la
medición de quien los escribió, y el plan la exige antes de cerrar un bloque.
El handoff nombra las dos hipótesis concretas que hay que atacar.
Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves ·
`component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno
con un commit de esta sesión en su código) · R-5.4 verde en los 22 ·
suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero
limpio.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
1. **El plano `overlay` impone tipografía a VEINTE componentes**
(`font-family` + `line-height` junto a superficie/borde/sombra). ¿Un plano
de profundidad manda sobre la tipografía o sólo sobre la superficie?
— §12.9. **Es la que más urge: cada overlay que se tokenice la arrastra.**
1.bis **La familia text-effects ACUÑA tipografía propia contra el canon**
(autor, 2026-08-22: «¿qué sentido tiene que text-effects introduzca
diferentes nombres de tipografía cuando ya hay un canon establecido?»).
uix(theming): text-scramble no tiene contrato que escribir — y el barrido de fantasmas, hecho
Sin cambios de código: su veredicto §5 y el registro.
Su 50 % no es ni deuda ni logro, es la suma de dos cosas que no son knobs:
- **Los dos literales son la técnica sr-only** (`width`/`height: 1px` en
`[data-text-scramble-sr]`), idéntica a `text-blur` y al resto del catálogo:
existe para que el lector de pantalla lea el texto entero mientras la versión
animada se parte en caracteres. Moverlos rompe la técnica.
- **Los dos «públicos» son tipografía que el componente no debería acuñar**
(`--text-scramble-font` / `-size`, con fallback y sin declarar): es la firma
1.bis pendiente. Escribirles contrato ahora sería acuñar exactamente lo que
esa firma propone retirar. Con esto, 1.bis son **7 claves en 4 de los 5**
componentes de la familia; `text-blur` es el único limpio: hereda.
Y el `font` apunta a un FANTASMA, medido en navegador: `var(--font-mono, …)` no
existe —el canónico es `--font-family-mono`— así que pinta `ui-monospace` e
**ignora la mono del tema** (`Azeret Mono`, que está ahí al lado).
EL BARRIDO MECÁNICO QUE PEDÍAN `--radius-xs` Y `--font-size-4xl`, HECHO:
5.793 custom properties declaradas en todo `src/uix/eidos` (fundación + capas +
recetas + `generated/`) contra los `var(--x, fallback)` de las recetas cuyo `--x`
sea vocabulario de SISTEMA → **9 referencias fantasma**. (Las que van sin
fallback ya las guarda `recipe-css-contract`; el hueco eran justo las que
«funcionan por accidente».) Tabla completa en next-features §13:
- `--color-content-tertiary` ×3 (float-panel, proof-of-human clock y
rotate-align) — **es el caso histórico** que `recipe-contract` §1 cita como
origen de su propia regla, y sigue vivo en tres recetas.
- `--style-{body,prose,label,caption}-font-variation-settings` ×4 (`text.css`) —
los estilos con nombre emiten 6 claves y ésta no está: **el eje de fuente
variable no llega nunca al primitivo `Text`**.
- `--font-family-sans` (palabras, track WIP) y `--font-mono` (text-scramble).
Ninguna se arregla aquí. El barrido debería ser un guard: misma lógica que el
test de fantasmas sin fallback que ya existe, con el filtro de vocabulario de
sistema encima.
Guards: censo `--only` sin cambio (50 %, es el veredicto lo que cambia) ·
component-audit PASS · docs:check 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**Siete claves en CUATRO de los cinco** (medido al adjudicar
`text-scramble` ): `text-focus.word-size` (3rem, vía el fantasma
`--font-size-4xl` ) y `word-weight` (900) · `text-circular.weight` (900) y
uix(theming): text-scramble no tiene contrato que escribir — y el barrido de fantasmas, hecho
Sin cambios de código: su veredicto §5 y el registro.
Su 50 % no es ni deuda ni logro, es la suma de dos cosas que no son knobs:
- **Los dos literales son la técnica sr-only** (`width`/`height: 1px` en
`[data-text-scramble-sr]`), idéntica a `text-blur` y al resto del catálogo:
existe para que el lector de pantalla lea el texto entero mientras la versión
animada se parte en caracteres. Moverlos rompe la técnica.
- **Los dos «públicos» son tipografía que el componente no debería acuñar**
(`--text-scramble-font` / `-size`, con fallback y sin declarar): es la firma
1.bis pendiente. Escribirles contrato ahora sería acuñar exactamente lo que
esa firma propone retirar. Con esto, 1.bis son **7 claves en 4 de los 5**
componentes de la familia; `text-blur` es el único limpio: hereda.
Y el `font` apunta a un FANTASMA, medido en navegador: `var(--font-mono, …)` no
existe —el canónico es `--font-family-mono`— así que pinta `ui-monospace` e
**ignora la mono del tema** (`Azeret Mono`, que está ahí al lado).
EL BARRIDO MECÁNICO QUE PEDÍAN `--radius-xs` Y `--font-size-4xl`, HECHO:
5.793 custom properties declaradas en todo `src/uix/eidos` (fundación + capas +
recetas + `generated/`) contra los `var(--x, fallback)` de las recetas cuyo `--x`
sea vocabulario de SISTEMA → **9 referencias fantasma**. (Las que van sin
fallback ya las guarda `recipe-css-contract`; el hueco eran justo las que
«funcionan por accidente».) Tabla completa en next-features §13:
- `--color-content-tertiary` ×3 (float-panel, proof-of-human clock y
rotate-align) — **es el caso histórico** que `recipe-contract` §1 cita como
origen de su propia regla, y sigue vivo en tres recetas.
- `--style-{body,prose,label,caption}-font-variation-settings` ×4 (`text.css`) —
los estilos con nombre emiten 6 claves y ésta no está: **el eje de fuente
variable no llega nunca al primitivo `Text`**.
- `--font-family-sans` (palabras, track WIP) y `--font-mono` (text-scramble).
Ninguna se arregla aquí. El barrido debería ser un guard: misma lógica que el
test de fantasmas sin fallback que ya existe, con el filtro de vocabulario de
sistema encima.
Guards: censo `--only` sin cambio (50 %, es el veredicto lo que cambia) ·
component-audit PASS · docs:check 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
`font-size` (1.5rem) · `text-gradient.font-weight` (500, subida el 22) ·
`text-scramble.font` y `.size` (sin declarar). La de `text-scramble`
apunta además a `--font-mono` , que NO existe: pinta `ui-monospace` e
**ignora la mono del tema** . `text-blur` es el único limpio: hereda.
Son opiniones del seed react-bits convertidas en contrato por «valor
verbatim». Doctrina F2-B regla 2: una receta CONSUME la capa tipográfica
(`--style-*`) o HEREDA (`link` → `inherit` ); un tratamiento de pintura
sobre texto real no tiene tipografía propia (un TextGradient dentro de un
`h1` ES un h1). **Resolución propuesta** : retirar las cinco claves y
heredar; mueve defaults (TextFocus deja de imponer 3rem/900, TextGradient
deja de forzar 500) → firma. Con ello muere el fantasma `4xl` . El módulo
de tipografía NO se toca. Registrado en next-features §13.
uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca
código y es la que importa.
1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré
como «declaraciones muertas» apoyándome en los precedentes de
`table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el
mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el
plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es
neutral — **fija el empate a favor del plano** y con él su 1.25 donde la
receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9
tiene pendiente para VEINTE componentes. Un empate por orden de carga se
registra, no se resuelve dentro de un commit de tokenización.
Y la medición era más débil de lo que el commit daba a entender: estable en
cinco recargas **en DEV**, sin verificar en un bundle de producción.
Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5
tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa
razón. §12.9 y el README dicen ahora que el par se deja declarado A
PROPÓSITO.
2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al
100 % o excepción escrita», y la válvula que `component-audit` lee es esa
marca (`readmeException`), no la prosa. Tenía la razón documentada en
veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2,
los 16 habrían dado error. Una marca por clase medida — forward de paleta
THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de
layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan.
3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por
grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el
protocolo lo pide por componente: una sesión nueva habría arrancado con el
mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la
revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8
firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus
seis lecciones, y actualizada la memoria del eje.
4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el
bloque, cada una nombrando la forma en que el instrumento mentía, más la
lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso
— `picker-shell` medía CERO y reportaba éxito.
5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`,
`virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md
(md→sm); los valores eran correctos (0 diffs), el texto no.
Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B
sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la
medición de quien los escribió, y el plan la exige antes de cerrar un bloque.
El handoff nombra las dos hipótesis concretas que hay que atacar.
Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves ·
`component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno
con un commit de esta sesión en su código) · R-5.4 verde en los 22 ·
suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero
limpio.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
2. **El harness del sitio tapa a `code-block` y `code`** : `[data-uix-docs] pre`
pisa SIETE propiedades del `<pre>` , y `[data-uix-docs] code` el radio del
inline. Acotarlo toca el CSS que sirve a ~162 páginas — §13.
3. **El censo no distingue «0 % por deuda» de «0 % POR NATURALEZA»** (cinco
componentes ya). Sin una clase `structural` , **el gate de F3 es
inalcanzable por construcción** — §13.
4. **El velo derrama sobre el subárbol entero en `tree-view`** (`archetype:
'item'` en el `<li>` branch: 336 px velados en vez de 36) — §12.8, morfo.
5. **La selección de fila de `grid-list` nunca ha pintado** (soma estampa
`data-state` , no `data-selected` ) — §13, morfo.
6. **El borde de foco de `textarea` sólo se ve con Tab** : hover (0,4,0) >
foco (0,3,0) > invalid (0,2,0) — §13.
7. ** `picker-shell` sin demo propia** (404) y con partes de nombre genérico:
el instrumental está ciego por convención — §13.
8. Extender la clase `system` del censo a las **capas compartidas** (viene de
antes de esta sesión).
feat(calendar-surface): la capa que ya existía, con nombre, casa y su agujero tapado
Firma 1 del acta, diseño presentado y firmado. **La medición desmontó el
encargo**: el handoff la vendía como una capa NUEVA de ~220 knobs, y la capa ya
existía de hecho, sin nombre. `--calendar-*` se emite en `:root` (76 claves) y
sus consumidores no acuñan NADA — medido: `range-calendar` 114 referencias
prestadas y 0 propias, `month-grid` 77/0, `year-grid` 77/0, y lo único ajeno
que leen es sistema puro (`--focus-ring-*`, `--state-hover`). Su 0 % era el
artefacto de `listbox` (§13), pero total.
**`lib/calendar-surface.css`** (hook `data-calendar-surface`):
- cuatro coordenadas por talla — `padding`, `control-size`, `day-size`,
`font-size` — xs..lg, porque la familia NO tiene xl, y con la celda DOS pasos
por debajo del bundle de control. Esa desviación estaba escondida en cuatro
bloques `[data-size]` idénticos, uno por receta, cada uno puenteando a un
privado con otro nombre; ahora se lee en un sitio.
- la FORMA del anillo de evento y de la marca de festivo.
**Capa HÍBRIDA, y es lo que la distingue de sus hermanas**: `list-surface` y
`viewport-placement` componen primitivos del sistema, así que declaran sus
públicos en el fichero y no tienen entrada de receta. Ésta no puede: su
vocabulario son 76 claves SEMÁNTICAS que un tema alcanza una a una por config,
así que la entrada `calendar` de `recipes/base.ts` pasa a ser la de la FAMILIA
y la capa posee sólo lo que una entrada de receta no sabe expresar.
**El defecto que la justificaba, medido**: `--calendar-event-shadow` y
`--calendar-day-holiday-shadow` se emitían con ámbito `[data-calendar]`
(audit B.2 los host-scopeó por buenas razones) mientras `range-calendar`,
`month-grid` y `year-grid` los leían desde hosts que nunca llevan ese atributo:
variable VACÍA, `box-shadow` inválido en computed, **el anillo sema de
`commit-select` / `commit-set` no pintaba jamás en tres componentes**. Un token
prestado cuyo ÁMBITO no te cubre no es un préstamo, es un agujero silencioso, y
ningún guard lo veía. Ahora la forma vive en la capa y el acento entra por
`--_calendar-surface-accent`, que cada superficie alimenta con su propio
forward de paleta THM-2.
**Siete wrappers estampan, no cuatro** — y esto casi se me cuela: `DatePicker`
y `DateRangePicker` renderizan la superficie soma por sus PROPIOS wrappers
(`date-picker-calendar`, `-month-view`, `-year-view`,
`date-range-picker-calendar`) y un panel portalado no hereda nada del root del
picker. Con sólo los cuatro standalone sellando, ambos quedaban con
`--calendar-padding` VACÍA y el panel a padding 0 (medido). La tentación era
enganchar la capa a las cuatro identidades de componente: eso viola la regla 1
de capas compartidas, y la respuesta correcta es un sello por wrapper.
computed 0 diffs en range-calendar (19.285 valores) · month-grid (3.451) ·
year-grid (3.451) · date-picker (464) · date-range-picker (406).
`calendar` da 12, y son del INSTRUMENTO: dos corridas del MISMO
código dan 24 en los mismos nodos y las mismas dos propiedades.
Los «missing node» son el propio sello entrando en la clave.
**Una incidencia nueva, medida y NO arreglada aquí** (§13): el font-size de los
selectores month/year es una moneda al aire —
`[data-calendar-month-select][data-button]` (0,2,0) empata con
`[data-popover-trigger]:not([data-archetype='field-trigger'])` (0,2,0), la
MISMA regla de popover que dejó muerto el cromo de `gradient-picker`, y gana la
hoja que cargue después: 16px o 14px según la recarga. Arreglarlo fija el píxel
en un lado ⇒ decisión.
De paso, `calendar-select.css` deja de puentear un privado que sólo
`[data-calendar]` declaraba: en range / date-range corría SIEMPRE por el
fallback, clavado a md fuera cual fuera la talla.
**El censo deja de penalizar hacer lo correcto**: `LAYER_VOCABULARY` en
`theming-census.ts`, mismo precedente que D-TH.2-b con `--style-*`. Global
43 % → **45 %**, `calendar` 75 % → **84 %**. `list-surface` NO se registra: sus
consumidores puentean por privados, otra forma, y mueve diez componentes de
golpe. Y aparece el techo de debajo, anotado: a los tres consumidores sólo les
quedan los forwards de paleta THM-2 —que el censo cuenta como `private` en TODO
el catálogo— así que siguen leyendo 0 %.
`recipe-css-contract` aprende que una CAPA también declara públicos (antes sólo
miraba la receta, y una capa que comparte prefijo con un componente la hacía
fallar). Sin debilitarla: un nombre que no declara nadie sigue en rojo.
eidos-lint 0 invalid (calendar 29/8 · range-calendar 41/12 · los grids 23/5) ·
audit --only calendar PASS · vitest eidos 434/435 (el rojo conocido) ·
rtl 0/181 · docs 0/813 · check 0 errores en tocados · prettier: revertido el
reformateo en masa que se coló en cuatro README, el test y el censo (churn
ajeno, no mío)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## EL PROTOCOLO, EN CORTO (el largo está en PLAN-theming.md §7)
Un componente = un commit. Por componente:
1. Leer entero: receta, su bloque en `recipes/base.ts` , README de eidos, morfo,
y el veredicto §5 de su ficha.
2. **Sonda ANTES** (`__theming-probe.ts {c} antes.json < url > `).
3. 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).
4. 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.
5. `npm run generate:eidos-css` , sonda DESPUÉS, **diff = 0** .
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4
Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree
con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba
más dura que la del autor), sonda x2 por componente para fijar el suelo de
ruido. 34 hipótesis · 26 refutadas · 8 reales.
Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs;
command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 ·
listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen
idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens
huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano»
de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación;
eidos-lint 0 invalid.
Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command
8.091 y carousel 2.842 valores, 0 diffs):
1. command: el re-point de `--command-radius` dentro del Dialog era una
declaración muerta con comentario falso — la misma regla pone el radio a 0
y el input lee `--command-input-radius`, nunca este token (medido:
`--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px`
movía la paleta inline y no la del dialog). Retirado; comentarios y README
reescritos con lo medido.
2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo
mientras la horizontal leía `active-indicator-scale` — el token alcanzaba
media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora
lo leen las dos.
3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap`
INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por
CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y
las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es
la prueba. Canal de valor de soma, como --gp-current-gradient. Censo
carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto
(5.190 · 2.102).
4. table: `striped-row-bg` no alcanza NADA en la demo publicada —
`:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos
quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel →
PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`);
adjudicado en el ledger mientras tanto.
El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas
medidas, y la cuarta era de la sonda:
- el paso de abrir hacía 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 (era el «falso negativo sin causa» de §13);
- el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de
`{c}-root` no lo recibían;
- `::before`/`::after` eran invisibles (el aro de buffering, el spinner);
- la no-determinación de feed no era una animación sin localizar: era la demo
aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma
`commit-settle` anima box-shadow sobre el nodo medido. Con la espera de
asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas.
Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y
que carousel convirtió en patrón — un estilo inline de otra capa es invisible
para todo análisis estático): `npm run theming:sentinel -- <c> <url>`
(scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en
todos los nodos del componente, pseudos, pase de hover, settle) + ledger
scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto
sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en
verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 ·
gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46
excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías
salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró;
y dos muertos nuevos los produjeron los propios arreglos (el blur apaga
focus-input-border; el settle desmonta el spinner de feed) — adjudicados con
su porqué. Doctrina en recipe-contract §4.
También: tres cifras de especificidad de next-features §12 recontadas (la
banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0);
y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el
velo en la celda), el comentario de listbox que mandaba pisar el privado
`--_listbox-max-height` apunta ahora al público, y quedan registrados en §13
los dos `header-z` con literal '2' y el spinner de media-player a 720ms
mientras el gemelo de feed lleva tokens.
Guards: censo global 43 % intacto · audit --only command+carousel PASS ·
eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest
eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs
0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier
limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en
HEAD y no se tocan).
Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el
arquetipo en fila+celda, y que soma lea el gap de un token.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
6. 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.
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
7. Guards: censo `--only {c}` · `component-audit --only {c}` · `eidos-lint {c}`
· `vitest run src/uix/eidos` (comparar POR FICHERO: el rojo conocido es
`skin-media-player` ) · `rtl:check` · `docs:check` · `npm run check`
atribuido POR FICHERO (73 errores globales son de otras sesiones).
8. README del componente con su sección «Talla y tema».
9. **Tab `Tokens` en la demo** — una línea:
`<TokensPanel component="x" stage={stageRef ?? undefined} />` + su botón.
10. Commit con las cifras y los artefactos en el mensaje.
uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca
código y es la que importa.
1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré
como «declaraciones muertas» apoyándome en los precedentes de
`table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el
mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el
plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es
neutral — **fija el empate a favor del plano** y con él su 1.25 donde la
receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9
tiene pendiente para VEINTE componentes. Un empate por orden de carga se
registra, no se resuelve dentro de un commit de tokenización.
Y la medición era más débil de lo que el commit daba a entender: estable en
cinco recargas **en DEV**, sin verificar en un bundle de producción.
Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5
tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa
razón. §12.9 y el README dicen ahora que el par se deja declarado A
PROPÓSITO.
2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al
100 % o excepción escrita», y la válvula que `component-audit` lee es esa
marca (`readmeException`), no la prosa. Tenía la razón documentada en
veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2,
los 16 habrían dado error. Una marca por clase medida — forward de paleta
THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de
layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan.
3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por
grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el
protocolo lo pide por componente: una sesión nueva habría arrancado con el
mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la
revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8
firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus
seis lecciones, y actualizada la memoria del eje.
4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el
bloque, cada una nombrando la forma en que el instrumento mentía, más la
lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso
— `picker-shell` medía CERO y reportaba éxito.
5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`,
`virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md
(md→sm); los valores eran correctos (0 diffs), el texto no.
Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B
sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la
medición de quien los escribió, y el plan la exige antes de cerrar un bloque.
El handoff nombra las dos hipótesis concretas que hay que atacar.
Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves ·
`component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno
con un commit de esta sesión en su código) · R-5.4 verde en los 22 ·
suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero
limpio.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
## LO QUE F2-B ENSEÑÓ (2026-08-22 — léelo antes de tocar nada)
**Un alcance bajo puede ser doctrina bien aplicada, no deuda.** `menu-dial`
25 % porque sus ejes de colocación son de la capa `viewport-placement` y la
receta escribe en sus RANURAS DE OVERRIDE; `link` 36 % porque sus escotillas
caen en `inherit` , que es semántico (el enlace toma la tipografía del párrafo);
`scroll-frames` 33 % porque es layout puro. **El % no es la métrica de
calidad** — lo que importa es que lo que queda fuera esté MEDIDO y escrito.
**Cuatro clases que se repiten, con su veredicto ya fijado:**
1. **Un privado con DOS fuentes es un conmutador, no deuda.** Cinco casos
(`textarea`, `spinner` , `skeleton` , `code` , `label` ): aplanarlo obligaría a
duplicar cada regla por color. Se queda privado y se adjudica.
2. **Lo que la capa tipográfica posee NO se acuña** (`--style-*`): cinco casos
(`code-block`, `kbd` , `announce` , `code` , `label` ).
3. **Un forward de paleta THM-2 leído a pelo** no admite público sin duplicar
la regla.
4. **Identidad ≠ knob** : `100%` , `50%` de un círculo, `line-height: 1` de una
tecla, `transparent` de una variante ghost.
**La propuesta §4 generada FALLA de tres maneras distintas**, y las tres
costaron medición:
- **Hereda el incumplimiento del CSS de partida**: copia el valor verbatim, así
que si ya violaba `recipe-css-contract` (primitivo crudo en vez del bundle),
la propuesta lo repite. Lo cazó la suite en `code-block` .
- **Infla hasta 4× **: en `spinner` pedía **71 tokens para 23 knobs** ,
multiplicando UNA coordenada de talla por variante × parte × dimensión —
veinte claves con el mismo valor.
- **Nombra por el primer selector de la regla**, no por el nodo pintado: en
`tree-view` llamaba `root-width` / `root-bg` a la GUÍA de indentación, y
fundía en un solo nombre el `padding-inline` de la fila y el paso de
profundidad, que se SUMAN en el mismo `calc` .
**El instrumento estándar no ve tres clases de componente**, y una sonda que
compara pocos nodos pasa en falso: panel portalado que abre en hover
(`link-preview`: 1 nodo), componente sin demo propia y con partes de nombre
genérico (`picker-shell`: **0 nodos** ), y parte que sólo existe durante una
interacción (el fantasma de `drag-drop` , el indicador de `clipboard` , el rótulo
de `scroll-frames` ). **Cuenta siempre los nodos que ve la sonda antes de creerte
un gate en verde.**
**El guard R-5.4 ganó SEIS capacidades en este bloque**, todas nacidas de una
medición y con los del ledger re-verificados sin regresión tras cada una:
puntero parqueado tras el blur (`reopen()` corre antes de CADA token), lectura
de `::placeholder` (la nota de §13 que decía que no se podía era falsa),
`COMPONENT_OVERRIDES` de prefijo de atributo y selector de apertura, apertura
por `hover` , y `text-decoration-thickness` / `-underline-offset` en la lista de
propiedades.
**Y dos errores de proceso que costaron tiempo:**
- **El guard de claves duplicadas del handoff inspeccionaba el VACÍO**:
`grep -E` lee ` ` como una `t` literal — 0 coincidencias sobre 130 bloques.
Corregido más abajo con una versión que CUENTA.
- **`$?` tras una tubería es el del último comando**, no el del guard: un
fichero de excepciones con sintaxis inválida pasó por bueno. Comprueba el
exit REAL.
## LO QUE F2-A ENSEÑÓ (léelo antes de tocar nada)
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**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í:
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4
Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree
con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba
más dura que la del autor), sonda x2 por componente para fijar el suelo de
ruido. 34 hipótesis · 26 refutadas · 8 reales.
Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs;
command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 ·
listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen
idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens
huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano»
de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación;
eidos-lint 0 invalid.
Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command
8.091 y carousel 2.842 valores, 0 diffs):
1. command: el re-point de `--command-radius` dentro del Dialog era una
declaración muerta con comentario falso — la misma regla pone el radio a 0
y el input lee `--command-input-radius`, nunca este token (medido:
`--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px`
movía la paleta inline y no la del dialog). Retirado; comentarios y README
reescritos con lo medido.
2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo
mientras la horizontal leía `active-indicator-scale` — el token alcanzaba
media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora
lo leen las dos.
3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap`
INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por
CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y
las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es
la prueba. Canal de valor de soma, como --gp-current-gradient. Censo
carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto
(5.190 · 2.102).
4. table: `striped-row-bg` no alcanza NADA en la demo publicada —
`:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos
quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel →
PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`);
adjudicado en el ledger mientras tanto.
El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas
medidas, y la cuarta era de la sonda:
- el paso de abrir hacía 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 (era el «falso negativo sin causa» de §13);
- el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de
`{c}-root` no lo recibían;
- `::before`/`::after` eran invisibles (el aro de buffering, el spinner);
- la no-determinación de feed no era una animación sin localizar: era la demo
aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma
`commit-settle` anima box-shadow sobre el nodo medido. Con la espera de
asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas.
Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y
que carousel convirtió en patrón — un estilo inline de otra capa es invisible
para todo análisis estático): `npm run theming:sentinel -- <c> <url>`
(scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en
todos los nodos del componente, pseudos, pase de hover, settle) + ledger
scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto
sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en
verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 ·
gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46
excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías
salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró;
y dos muertos nuevos los produjeron los propios arreglos (el blur apaga
focus-input-border; el settle desmonta el spinner de feed) — adjudicados con
su porqué. Doctrina en recipe-contract §4.
También: tres cifras de especificidad de next-features §12 recontadas (la
banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0);
y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el
velo en la celda), el comentario de listbox que mandaba pisar el privado
`--_listbox-max-height` apunta ahora al público, y quedan registrados en §13
los dos `header-z` con literal '2' y el spinner de media-player a 720ms
mientras el gemelo de feed lleva tokens.
Guards: censo global 43 % intacto · audit --only command+carousel PASS ·
eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest
eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs
0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier
limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en
HEAD y no se tocan).
Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el
arquetipo en fila+celda, y que soma lea el gap de un token.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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` .
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## LAS INCIDENCIAS NO SE ARREGLAN AQUÍ
Todo lo que este eje destapa y no le toca arreglar está en
[`docs/next-features.md` ](../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 lleva `archetype: 'thumb'` , que no recibe velo. Cambiarlo es morfo.
- **Extender la clase `system` del censo a las capas compartidas**, como se
hizo con `--style-*` para los primitivos tipográficos (§13 del registro).
fix(table): el striped cuenta filas de DATOS — la banda vuelve a existir
Firmado por el autor tras la revisión adversarial: el hallazgo 4 (679dfdb29)
midió que `:nth-of-type(even)` cuenta los <tr> hermanos, y con un RowDetail
intercalado tras cada fila las filas de datos quedan todas impares — la demo
publicada no bandeaba NADA (diez tr inmunes al token, medido uno a uno).
El selector pasa a `:nth-child(even of [data-table-row])`, con dos decisiones
de peso dentro:
- **conserva el (0,6,0) del viejo**: el pseudo más el argumento `of` pesan
exactamente lo que pesaba `[data-table-row]:nth-of-type()`, así que el hover
—posterior en el fichero— sigue ganando su empate y la banda NO mata el
hover. Subirlo de peso habría importado a table el defecto §12.4 de
tree-grid.
- **`:where(:not([data-selected]))` a peso CERO**: la fila seleccionada
siempre estuvo prometida al acento («Overrides hover + striped», dice la
regla de al lado a (0,3,0)) pero la banda vieja, de haber casado, se la
habría comido por especificidad. El :where excluye sin pesar.
Medido (sonda antes/después, 13.195 valores en 7 estados): **12 diffs
exactos** — 2 filas pares × 6 estados, backgroundColor 0.9911 → 0.9551 — y
nada más se movió. Interacciones verificadas en vivo: selected par conserva
el acento (0.9588 con croma) · hover sobre par pinta su tinte (0.931).
El ledger de R-5.4 pierde la excepción de `striped-row-bg` (era «genuinely
unreachable, pending signature»): el guard adjudica ahora table 42/45 con 3
excepciones, exit 0, sin STALE.
eidos-lint 0 invalid · audit --only table PASS · censo table 86 % intacto ·
vitest eidos 434/435 (el rojo conocido) · rtl 0 · docs 0 · check 0 en
tocados · prettier limpio en lo tocado (el wrap de la regla del hover ya
estaba desviado en HEAD y no se toca)
Incidencia operativa, reparada y anotada: al retirar el worktree de medición
de la revisión, `git worktree remove --force` recursó A TRAVÉS de la junction
de node_modules y borró parte del real (node_modules/.bin incluido) antes de
fallar; `npm install` lo reconstruyó y la suite volvió a 434/435. El árbol
git nunca se tocó.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- ~~El `striped` de `table` inerte con RowDetail~~ — **FIRMADO Y EJECUTADO
2026-08-21**: `: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.
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4
Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree
con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba
más dura que la del autor), sonda x2 por componente para fijar el suelo de
ruido. 34 hipótesis · 26 refutadas · 8 reales.
Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs;
command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 ·
listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen
idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens
huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano»
de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación;
eidos-lint 0 invalid.
Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command
8.091 y carousel 2.842 valores, 0 diffs):
1. command: el re-point de `--command-radius` dentro del Dialog era una
declaración muerta con comentario falso — la misma regla pone el radio a 0
y el input lee `--command-input-radius`, nunca este token (medido:
`--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px`
movía la paleta inline y no la del dialog). Retirado; comentarios y README
reescritos con lo medido.
2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo
mientras la horizontal leía `active-indicator-scale` — el token alcanzaba
media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora
lo leen las dos.
3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap`
INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por
CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y
las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es
la prueba. Canal de valor de soma, como --gp-current-gradient. Censo
carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto
(5.190 · 2.102).
4. table: `striped-row-bg` no alcanza NADA en la demo publicada —
`:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos
quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel →
PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`);
adjudicado en el ledger mientras tanto.
El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas
medidas, y la cuarta era de la sonda:
- el paso de abrir hacía 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 (era el «falso negativo sin causa» de §13);
- el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de
`{c}-root` no lo recibían;
- `::before`/`::after` eran invisibles (el aro de buffering, el spinner);
- la no-determinación de feed no era una animación sin localizar: era la demo
aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma
`commit-settle` anima box-shadow sobre el nodo medido. Con la espera de
asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas.
Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y
que carousel convirtió en patrón — un estilo inline de otra capa es invisible
para todo análisis estático): `npm run theming:sentinel -- <c> <url>`
(scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en
todos los nodos del componente, pseudos, pase de hover, settle) + ledger
scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto
sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en
verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 ·
gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46
excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías
salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró;
y dos muertos nuevos los produjeron los propios arreglos (el blur apaga
focus-input-border; el settle desmonta el spinner de feed) — adjudicados con
su porqué. Doctrina en recipe-contract §4.
También: tres cifras de especificidad de next-features §12 recontadas (la
banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0);
y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el
velo en la celda), el comentario de listbox que mandaba pisar el privado
`--_listbox-max-height` apunta ahora al público, y quedan registrados en §13
los dos `header-z` con literal '2' y el spinner de media-player a 720ms
mientras el gemelo de feed lleva tokens.
Guards: censo global 43 % intacto · audit --only command+carousel PASS ·
eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest
eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs
0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier
limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en
HEAD y no se tocan).
Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el
arquetipo en fila+celda, y que soma lea el gap de un token.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **El arquetipo `item` de `table` vive 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 del `hoverable` (§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ío `item-gap*` se retiró muerto; darle
superficie de tema toca soma (§13).
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
# REGISTRO DE LA SESIÓN DEL 2026-08-20/21 (histórico — no es la agenda)
feat(calendar-surface): la capa que ya existía, con nombre, casa y su agujero tapado
Firma 1 del acta, diseño presentado y firmado. **La medición desmontó el
encargo**: el handoff la vendía como una capa NUEVA de ~220 knobs, y la capa ya
existía de hecho, sin nombre. `--calendar-*` se emite en `:root` (76 claves) y
sus consumidores no acuñan NADA — medido: `range-calendar` 114 referencias
prestadas y 0 propias, `month-grid` 77/0, `year-grid` 77/0, y lo único ajeno
que leen es sistema puro (`--focus-ring-*`, `--state-hover`). Su 0 % era el
artefacto de `listbox` (§13), pero total.
**`lib/calendar-surface.css`** (hook `data-calendar-surface`):
- cuatro coordenadas por talla — `padding`, `control-size`, `day-size`,
`font-size` — xs..lg, porque la familia NO tiene xl, y con la celda DOS pasos
por debajo del bundle de control. Esa desviación estaba escondida en cuatro
bloques `[data-size]` idénticos, uno por receta, cada uno puenteando a un
privado con otro nombre; ahora se lee en un sitio.
- la FORMA del anillo de evento y de la marca de festivo.
**Capa HÍBRIDA, y es lo que la distingue de sus hermanas**: `list-surface` y
`viewport-placement` componen primitivos del sistema, así que declaran sus
públicos en el fichero y no tienen entrada de receta. Ésta no puede: su
vocabulario son 76 claves SEMÁNTICAS que un tema alcanza una a una por config,
así que la entrada `calendar` de `recipes/base.ts` pasa a ser la de la FAMILIA
y la capa posee sólo lo que una entrada de receta no sabe expresar.
**El defecto que la justificaba, medido**: `--calendar-event-shadow` y
`--calendar-day-holiday-shadow` se emitían con ámbito `[data-calendar]`
(audit B.2 los host-scopeó por buenas razones) mientras `range-calendar`,
`month-grid` y `year-grid` los leían desde hosts que nunca llevan ese atributo:
variable VACÍA, `box-shadow` inválido en computed, **el anillo sema de
`commit-select` / `commit-set` no pintaba jamás en tres componentes**. Un token
prestado cuyo ÁMBITO no te cubre no es un préstamo, es un agujero silencioso, y
ningún guard lo veía. Ahora la forma vive en la capa y el acento entra por
`--_calendar-surface-accent`, que cada superficie alimenta con su propio
forward de paleta THM-2.
**Siete wrappers estampan, no cuatro** — y esto casi se me cuela: `DatePicker`
y `DateRangePicker` renderizan la superficie soma por sus PROPIOS wrappers
(`date-picker-calendar`, `-month-view`, `-year-view`,
`date-range-picker-calendar`) y un panel portalado no hereda nada del root del
picker. Con sólo los cuatro standalone sellando, ambos quedaban con
`--calendar-padding` VACÍA y el panel a padding 0 (medido). La tentación era
enganchar la capa a las cuatro identidades de componente: eso viola la regla 1
de capas compartidas, y la respuesta correcta es un sello por wrapper.
computed 0 diffs en range-calendar (19.285 valores) · month-grid (3.451) ·
year-grid (3.451) · date-picker (464) · date-range-picker (406).
`calendar` da 12, y son del INSTRUMENTO: dos corridas del MISMO
código dan 24 en los mismos nodos y las mismas dos propiedades.
Los «missing node» son el propio sello entrando en la clave.
**Una incidencia nueva, medida y NO arreglada aquí** (§13): el font-size de los
selectores month/year es una moneda al aire —
`[data-calendar-month-select][data-button]` (0,2,0) empata con
`[data-popover-trigger]:not([data-archetype='field-trigger'])` (0,2,0), la
MISMA regla de popover que dejó muerto el cromo de `gradient-picker`, y gana la
hoja que cargue después: 16px o 14px según la recarga. Arreglarlo fija el píxel
en un lado ⇒ decisión.
De paso, `calendar-select.css` deja de puentear un privado que sólo
`[data-calendar]` declaraba: en range / date-range corría SIEMPRE por el
fallback, clavado a md fuera cual fuera la talla.
**El censo deja de penalizar hacer lo correcto**: `LAYER_VOCABULARY` en
`theming-census.ts`, mismo precedente que D-TH.2-b con `--style-*`. Global
43 % → **45 %**, `calendar` 75 % → **84 %**. `list-surface` NO se registra: sus
consumidores puentean por privados, otra forma, y mueve diez componentes de
golpe. Y aparece el techo de debajo, anotado: a los tres consumidores sólo les
quedan los forwards de paleta THM-2 —que el censo cuenta como `private` en TODO
el catálogo— así que siguen leyendo 0 %.
`recipe-css-contract` aprende que una CAPA también declara públicos (antes sólo
miraba la receta, y una capa que comparte prefijo con un componente la hacía
fallar). Sin debilitarla: un nombre que no declara nadie sigue en rojo.
eidos-lint 0 invalid (calendar 29/8 · range-calendar 41/12 · los grids 23/5) ·
audit --only calendar PASS · vitest eidos 434/435 (el rojo conocido) ·
rtl 0/181 · docs 0/813 · check 0 errores en tocados · prettier: revertido el
reformateo en masa que se coló en cuatro README, el test y el censo (churn
ajeno, no mío)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## EJECUTADO 2026-08-21 — la capa `calendar-surface` (firmada y medida)
**La medición cambió el encargo.** El handoff la vendía como «una capa nueva,
~220 knobs, 43 % → ~48 %». No lo era: la capa **ya existía de hecho** , sin
nombre ni casa. `--calendar-*` se emite en `:root` (76 claves) y sus
consumidores **no acuñan NADA** — `range-calendar` 114 referencias prestadas y
0 propias, `month-grid` 77/0, `year-grid` 77/0, y lo único ajeno que leen es
sistema puro (`--focus-ring-*`, `--state-hover` ). Su 0 % era el artefacto de
`listbox` (§13) pero TOTAL.
**Lo que se escribió**: `lib/calendar-surface.css` con el hook
`data-calendar-surface` , cuatro coordenadas por talla (`padding`,
`control-size` , `day-size` , `font-size` , xs..lg — la familia **no tiene xl** , y
la celda va DOS pasos por debajo del bundle de control, deviación que ahora se
lee en un sitio en vez de esconderse en cuatro bloques `[data-size]` idénticos)
y la FORMA del anillo de evento. El vocabulario **se queda en la entrada
`calendar` de la receta**, que pasa a ser la de la FAMILIA: ésta es una capa
HÍBRIDA y es lo que la distingue de `list-surface` / `viewport-placement` , que
componen primitivos y no tienen entrada de receta (76 claves semánticas tienen
que seguir siendo alcanzables una a una por config).
**El defecto que la justificó, medido**: `--calendar-event-shadow` y
`--calendar-day-holiday-shadow` se emitían con ámbito `[data-calendar]` y los
otros tres los leían desde hosts que nunca llevan ese atributo → variable
VACÍA, `box-shadow` inválido en computed, **el anillo sema no pintaba jamás en
tres componentes**. Ahora la forma vive en la capa y el acento entra por
`--_calendar-surface-accent` , que cada superficie alimenta con su forward de
paleta THM-2.
⚠️ **Lo que casi se me cuela, y la lección** : estampar el hook sólo en los
cuatro wrappers standalone dejaba a `DatePicker` y `DateRangePicker` con
`--calendar-padding` VACÍA y el panel a padding 0 — renderizan la superficie
soma por sus PROPIOS wrappers (`date-picker-calendar`, `-month-view` ,
`-year-view` , `date-range-picker-calendar` ) y un panel portalado no hereda
nada. **Son SIETE wrappers, no cuatro.** La tentación era enganchar la capa a
las cuatro identidades de componente; eso viola la regla 1 de capas
compartidas, y la respuesta correcta es un sello por wrapper.
**El censo aprendió a no penalizar**: `LAYER_VOCABULARY` en
`theming-census.ts` (mismo precedente que D-TH.2-b con `--style-*` ). Global
43 % → **45 %** , `calendar` 75 % → **84 %** . Los tres consumidores **siguen
leyendo 0 %**, y es honesto: con el vocabulario de la capa fuera del
denominador sólo les quedan los forwards de paleta THM-2 (que el censo cuenta
como `private` en TODO el catálogo) y literales de layout. Reclasificar el
forward de paleta es la misma pregunta un piso más abajo — anotada en §13.
`list-surface` NO se registró: sus consumidores puentean por privados, otra
forma, y mueve diez componentes de golpe.
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4
Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree
con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba
más dura que la del autor), sonda x2 por componente para fijar el suelo de
ruido. 34 hipótesis · 26 refutadas · 8 reales.
Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs;
command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 ·
listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen
idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens
huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano»
de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación;
eidos-lint 0 invalid.
Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command
8.091 y carousel 2.842 valores, 0 diffs):
1. command: el re-point de `--command-radius` dentro del Dialog era una
declaración muerta con comentario falso — la misma regla pone el radio a 0
y el input lee `--command-input-radius`, nunca este token (medido:
`--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px`
movía la paleta inline y no la del dialog). Retirado; comentarios y README
reescritos con lo medido.
2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo
mientras la horizontal leía `active-indicator-scale` — el token alcanzaba
media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora
lo leen las dos.
3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap`
INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por
CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y
las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es
la prueba. Canal de valor de soma, como --gp-current-gradient. Censo
carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto
(5.190 · 2.102).
4. table: `striped-row-bg` no alcanza NADA en la demo publicada —
`:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos
quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel →
PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`);
adjudicado en el ledger mientras tanto.
El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas
medidas, y la cuarta era de la sonda:
- el paso de abrir hacía 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 (era el «falso negativo sin causa» de §13);
- el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de
`{c}-root` no lo recibían;
- `::before`/`::after` eran invisibles (el aro de buffering, el spinner);
- la no-determinación de feed no era una animación sin localizar: era la demo
aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma
`commit-settle` anima box-shadow sobre el nodo medido. Con la espera de
asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas.
Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y
que carousel convirtió en patrón — un estilo inline de otra capa es invisible
para todo análisis estático): `npm run theming:sentinel -- <c> <url>`
(scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en
todos los nodos del componente, pseudos, pase de hover, settle) + ledger
scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto
sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en
verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 ·
gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46
excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías
salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró;
y dos muertos nuevos los produjeron los propios arreglos (el blur apaga
focus-input-border; el settle desmonta el spinner de feed) — adjudicados con
su porqué. Doctrina en recipe-contract §4.
También: tres cifras de especificidad de next-features §12 recontadas (la
banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0);
y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el
velo en la celda), el comentario de listbox que mandaba pisar el privado
`--_listbox-max-height` apunta ahora al público, y quedan registrados en §13
los dos `header-z` con literal '2' y el spinner de media-player a 720ms
mientras el gemelo de feed lleva tokens.
Guards: censo global 43 % intacto · audit --only command+carousel PASS ·
eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest
eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs
0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier
limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en
HEAD y no se tocan).
Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el
arquetipo en fila+celda, y que soma lea el gap de un token.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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.
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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** .
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente
El autor, al ver que el radio del trigger del nav vivía en un privado clavado a
--radius-default: «¿los componentes son themables? si no lo son, es un error
como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs
en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo
comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en
var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar
moviendo el sistema entero.
Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205
knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del
componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son
literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen
alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %.
El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla
regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens ·
talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para
component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el
autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar
nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error ·
panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el
PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla
mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda
headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la
forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada
token nuevo, desde el píxel hacia arriba, registro de animationstart/end),
guards por fichero, un componente = un commit con sus artefactos, y revisión
adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho.
El instrumento se probó por mutación antes de creerle, y falló la primera vez:
no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que
meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos
mutaciones. Las cifras del plan son las del instrumento final, y el handoff
(CONTINUE-theming.md) obliga a reproducirlas antes de seguir.
Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del
script limpio · tsc del script sin errores · el plan cita ficheros que existen
(los once, comprobados).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## EJECUTADO 2026-08-20 — vocabulario de tokens: D-TH.6 (codemod) + R-5.3 (guard)
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un
catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 +
recipe-contract §1) pero no tiene guard, y la medición fundacional del
2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora
con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre
3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el
modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos
falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la
familia del sistema, y los `orb-color-*` de aura usan color como sustantivo.
El handoff pone el bloque como primera tarea, con el orden que evita el error
clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante),
luego el codemod value-preserving con el triple muro del precedente px/py
(renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas),
y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el
vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla
contra las siete canónicas, el modificador cerrado y delante, el slot contra
sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint.
En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn
es como estos guards mueren. Con muta-prueba antes de darlo por bueno.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**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»).
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**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` (molde
`PhysicalAxisKey` ) está escrito y probado: dispara sobre **17 claves** , que
son los hovers neutros con `color` en cola de migración. Meterlo hoy rompe
`npm run check` a todo el mundo. Entra cuando la migración las vacíe — dos
líneas: una rama `ColorSlotKey` y las tres excepciones firmadas
(`*focus-ring-color`, `orb-color-*` , `stop-color-*` ) deletreadas.
- **`__names-inventory.ts` quedó superado** por `theming-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:
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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):
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un
catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 +
recipe-contract §1) pero no tiene guard, y la medición fundacional del
2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora
con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre
3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el
modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos
falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la
familia del sistema, y los `orb-color-*` de aura usan color como sustantivo.
El handoff pone el bloque como primera tarea, con el orden que evita el error
clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante),
luego el codemod value-preserving con el triple muro del precedente px/py
(renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas),
y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el
vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla
contra las siete canónicas, el modificador cerrado y delante, el slot contra
sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint.
En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn
es como estos guards mueren. Con muta-prueba antes de darlo por bueno.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
1. ** `--names` en el censo**: el inventario exacto por clase de desviación,
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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.
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un
catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 +
recipe-contract §1) pero no tiene guard, y la medición fundacional del
2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora
con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre
3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el
modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos
falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la
familia del sistema, y los `orb-color-*` de aura usan color como sustantivo.
El handoff pone el bloque como primera tarea, con el orden que evita el error
clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante),
luego el codemod value-preserving con el triple muro del precedente px/py
(renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas),
y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el
vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla
contra las siete canónicas, el modificador cerrado y delante, el slot contra
sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint.
En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn
es como estos guards mueren. Con muta-prueba antes de darlo por bueno.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
2. **Codemod value-preserving** (molde: px/py del 2026-07-06 y su triple muro):
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
`-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
en `recipes/base.ts` Y todos los consumidores (`var(--{c}-{clave})` en
css/svelte/ts/md — con `git ls-files` , no de memoria; los .md de HISTORIA
— changelog, errores-toxico, PLAN-affix/background — NO se reescriben),
regenera, y verifica: diff de `generated/` = **renombres 1:1** ,
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un
catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 +
recipe-contract §1) pero no tiene guard, y la medición fundacional del
2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora
con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre
3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el
modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos
falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la
familia del sistema, y los `orb-color-*` de aura usan color como sustantivo.
El handoff pone el bloque como primera tarea, con el orden que evita el error
clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante),
luego el codemod value-preserving con el triple muro del precedente px/py
(renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas),
y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el
vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla
contra las siete canónicas, el modificador cerrado y delante, el slot contra
sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint.
En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn
es como estos guards mueren. Con muta-prueba antes de darlo por bueno.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
censo global IDÉNTICO (un rename no cambia alcance), sonda de computed en 3
componentes renombrados = **0 diffs** . Un commit, script commiteado como
registro.
3. **Guard R-5.3 en `component-audit`** — de **GRAMÁTICA** , no de lista (a
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un
catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 +
recipe-contract §1) pero no tiene guard, y la medición fundacional del
2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora
con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre
3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el
modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos
falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la
familia del sistema, y los `orb-color-*` de aura usan color como sustantivo.
El handoff pone el bloque como primera tarea, con el orden que evita el error
clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante),
luego el codemod value-preserving con el triple muro del precedente px/py
(renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas),
y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el
vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla
contra las siete canónicas, el modificador cerrado y delante, el slot contra
sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint.
En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn
es como estos guards mueren. Con muta-prueba antes de darlo por bueno.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
`[variante-]?[modificador-]?[parte-]?[slot][-talla]?` donde talla ∈ las 7
canónicas, modificador ∈ vocabulario cerrado y DELANTE, slot ∈ (§6.7
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
recipe-level ∪ ejes dimensionales de recipe-contract §1), y la PARTE contra
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un
catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 +
recipe-contract §1) pero no tiene guard, y la medición fundacional del
2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora
con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre
3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el
modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos
falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la
familia del sistema, y los `orb-color-*` de aura usan color como sustantivo.
El handoff pone el bloque como primera tarea, con el orden que evita el error
clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante),
luego el codemod value-preserving con el triple muro del precedente px/py
(renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas),
y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el
vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla
contra las siete canónicas, el modificador cerrado y delante, el slot contra
sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint.
En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn
es como estos guards mueren. Con muta-prueba antes de darlo por bueno.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
`compileMorfo(morfo).parts` + la allowlist eidos-only (el mismo criterio que
`eidos-lint` ). Severidad ** `error` directo**: tras el codemod no hay deuda
que tolerar, y la rampa `warn` es como estos guards mueren. **Muta-prueba
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
obligatoria de tres caras** antes de darlo por bueno: `-bg-hover` rojo ·
`trigger-color` rojo · `primary-solid-hover` VERDE (memoria
`a-guard-that-inspects-nothing-passes` ).
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un
catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 +
recipe-contract §1) pero no tiene guard, y la medición fundacional del
2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora
con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre
3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el
modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos
falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la
familia del sistema, y los `orb-color-*` de aura usan color como sustantivo.
El handoff pone el bloque como primera tarea, con el orden que evita el error
clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante),
luego el codemod value-preserving con el triple muro del precedente px/py
(renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas),
y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el
vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla
contra las siete canónicas, el modificador cerrado y delante, el slot contra
sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint.
En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn
es como estos guards mueren. Con muta-prueba antes de darlo por bueno.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
4. **Docs en el mismo pass** : recipe-contract §1 gana la fila del modificador
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
(hoy sólo la insinúa §6.7) y §4 la fila R-5.3; theming §6.7 nota fechada
con la firma de `fg` y el acotamiento del principio de plataforma a los
ejes dimensionales; T-TH.2 del plan se cierra.
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un
catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 +
recipe-contract §1) pero no tiene guard, y la medición fundacional del
2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora
con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre
3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el
modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos
falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la
familia del sistema, y los `orb-color-*` de aura usan color como sustantivo.
El handoff pone el bloque como primera tarea, con el orden que evita el error
clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante),
luego el codemod value-preserving con el triple muro del precedente px/py
(renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas),
y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el
vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla
contra las siete canónicas, el modificador cerrado y delante, el slot contra
sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint.
En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn
es como estos guards mueren. Con muta-prueba antes de darlo por bueno.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente
El autor, al ver que el radio del trigger del nav vivía en un privado clavado a
--radius-default: «¿los componentes son themables? si no lo son, es un error
como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs
en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo
comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en
var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar
moviendo el sistema entero.
Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205
knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del
componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son
literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen
alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %.
El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla
regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens ·
talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para
component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el
autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar
nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error ·
panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el
PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla
mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda
headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la
forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada
token nuevo, desde el píxel hacia arriba, registro de animationstart/end),
guards por fichero, un componente = un commit con sus artefactos, y revisión
adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho.
El instrumento se probó por mutación antes de creerle, y falló la primera vez:
no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que
meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos
mutaciones. Las cifras del plan son las del instrumento final, y el handoff
(CONTINUE-theming.md) obliga a reproducirlas antes de seguir.
Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del
script limpio · tsc del script sin errores · el plan cita ficheros que existen
(los once, comprobados).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
1. `node --import tsx/esm scripts/theming-census.ts` — debe dar **162 recetas ·
docs(theming): mañana lo primero — el vocabulario de tokens: D-TH.6 (codemod) y R-5.3 (guard de gramática)
La conversación de hoy destapó que el backfill está escribiendo canon sobre un
catálogo que habla al revés: el vocabulario de nombres existe (theming §6.7 +
recipe-contract §1) pero no tiene guard, y la medición fundacional del
2026-07-01 ya dijo lo que le pasa a un canon defendido por prosa. Medido ahora
con `scripts/__names-inventory.ts` (semilla del `--names` del censo): sobre
3.333 claves públicas hay 370 desviadas en 68 componentes — 125 con el
modificador detrás, 215 con `color` como slot o segmento, 30 con ambas. Y dos
falsos amigos que NO se renombran de oficio: `*-focus-ring-color` espeja la
familia del sistema, y los `orb-color-*` de aura usan color como sustantivo.
El handoff pone el bloque como primera tarea, con el orden que evita el error
clásico: primero la FIRMA (D-TH.6, una decisión con el inventario delante),
luego el codemod value-preserving con el triple muro del precedente px/py
(renombres 1:1 en generated, censo idéntico, computed intacto en tres sondas),
y sólo entonces el guard — R-5.3 como gramática y no como lista, porque el
vocabulario de tokens es compositivo donde el de eventos era cerrado: la talla
contra las siete canónicas, el modificador cerrado y delante, el slot contra
sus tres fuentes, y la parte contra el morfo compilado como ya hace eidos-lint.
En `error` directo: tras el codemod no hay deuda que tolerar, y la rampa warn
es como estos guards mueren. Con muta-prueba antes de darlo por bueno.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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.
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
2. 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
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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-color` de la fila tree-grid de la cola
es `guide-fg` ).
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
3. Ejecutar con el protocolo §7 entero. **Un componente = un commit.**
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente
El autor, al ver que el radio del trigger del nav vivía en un privado clavado a
--radius-default: «¿los componentes son themables? si no lo son, es un error
como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs
en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo
comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en
var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar
moviendo el sistema entero.
Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205
knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del
componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son
literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen
alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %.
El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla
regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens ·
talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para
component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el
autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar
nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error ·
panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el
PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla
mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda
headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la
forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada
token nuevo, desde el píxel hacia arriba, registro de animationstart/end),
guards por fichero, un componente = un commit con sus artefactos, y revisión
adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho.
El instrumento se probó por mutación antes de creerle, y falló la primera vez:
no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que
meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos
mutaciones. Las cifras del plan son las del instrumento final, y el handoff
(CONTINUE-theming.md) obliga a reproducirlas antes de seguir.
Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del
script limpio · tsc del script sin errores · el plan cita ficheros que existen
(los once, comprobados).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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.
docs(theming): el handoff, reescrito para que otra sesión arranque sin arqueología
La sesión llega llena. El handoff había crecido por acumulación —cada bloque
añadía su sección— y para saber qué hacer había que leerlo entero y deducirlo.
Ahora la AGENDA va arriba y el registro histórico abajo, separado por su propio
encabezado.
Arriba: qué comprobar en los primeros cinco minutos (las dos cifras que deben
cuadrar, el dev server antes de medir, leer el veredicto §5); tres opciones con
recomendación —revisión adversarial de F2-A, el diseño de `calendar-surface`, o
seguir la cola por alcance, con la cola ya recalculada—; el protocolo en diez
pasos; y **lo que este bloque enseñó**, que es lo que evita repetir el día:
el veredicto orienta pero la medición decide (tres de ocho tenían un error de
lectura) · un token que no mueve nada miente, y lo que procede es retirar la
declaración muerta · un alias puro se borra, no se renombra · lo que la capa
posee el consumidor no lo acuña · y la sonda miente de tres maneras
distintas, así que ante un diff inesperado se corre DOS VECES sobre el mismo
código antes de sospechar del cambio.
El plan §8 gana la entrada de la sesión con las cifras y los commits, y su
cabecera de estado pasa a 43 %.
Estado que hereda la sesión siguiente: alcance global 43 % (era 33 % al abrir el
eje), 15 componentes con contrato, 49 sin él, 7 al 100 %, gramática con 0
desviadas y guard en `error`. 29 commits, sin pushear.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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**:
1. **Diseño de la capa `calendar-surface`** (firma 1) — se PRESENTA antes de
escribirla (nombre, ejes, qué posee; molde `list-surface` y sus 4 reglas).
Desbloquea los tres gordos del catálogo: `range-calendar` (98) +
`month-grid` (61) + `year-grid` (61) y media `date-range-picker` — 220
knobs, el salto de ~42 % a ~47 %.
2. **Mandato Field ejecutable** (firma 2) — `field-langs` (41) + las mitades
de `time-picker` / `time-range-picker` / `date-range-picker` .
3. El resto del orden B1-B8 de D-TH.4 (`grid-list` 32 · `textarea` 28 · …).
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
docs(theming): partir recipes/base.ts, al terminar el eje entero
Pregunta del autor: por qué los tokens de 128 componentes viven en un fichero
de 6.223 líneas y no junto a su componente.
La respuesta medida: NO es un fichero de tokens, es el valor del campo
`recipes` de `EidosConfig` —serializable, que un tema hidrata—, y por eso los
valores no pueden mudarse a `components/{x}/`: ya se intentó y se retiró («The
old `tokens/components/*` were retired»). Lo que sí puede repartirse es la
AUTORÍA, con un fichero por componente que `base.ts` compone.
Verificado antes de proponerlo: el muro de tipos de `defineRecipes` sigue
disparando cuando las claves llegan por spread desde un fichero suelto —
probado con `trigger-color` y `padding-x`, ambas siguen sin compilar. Y el
único consumidor sensible a la forma (`eidos-purge`, que hace `Object.keys()`)
es indiferente.
Va al final de TODO el themeable, no al cerrar el bloque: mientras quede un
componente por tokenizar, ese fichero se toca en cada commit, y mover 6.000
líneas en medio garantiza conflicto con cualquier sesión que esté trabajando.
Los tres costes que justifican hacerlo se midieron hoy: casi entra un SEGUNDO
bloque `table` que el catálogo habría descartado en silencio, una coma perdida
rompió el catálogo entero dos veces, y es el punto de conflicto de todas las
sesiones concurrentes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
## 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:
```ts
// 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`).
docs(next-features): el registro de incidencias del eje theme-reach
Encargo del autor: anotar todas las incidencias de esta clase para resolverlas
después. Van al registro canónico (`next-features.md`), no a un sitio nuevo, y
con su MEDICIÓN — no como impresión.
**§12 — El contrato de cascada del velo de estado.** Seis incidencias que
resultaron ser la misma: nadie fijó nunca cómo compone el velo de
`archetypes.css` con las reglas de una receta. Y la raíz explica por qué se
manifiestan de formas tan distintas: el velo se pinta con DOS pesos según el
arquetipo — `:where()` (0,0,0) para `trigger`, especificidad plena (0,5,0) para
`item`/`option`.
131 declaraciones-atajo en 45 componentes cancelan el velo (66 BASE ·
32 ACENTO · 23 HOVER · 10 OTRA; cero valores con gradiente, así que el paso a
longhand no tiene riesgo técnico; 32 nodos velados que hoy no reaccionan al
ratón pasarían a hacerlo) · la prop `hoverable` de table no suprime nada · en
tree-grid el empate (0,5,0) lo decide el ORDEN DE CARGA y varía entre
recargas · la banda de tree-grid mata el hover en las filas pares · un token
de tinta no puede ganar al arquetipo (`selected-row-fg` se retiró por eso) ·
el thumb de scroll-area no tiene velo al que migrar.
Esto explica de paso la adopción 21/135 que midió la auditoría del 2026-07-01:
el velo estaba escrito y estructuralmente derrotado.
**§13 — Huecos de instrumento y de demo.** Lo que hizo que una medición
mintiera o no existiera, que importa porque el eje entero se apoya en ellas:
falsos negativos del centinela sobre propiedades transicionadas, 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**—, con la lista de partes condicionales afectadas.
El handoff gana la regla para las sesiones del 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.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 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` ](../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.css` con las reglas
de una receta. Incluye las 131 declaraciones-atajo en 45 componentes, la prop
`hoverable` que no suprime nada, el empate (0,5,0) que resuelve el orden de
carga, la banda de `tree-grid` que mata el hover, el token de tinta que no
puede ganar al arquetipo, y el thumb de `scroll-area` sin 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
1. ** `scroll-area.thumb-bg-hover` — pide DECISIÓN de morfo.** El thumb lleva
`archetype: '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.
2. **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.**
3. **Hueco de demo** : `date-range-picker` no expone `kind='month'` /`'year'`
(norma N-6), así que la mitad de su API no se ve ni se mide.
---
docs(theming): el acta — las catorce decisiones que faltaban, firmadas
El eje llevaba desde el 19 ejecutando sólo lo que no dependía de una firma.
Hoy se presentaron las catorce con recomendación fundada y el autor las firmó
en bloque. Quedan escritas donde se ejecutan: §4 del plan fila a fila, y una
sección de acta en el handoff con el QUÉ ejecutable de cada una.
La que cambió de sentido al presentarla fue D-TH.6. La pregunta no era
«normalizar hacia lo documentado» sino una CONTRADICCIÓN entre dos doctrinas
firmadas: theming §6.7 r7 dice que el slot de tinta es `fg`; el principio de
plataforma del codemod px/py («el token se llama como la propiedad») dice
`color`. Se adjudicó `fg`, y con una razón que acota el principio en vez de
romperlo: gobierna los ejes DIMENSIONALES, no la pareja `bg`/`fg` — si la
gobernara, `bg` tendría que llamarse `background-color`, y nadie lo propone.
De la misma lectura salió que el inventario heredado estaba inflado: de las
370 claves «desviadas», 47 son `{rol}-{slot-de-rol}` (`primary-solid-hover`,
`palette-hover`) donde COLOR_ROLE_SLOTS pone el modificador detrás POR
CONSTRUCCIÓN. Un codemod sobre las 370 las habría roto — `button` entero.
El inventario real es 301, y la gramática es POR FAMILIA, no única.
Los 22 «falsos amigos» resultaron ser tres cosas distintas: los que se quedan
(la familia del sistema `--focus-ring-*`, el color como sustantivo de `aura`,
y `stop-color` que ES una parte de gradient-builder), los que el morfo ya
resuelve (`partial`, `read`, `failed` son valores declarados de `data-state` y
`data-delivery`), y los dos `scrim-color-on-*`, que chocaban con el prefijo
canónico `on-` del acento y pasan a `scrim-fg-over-*`.
Y una que no era limpieza sino decisión: los hovers neutros NO se renombran,
MIGRAN a la capa de estado (§38 + R-4.3). Renombrarlos habría presumido que
sobreviven.
Con las firmas, la revisión de los 7 componentes ya corregidos contra la
gramática nueva: 6 limpios y una corrección (`gradient-builder.checker-color`
→ `checker-fg`), que entra en el codemod. Que salgan limpios no es suerte —
el backfill de este eje ya venía escribiendo `fg`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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:
1. **Familia calendar = capa compartida** (molde `lib/list-surface.css` : attr
de capa, un eje = token público + ranura privada `var(--_x, var(--x))` ,
guardada por `shared-layer-contract.test.ts` + `layer:check` ). NUNCA ~150
alias `--calendar-*` . Desbloquea `range-calendar` (98), `month-grid` (61),
`year-grid` (61) y media `date-range-picker` . El diseño de la capa
(nombre, ejes, qué posee) se presenta antes de escribirla.
2. **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ón `height-{xs..xl}`
por componente (theming-audit §2-B: computaban `auto` desde su nacimiento).
3. **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-hover` huérfanos, incluido
`pagination.selected-bg-hover` = capa sobre acento).
4. **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`) ∪ valores `data-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).
5. **D-TH.1** R-5 dura · **D-TH.3** tracks WIP en censo, `error` sólo fuera de
carril · **D-TH.5** el default no cambia (única excepción: la firma 3, con
captura).
6. **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 lista `KNOB_PROPS` es el perímetro; cambios = firma.
7. **D-TH.4** con enmienda: la cola revisada de este CONTINUE prioriza sobre
el orden de familias B1-B8.
8. **D-TH.7** con enmienda: **R-5.3 a `error` directo tras el codemod** ;
R-5.1/5.2 esperan F3.
9. **D-TH.8** : panel de tokens en `/temas` , en F4.
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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
`--write` en 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-hover` lee `var(--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.
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| 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)
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente
El autor, al ver que el radio del trigger del nav vivía en un privado clavado a
--radius-default: «¿los componentes son themables? si no lo son, es un error
como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs
en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo
comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en
var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar
moviendo el sistema entero.
Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205
knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del
componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son
literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen
alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %.
El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla
regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens ·
talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para
component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el
autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar
nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error ·
panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el
PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla
mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda
headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la
forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada
token nuevo, desde el píxel hacia arriba, registro de animationstart/end),
guards por fichero, un componente = un commit con sus artefactos, y revisión
adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho.
El instrumento se probó por mutación antes de creerle, y falló la primera vez:
no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que
meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos
mutaciones. Las cifras del plan son las del instrumento final, y el handoff
(CONTINUE-theming.md) obliga a reproducirlas antes de seguir.
Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del
script limpio · tsc del script sin errores · el plan cita ficheros que existen
(los once, comprobados).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
```bash
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4
Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree
con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba
más dura que la del autor), sonda x2 por componente para fijar el suelo de
ruido. 34 hipótesis · 26 refutadas · 8 reales.
Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs;
command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 ·
listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen
idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens
huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano»
de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación;
eidos-lint 0 invalid.
Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command
8.091 y carousel 2.842 valores, 0 diffs):
1. command: el re-point de `--command-radius` dentro del Dialog era una
declaración muerta con comentario falso — la misma regla pone el radio a 0
y el input lee `--command-input-radius`, nunca este token (medido:
`--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px`
movía la paleta inline y no la del dialog). Retirado; comentarios y README
reescritos con lo medido.
2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo
mientras la horizontal leía `active-indicator-scale` — el token alcanzaba
media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora
lo leen las dos.
3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap`
INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por
CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y
las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es
la prueba. Canal de valor de soma, como --gp-current-gradient. Censo
carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto
(5.190 · 2.102).
4. table: `striped-row-bg` no alcanza NADA en la demo publicada —
`:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos
quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel →
PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`);
adjudicado en el ledger mientras tanto.
El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas
medidas, y la cuarta era de la sonda:
- el paso de abrir hacía 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 (era el «falso negativo sin causa» de §13);
- el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de
`{c}-root` no lo recibían;
- `::before`/`::after` eran invisibles (el aro de buffering, el spinner);
- la no-determinación de feed no era una animación sin localizar: era la demo
aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma
`commit-settle` anima box-shadow sobre el nodo medido. Con la espera de
asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas.
Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y
que carousel convirtió en patrón — un estilo inline de otra capa es invisible
para todo análisis estático): `npm run theming:sentinel -- <c> <url>`
(scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en
todos los nodos del componente, pseudos, pase de hover, settle) + ledger
scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto
sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en
verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 ·
gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46
excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías
salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró;
y dos muertos nuevos los produjeron los propios arreglos (el blur apaga
focus-input-border; el settle desmonta el spinner de feed) — adjudicados con
su porqué. Doctrina en recipe-contract §4.
También: tres cifras de especificidad de next-features §12 recontadas (la
banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0);
y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el
velo en la celda), el comentario de listbox que mandaba pisar el privado
`--_listbox-max-height` apunta ahora al público, y quedan registrados en §13
los dos `header-z` con literal '2' y el spinner de media-player a 720ms
mientras el gemelo de feed lleva tokens.
Guards: censo global 43 % intacto · audit --only command+carousel PASS ·
eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest
eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs
0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier
limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en
HEAD y no se tocan).
Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el
arquetipo en fila+celda, y que soma lea el gap de un token.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
npm run theming:sentinel -- < componente > < url > # guard R-5.4: ¿alcanza cada token? (ya no es `__` )
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
node scripts/__shot.ts < componente > < salida.png > # captura 2× del stage
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente
El autor, al ver que el radio del trigger del nav vivía en un privado clavado a
--radius-default: «¿los componentes son themables? si no lo son, es un error
como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs
en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo
comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en
var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar
moviendo el sistema entero.
Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205
knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del
componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son
literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen
alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %.
El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla
regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens ·
talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para
component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el
autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar
nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error ·
panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el
PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla
mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda
headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la
forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada
token nuevo, desde el píxel hacia arriba, registro de animationstart/end),
guards por fichero, un componente = un commit con sus artefactos, y revisión
adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho.
El instrumento se probó por mutación antes de creerle, y falló la primera vez:
no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que
meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos
mutaciones. Las cifras del plan son las del instrumento final, y el handoff
(CONTINUE-theming.md) obliga a reproducirlas antes de seguir.
Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del
script limpio · tsc del script sin errores · el plan cita ficheros que existen
(los once, comprobados).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
```
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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` , NUNCA `animation` **: una propiedad
transicionada devuelve su valor INICIAL justo tras escribir el token (un token
vivo parecía muerto), pero congelar `animation` impide 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
`:root` **y** 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 con `parts` ), y un override scoped no lo alcanza.
- **La demo puede tapar el componente**: `proof-of-human` declara su propio
`min-block-size` y fondo sobre el stage — ahí el centinela no sirve, y se dice.
- **Un valor centinela igual al real lee como «no efecto»** (`9999px` contra un
`--radius-full` que 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
uix(code-block): temable — 0 % → 81 %, y la demo que no enseña el componente
20 claves en una entrada NUEVA de `recipes/base.ts` (no tenía). Censo 0 % →
81 %. Global 46 % → 47 %, sin contrato 46 → 45.
El handoff lo llamaba «el más mecánico de los que quedan». No lo es: sus 26
knobs se parten en dos grupos con doctrinas distintas.
1. **El nodo del código NO acuña nada, y la §4 pedía que lo hiciera.**
`[data-code-block-code]` consume la capa tipográfica `--style-code-*` en sus
cinco declaraciones, y las dos que pasan por privado usan la forma canónica
de la escotilla por instancia
(`var(--_code-block-font-size, var(--style-code-font-size))`, escrita por el
wrapper desde la prop `size` y sólo si llega). Es el patrón que D-TH.2-b
fijó para heading/text/code. Acuñar `code-font-family` y compañía sería el
vocabulario paralelo que la regla 2 de capas prohíbe. Todo lo que entra al
contrato es el CASCO alrededor.
2. **Los tres `font-size` iban al primitivo crudo, y lo cazó un guard.** La
propuesta copia el valor del CSS verbatim, y ese valor ya violaba
`recipe-css-contract` («recipes consume the size bundle, not the raw
size-coordinate primitives»): `title/lang/copy-font-size` corregidos a
`var(--size-{k}-font-size)`. Los alias son 1:1 → 0 diffs tras aplicarlo.
Aviso general: cuando el CSS de partida ya incumple el contrato, la
propuesta generada hereda el incumplimiento.
3. Shorthand partidos: los dos `border` y el `border-bottom` de la cabecera
comparten anchura y color → `border-width` + `border`; el `padding` del
`<pre>` → `pre-padding`. Los `transparent` de outline/ghost y el `border: 0`
de ghost son identidad de variante.
El 19 % que falta son esas cinco de la capa, que el censo cuenta como `global`
porque `code-block` no está en `TYPOGRAPHIC_PRIMITIVES` — es un compuesto que
CONTIENE un primitivo tipográfico, no uno de ellos. Extender la clase `system`
del censo a las capas sigue pendiente de tu firma; la métrica penaliza hacer lo
correcto y no se fuerza de oficio.
DOS HALLAZGOS QUE NO SE ARREGLAN AQUÍ (→ next-features §13):
- **La demo de `code-block` no enseña el `code-block`.** `[data-uix-docs] pre`
(`web/routes/uix/uix.css:801`) pesa (0,1,1) contra los (0,1,0) de
`[data-code-block-pre]`, y `[data-uix-docs]` es el shell de TODAS las
páginas. Siete propiedades del `<pre>` las pinta el harness y no la receta:
padding (16px 20px contra 12px), border (1px contra ninguno), radius (8px
contra 0), background (gris contra transparente), font-family, font-size
(13 contra 14) y line-height (18.2 contra 20.3). El fondo, el borde y el
radio que se ven ahí son del sitio. Por eso `pre-padding` lee muerto en el
guard, adjudicado con esa medición. Acotar la regla toca el CSS del SITIO,
que sirve a ~162 páginas: decisión aparte.
- **El guard de claves duplicadas del handoff inspeccionaba el VACÍO.** El
comando documentado usa `grep -oE "^\t…"`, y `grep -E` lee `\t` como una `t`
literal: 0 coincidencias sobre 130 bloques reales, así que salía «vacío = OK»
sin mirar nada — el antipatrón que el proyecto tiene registrado, dentro del
propio handoff. Comprobado con un contador fiable: cero duplicados en el
árbol, así que no hay daño en los commits que confiaron en él. CONTINUE
queda corregido con una versión que CUENTA los bloques y avisa de que «0
bloques» significa guard roto, no fichero limpio.
Artefactos y gates:
- Sonda antes/después = 0 diffs en 1.305 valores × 7 estados (dos veces: tras
la tokenización y tras la corrección del bundle). Determinismo verificado.
- R-5.4: 19/20; `pre-padding` adjudicado con las siete propiedades medidas.
- censo --only 81 % · component:audit PASS · eidos-lint invalid 0, class-hooks
0 · suite eidos de vuelta al único rojo conocido (skin-media-player) tras
corregir el bundle · rtl:check 0 · docs:check 0 · check por fichero limpio.
- README con su «Talla y tema» y tab `Tokens` en la demo (20 claves, en vivo).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
descarta **en silencio** (gana la segunda). Guard antes de generar — y que
**cuente cuántos bloques ve** : la versión con `\t` que vivió aquí hasta el
2026-08-21 no casaba NINGUNO (`grep -E` lee `\t` como una `t` literal) y salía
«vacío = OK» sin haber mirado nada — el antipatrón del guard que inspecciona el
vacío, en el propio handoff.
```bash
node -e "const t=require('fs').readFileSync('src/uix/eidos/lib/recipes/base.ts','utf8').replace(/\r\n/g,'\n');const k=[...t.matchAll(/^\t'?([a-z0-9-]+)'?: \{/gm)].map(m=>m[1]);const d=k.filter((x,i)=>k.indexOf(x)!==i);console.log('bloques',k.length,'| duplicados',d.length?d:'ninguno')"
```
Debe imprimir los ~130 bloques y `duplicados ninguno` . **Si dice 0 bloques el
guard está roto**, no es que el fichero esté limpio.
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **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:audit` DESPUÉS de `prettier` , 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 en `natural-time-picker` , cazada un día tarde por correr el audit antes
del formateo.
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **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 --write` sobre un README** no protege de que un Edit se coma un
encabezado: comprobar `grep -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 `stash` ni
`--amend` , `check` **por fichero** .
docs(theming): el plan del eje theme-reach — medir qué alcanza un tema, y corregir componente a componente
El autor, al ver que el radio del trigger del nav vivía en un privado clavado a
--radius-default: «¿los componentes son themables? si no lo son, es un error
como framework». Lo es. La doctrina ya exige que cada receta declare sus knobs
en recipes/base.ts (recipe-contract §1, theming §6), pero R-1…R-4 sólo
comprueban «sin literales», no «alcanzable por un tema»: una receta con todo en
var(--radius-md) y privados pasa component:audit en PASS y sólo se puede temar
moviendo el sistema entero.
Medido, no opinado — scripts/theming-census.ts (instrumento, NO guard): de 5.205
knobs de apariencia en 162 recetas, 1.621 (33 %) pasan por un token público del
componente; 1.880 atan directo a un primitivo global, 856 a privados, 614 son
literales; 62 componentes no tienen una sola entrada en el contrato; 59 tienen
alcance < 20 %; 6 al 100 %. navigation-menu, tras todo el día de hoy, 44 %.
El plan (PLAN-theming.md): §1 el censo por familia y por componente (tabla
regenerable, no editable) · §2 los cuatro ejes de «personalizable» (tokens ·
talla · color/variante · estados) · §3 la regla R-5 «alcance de tema» para
component-audit, con su válvula de excepción · §4 ocho decisiones D-TH para el
autor (R-5 dura · perímetro de knob · WIP · orden · DEFAULT IDÉNTICO · normalizar
nombres de slot, que hoy incumplen tabs y el propio nav · cuándo gradúa a error ·
panel de temas) · §5 fases F0–F4 con gates · §6 deuda transversal · §7 el
PROTOCOLO DE VERIFICACIÓN por componente, porque «lo ejecutará Opus, y Opus falla
mucho»: cinco preguntas, medir ANTES (computed por parte/estado/talla, sonda
headless desde la raíz, fuera de callbacks de MutationObserver), editar sólo la
forma firmada, medir DESPUÉS (diff de computed = 0, prueba de CENTINELA por cada
token nuevo, desde el píxel hacia arriba, registro de animationstart/end),
guards por fichero, un componente = un commit con sus artefactos, y revisión
adversarial por bloque. Cada paso tiene un artefacto; sin artefacto no está hecho.
El instrumento se probó por mutación antes de creerle, y falló la primera vez:
no contaba una regla de una sola línea (`[x] { padding-inline: 8px; }`), así que
meter un literal no movía la cifra. Regex endurecido; ahora 6→7→8 con una y dos
mutaciones. Las cifras del plan son las del instrumento final, y el handoff
(CONTINUE-theming.md) obliga a reproducirlas antes de seguir.
Nada firmado, nada construido. Gates del commit: docs:check 0/642 · prettier del
script limpio · tsc del script sin errores · el plan cita ficheros que existen
(los once, comprobados).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## Fuentes
docs(theming): handoff del eje al día — cola revisada, firmas pendientes y el instrumental que las verifica
El eje deja de ser «plan entregado» y pasa a «en ejecución»: siete componentes
corregidos, veinte fichas revisadas y el informe vivo. El handoff y el registro
del plan lo dicen con cifras y commits, para que mañana se entre por la ficha y
no por la memoria de nadie.
`CONTINUE-theming.md` se reescribe con lo que hace falta para continuar: la cola
inmediata de ocho componentes EJECUTABLES sin firma —cada uno con la trampa
concreta que su veredicto ya destapó—, la lista de lo que ESPERA firma (familia
calendar, mandato de Field, el perímetro del knob para la familia tipográfica,
el hover a la capa de estado) y la tabla de lo ejecutado con su censo antes →
después. El plan gana su §8 con el registro completo y corrige su cabecera, que
seguía diciendo que no había nada construido.
Y se commitea el instrumental, porque sin él el protocolo §7 es una intención:
la sonda de computed antes/después, el diff que es el gate (vacío o se para), el
centinela que pregunta si cada token alcanza de verdad, y la captura del stage.
Se ejecutan con `node` a secas —el loader de tsx inyecta helpers que no existen
dentro de `page.evaluate` y toda sonda revienta—, desde la raíz y contra el dev
server.
Lo que el instrumento aprendió a fuerza de mentir queda escrito donde se
consulta: congelar `transition` pero NUNCA `animation` (Presence necesita el
`animationend` para montar el panel, y si lo congelas mides un popup que no
existe); un token resuelto no se mueve desde `:root` por diseño; un panel
portalado no ve el ámbito del componente; la demo puede tapar el componente que
mides; y un valor centinela igual al real se lee como «no efecto».
Igual las cuatro trampas que costaron un commit cada una, con su guard al lado:
la clave duplicada en `recipes/base.ts` que descarta el bloque en silencio, los
bloques `[data-size]` del CSS que sobreviven a la cascada del TSC y hacen que el
diff dé cero por la ruta vieja, los backticks en `node -e` desde bash, y el
encabezado que un Edit se come al insertar una sección.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- Plan y decisiones: [`PLAN-theming.md` ](./PLAN-theming.md ) · informe:
[`docs/audit/theming/README.md` ](../audit/theming/README.md ).
- Auditoría del SISTEMA (fase 1, cerrada): [`theming-audit.md` ](../audit/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, columna `unused` ) ·
`src/uix/eidos/components/README.md` (capas compartidas).
- Precedente de la forma por talla: [`PLAN-sidebar.md` ](./PLAN-sidebar.md ) §3 +
F3 y el bloque `sidebar` de `recipes/base.ts` .