# CONTINUE — eje Theming «theme-reach» — **CERRADO 2026-08-25** ## EMPIEZA AQUÍ **EL EJE ESTÁ CERRADO** (gate de F3 rediseñado y en verde el 2026-08-25; sección §«EL CIERRE, EJECUTADO» abajo). Lo que vigila el estado desde hoy no es este documento sino los guards: `theming-reach-floor.test.ts` (newDebt=0 · STALE=0 · reach ≥ 73 % · ≥65 al 100 %), `component-audit` R-5.1/5.2/5.3 en `error`, y el centinela R-5.4. La deuda restante vive NOMBRADA clave a clave en `scripts/theming-census-debt.ts` (1154 entradas que sólo pueden menguar). Trabajo nuevo de theming se abre como entrada nueva de §13/next-features, no heredando de aquí. Las DOS firmas que el cierre dejó pendientes están **EJECUTADAS**: la de los 318 privados no-derivados (§«FIRMA EJECUTADA — los 318» abajo) y la del idioma `var(--{c}-x, fallback)` sin contrato (§«FIRMA EJECUTADA — la ley del espacio cerrado»). Heredada de la segunda queda la re-clasificación de los 117 nombres `--palabras-*` que el censo puntúa `public` siendo canal de VALOR, más DOS deudas nuevas de eje propio que ella misma nombró: el codemod de los 14 canales públicos a `--_{c}-*` — **EJECUTADO 2026-08-26 (14 de 14: 13 en el codemod + el 14.º por firma propia, ver §«FIRMA EJECUTADA — el indicador de navigation-menu»)** — y el cableado `EidosConfig.motion` → CSS (las dos, con su lista, en `next-features.md`). **Lo que sigue pendiente en este carril: el cableado de motion · la re-clasificación de `palabras` · la `timeline` §5 pre-B′.** **El eje ya no tiene cola.** Los 16 componentes que nunca habían pasado están cerrados o adjudicados, la deuda de artefactos está saldada, y **§12.9 y la firma B′ quedaron FIRMADAS Y EJECUTADAS** — las dos decisiones más caras pendientes. Global **73 %** (68 % antes de la clase `structural`, 69 % antes de `bridge`/`channel`), **65 componentes al 100 %**. ## HANDOFF 2026-08-26 (noche) — POR AQUÍ MAÑANA **El día ejecutó CINCO firmas más, todas con su adversarial**: la clasificación de los 318 privados (`e812d3ffd`+`a74aeec7b`+`90b93a79e`, adversarial 13/13) · la ley del ESPACIO CERRADO (`6499eb2a4`, 7/7) · el codemod de los 13 canales (`e100ea520`, atómico verificado) · el indicador de navigation-menu, (a) completada (`de2e136a0`, 6/6 — la especie `output` se propuso y se RECHAZÓ por abrir excepciones) · **§14, MeasuredIndicator con nombre poseído** (`8ac23047f` — «la capa es la pluma, no la dueña»; `tabs` cierra al 100 %). La familia de indicadores entera habla UNA doctrina. ### Lo PRIMERO mañana, por orden de valor 0. ~~**Los DOS guards rojos de `contracts.test.ts` que tocan el indicador**~~ — **✅ EJECUTADO 2026-08-26 (`1cb07c1e1`, firma ii del autor)**: no la declaración desnuda — la PLENA. `ready` es ESTADO de la parte (la especie de `checked`, no un prop): los dos morfos declaran `{ attr: 'data-ready', value: v.stateRef('ready'), severity: 'optional' }` con `states: ['ready']`, los providers registran la fuente (`() => this.indicator.ready`) y **el estampado manual muere** — la bolsa es la única tubería también aquí. `data-ready` SALE de `EIDOS_ONLY_ATTRS` con acta (ya no es eidos-only: es contrato). Verificado: guard sin los 2 rojos · morfo 222/222 · suites 3/3 · eidos-lint `[data-tabs-indicator]:not([data-ready])` morfo-backed, 0 invalid · check 0 atribuibles POR FICHERO · dinámica CDP: tabs `ready=""` desliza (0→103.9px), radio-group `segmented` ídem; control negativo GRATIS: pane oculto (rAF congelado) ⇒ sin medida ⇒ sin attr ⇒ `opacity: 0` — la puerta anti-flash aguanta. Coordinación con el peer P0 (vicen-42) por canal: `eidos-only-attrs.ts` era edición COMPARTIDA (sus altas de aura/waveform viajaron en mi commit con atribución declarada; su commit va detrás por dependencia de su guard). ⚠ Su adjudicación THEME-SYS-1 sobre `header-z` fue OBJETADA con tres hechos (token MEDIDO VIVO en sentinel-exceptions:410/437 · dos STALE · decisión del autor pendiente en `next-features.md:808` en dirección CONTRARIA) y ACEPTADA: revirtió entero y su guard registra la deuda NOMBRADA. El peldaño de «cabecera pegajosa» sigue esperando TU firma. Expediente original: (aviso de cierre del peer P0: su primera corrida histórica de la suite completa destapó 8 rojos preexistentes; 6 son suyos para su mañana, DOS son de este eje). El guard «hardcoded Soma component data attrs» flaguea `data-ready` estampado a mano en `tabs-provider.svelte.ts:473` y `radio-group-provider.svelte.ts:366`. **Proveniencia VERIFICADA: NO es de §14** (0 apariciones en `8ac23047f`) — lo introdujo `76f18a5e8`, cuyo propio mensaje ya avisaba «aparece un punto ciego del guard». La adjudicación está pre-masticada: `data-ready` es contrato REAL — lo leen `tabs.css:329` y `sliding-indicator.css:64-66` (la puerta anti-flash hasta la primera medida) — así que se DECLARA en los morfos de las dos partes indicadoras (tabs + radio-group), que es lo que el guard exige; ni exención ni retirada. Cambio de morfo ⇒ con su rigor, no de madrugada. ⚠ El hook pre-push sigue SIN ARMAR por esto (el peer no arma puerta en rojo — promesa cumplida); detalle de los 8 en `CONTINUE-audit-p0.md`. 1. **§15 — el GEMELO un piso arriba** (redactado por el adversarial de §14, abajo en `next-features.md` §15): la capa flotante publica CINCO medidas de instancia como `--floating-*` — forma pública sin dueño, ~8 recetas lectoras. La especie exacta que §14 cerró, con más superficie y con el matiz de que el posicionador sirve a MUCHOS hosts. Dimensionar antes de firmar; la cadena de verificación de §14 es el molde. 2. **La especie de FORMA sin dueño**: `--indicator-duration`/`-ease` (flagged en §14) — ¿contrato de qué componente, o vocabulario de capa declarado? 3. **Cableado `EidosConfig.motion`→CSS** — mata la última entrada del registro `PENDING_PRIVATE_RENAME`. 4. **Re-clasificar `palabras`** (canal de valor del scheme, no tema) y **reabrir `timeline`** (§5 pre-B′) — los dos con expediente escrito. ### Estado de la RAMA al cerrar el día (léelo antes de tocar nada) - **SIN PUSH desde `e100ea520`**: el codemod de canales, navigation-menu, §14 (`8ac23047f`) y el cierre documental del día. El resto del día está pusheado hasta `cec75d04e`. - **Sesión peer `vicen-63` ACTIVA en la rama** (auditoría P0): su C2c retiró los `syncAttrs` (los de tabs/radio-group VIAJARON en `8ac23047f` con atribución declarada y acuerdo por canal), su cirugía del runtime y su commit venían detrás, y **re-armará el hook pre-push** (`scripts/hooks/ pre-push` es la fuente; el gate ya pasa en verde sobre HEAD con el lint fuera hasta el one-shot de formato). Si un push falla, el mensaje del gate dice qué. La sesión de fichas (`vicen-3c`) commiteó `cc2894b36`. - **Protocolo de rama compartida que el día confirmó tres veces**: staging por RUTA explícita (nunca `add -A` — 375 ficheros a punto de colarse una vez), la RAÍZ del repo también se stagea (STUMBLES.md se quedó fuera una vez), y antes de medir un fichero, `git diff HEAD -- ` por si el peer lo movió. ### Las leyes nuevas del día (además de las firmas) - **El nombre dice la verdad en las DOS direcciones** — ni knobs que un tema no puede mover (el inline gana), ni privados que un README enseña como API. Cuando ninguna de las dos ortografías dice la verdad, la respuesta NO es una especie nueva: es preguntar qué está mal en el DISEÑO (así cayó `output`, y así el README de navigation-menu aprendió a enseñar la PARTE). - **El grep literal es ciego a la forma con llaves** (`--x-{a,b,c}`) — se barre por STEM. - **La sonda estándar es CIEGA a los canales** (PROPS sin translate/rotate/gridTemplateColumns/animationDuration): su 0-diff prueba lo circundante; el canal se verifica en DINÁMICA por CDP con control negativo y positivo. - **Un alias sin prefijo propio ni siquiera entra al clasificador** — y una causa escrita sin su contrafáctico medido acaba corregida por el adversarial (pasó dos veces hoy). ## HANDOFF 2026-08-25 (noche) — histórico **Las CINCO firmas del eje están EJECUTADAS y pusheadas** (`2daa683fa` file-upload · `df3a4d054` form/compuesto · `2ce43db56` tags-input aria · `1f8ce293b` avatar · `466b24f34` chip/suelo §55). Después cayeron los expedientes de §13: `d5a27b87e` (árbol ARIA de tags-input, cierra F-1) · `3bf7930ba` (el velo no muere por un atajo + pin de chronos, §56) · `318aac207` (la talla de form LLEGA, doctrina anchura-vs-densidad). ### Lo PRIMERO mañana: dos firmas de INSTRUMENTO, en este orden 1. ~~**«La mitad gratis»**~~ — **✅ FIRMADA Y EJECUTADA 2026-08-25** (`047464389`; ver §«FIRMA EJECUTADA — la mitad gratis» justo abajo). Expediente original: scratchpad de la sesión `6314e22b…/scratchpad/bprime/exped/va/informe.md` (1.019 líneas). 2. ~~**La GRANDE: cuatro cegueras + TRES BARRIDOS escalonados.**~~ — **✅ FIRMADA Y EJECUTADA 2026-08-25** (`6392d6003` S1 · `b4dd5a906` S2 · `b9cef9a43` S3; ver §«FIRMA EJECUTADA — la GRANDE» abajo). Se ejecutó exactamente con el plan de VA: tres barridos escalonados, sin fusionar S2 y S3, y la atribución quedó limpia. ### FIRMA EJECUTADA (2026-08-26) — el codemod de los 14 canales (13 de 14) **Qué se ejecutó**: los nombres públicos-con-forma que soma o el envoltorio escriben POR INSTANCIA pasan a privados `--_{c}-*` — el destino que la firma de los 318 ya había firmado (clase `channel`) y que el registro `PENDING_PRIVATE_RENAME` sólo contaba. **Trece renombrados, sus trece entradas del registro RETIRADAS con acta**; el registro queda en **DOS** (no en una): la de motion, que era la prevista, y una PARADA. **El método, y es la mitad del resultado**: barrido `git ls-files` **de cada nombre por el árbol trackeado entero** (94 ocurrencias en 36 ficheros), y flip **ATÓMICO por nombre** — escritor y TODOS los lectores en la misma edición. Nada de grepear los propios patrones: el barrido destapó lectores que ninguna lista traía (el comentario del morfo de `knob`, las dos páginas de demo, nueve menciones en `PLAN-background.md`). **✅ RESUELTO — `--navigation-menu-indicator-h`, con (a) FIRMADA Y EJECUTADA (2026-08-26, tarde).** Estuvo PARADO porque no era un canal suelto: el provider escribe **CUATRO hermanas** en un mismo literal, el README de soma las enseñaba las cuatro bajo `## CSS Variables` como forma controlada por el consumidor (con dos ejemplos leyendo `-h`), y **`-w` era clave DECLARADA del contrato** — la única razón por la que el guard veía a `-h` sola. Ver §«FIRMA EJECUTADA — el indicador de navigation-menu» abajo. **El registro queda en UNA** (motion). **⚠ LA TRAMPA DEL CENSO NO MORDIÓ, y la predicción estaba MAL por un orden de magnitud.** Se esperaba que ~15 knobs pasaran de `public` a `channel` y que el suelo pudiera rozar el redondeo. Medido: **UNA sola clave** se reclasifica —`color-swatch.background`— porque el censo cuenta **knobs de APARIENCIA**, y las otras doce viven en declaraciones de GEOMETRÍA (`translate`, `rotate`, `inline-size`, `grid-template-columns`, `animation-duration`) o en declaraciones que **conservan un público hermano** y siguen puntuando `public` (el `conic-gradient` de `knob` lee `--knob-track-bg`; el panel de `menubar` lee `--menubar-panel-min-width` como fallback). Números: `public` 3074 → **3073**, `channel` 36 → **37**, denominador 4228 → 4227, reach **73 %**, `atHundred` **65**, ledger **1154 · 0 NEW · 0 STALE**. **El suelo NO se toca** — no hubo nada que ajustar ni que fingir. **Neutralidad, medida en dos planos.** Estática: sonda estándar antes/después sobre los 7 componentes, **0 diffs reales**; el único diff (`emoji-picker`, un `background-image` de velo en el trigger, bloque `open`) se **reprodujo 2/2 sobre código idéntico** — ruido del instrumento. ⚠ `color-swatch` **no tiene ruta de demo** (404) y la sonda devuelve **0 nodos**: su «0 diffs» es un guard que no inspecciona nada y NO cuenta como prueba; se midió donde de verdad monta. Dinámica (donde estos canales viven y la sonda no llega), 7/7 por CDP: `background` puntero →`_x −0.7/0.8`, `_y −0.6/0.7`, `_px 15 %/90 %`, `_py 20 %/85 %`, translate `−28px −24px` → `32px 28px` (= x × depth 40px, exacto) y el foco del spotlight `at 15 % 20 %` → `at 90 % 85 %`; el quinto canal (`--_background-progress`) es la rama `@supports not(view())` que Chrome nunca corre, verificada forzando el gate: `0.379 → 1` al scrollear · `knob` accionado por teclado, `_angle −27deg → 0deg`, `_progress 0.4 → 0.5`, el indicador rota igual y la parada del arco `108deg → 135deg` (= progress × sweep) · `color-swatch` tres chips, `#7c3aed/#06b6d4/#f59e0b` → `rgb(124,58,237)` / `rgb(6,182,212)` / `rgb(245,158,11)` · `emoji-picker` privado 9 → 9 tracks, y moverlo a 6 y a 12 da 6 y 12 tracks, **mientras fijar el nombre VIEJO a 3 no mueve nada** (control negativo) · `menubar` panel anclado, `_anchorWidth 886px` = barra 886 = panel 886 · `navigation-menu` (el PARADO) sin regresión, `-h` sigue pública en 36px = altura del trigger activo · `text-gradient` inline `8s` → `animation-duration: 8s` en el hijo `[data-text-gradient-fill]`. **En los siete, el nombre VIEJO computa vacío.** **Guard mutado por sus dos caras**: reintroducir una entrada retirada → **STALE nombrándola**; devolver un nombre público viejo a una receta → **`missing` nombrándolo**. Suites de los tocados 18/18, `component:audit` **162 PASS / 4 NEEDS-WORK** idéntico, `docs:check` **0/0**, `--names` **0 desviadas**, `npm run check` **0 errores atribuibles** (verificado POR FICHERO). **Docs: se ANOTARON, no se reescribieron** — fichas medidas, README de `color-swatch`, `PLAN-background.md` (una anotación fechada cubre sus nueve) y `STUMBLES.md` §7. El nombre nuevo sí se volcó donde el texto describe código VIVO (READMEs de componente y las dos demos). Queda anotada la fila GENERADA `audit/theming/background.md:72`, que la regenerará el `--report` pendiente. **Hallazgo lateral, ajeno**: el control `columns` de la demo de `emoji-picker` es un `` DESNUDO y `slider.svelte` renderiza sólo su provider — un `div[data-slider]` **vacío**, sin raíl ni pulgar. No se puede accionar. ### FIRMA EJECUTADA (2026-08-26) — el indicador de navigation-menu **Qué se firmó**: la opción **(a)** del PARADO. Las CUATRO medidas del trigger activo pasan a privadas — `--_navigation-menu-indicator-{x,y,w,h}`, flip ATÓMICO escritor + todos los lectores, con barrido `git ls-files` por nombre —, **`indicator-w` se RETIRA del contrato** (44 → 43 filas) y la sección `## CSS Variables` del README de soma se reescribe al camino de la casa: el consumidor estiliza **la PARTE** (`[data-navigation-menu-indicator]` y su `data-state`) con los tres pomos de forma, no las medidas. **Lo que se RECHAZÓ, y por qué importa**: (b) declarar `-x/-y/-h` para casar con el README bendeciría como superficie de tema cuatro medidas por instancia — justo lo que la clase `channel` de la firma de los 318 existe para negar. Y se rechazó una especie nueva, **`output`** (tercera clase entre público y privado): abre excepciones sobre una clase barata con frontera de PROSA, patrón ya rechazado dos veces. **`-w` era un token-que-miente, MEDIDO** (CDP, transiciones CONGELADAS — en esta parte `inline-size` va transicionada y una lectura inmediata devuelve el valor VIEJO; el probe informaba «no se movió» de TODO hasta congelarlas): con el subrayado pintado, escribirlo en la raíz del componente —lo que hace el centinela— deja `inline-size` en **107px**, y en `:root` igual; **sólo una regla `!important` de autor llega** (1234px), porque soma escribe INLINE en el elemento. La única ventana donde la declaración asomaba es el indicador montado **SIN rect todavía** (`isOpen` true, `triggerRect` aún null; o `forceMount`): ahí `inline-size` lee el `0px` que daba esta clave y un write en el host lo mueve a 1234px. Es decir, llegaba **sólo donde no pintaba nada**. ⚠ El indicador está montado CONDICIONALMENTE (`{#if state.isOpen || forceMount}`): con la barra cerrada hay **0 nodos** en la página. **Números**: `public` 3073 → **3072**, `channel` 37 → **38**, denominador 4227 → 4226, reach **73 %** y `atHundred` **65** intactos, `--names` 4567 → **4566** (0 desviadas), ledger **1154 · 0 new · 0 stale**. Centinela `navigation-menu` **43/44 → 42/43**, misma única adjudicación (`disabled-trigger-fg`): el total baja porque la clave sale del contrato, no porque nada deje de llegar — **el fichero de excepciones no se toca**. `component:audit` **162 PASS / 4 NEEDS-WORK** idéntico. **Dinámica, que es la que vale aquí** (la sonda estándar es ciega a estos canales): Products `w=107px h=36px` = rect del trigger, Solutions `w=108.5625px h=36px`, `inline-size` las sigue (107 → 108.562) y `left` **0px → 111px** — el subrayado DESLIZA — con `translateY(36px)` estable. **Control negativo**: los nombres viejos computan **VACÍO** en el indicador, y fijarlos en la raíz a 777px/333px no mueve nada. **Control positivo**: los tres pomos del contrato siguen llegando (`block-size` 2 → 9px, `background` → `rgb(1,2,3)`). **Guard**: `PENDING_PRIVATE_RENAME` queda en **UNA** entrada (motion), con acta en el propio registro. Mutado por sus dos caras: reintroducir la entrada retirada → **STALE nombrándola**; devolver `--navigation-menu-indicator-w` a la receta → **`missing` nombrándolo**. **⚠ Trampa del barrido**: el grep del nombre LITERAL es CIEGO a la forma con llaves `--…-indicator-{x,y,w,h}`, que escondía tres menciones más (el comentario de `navigation-menu.svelte` de eidos y dos filas de la demo). Hay que barrer también la familia. **Hueco FLAGGED, no acuñado**: el ejemplo viejo de *píldora / fondo de trigger* no tiene traducción a pomos — la receta clava el `block-size` al grosor. Queda en §13 de `next-features.md`, sin inventar un eje de forma. **Abre expediente**: §14 de `next-features.md` — unificar `tabs` bajo la misma adjudicación (`--indicator-{x,y,w,h}` SIN prefijo, escritos por la capa compartida `MeasuredIndicator`, 2 entradas `global` en el ledger). **EJECUTADA el mismo día**; ver la firma inmediatamente debajo. ### FIRMA EJECUTADA (2026-08-26) — §14, MeasuredIndicator con nombre POSEÍDO **Qué se firmó**: la geometría que una capa COMPARTIDA publica es **canal** y aterriza en el **namespace del ANFITRIÓN** —`--_{c}-indicator-{x,y,w,h}`—, no en uno de capa. Tercera aplicación de la doctrina (`navigation-menu` fue la segunda). **La capa es la PLUMA, no la dueña**: el rect es un hecho de cada instancia del host. Un solo nombre de capa habría repetido el defecto un piso más abajo — una medida por instancia en un espacio que nadie posee. **Cómo llega el nombre**: `MeasuredIndicatorOpts` gana **un** campo, `component: string`, y los dos hosts —los únicos, `git grep "new MeasuredIndicator"`— pasan `tabsMorfo.kebab` / `radioGroupMorfo.kebab`, **nunca un literal**: el canal queda atado a la declaración de morfo que lo posee. **Cableado del CSS compartido** (`lib/sliding-indicator.css`, que no puede deletrear el nombre del host): cada receta declara su alias local explícito —`--_indicator-x: var(--_radio-group-indicator-x)`— y el compartido lee el alias. Forma del puente THM-2, la misma que `calendar-surface.css` usa para `--_calendar-surface-accent`: contrato EN el artefacto. `tabs` no consume la capa (tiene bloque propio) y lee el nombre poseído directo. **Números**: `tabs` **97 % → 100 %** (global 2 → 0, canal 0 → 2; 77 knobs y 79 claves de contrato INTACTOS — ni una acuñada ni retirada). Catálogo `atHundred` **65 → 66**, global 763 → 761, canal 38 → 41, reach global **73 % intacto**, `--names` 4566 · 0 desviadas. Ledger: las **2 entradas de `tabs` retiradas** (la clave entera desaparece), **1154 → 1152 · 0 NEW · 0 STALE**, con acta en la cabecera de `theming-census-debt.ts`. `radio-group` no mueve una cifra. **⚠ La trampa de clasificación, prevista y resuelta** — *causa CORREGIDA por el adversarial el mismo día*: la premisa del alias que «esquiva y cae a residuo» era CONTRAFACTUAL — el alias de §14 no lleva prefijo propio, el censo lo descarta antes de coleccionar privados, y un knob que lo lea clasifica `global` con la razón CORRECTA (medido construyéndolo: `1 new`, rojo). La extensión TRANSITIVA del tercer paso se justifica por `tooltip` (alias con prefijo propio sobre canal propio, reclasificado `exception` → `channel` como su comentario firmado afirmaba); molde de `derivesFromPublic`. **Mutación de rigor**: un alias hacia un privado que SÍ está declarado NO clasifica canal — `refs.length > 0` es la línea que lo impone. Y el ORDEN de pasos es carga estructural: el segundo (deriva de un público) corre antes, y por eso `--_tooltip-max-width` sigue siendo público. **⚠ Un knob AJENO se reclasificó, con acta**: `tooltip.css:27` pasó de `exception` a `channel`. **No es regresión**: el comentario firmado de esa declaración ya decía «per-instance VALUE CHANNEL» con esas palabras — faltaba el mecanismo, no el criterio, que es literalmente lo que el cuarto paso del censo dice de sí mismo. `tooltip` no mueve su alcance. Acta en su ficha, §2-quater. **⚠ La sonda estándar es CIEGA a esta clase de firma**: lee valores computados, no nombres de var, así que un flip de nombre correcto le sale idéntico por construcción. Sus **0 diffs en 2528 valores × 10 estados** prueban lo CIRCUNDANTE, no el flip. Quien prueba el flip es la dinámica, y hay que CONGELAR transiciones o `inline-size` devuelve el valor viejo: `tabs` desliza (`translate 0 → 183.719px`, `inline-size 99.906 → 71.359px`, indicador exacto sobre el trigger activo), `radio-group` sólo manifiesta su indicador en la variante **`segmented`** y desliza igual (`4px 32px → 4px 60px`). Los cuatro nombres viejos computan **cadena vacía** en ambos; control negativo en `:root` a cero; control positivo probando además la CADENA del alias. **Hueco FLAGGED, no acuñado**: `--indicator-duration` / `--indicator-ease` siguen bare, sin dueño y sin entrada de contrato — la misma fuga de espacio, una especie más allá (son ranuras de FORMA, no canal). Su puerta es el contrato, no ésta. ### FIRMA EJECUTADA (2026-08-26) — la ley del espacio cerrado **Qué se firmó**: el espacio de nombres público es **CERRADO sobre el contrato** — un `--{c}-*` existe **si y sólo si** `base.ts` lo declara, no hay tercer estado. Corolarios: el fallback es dispositivo de seguridad de VALOR, no escapatoria de contrato; declarada la clave, el fallback inline sobre el nombre público PROPIO muere (el default vive en UN sitio); el guard de fantasmas pasa al «si y sólo si» — un `--{c}-*` propio fuera de contrato es rojo, con o sin fallback. El vocabulario AJENO (préstamo de otro componente) y el de sistema conservan sus reglas: el guard filtra por prefijo propio y no los ve. **Lo que el barrido previo encontró — y por qué importa**: no eran dos componentes. El catálogo tenía **25 nombres propios con fallback fuera de contrato en 10 componentes** (27 sitios), y se parten en DOS CLASES: - **11 knobs de tema de verdad** → declarados: `mockup` ×9, `text-scramble` ×2. - **14 CANALES DE VALOR** que soma o el envoltorio escriben inline por instancia → **NO se declaran** (declararlos revertiría la firma medida del 2026-08-23 que retiró `--color-swatch-fill` del contrato) **ni se renombran aquí** (es codemod de otro eje). Van a un REGISTRO nombrado y fechado dentro del propio test, `PENDING_PRIVATE_RENAME`, con razón y destino por entrada. \+ `--motion-stagger-each-default`, que no es canal (nadie lo escribe): su default pertenece a `EidosConfig.motion`. **15 entradas.** El registro es el patrón ledger de la casa: **no bendice, cuenta**, y **sólo puede menguar** — una entrada que deja de describir un consumo (renombrada, declarada, muerta) se reporta **STALE** y sigue roja hasta borrar la línea. **La mentira que la ley mató, en concreto**: `--mockup-shadow` cargaba DOS defaults bajo un nombre — `var(--shadow-4)` en el marco, `var(--shadow-5)` en el bisel del teléfono — así que un tema que lo moviera no podía saber cuál de los dos tocaba. Se acuñó `--mockup-phone-shadow`: superficie distinta, nombre distinto. El TSC no podía expresarlo de otro modo (sus ejes de scope son cerrados y `data-chrome` no es uno). **Neutra en píxel, medida dos veces**: el diff de `generated/` son esas 11 líneas con su default verbatim y NADA más; y el A/B en navegador — cada token nuevo forzado a `initial` con las declaraciones viejas restauradas inline — dio computed idéntico en los tres mockups de `/blocks/feature-split` (ambos cromos; el bisel conserva `shadow-5` y el marco `shadow-4`, que era justo lo que la acuñación protegía) y en `text-scramble`. ⚠ `mockup` **no tiene ruta de demo propia**: se mide donde de verdad monta, en `/blocks/*`. **Números**: `no contract entry` **18 → 16**; todo lo demás QUIETO — reach 73 %, 65 al 100 %, ledger 1154 · 0 NEW · 0 STALE, audit **162 PASS / 4 NEEDS-WORK** idéntico, `--names` 0 desviadas, `docs:check` 0/0. El suelo NO sube: la ley cerraba un hueco de CONTRATO, no de alcance (esas declaraciones ya puntuaban `public` — lo que faltaba era declarar el NOMBRE). **Muta-prueba de tres caras**: `var(--mockup-inventado, red)` (propio, CON fallback) → **rojo nombrando la clave** · el mismo inyectado con un nombre AJENO y fallback → **verde** · una entrada del registro que deja de describir un consumo → **rojo STALE nombrándola**. **Efecto lateral que hay que saber**: el crudo `var(--primitive-neutral-12)` migró verbatim de `mockup.css:73` a `base.ts` como default de `mockup.phone-bezel`. Sigue siendo deuda R-4.6 — pero la regla escanea CSS de componente y ya no lo ve, así que la fila de `mockup` perdió ese error **sin que nadie lo arreglara**. Queda anotado en la propia clave y en `next-features.md`; `mockup` sigue NEEDS-WORK por su E-2.3 (README ausente). ### FIRMA EJECUTADA (2026-08-26) — los 318 privados no-derivados **Qué se firmó**: la «clasificación de los 318», en tres piezas — `e812d3ffd` (F1: el censo aprende a MEDIR puente y canal) · `a74aeec7b` (F2: el residuo adjudicado clave a clave) · este commit (F3: el suelo sube y la prosa se salda). Cierra el agujero que la cabecera del ledger nombraba: la tercera clase no-alcanzada ya no vive sólo bajo el `reachPct` grueso. **La aritmética, sin resto**: 318 = **132 puente** + **36 canal** + **84 anotados** + **66 al ledger**. Las dos primeras las decide la MEDIDA (el puente contra la salida real de `renderRecipePaletteForward`, no contra la forma del nombre; el canal contra quién escribe el privado — nadie lo declara, lo pone soma o el envoltorio por instancia); las otras dos son actos: la válvula nueva `/* private: */` sobre la declaración (clase `exception`, la de siempre, no un tier nuevo) y una entrada de ledger con `private` como `DebtClass` de pleno derecho, con `newDebt` y STALE simétricos. **Los cuatro números que quedan vigentes**: reach **73 %** (3074/4228 — el numerador NO se movió, cayó el denominador 4480 → 4228) · **65 componentes al 100 %** · ledger **1154** claves (763 global + 325 literal + 66 private) · **162 PASS** en `component:audit`, idéntico. **Las cuatro contradicciones con ficha que la medida destapó** (F2 las dejó señaladas; ninguna se resolvió a ojo): - `metrics` — su §5 firmado decía que el conmutador del icono derivaba ENTERO de públicos. Cierto para `track`/`text`, **falso** para `solid`/`contrast`, que leen roles crudos. Resuelto POR MEDIDA: 3 anotados, 2 al ledger. - `timeline` — su §5 es **pre-B′** y se respetó tal cual (10 knobs, 5 notas). Queda SEÑALADO para reabrir si la escalera de paleta de B′ pesa más que aquel veredicto. - `proof-of-human` — 17 knobs adjudicados por DOS notas, **sin §5 propia** que lo dijera: la base del alias es verbatim la que el forward declara para ese slot (medido dos veces). El alias `--_proof-of-human-accent{,-soft}` es **borrable a coste cero de píxel** — leer el puente directo clasificaría `bridge` sin anotación; registrado como trabajo futuro en §13. - `listbox` — sus 6 knobs (5 notas) dicen «adopto la capa `list-surface`», y son **reversibles** el día que §13 le dé receta propia a esa capa (diez componentes de golpe). ### FIRMA EJECUTADA (2026-08-25) — «la mitad gratis»: cegueras 4 y 5, coste de ledger CERO **Qué se firmó** (`047464389`): las dos piezas del expediente VA que NO compartían el coste caro — el codemod ciego a la IIFE con sus dos hermanos, y los cinco huecos de la sonda más el sexto propio (el pase de FOCO prometido e inexistente). Tres ficheros, **ni una entrada del ledger**. Ejecutada por cuatro agentes (tres constructores + uno ADVERSARIAL con mandato de refutar), con bases capturadas ANTES de editar. **Los números**: centinela `label` 1/2 y `switch` 27/52 **byte a byte idénticos** a las bases pre-firma; sonda de `card` **0 diffs en sus 7 bloques viejos**; `editable` estrena bloque `open` (25 filas, 6 con `data-editing`) y dos corridas sobre código idéntico dan **0 diffs en TODOS los bloques**; `menubar` abre por la entrada del centinela (6 nodos → 7, con el panel) y `chat-message` 24 filas × 2 corridas = 0 diffs; codemod 145/145 bloques, avatar `depth 3` con **97** claves (86 públicas + 11 privadas), laboratorio sobre copias (duplicado inyectado DETECTADO, renombrado = 1 línea); importar el centinela: 0,85 s sin navegador. **Las decisiones que deja escritas**: - `COMPONENT_OVERRIDES` es **UNA tabla**: la sonda la importa y `OPEN_TRIGGER` se poda de sus copias literales. La guarda de entrypoint lleva `realpathSync` — hallazgo del adversarial: sin el realpath muere a través de un junction (exit 0 sin inspeccionar nada), y este repo usa worktrees con junctions. - La vía de carga del codemod **SE QUEDA en tsx**: «el censo corre con node pelado» era FALSO (todo `src/uix/morfo` importa sin extensión). - `prepareWith` en la sonda sólo corre sin fila en `DEMO_VARIANTS` (re-clicar APAGA los checkboxes que `prepare()` ya encendió) — y eso deja a la vista la deuda de `search-field` (§13). - El no-determinismo de `chat-message` quedó ACOTADO al bloque `rest` de la sonda (13 vs 16 nodos, chips sin cuajar al medir) — dato para la GRANDE. **Lo que quedó fuera, con su registro en §13**: el barrido de variantes de la sonda (ensanche diferido por el propio expediente) · el clear-trigger de `search-field` en el pase `open` · la asimetría de escenario de los seis con `prepareWith` sin fila · el cortocircuito `alreadyOpen` que la sonda no tiene · y **TRES lectores independientes** de la estructura de `base.ts` (codemod, censo, centinela — la próxima forma nueva se enseña tres veces). El `catch` del `prepareWith` del CENTINELA sigue MUDO **a propósito**: es la ceguera 6, S1 de la GRANDE. ### FIRMA EJECUTADA (2026-08-25) — «la GRANDE»: el guard deja de estar ciego, en tres barridos **Qué se firmó** (`6392d6003` · `b4dd5a906` · `b9cef9a43`): las cegueras 6+1+3+2 del expediente VA, UNA firma, TRES barridos del ledger ENTERO (91 componentes cada uno), ejecutada con el mismo esquema de agentes que «la mitad gratis» (tres constructores en serie + adversarial con mandato de refutar: **12/12 claims confirmados**). - **S1** — el `prepareWith` GRITA (lo que cae en su catch es un control que estaba y falló — la fuente de las muertes fantasma) y el STALE ve los PATRONES (`reasonFor`, mensaje que distingue «retira la exacta» de «ESTRECHA el patrón»). Barrido: 91/91 verdes, 40,8 min, 0 avisos. Cosecha: los dos patrones `PLANE_SUPERSEDED` de `toast` (18 claves), rancios desde la noche de §12.9, RETIRADOS con 3/3 de reproducción. - **S2** — el chooser tipa la sonda por el VALOR por defecto leído de `base.ts`, no por el nombre (357 claves cambian de sonda; 320 de la clase A «vivo por accidente» IACVT). MONÓTONO medido: 91/91, sólo `avatar` +1 y `stepper` +1, sus dos exactas retiradas. Ni `%` ni enteros se clasifican (tipar el `0` habría matado en falso 7 claves vivas). - **S3** — tono+estado: tono en el pase de hover · tono MEDIAL `(^|-)` (las 16 de `tag-group`) · hover para claves de tono muertas (las 21 de `file-upload`) · seis ejes de estado como DATO, valores del morfo/receta/DOM real. Barrido: 91/91, 43,8 min, exactamente los 9 previstos, **+140 claves (catálogo 2776 → 2918, 78 % → 82 % de claves vivas)**. **130/132 `TONE_UNREACHED` reviven; las 2 que no eran `PALETTE_FLOOR`** (el tono HOST por defecto — test de dos caras en checkbox y radio-group, firma idéntica en los ocho componentes). **El ledger después**: 10 patrones retirados + 2 estrechados + 7 exactas retiradas + la constante `TONE_UNREACHED` RETIRADA (la muerte que su docstring pedía). Tier de patrones **28 → 16, 15 de los 16 son el único hecho `PALETTE_FLOOR`**, y el adversarial verificó que los 16 cubren SOLO claves muertas — cero zombis — y que ninguna clave muerta quedó sin razón (659 muertas, 0 sin adjudicar). **Dos correcciones al expediente, reproducidas dos veces antes de tocar**: las `-text` de `tags-input` SÍ reviven (la causa del arquetipo sólo tapa el ítem RESALTADO; la escena no tiene ninguno, y el token pinta además dos nodos hermanos fuera de esa regla) · `tag-group.hover-item-fg` la callaba su `data-state='selected'` nativo — el nodo es un `` que ninguna regla de arquetipo toca. **Deuda nueva registrada en §13** (adversarial, ninguna bloquea): la asimetría del estampado (`data-color` en TODOS los nodos vs sólo el proveedor en producción — medido que NO es la causa de ninguna revivida) · el ternario falsy de la restauración de atributos (un `attr=""` se borra en vez de restaurarse; hoy inocuo) · `checkbox.stroke-ease` con sonda de color (única de las 51 de easing) · el residuo del recorte del hover (`button.palette-element`, escrito en su propia razón). ### EL CIERRE, EJECUTADO (2026-08-25) — deuda B + clase `structural` + R-5 a `error` con LEDGER **✅ EJECUTADO** con el mismo esquema de agentes (tres constructores + un adversarial que confirmó 11 claims y refutó dos números de prosa). Cinco commits: `2c61946fb` (clase `structural`) · `ef47f3447` (deuda B) · `69d69d43c` (el ledger de deuda) · `1ba518fcc` (correcciones del adversarial). - **Deuda B saldada**: `proof-of-human` (20 claves derivadas) · `audio-player` (README nuevo; es un skin sobre media-player que consume sus 61 claves y añade 8; sin demo propia a propósito) · `palabras` (sección honesta SIN tabla: sin contrato en `base.ts`, y sus 117 nombres `--palabras-*` son la proyección del `scheme-style` del documento — canal de VALOR, otro eje; la clasificación del censo sobre él queda EN CUESTIÓN, §13). - **Clase `structural`**: los cinco 0 %-por-naturaleza (`aspect-ratio` · `text-blur` · `cascade` · `motion` · `date-picker`) salen del denominador con su razón EN el artefacto (forma `LAYER_VOCABULARY`). Reach 68 → 69 %, no-contract 23 → 18, el gate deja de ser inalcanzable por construcción. - **R-5 a `error` con LEDGER DE DEUDA — la firma que rediseñó F3**: la deuda real (1088 claves: 763 global + 325 literal, carriles WIP DENTRO) se registra UNA vez por clave en `scripts/theming-census-debt.ts` — deuda registrada, NO excepciones; la válvula sigue intacta y nunca ciega el ratchet. R-5.1/5.2 a `error` contra el ledger (R-5.3 ya lo estaba): deuda nueva = rojo NOMBRADO, entrada rancia = STALE hasta borrarla. Los techos burdos del suelo RETIRADOS (el ratchet es por clave); `atHundred` corrige su criterio (`public > 0`, 14 → 45). El gate de F3 queda redefinido honesto: **«censo 100 % ADJUDICADO»** — y está EN VERDE. - **Pendiente de TU firma, nombrado en §13 y en la cabecera del ledger**: ~~los **318 privados no-derivados**~~ — **✅ FIRMADA Y EJECUTADA 2026-08-26** (`e812d3ffd` · `a74aeec7b` + F3; ver §«FIRMA EJECUTADA — los 318» arriba) · ~~el idioma **`var(--{c}-x, fallback)` sin contrato**~~ — **✅ FIRMADA Y EJECUTADA 2026-08-26** (§«FIRMA EJECUTADA — la ley del espacio cerrado» arriba) · QUEDA la **re-clasificación de `palabras`** (117 nombres `--palabras-*` puntuados `public` que son canal de VALOR) y las DOS deudas de eje propio que la ley nombró (14 canales → `--_{c}-*` · cableado `EidosConfig.motion`), ambas detalladas en `next-features.md`. · Deuda menor registrada: las fichas de `docs/audit/theming/` esperan un `--report` supervisado post-`structural` (tarea aparte). ### FUERA del carril de theming (registrados HOY, no se ejecutan desde aquí) - **E1 — los botones del array de form COMPONEN el Button** (clase A-112): expediente completo de VC en `exped/vc/` + la corrección de E-talla: E2 ahorró el CONTEXTO pero no la LECTURA — E1 debe pasar `size` al Button que inyecte o es la regresión medida (26→36 px en xs). Y las partes son MUDAS (morfo sin evento; verbos canon: `commit-set-add`/`commit-remove`). - Lo abierto por el ARIA: `tag-group` filas SIN celdas (el «precedente» refutado midiendo) · `search-field` F-1 · la regla del censo de citas APG. - El párrafo anchura-vs-densidad querría vivir TAMBIÉN en `reference.md` junto a la norma tapada (dicho por E-talla, fuera de su alcance). ### Las leyes que dejó el día (sesión 2) - **Una interrupción del autor MATA a los agentes en vuelo** — informe.md a disco INCREMENTAL siempre; el disco sobrevive (V1 se recuperó íntegro). - **El supervisor MIRA las capturas antes de cada commit** (regla del autor). - **Las demos del sistema NO entran** ni como prueba central de una firma (reafirmada sobre la captura del velo; la prueba citable es el barrido de las 14 identidades del catálogo). - **F5 compartidas se RETIENEN y liberan EN SERIE** cuando varios ejecutores comparten `next-features`/`CONTINUE` — tres ejecutores hoy, cero pisadas. - La sonda estándar ganó DOS mentiras registradas (se pisa el `data-size` forzado sobre nodos compuestos · los `clamp()` fluidos no comparan sin declarar viewport) — están en §«Lo que el instrumento aprendió». Lo que queda de firma vieja NO cambia: la lista «Lo que espera TU FIRMA» de más abajo quedó VACÍA el 2026-08-25 y las entradas EJECUTADAS de hoy están intercaladas tras esta sección. ### FIRMA EJECUTADA (2026-08-25) — el velo del sistema no muere por un ATAJO **Qué se firmó** (changelog §56, reverso de §55 el mismo día): al bajar el sobre de trigger de `popover.css` al suelo, §55 lo dejó a **(0,0,0) — el mismo peldaño que `archetypes.css`**, que es quien pinta el velo de estado del sistema. Y el suelo declaraba su superficie con el ATAJO `background:`, que expande a `background-image: none` — **la propiedad exacta donde vive ese velo**. Resultado: el velo de hover de todo trigger desnudo dependía de qué hoja cayera después. No sólo por la regla de hover: el `:where()` deja también la de REPOSO empatada con la de hover del arquetipo, y ésa sola bastaba. La corrección son DOS PALABRAS — `background-color:` en `popover.css:60` y `:75`. **Los números**: **0 px** en el barrido de las **14 identidades del catálogo** que montan un `[data-popover-trigger]` (68 lecturas en reposo → 0 movidas, 68 en hover → 0 movidas, `rules split=2` en las 14) con control negativo diff 5 / 4; las dos sondas del eje a **0 diffs** (`popover` 448 valores / 8 estados, `chronos` 36 832 / 7); y **el velo sobrevive al orden invertido**, donde con el atajo moría (`linear-gradient(…8 %…)` → `none`). Cae con la firma el pin `chronos.css:417` (cinco declaraciones a (0,3,0) que ya no defendían nada): **0 diffs en 20 combinaciones `variant` × `size`**, control negativo 20/20 — §13 lo había adjudicado mal, al velo del `+N more` lo mata `button.css:69`/`:88`. Guarda §55 apartado **(f)** en `active-eidos-config.test.ts`, **4 mutaciones / 4 mordidas** (las dos primeras muerden la mitad positiva, las dos segundas —el atajo AÑADIDO junto al longhand— la negativa). **Lo que quedó fuera, con su número en `next-features.md` §13**: el residuo `transition` del MISMO empate (suelo `background, border-color` vs arquetipo `opacity, background-color`, las dos a (0,0,0) — hoy el `border-color` del hover de popover salta en vez de animarse), la decisión de producto «popover deja de declarar hover propio» (no es coste cero: 0.9821 → 0.931 **más** el velo hoy, sólo el velo después) y `calendar-select.css:14`, que **no es un pin** — retirarlo rompe el tipo en 3 de 4 tallas (12 / 14 / 20 → 16 px). **Fichas y prosa saneadas**: `chronos.md` (censo 202→198, propuesta 50→47, en bloques `` para que sobrevivan a `--report`), `calendar.md` (su ⚠ HALLAZGO «no determinista» lo cerró §55 y ya no apunta a §13), `emoji-picker.md` y los tres comentarios de código que seguían afirmando **(0,2,0)** del sobre (`emoji-picker.css`, `gradient-picker.css` —sólo su mitad base, la de estados sigue viva— y `lib/recipes/base.ts`). Se ANOTAN, no se reescriben: cada medida fue correcta con la cascada de su día. ### FIRMA EJECUTADA (2026-08-25) — el eje `size` de `form` LLEGA a sus acciones **Qué se firmó**: ceder el cromo de `submit`/`reset` al `Button` compuesto (firma del 2026-08-24) destapó el reverso. `form-submit.svelte:20` y `form-reset.svelte:15` traían su propio `size = 'md'` y **nada les pasaba la talla del formulario**, así que un `
` los renderizaba con `data-size="md"` —36 px de alto y 16 px de letra— **en las cinco tallas**; del eje sólo sobrevivía el `row-gap`. Quitar el default no arreglaba nada (el `Button` tiene el suyo): hacía falta el puente, y el precedente es literal. Nuevo `eidos/components/form/context.ts` calcado **1:1 de `toolbar/context.ts`** (`Symbol('form-eidos-ctx')` + getter de propiedad), `form.svelte` publica el `resolvedSize` que ya calculaba, y las dos acciones leen `size={size ?? form?.size}` — la prop explícita gana, y standalone cae en el `md` del propio `Button` sin re-declararlo. **Sólo eidos: cinco ficheros, cero morfo / soma / sema; el contrato de 64 claves y el ledger, intactos.** **Los números**: alto **−10 / −6 / 0 / +8 / +16** px y letra **−4 / −2 / 0 / +4 / +12** px, sobre `submit` y `reset` por igual; **`md` no mueve un solo valor**; 84 diffs en total y **cero** en los cuatro nodos del array, el widget y el resumen de errores. Sonda estándar antes/después **0 sobre 832 valores en 7 estados**; instrumento propio reproducible (dos pasadas sobre código idéntico ⇒ 0 diffs). `vitest src/uix/eidos` 443/444 (el rojo es el ajeno conocido `skin-media-player`), `check` 0 errores atribuidos a los cinco ficheros, `docs:check` 0/0. **La doctrina que hubo que adjudicar**: **VERBATIM, no la norma tapada en `md`**. La tapa de `reference.md` §«Container→part derivation» gobierna contenedores cuyo eje es ANCHURA (su primer consumidor es `Dialog.Close`); el de `form` es DENSIDAD declarada, y su propia receta ya había escrito los cinco peldaños distintos de la acción. Escrita en `next-features.md` §13 §«Anchura vs densidad», con el corolario de que a `alert-dialog` —mismo agujero, eje de anchura— le toca la TAPADA. **Lo que quedó fuera, con su entrada en §13**: `alert-dialog` (no medido), `Form.ErrorSummary` con un `data-size` que ninguna regla lee, la escalera de fuente `12·12·14·14·16` de la acción de `form` (que corrige E1 en su carril) y la cita colgada de `dialog/context.ts:9`. ### FIRMA B′ EJECUTADA (2026-08-24) — la cascada de paleta es una ESCALERA **Qué se firmó**: los tres escritores de `--{c}-palette-*` pasan a tres peldaños que **no empatan** — suelo `:where([data-{c}])` `(0,0,0)` < forward `[data-{c}]:where([data-color], [data-color-custom])` `(0,1,0)` < tono `[data-{c}][data-color='X']` `(0,2,0)`. Antes el forward genérico se emitía el ÚLTIMO empatado a `(0,2,0)` con los bloques por tono, así que **ganaba por orden de emisión** y 419 claves públicas de tono en 49 recetas no pintaban nunca. **Por qué B′ y no reordenar la emisión** (que es lo que §13 proponía): el orden es **contrato invisible** —no se lee en el artefacto, no lo verifica un guard sin recorrer la hoja entera, se rompe moviendo una línea—. La especificidad es **contrato EN el artefacto**. Y es la misma ley que §12.9 acababa de firmar para el plano de profundidad: **una doctrina aplicada dos veces**, no dos parches. **Los números**: neutra en píxel sobre **~297.000 valores computados** comparados antes/después (70.912 + 34.336 planos en dos lotes, 57.720 en el tercero, 134.464 con los tonos ESTAMPADOS en las 49 unidades) — **0 diffs reales**, y los 17 crudos probados ruido reproduciéndolos sobre código idéntico. **207 de 353 claves** de tono volvieron a mover un valor computado. Guarda por **mutación** en `active-eidos-config.test.ts:1761` (6 mutaciones, 6 mordidas; cierre estructural: toda declaración de paleta vive en uno de los tres peldaños; anti-vacío `> 40`). Contrato del forward **ensanchado de 2 a 49 recetas** — vigilaba sólo las de paleta pública y era ciego a las 47 privadas. Suite 35/36 (el rojo `skin-media-player` es AJENO y conocido). **Dónde vive todo**: doctrina en [`theming/changelog.md`](../theming/changelog.md) §53 · contrato en [`theming/reference.md`](../theming/reference.md) (bloque THM-2) · emisor `src/uix/eidos/lib/render-css.ts` (`isPaletteSlotToken` parte el bucket host — **sólo las ranuras de paleta bajan**, el chasis se queda a `(0,1,0)`) · guarda `active-eidos-config.test.ts` · ledger `scripts/theming-sentinel-exceptions.ts`. **Tres leyes que deja escritas, y las tres costaron una medida**: 1. **El silencio del guard NO es señal cuando la adjudicación es por PATRÓN.** `theming-sentinel.ts` detecta STALE sólo contra el ledger EXACTO (`k in ledger`), nunca contra `SENTINEL_PATTERN_EXCEPTIONS`. Las 207 claves resucitadas no dispararon ni un aviso: el estrechamiento hubo que hacerlo comparando a mano contra las 13 entradas. La única que el guard sí vio fue `editable.risk-border`, que era exacta. 2. **Un tono por defecto ESTAMPADO resuelve por el forward, no por el suelo.** El envoltorio estampa `data-color` en toda instancia, la default incluida, así que `