19 KiB
REVIEW — theming «theme-reach»: lo ejecutado tras la revisión adversarial del 22 (2026-08-23)
Alcance: los 26 commits posteriores a cd6a9f4e3 (la revisión §7.7 del
bloque de catorce, 2026-08-22), que no han pasado ningún análisis posterior:
f25f5bb1d (drawer, rojos) · 27dc6dba3 (metrics + color-picker, rojos) · los 17
de la mañana del 23 (menubar · field · onion-menu · button · callout · empty-state
· dropdown-menu + context-menu · checkbox · image · anchor-nav · banner · surface ·
date-range-field · time-range-field · tooltip · cola pequeña de seis · audio-player)
· los 7 de la tarde (navigation-menu · waveform · color-field · background ·
s-text-virtual-list · badge · cropper). 33 componentes.
Qué se ha comprobado aquí, con medición propia (instrumental en
scripts/__rv-*.mjs, un solo uso): gramática de nombres (--names) · pureza del
generated/base.css commit a commit · equivalencia ESTÁTICA «valor viejo = valor
del contrato» par a par en los 26 commits · npm run check atribuido por fichero ·
prettier --check en los ficheros tocados (y si la no conformidad era previa) ·
component:audit completo · eidos-lint-all · sonda ×2 de los siete de la tarde
contra su línea base, con el servidor reiniciado · barrido del centinela sobre
TODOS los componentes con ledger · re-medición dirigida de las adjudicaciones
dudosas · presencia de README «Talla y tema», tab Tokens y veredicto §5.
Qué NO es esto: la revisión §7.7 completa. Falta la re-medición del «default
idéntico» contra una BASE (worktree en cd6a9f4e3) para los 26 de la mañana, el
«píxel hacia arriba» en los hover tocados y el registro de doble animación. Es el
punto P5 del plan.
1. Errores reales (código o ledger) — 5
| # | dónde | qué | prueba |
|---|---|---|---|
| E1 | badge.min-block-size-{xs,sm,md,lg} + min-block-size (271fcd4df) |
Nombre fuera de recipe-contract §1: «Control height per size → control-height-{size} (o {part}-height-{size}) — inventing a third name per component». El catálogo ya lo dice: navigation-menu.trigger-height-{k} se CONSUME como min-block-size, accordion.trigger-min-height-{k}, field.control-height-{k}. R-5.3 no lo caza (guarda tinta y modificador, no el vocabulario dimensional). |
base.ts: 41 claves -height-, 0 precedentes de min-block-size-{k}; §1 fila 40 |
| E2 | ledger background.parallax-travel (9bdc5a5d5) |
Adjudicación MAL CLASIFICADA: dice «structural — only enters via the scroll-linked keyframes; the measured stage has no scroll timeline». Medido hoy: el escenario SÍ tiene animation-timeline: view(); forzando data-parallax + speed 1, el translate sigue al scroll (−9,7 → 14,6 px) y el token alcanza (14,6 → 60,5 px al mismo scroll con --background-parallax-travel: 400px). Lee muerto en el guard porque (a) la demo arranca con speed 0 (sin data-parallax) y (b) el guard no fotografía translate/transform (tampoco mask-image). |
scripts/__bg-parallax.mjs |
| E3 | README de cropper (f03ee5af4) |
Dice «hueco de composición (el botón de zoom debería componer IconButton), anotado» — y no está anotado en ningún sitio: ni fila en ## Gaps, ni next-features §13. |
grep IconButton sólo en la prosa |
| E4 | scripts/theming-sentinel-exceptions.ts |
Mis entradas de color-field / background / badge añaden 4 hunks de prettier (63 preexistentes → 67). Protocolo §7.5.18. |
prettier --check base vs HEAD |
| E5 | float-panel (ledger) + handoff |
12 tokens públicos muertos SIN adjudicar — bg, border, border-width, shadow, active-shadow, font-family, line-height, resize-size, button-size, accent, transition-duration, transition-ease — y el guard R-5.4 falla en él hoy. No es regresión: el commit b4510fbf8 (08-22) ya lo dejó escrito («lo rojo que queda son 12 tokens anteriores; seis son el plano de profundidad, §12.9») y nadie los midió después. La afirmación del handoff «desde el 2026-08-23 NO queda ningún rojo del bloque viejo» es FALSA: drawer, metrics y color-picker se adjudicaron; float-panel no. |
barrido del centinela, repetido a solas: 17/40, 12 sin adjudicar; ledger idéntico al del 22; ningún commit tocó el componente desde entonces |
2. Desviaciones del protocolo — 5
| # | quién | qué | alcance |
|---|---|---|---|
| D1 | sesión de la mañana | Ni README «Talla y tema» ni tab Tokens en la demo en NINGUNO de sus commits de tokenización (PLAN §5 F2 «README de eidos con la tabla de tokens»; handoff, protocolo pasos 8 y 9). git show --stat: sólo docs/audit/theming/, CSS, base.ts, ledger. |
20 componentes: menubar · field · onion-menu · button · callout · empty-state · dropdown-menu · context-menu · checkbox · image · anchor-nav · banner · date-range-field · time-range-field · tooltip · section · sticky · button-group · qr-code · toggle-group · split-button (+ surface y audio-player, sin demo: sólo README) |
| D2 | yo (tarde) | waveform y cropper: la tabla de tokens fue a una sección existente (## Tokens, ## Recipe tokens) y no a «Talla y tema» (molde navigation-menu). |
2 |
| D3 | las dos sesiones | Sin capturas 2× antes/después (§7.2.6, §7.4.13) en ningún commit; sin «píxel hacia arriba» (§7.4.12) — relevante sólo donde se tocaron reglas :hover (mañana: dropdown/context-menu hover-item-*, anchor-nav hover-link-fg, menubar; tarde: ninguno). |
33 / los de hover |
| D4 | yo (tarde) | npm run check sólo en navigation-menu; prettier --check en ninguno; guard re-verificado tras cada cambio sobre 3–5 componentes, no sobre el ledger entero. Subsanado en esta revisión: check 0 errores en mis ficheros (los 72 globales son ajenos y preexistentes); prettier: los 11 ficheros ya no pasaban ANTES (sólo E4 es mío); barrido completo del ledger abajo. |
6 |
| D5 | las dos sesiones | Sin revisión adversarial §7.7 de ningún bloque desde el 22: 26 commits, 33 componentes. | todo el alcance |
3. Refutado (se comprobó, no hay hallazgo)
- Gramática:
--names→ DESVIADAS 0 (3.931 conformes, 447 rol-slot canónicas). - Pureza del generado: 26 commits, cada línea ± de
generated/base.csses del componente del commit. - «El default es idéntico», estáticamente: los 26 commits, par a par
prop: viejo → prop: var(--c-key): cada valor del contrato ES el viejo — o el respaldo que se quitó (var(--x, V)→ contratoV), o el alias de bundle verificado en el generado (--size-{k}-control-height: var(--control-height-{k}),--size-{k}-font-size: var(--font-size-{k}),--size-{k}-font-line-height: var(--font-line-height-{k})). Los tres que parecían cambiar valor eran artefactos del emparejamiento:anchor-nav(hover →hover-link-fgcontent-primary; active →active-link-fgprimary-text, correcto),dropdown-menu.separator-bg(el respaldo--color-border-subtleera código MUERTO: la clave ya valía--color-neutral-separator),menubar(--floating-gappor talla:space-1 * 0.5→padding-sm = --space-0-5, misma aritmética; elfont-sizedel panel por talla). - Sonda ×2 de los siete, servidor reiniciado: 0 diffs en las 14 corridas salvo
backgroundcorrida 1 — 2 diffs deinlineSizedel ESCENARIO (870 ↔ 1.350 px), el ancho del layout de la demo, no el componente; corrida 2 limpia. Ruido de instrumento, a §13. component:audit: 162 PASS · 4 NEEDS-WORK (badgeR-1.5 PREEXISTENTE y ya anotado;gradient-picker,mockup,motionajenos).eidos-lint-all: 0 invalid, 0 class-hooks.- Ledger: 196 adjudicaciones en alcance; 26 sin medida explícita en su propia línea, de las que 25 son continuaciones «same as…» de una hermana medida y 1 es E2.
- Barrido del centinela sobre todo el ledger (59 componentes, tras los cambios del guard de hoy:
extraNodes,openBy: 'hover',prepareWith): 0 STALE, 0 errores de corrida, y un único componente con muertos sin adjudicar —float-panel(E5), que ya lo estaba el 22. Los cambios del guard no han roto nada del ledger. Detalle en §5. - Mis adjudicaciones de
background.fade-*(«the guard does not snapshot mask-image») son ciertas:maskImageno está enPROPS. - El barrido de variantes de
badge(soft/solid/outline/ghost) casa conEIDOS_VARIANTS.chip. TokensPanelaceptastageopcional (s-text-virtual-list lo omite): sin error de tipos.
4. Plan de solución — FIRMADO Y EJECUTADO 2026-08-23
| P | estado | commit |
|---|---|---|
| P8 | ✅ float-panel: los 12 medidos y adjudicados; guard en verde; frase del handoff corregida |
233ef989b |
| P2 | ✅ background.parallax-travel reescrito; hueco translate/transform en §13 |
233ef989b |
| P3 | ✅ cropper §Gaps real; «Talla y tema» en cropper y waveform; ledger sin hunks nuevos |
233ef989b |
| P1 | ✅ badge.min-block-size-{k} → min-height-{k}; R-5.3 sin vocabulario dimensional, a §13 |
bfe7727b0 |
| P4 | ✅ 22 READMEs «Talla y tema» derivados del contrato + 21 pestañas Tokens, verificadas en el navegador |
e522e04e6 |
| P6 | ✅ §13: flicker del escenario de background · audio-player sin README · los tres huecos de PROPS |
233ef989b + éste |
| P7 | ✅ PLAN §7: las capturas se exigen DESDE el siguiente componente; píxel-arriba obligatorio si se toca :hover |
éste |
| P5 | ✅ revisión adversarial §7.7: 5 hipótesis, 5 refutadas, 0 hallazgos nuevos (§6) | éste |
El plan original
| P | qué | cómo | toca píxel | firma |
|---|---|---|---|---|
| P1 | E1 — renombrar badge.min-block-size-{xs,sm,md,lg} → height-{xs,sm,md,lg} y el resuelto min-block-size → height |
un commit: base.ts + badge.css + regenerar + sonda (diff 0 por construcción) + guard (29/71, mismas adjudicaciones) + README + ficha + PLAN §8 («nombre corregido»). El tab Tokens sigue en 71. |
no | sí — renombra una clave pública recién acuñada; el nombre canónico es de §1, pero la decisión de renombrar hoy es tuya |
| P2 | E2 — reescribir la adjudicación de background.parallax-travel («parallax off in the demo (speed 0); forced data-parallax + speed 1 → reaches through the scroll-linked translate (14.6 → 60.5 px at the same scroll); the guard does not snapshot translate») y registrar en §13 el hueco translate/transform del guard (ya hay precedente en drawer.handle-active-scale) |
ledger + next-features; un commit docs | no | no |
| P3 | E3 + D2 + E4 — cropper README: fila en ## Gaps («el botón de zoom compone cromo propio; debería componer IconButton — y por eso su hover queda fuera de la capa de estado»); waveform / cropper: la sección pasa a llamarse «Talla y tema»; ledger: partir mis 4 entradas largas como prettier las quiere (a mano, sin --write sobre el fichero entero: tiene 63 hunks ajenos) |
un commit docs | no | no |
| P4 | D1 — los 20 componentes de la mañana: README «Talla y tema» (tabla de claves públicas + lo adjudicado, molde navigation-menu) y tab Tokens en la demo (<TokensPanel component="x" stage={stageRef ?? undefined} /> + su botón con el conteo); surface y audio-player sólo README (sin demo). |
mecánico, sin píxel. Propuesta: un commit por componente (el protocolo es «un componente = un commit» y el registro §8 lo referencia así), o un único commit de lote si prefieres cerrar rápido | no | sí — por el alcance (20) y por la forma (por componente vs lote) |
| P5 | D5 — la revisión adversarial §7.7 de los 26 commits: worktree en cd6a9f4e3 con server.fs.allow (trampa de la junction), comprobar que un componente MONTA antes de comparar, sonda base vs HEAD por componente (33), «píxel hacia arriba» en los que tocaron :hover (dropdown-menu, context-menu, anchor-nav, menubar), registro de animationstart/end donde se movió un sello (ninguno de los 26, a comprobar), centinela rehecho. Lo estático de esta revisión (§3) entra como hipótesis ya refutadas. |
una sesión | no | sí — es el guard de bloque del PLAN; decide si antes o después de seguir la cola |
| P6 | instrumento — registrar en §13: el flicker de inlineSize del escenario de background (870/1.350) · translate/transform/mask-image fuera de PROPS del guard (tres tokens vivos leen muertos: parallax-travel, fade-size, fade-at; y drawer.handle-active-scale). Si se añaden a PROPS: re-verificar los ~35 con ledger antes de commitear (el barrido de §5 es la base de comparación). |
docs (+ opcional guard) | no | para tocar PROPS, sí |
| P7 | D3 — capturas 2× antes/después y «píxel arriba» no se recuperan retroactivamente para 33 componentes: se hacen dentro de P5 para los que tocaron hover; para el resto, el diff de computed 0 + el centinela son el artefacto. Anotar en PLAN §7 que las capturas se exigen DESDE el siguiente componente. | docs | no | no |
| P8 | E5 — float-panel: medir los 12 uno a uno con el panel abierto y adjudicar o retirar. Hipótesis ya escrita por el propio commit del 22: los seis de superficie/tipografía son el plano overlay (misma clase que tooltip / popover → adjudicación «SUPERSEDED by the overlay plane», como la de tooltip); active-shadow sólo mientras se arrastra; accent alimenta un privado que pinta en foco/arrastre (forzar estado); resize-size / button-size deberían alcanzar (si no, están mal cableados); transition-* la ley conocida (pasada sin congelar). Y corregir la frase del handoff. | un commit (ledger + handoff + ficha §5 de float-panel) | no | no — es adjudicar lo medido; si alguno resulta MUERTO de verdad, su retirada sí se presenta |
Orden propuesto: P8 (el guard está en rojo hoy) → P2 + P3 (docs, diez minutos) → P1 (una clave) → P4 (los 20, mecánico) → P6 → P5 (la revisión de verdad, una sesión). Nada de esto mueve un píxel.
6. P5 — la revisión adversarial §7.7, ejecutada (2026-08-23)
Molde Sidebar: intentar REFUTAR cada afirmación con medición propia, no leer
la del commit. Base = worktree en cd6a9f4e3 (G:/tmp/rv-base), dev server
propio en 5301, node_modules por junction y server.fs.allow con las dos
rutas — la trampa que el handoff registra: sin eso Vite responde 403 a /@fs/…
y la página NO hidrata, y se compara contra una base muerta. Comprobado ANTES
de comparar nada: los diez primeros componentes montan e hidratan en la base
(10, 4, 10, 2, 57, 17, 6, 25, 2 y 1 nodos).
H1 — «el default es idéntico»: REFUTADA la duda, 33 de 33
Sonda del HEAD (la misma en los dos lados, para no comparar instrumentos) contra base y contra HEAD, componente a componente:
- 31 con 0 diffs, de 192 a 7.360 valores por componente y de 7 a 8 estados. Total comparado: ~48.000 valores computados.
surfacedaba 0 valores por una URL mía equivocada; en su ruta real (/temas/gradientes, la que su propio override declara) → 0 diffs sobre 896 valores, 4 nodos.image(1 diff) yaudio-player(7 diffs + 1 nodo ausente) son ruido del instrumento, probado: corriendo la sonda DOS VECES contra el MISMO código,imagereproduce su diff yaudio-playerda once — más que la comparación base↔HEAD. Las dos propiedades implicadas son estado vivo: laopacityde un<img>con animación de entrada (la sonda congelatransition, nuncaanimation— ley conocida) y elinline-sizedel rango buffered de un audio, que depende de lo que lleve cargado.
H2 — «el velo cae donde debe»: verde en los cuatro que tocaron :hover
Píxel hacia arriba (elementsFromPoint en el centro del control, pila de
ancestros con backgroundImage/backgroundColor):
dropdown-menuycontext-menu: el velo está en[data-…-item], radio 4px — el nodo CON forma; ellist-surfacede debajo (radio 6px) limpio.menubar: la barra pinta el abierto en el propio<button>(radio 2px) sin derramar al[data-menubar]; la fila del panel, el velo en su item (4px).anchor-nav: sin velo en ninguna capa, y es correcto — su hover es sólo tinta (hover-link-fg), no capa de estado. Medido sobre un enlace NO activo, porque el primero que casa el selector llevadata-activey pinta otra cosa.
(El data-uix-stage-area sale con velo en varias pilas: es el fondo a cuadros
del harness, y las demos están fuera del eje.)
H3 — «no hay doble animación»: no aplica, y se comprobó
Ninguno de los 26 commits toca animation, @keyframes, data-event ni
motion-* en src/uix/eidos/components/. No hay sello movido que pueda
duplicarse.
H4 — «cada token alcanza»: rehecho, y destapó E5
El barrido del centinela sobre los 59 con ledger (§5): 0 STALE, 0 errores, y un
único componente en rojo — float-panel, ya arreglado (P8).
H5 — «el contrato no tiene duplicados ni huérfanos»: limpio
144 bloques en base.ts, cero duplicados y cero claves repetidas dentro de un
bloque. El test oficial de huérfanos (recipe-css-contract.test.ts) pasa.
⚠ Y aquí mintió mi propio instrumento: un guard estricto de un solo uso
reportó 185 huérfanos — los pasos de escala por talla y todas las claves de
tono. Falso: excluía generated/, que es exactamente donde el TSC emite el
nombre resuelto y donde el forward de paleta lee los tonos. Un guard de
huérfanos que no mira el CSS GENERADO ve huérfano todo lo que el generador
resuelve.
Veredicto de P5
5 hipótesis · 5 refutadas · 0 hallazgos nuevos. Los dos únicos diffs de la comparación son ruido reproducible del instrumento, no regresiones, y quedan registrados en §13. Los hallazgos reales de esta revisión estaban en el análisis ESTÁTICO (E1–E5) y ya están arreglados.
5. Barrido del centinela (todo el ledger, tras los cambios del guard de hoy)
rv-dyn.sh, 59 componentes con ledger, uno tras otro contra el mismo dev server
(5180), 2026-08-23 por la tarde:
- 0 excepciones STALE en los 59 (ninguna adjudicación ha empezado a moverse).
- 0 corridas con error (ninguna ruta 404 ni timeout: los
urlsde picker-shell / audio-player / surface resuelven). - 1 componente con muertos sin adjudicar:
float-panel, 17/40, 12 sin adjudicar (E5). Repetido a solas: idéntico, así que no es el falso exit por lote del que avisa el handoff. - Los cinco que re-verifiqué a mano durante la tarde (command 51/57 · table 42/45 · tooltip 15/23 · navigation-menu 43/44 · waveform 11/11) dan lo mismo en el barrido: los cambios del guard son inocuos fuera de sus overrides.
- Sonda ×2 de los siete de la tarde contra su línea base, en la misma corrida:
0 diffs en las 14, salvo
backgroundcorrida 1 (2 diffs deinlineSizedel ESCENARIO, 870 ↔ 1.350 px; corrida 2 limpia) — ruido del layout de la demo, a §13 con P6.
Bisección del guard sobre float-panel (scripts/__sentinel-<sha>.ts, cada
versión de theming-sentinel.ts desde cd6a9f4e3 contra el ledger de hoy,
idéntico para este componente): la versión del 22 ya da 17/40 con los MISMOS
12 sin adjudicar, y cada versión intermedia también. No hay regresión del
guard: es un rojo del 22 que quedó sin medir y que el handoff dio por cerrado.