From cd6a9f4e3e20522b04ea76b7bc6564ce6075f02f Mon Sep 17 00:00:00 2001 From: dev Date: Sat, 22 Aug 2026 23:42:16 +0200 Subject: [PATCH] =?UTF-8?q?uix(theming):=20revisi=C3=B3n=20adversarial=20d?= =?UTF-8?q?el=20bloque=20=E2=80=94=20la=20pasada=20de=20HOVER=20envenenaba?= =?UTF-8?q?=20a=20todos=20los=20tokens=20de=20reposo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cinco hipótesis, un hallazgo real, cuatro refutadas. Bloque de CATORCE componentes; global del eje 56 % → **62 %**. HALLAZGO REAL — la anomalía de `float-panel` tenía causa, y era del guard. `resize-grip-fg` movía aislado y leía muerto en una corrida completa. Instrumenté el propio guard para probar UNA clave y salió `moved=true`; luego, en la corrida entera, muerto. La causa: **la pasada de HOVER deja el puntero sobre el último nodo que tocó**, y una regla `:hover` gana a la de reposo con la que comparte nodo — el asa re-apunta su color al acento al pasar el ratón. Es decir: **cualquier token de reposo probado DESPUÉS de un token `hover-*` podía leer muerto**. Es exactamente la clase que F2-A arregló con el clic-foco, una pasada más tarde: allí se aparcó el puntero tras ABRIR, aquí faltaba aparcarlo tras HOVERAR. Arreglado. Y entonces el propio guard delató DOS excepciones STALE — `float-panel.resize-grip-fg` y `media-player.track` —, retiradas: dos tokens que el ledger daba por muertos están vivos. Se confirma la regla de F2-A: **un falso negativo siempre tiene causa**; registrarlo como «sin causa» era el paso previo, no el final. REFUTADA «hay tokens huérfanos»: comprobación ESTRICTA —comentarios despojados, que es el hueco por el que `popover.bg` sobrevivió, sobre todo el CSS del árbol— de las **553 claves** de los catorce bloques: ninguna declarada sin que una regla la lea. REFUTADA «el cambio de instrumento rompió algo»: los **27 componentes con ledger re-verificados** tras aparcar el puntero — cero regresiones. Los tres rojos que quedan (`drawer` 24, `metrics` 16, `color-picker` 23) son ANTERIORES al bloque y están anotados con lo que ya se descartó de cada uno. REFUTADA «quedan fantasmas del barrido en lo tocado»: pasa de 9 a **8** — el `--color-content-tertiary` de float-panel murió con su costura. Los ocho restantes son de componentes que este bloque no tocó. CONFIRMADO, y ya estaba escrito: hay gates FINOS. `popover` mide 1 nodo en reposo (7 en abierto), `drawer` 1, `context-menu` 1 + 14 en abierto, `color-picker` 5 + 12. Cada veredicto lo dice, y lo que sostiene esas cifras es el forzado a mano de sus estados, no la sonda. Guards: `--names` sin desviadas · docs:check 0 · censo 62 % (2.877 públicos de 5.137 knobs; `global` 1.145 → 912). Co-Authored-By: Claude Opus 5 --- docs/audit/theming/float-panel.md | 12 ++++----- docs/next-features.md | 24 ++++++++--------- docs/process/PLAN-theming.md | 36 ++++++++++++++++++++++++++ scripts/theming-sentinel-exceptions.ts | 11 +------- scripts/theming-sentinel.ts | 12 ++++++++- 5 files changed, 66 insertions(+), 29 deletions(-) diff --git a/docs/audit/theming/float-panel.md b/docs/audit/theming/float-panel.md index 5388cdd96..3dd0ec4ce 100644 --- a/docs/audit/theming/float-panel.md +++ b/docs/audit/theming/float-panel.md @@ -116,12 +116,12 @@ cierre—, así que se forzaron el atributo de variante y se montó el botón do el CSS lo selecciona; el radio del asa sólo pinta bajo `:focus-visible`. Las once alcanzan. -**Y una ANOMALÍA registrada en vez de escondida**: `resize-grip-fg` MUEVE bajo -una réplica a mano de la secuencia del propio guard —congelar, blur, aparcar el -ratón, escribir en `:root` y en cada nodo con el prefijo, fotografiar con -pseudos— (oklch(0.5032 0 0) → rgb(1,2,3)), y aun así el guard lo da por muerto. -La causa no está encontrada. El token está vivo; lo que está en duda es la -lectura → next-features §13. +**La ANOMALÍA que registré aquí quedó RESUELTA el mismo día**, en la revisión +adversarial del bloque: `resize-grip-fg` movía aislado y leía muerto en una +corrida completa porque **la pasada de hover deja el puntero sobre el último +nodo que tocó**, y el asa re-apunta su color al acento bajo `:hover`. Con el +puntero aparcado tras esa pasada, el token vive y el guard delató su propia +excepción como STALE. **Lo rojo que queda es ANTERIOR**: 12 tokens del bloque viejo, y seis de ellos —`bg`, `border`, `border-width`, `shadow`, `font-family`, diff --git a/docs/next-features.md b/docs/next-features.md index 68181362a..62397a2f9 100644 --- a/docs/next-features.md +++ b/docs/next-features.md @@ -1060,18 +1060,18 @@ Lo que `chart` destapó (2026-08-22): de `data-size`) ni que el guard no vea sus nodos (ya se le dieron las dos familias que le faltaban). -- **⚠ Un falso negativo del centinela SIN causa encontrada** (2026-08-22): - `float-panel.resize-grip-fg`. Réplica a mano de la secuencia del propio guard - —congelar transiciones, blur, aparcar el ratón, escribir en `:root` y en cada - nodo con el prefijo, fotografiar los mismos props con `::before`/`::after`/ - `::placeholder`— y el color del asa SÍ se mueve (oklch(0.5032 0 0) → - rgb(1,2,3)). El guard lo reporta muerto. Descartado: que el nodo no lleve el - atributo (lo lleva), que la regla necesite un estado (es la regla base), que - sea el valor centinela (es el mismo) y que sea HMR (dos corridas). Queda - adjudicado con la medición y marcado como anomalía: **el token está vivo; lo - que está en duda es la lectura**. La revisión adversarial del bloque debería - empezar por aquí — el precedente de F2-A dice que estos casos SÍ tienen causa - (22 falsos negativos de 26, tres causas distintas). +- **~~Un falso negativo del centinela sin causa~~ — RESUELTO en la revisión + adversarial del bloque (2026-08-22).** `float-panel.resize-grip-fg` movía + aislado y leía muerto en una corrida completa. La causa: **la pasada de HOVER + deja el puntero sobre el último nodo que tocó**, y una regla `:hover` gana a + la de reposo con la que comparte nodo — el asa re-apunta su color al acento + al pasar el ratón. Así que CUALQUIER token de reposo probado después de un + token `hover-*` podía leer muerto. Es la misma clase que el envenenamiento + por clic-foco que F2-A arregló, una pasada más tarde. Arreglado aparcando el + puntero tras la pasada de hover; el propio guard delató entonces DOS + excepciones STALE (`float-panel.resize-grip-fg` y `media-player.track`), + retiradas. Confirma la regla de F2-A: **un falso negativo siempre tiene + causa.** **Deps**: ninguna. Son mejoras del instrumental del eje, ejecutables cuando estorben. diff --git a/docs/process/PLAN-theming.md b/docs/process/PLAN-theming.md index 53ee2d7e1..9a5891b2d 100644 --- a/docs/process/PLAN-theming.md +++ b/docs/process/PLAN-theming.md @@ -455,6 +455,42 @@ de commit. Sin artefacto, el paso no se ha hecho. ## 8. Registro +- 2026-08-22 — **REVISIÓN ADVERSARIAL DEL BLOQUE (§7.7): 5 hipótesis, 1 + hallazgo real, 4 refutadas.** Bloque de CATORCE componentes; global 56 % → + **62 %**. + + **HALLAZGO REAL — la anomalía de `float-panel` tenía causa, y era del guard.** + `resize-grip-fg` movía aislado y leía muerto en una corrida completa. La + causa: **la pasada de HOVER deja el puntero sobre el último nodo que tocó**, y + una regla `:hover` gana a la de reposo con la que comparte nodo (el asa + re-apunta su color al acento). Es decir: **cualquier token de reposo probado + después de un token `hover-*` podía leer muerto** — misma clase que el + envenenamiento por clic-foco que F2-A arregló, una pasada más tarde. + Arreglado aparcando el puntero tras la pasada de hover. El propio guard + delató entonces DOS excepciones STALE —`float-panel.resize-grip-fg` y + `media-player.track`—, retiradas: dos tokens que se daban por muertos están + vivos. Se confirma la regla de F2-A: **un falso negativo siempre tiene causa**; + registrarlo como «sin causa» era el paso previo, no el final. + + **REFUTADA «hay tokens huérfanos»**: comprobación ESTRICTA (comentarios + despojados —el hueco que dejó vivo el `popover.bg`— sobre todo el CSS del + árbol) de las **553 claves** de los catorce bloques: ninguna declarada sin que + una regla la lea. + + **REFUTADA «el cambio de instrumento rompió algo»**: los **27 componentes con + ledger re-verificados** tras aparcar el puntero — cero regresiones. Los tres + rojos que quedan (`drawer` 24, `metrics` 16, `color-picker` 23) son + ANTERIORES al bloque y están anotados. + + **REFUTADA «quedan fantasmas del barrido en lo tocado»**: el barrido pasa de + 9 a **8** — el `--color-content-tertiary` de float-panel murió con su costura. + Los ocho restantes son de componentes que este bloque no tocó. + + **CONFIRMADO, y ya estaba escrito: hay gates FINOS.** `popover` mide 1 nodo en + reposo (7 en abierto), `drawer` 1, `context-menu` 1 + 14 en abierto, + `color-picker` 5 + 12. Cada veredicto lo dice; el forzado a mano de sus + estados es lo que sostiene esas cifras, no la sonda. + - 2026-08-22 — **`password-field` 41 % → 62 %** (7 claves) **y el guard en VERDE**: sus siete silencios quedan medidos, incluidos los tres que arrastraba de antes. diff --git a/scripts/theming-sentinel-exceptions.ts b/scripts/theming-sentinel-exceptions.ts index 849884a1a..6a98355ed 100644 --- a/scripts/theming-sentinel-exceptions.ts +++ b/scripts/theming-sentinel-exceptions.ts @@ -37,8 +37,6 @@ export const SENTINEL_EXCEPTIONS: Record> = { }, 'media-player': { 'accent-strong': 'consumed by the live indicator, which only paints under [data-live]', - track: - 'forwarded into the composed Slider — paints on [data-slider-*] nodes, outside this component attribute space', 'buffered-bg': 'forwarded into the composed Slider (SecondaryRange) — outside this component attribute space', 'captions-bg': 'captions are conditional and the demo video carries no track', @@ -354,14 +352,7 @@ export const SENTINEL_EXCEPTIONS: Record> = { 'action-font-size': 'same button; mounted -> reaches (16px -> 1234px)', 'hover-action-bg': 'same button, and only under :hover; same mounting pass', 'hover-action-fg': 'same button, and only under :hover; same mounting pass', - 'resize-grip-radius': 'only under :focus-visible; focused the grip -> reaches (4px -> 1234px)', - // ANOMALY, registered rather than hidden: this one MOVES under a hand - // replication of the guard's own sequence (freeze, blur, park, write on - // :root + every prefix node, snapshot with pseudos): oklch(0.5032 0 0) -> - // rgb(1,2,3). The guard still reports it dead and the cause is not found - // yet — 2026-08-22, next-features §13. The token is alive; the reading is - // what is in doubt. - 'resize-grip-fg': 'MEASURED ALIVE by hand under the guard own conditions (oklch(0.5032 0 0) -> rgb(1,2,3)); the guard reports no effect and the cause is open - next-features 13' + 'resize-grip-radius': 'only under :focus-visible; focused the grip -> reaches (4px -> 1234px)' }, drawer: { // The guard OPENS the drawer before testing, and the overlay then covers the diff --git a/scripts/theming-sentinel.ts b/scripts/theming-sentinel.ts index 1b5ca84b9..218956d8c 100644 --- a/scripts/theming-sentinel.ts +++ b/scripts/theming-sentinel.ts @@ -489,7 +489,17 @@ async function main() { await reopen(); const value = sentinelFor(key); let moved = await staticPass(key, value); - if (!moved && /hover/.test(key)) moved = await hoverPass(key, value); + if (!moved && /hover/.test(key)) { + moved = await hoverPass(key, value); + // PARK THE POINTER AFTERWARDS. The hover pass leaves the cursor on the + // LAST node it hovered, and a hover rule outweighs the rest-state one it + // shares a node with: every rest token tested after a hover token could + // read dead. Measured 2026-08-22 on float-panel — `resize-grip-fg` moved + // in isolation and read dead in a full run, because the grip's own + // `:hover` rule re-points its colour to the accent. Same class as the + // click-focus poisoning fixed in F2-A, one pass later. + if (override.openBy !== 'hover') await page.mouse.move(0, 0); + } if (moved) live.push(key); else stillDead.push(key); }