You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/process/CONTINUE-audit-p0.md

934 lines
70 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# CONTINUE — P0 de la auditoría arquitectónica (handoff, act. 2026-09-16)
**⚡ BOOT EN PRODUCCIÓN — FILA 1 EJECUTADA `9ae58c236` (2026-09-16), FILA 2 EN VUELO.** El autor firmó el
diseño que salió de un PANEL DE 9 AGENTES (5 diseños independientes · 3 jueces con lentes distintas ·
síntesis), no la cura que yo había propuesto: la mía metía los parámetros en el cuerpo del script, lo que
hace que su hash cambie en cada sitio, y su «función hermana» del hash era inviable (`render.ts` importa
`$libs/errs`, así que no es importable desde `svelte.config.js`). **Fila 1, anclada**: cuerpo CONSTANTE +
parámetros en el atributo `data-uix-boot` + el generador emite `UIX_BOOT_SCRIPT` y `UIX_BOOT_CSP_HASH` en
el MISMO `generated/boot.js` (sin borrar nada, sin `globalName`: muere el envoltorio escrito a mano) +
`render.ts` sin `node:fs` + UNA receta por HASH (el nonce baja a salida de emergencia) + el placeholder
DEBAJO de `%sveltekit.head%` + reemplazo FUNCIÓN + el aviso de `'unsafe-inline'` + tercer snapshot de la
delta contra documento LIMPIO + audit dev que NOMBRA el eje discrepante + `boot/pack.test.ts` que empaqueta
y ejecuta. Adversarial Opus: hash medido bajo CSP real en Chromium 145 / Firefox 146 / WebKit 26 con
control negativo bloqueado; app SvelteKit real siguiendo la guía al pie de la letra: construye,
prerenderiza y arranca sin violaciones; y el ENOENT probado con un build real (prerender ⇒ 500). 5 defectos
del lote cerrados en ronda 2 y re-verificados; el sexto, de redacción, lo cerré yo. Suite 461/5404, check 0
en `src/` y `scripts/`, docs:check 0/0/819.
**Fila 2 (en vuelo)**: el generador acepta `--schema` del SITIO, `composeUixPrefsSchema` compartida por
runtime y boot, guard de rancidez que TUMBA el build, y fixtures de delta por esquema. Cierra el defecto
medido que NO era un flash: con el catálogo de un solo idioma del boot por defecto, un usuario árabe de un
app multiidioma se queda en LTR toda la sesión, porque el runtime adopta el `dir` que el boot escribió.
⚠ FILAS QUE DEJA LA FILA 1 (autor): los 1 428 B del audit dev viajan al bundle de producción aunque NO
corren (un ternario que el bundler no pliega; misma convención que `engine-logger.ts`) · el audit queda
mudo si el runtime se desecha en el mismo turno · la entrada `bin` (`uix-boot`) y `esbuild` como
devDependency declarada · la decisión de producto sobre si el boot debe SELLAR `dir` (propuesta del panel:
que el boot marque su sello y `readPrefsEnvironmentFromDom` lo ignore, ~6 líneas) · el modo ATTACH no lo
cierra ningún diseño (no hay hidratación síncrona del intent) · un `themeResolver` atado a la instancia
sigue sin poder compilarse · el script pesa ~15 KB bloqueantes en `<head>`, diez veces lo que envía
cualquier librería del mercado.
**⚡ FILAS NUEVAS (2026-09-16, destapadas por el adversarial de la fila `render.ts`, commit `418297a82`)** —
PREEXISTENTES, no tocadas; esperan firma del autor. La receta documentada del boot pre-hidratación
(guía de theming, §59) NO funciona en ningún despliegue real, y hoy es latente porque ninguna app del
repo la cablea (`src/app.html` sin placeholder, nada en `web/` la llama). (1) **CÓDIGO, la más grave**:
en cualquier build de servidor empaquetado `renderUixBootScript` lanza `ENOENT` al leer
`generated/boot.js` junto al chunk (medido con Vite 7.3.1 SSR, suelto y con disposición tipo Kit, y con
esbuild; en dev funciona, por eso nadie lo vio). Cura medida en 5 entornos: `generate-boot.ts` emite un
módulo TS con el bundle como cadena y `render.ts` pierde `node:fs` (`?raw` de Vite descartado: fuera de
Vite genera un tag basura EN SILENCIO); exige borrar `generated/boot.js` (autorización del autor) y cambia
la razón de tener `render.ts` fuera del barrel (de `node:fs` al peso, 15 KB). (2) **DOC**: la receta pasa
`event.locals.nonce`, que Kit 2.55.0 nunca rellena: el navegador bloquea el script y vuelve el flash sin
error. Formas correctas propuestas: SSR con CSP por nonce ⇒ `%uix.boot:%sveltekit.nonce%%` en `app.html` +
reemplazo función; prerender / adapter-static (este repo) ⇒ Kit prohíbe el nonce y toca CSP por hash, con
el sha256 del tag en `script-src` (decisión abierta: ¿función hermana que lo calcule?). (3) **DOC**:
`html.replace('%uix.boot%', tag)` con cadena interpreta `$'`, `$&` y `` $` `` y puede cerrar el script
(medido); con reemplazo función no. Acoplada a la fila nombrada «hueco del boot con esquema propio del
app» (mismos ficheros). Observación O1, no defecto: fuera del tipo `string`, un objeto con `toString` con
estado burlaría la validación del nonce (`typeof` bastaría si el autor lo quiere). Detalle y sondas:
informe del adversarial de la sesión 2026-09-16.
**⚡ PRIMERO (act. 2026-09-16)** — **D-full + sema §3.9 EJECUTADOS `035a74845`** (constructor Opus 5 +
adversarial Opus 5 independiente: doctrina SANA, 8 defectos, los 6 del lote cerrados antes de anclar; segunda
ronda del constructor). Forma: `emit()` → `EmitHandle { id, settled }` SÍNCRONO (modelo `Animation.finished`);
`pre`/`coincident` proyectan y despachan ANTES del handler en el mismo tick; `post` handler → tick → emit;
`TriggerResult.settled` siempre; a11y del runtime tras el despacho (en `post` anunciaba y movía el foco tras el
hold); SO4 re-firmado (target ausente ⇒ log ruidoso + el handler corre, en las tres secuencias);
`clear`/`clearTarget` honran ocurrencias PERSISTENTES en vuelo (carrera nueva del id síncrono, reproducida en
test; transitorias ni se honran ni se cuentan). §3.9: el motor enacta `'state'` con la `MotionSource` que ya
tenía; soma conserva `'text'`/`'focus'`; muere `a11yChannelsOverride`. **Fase 0 (censo, condición firmada por el
autor)**: 260 eventos, 39 pre + 21 coincident (0 sin `sequence`, 0 con `prewrite`), 14 con handler / 46 que ya
mutaban en el mismo tick que el `void trigger`; los 6 que desmontan tienen Presence ⇒ CERO migraciones; la regla
transversal `[data-event^='emerge-close'] { animation: dismiss-fade }` existe en LETRA sobre los 4 overlays pero
el preset de Presence gana la cascada en todo instante (el refutador tenía razón en EFECTO). **Medido en Chrome**
(misma sonda antes/después, dos sondas independientes): Escape → `data-state=closed` dialog 258.6→9.1 · drawer
286.6→9.1 · popover 1757.8→8.5 · float-panel 1807.8→49.7 ms; el sello precede al flip; `data-ending-style` en
los cuatro; toggle `queue` intacto (20/28/156). Suite entera 5366/5366 (458) · check 0 en src/ (89 en web/ =
ledger, intacto: los ~76 emit de routes son fire-and-forget) · gate VERDE (ronda 1) · +20 tests · 3+2+3
mutaciones (⚠ diferir la limpieza UN microtask es un no-op observable; la honesta cruza a macrotask).
**PENDIENTE DEL AUTOR**: mirar el cierre de Dialog y Toast en un Chrome que PINTE (los panes de los agentes
estaban ocultos: mutaciones y estilo computado medidos, ningún frame visto). Filas que deja (detalle en
CONTINUE-sema-audit.md §3.9): guard «pre + desmontaje sin Presence» (censo vitest + detector dev-only en
`VisualChannel`) · PREEXISTENTE `dispose()` en vuelo repuebla `active` · `form` `requiresFocusMove` inerte
sobre el `<form>` · §3.1 de sema afirma un tooltip sin `trigger(` que hoy existe (:277). Instrumentos
desechables sin trackear: `scripts/__dfull-*` y `scripts/__advD-*` (el autor decide). Siguiente: (2) filas
nombradas por coste del bloque de abajo · (3) decisiones del autor (esbuild devDep · one-shot format · axe-core
· AT). ⚠ Lección de la sesión: «¿qué estás esperando?» del autor era una PREGUNTA, no un «adelante»: lo leí
como firma y me lo corrigió; la firma llegó después, explícita, tras responder «¿parche o profesional?» con las
dos condiciones (censo de salidas `pre` y clear en vuelo) que hicieron el lote arquitectura.
**⚡ PRIMERO MAÑANA (act. 2026-09-15, 01:30)** — P2 #5 está CERRADO y PUSHEADO (cinco commits:
`6e0decb9e` #5 A+B+soma+C · `13293fe50` docs · `2ed9c5493` #5-D /blocks · `165c5b33b` docs · el
de este handoff). Decisiones del autor de hoy: (b) «la política perceptual de reduce pasa al
MOTOR» va a la cola de sema (§3.9 de CONTINUE-sema-audit.md, junto a D-full) · los cuatro
ficheros de `web/routes` descongelados en C/D se QUEDAN (opción 2; mueren en la reconstrucción;
una orden genérica «arréglalo» NO vuelve a leerse como «descongela»: se pregunta y se para).
Orden del día: (1) **sema §3.9 + D-full** — abrir con las cinco preguntas ANTES de tocar (leer
CONTINUE-sema-audit.md entero, sema.md, CANON §9.1, engine.ts dispatch :205-219 y :460-480,
soma runtime :1280-1400); forma esperada: la señal lleva la declaración a11y del morfo, el motor
aplica `'state'` en su pasada de reducción con la `MotionSource` que ya tiene, soma conserva `'text'`/`'focus'`;
D-full = `emit()` → `{id, settled}` (re-firma ~40 asserts y prosa engine.ts:639 · morfo.md:801 ·
sema.md:652; A-36 pinea open=post). Constructor Opus + adversarial Opus, la sesión coordina.
(2) Las filas nombradas del bloque de abajo, en este orden de coste: `.prettierignore` +
`src/uix/active-uix/generated/` (una línea; **HECHA 2026-09-16**) · test de `render.ts` (`pins` + escape de `</script>`; **HECHA `418297a82` 2026-09-16**) ·
idioma `reducedMotion ? 'reduce' : 'allow'` ×5 → helper en `ActiveEidos` · asimetría de tipo de
`prefs.intent` (sanea en silencio; `setIntent` lanza) · hueco del boot con esquema propio del app ·
prosa del theming-sentinel · escena lee la fuente en el montaje. (3) Decisiones del autor:
`esbuild` devDependency (servidores parados) · one-shot `npm run format` + `lint` al gate (árbol
quieto) · axe-core «sí, pero no ahora» · pasada AT. Luego distribución + LangBase + wrappers, y P3
(§5 del informe). ⚠ Todo lo de hoy fue con agentes Opus 5 (constructor + adversarial) y DOS
cortes por límite de sesión: tras un corte, releer el diff entero antes de reanudar.
**⚡ PRIMERO (act. 2026-09-14 noche)** — **P2 #5 EJECUTADO Y COMMITEADO `6e0decb9e` + D `2ed9c5493`** («una
fuente de motion»: eidos no lee medios y nadie en el árbol uix pregunta al SO), en DOS lotes
disjuntos construidos en paralelo por agentes Opus 5 con adversarial Opus 5 independiente cada
uno; la sesión coordinó. **A (CSS, changelog §61)**: 65 at-rules `@media
(prefers-reduced-motion)` en 63 CSS + 29 del generador → `[data-motion='reduce'] SEL` con la
misma declaración (paridad a máquina 63/65/89 reglas/178 pares, 0 discrepancias); 4 `!important`
quedan con rival nombrado; guard `reduced-motion-media.test.ts` (at-rule, anti-vacío, mordido
3/3 y 4/4); verificado en Chrome real por el adversarial con un contrafactual: bajo la media del
SO la forma de HEAD NO paraba el anillo del spinner. **B (JS, §62)**: el puerto muerto
`MotionSource` revivido; `createMotionSourceFromPrefs` en `arts/prefs`; las tres raíces la
inyectan (standalone · attach · fábricas con `coreDependencies: ['prefs']`); mueren
`MotionDom.prefersReducedMotion`, `SceneDom.prefersReducedMotion`, `HapticChannelDom`;
`ActiveEidos.reducedMotion` (tres puertas: prefs · prefs sin ranura ⇒ permitir · standalone ⇒
SO); ocho componentes + dos motores de escena por-dom (aura, Ambient) leen el efectivo; +23
tests; mutaciones 3/2/3 + tipo (la fuente devuelve la intención ⇒ 2 errores nuevos: el puerto es
gate de tipo). **Adenda (a) firmada**: soma (`runtime.svelte.ts` migración a11y S5) lee la misma
fuente, `sources.motion` OBLIGATORIO como `dom` (adversarial dirigido: 7 defectos cerrados — el
CONTRATO DE CAPA `src/uix/contracts.ts` seguía diciendo `requires: ['dom']` y su test no lo
asertaba; la bolsa #97 del bag-census sin `motion` bajo un casteo; el camino `'text'`, el único
que declaran 9 morfos, sin test; dos snippets de docs que ya no compilaban; cifras). **C (cazado por el AUTOR en el docs site,
texto invisible en oscuro — coste de (c′) que firmé sin mirar)**: cuatro layouts congelados
componen esquema de prefs propio SIN los ejes visuales y eidos degradaba MUDO a `mode='light'`
constante; ahora `createActiveUix` fusiona SIEMPRE `uixVisualPrefsDimensions()` bajo el esquema
del app (redefinir sí, omitir no), eidos avisa por logger en vez de degradar, y
`web/routes/uix/+layout@.svelte` (DESCONGELADO por orden del autor, +14/−29) lee la ranura `mode`
y su toggle escribe `setIntent('mode')` — verificado en Chrome por el coordinador (SO oscuro sin
clave ⇒ dark coherente; clave vieja migrada al sobre; toggle en los dos sentidos). Ledger 95 → 93.
Suite completa 457/5336 verde (dos veces por los adversariales). Gate entero verde antes del
commit.
**Decisión (b) del autor → fila §3.9 de `CONTINUE-sema-audit.md`** (la política perceptual de
reduce pasa al MOTOR: hoy soma dicta `channels: []` al motor y un `emit()` directo no recibe
tratamiento a11y; se ejecuta JUNTO a D-full).
⚠ Filas nombradas, no ejecutadas: (1) `scripts/theming-sentinel-exceptions.ts` tiene 3
excepciones medidas «emulando el media» (`feed/sentinel-spinner-reduced-duration`,
`spinner/track-opacity`, `skeleton/placeholder-base`) — la emulación ya no alcanza nada; medir
estampando `data-motion='reduce'` y reescribir la prosa (theming:sentinel no está en gate: nada
enrojece, pero mediría CERO y la excepción se lo tragaría); (2) escena lee la fuente en el
MONTAJE, no en vivo (paridad con HEAD; `arts/scene/README.md`); (3) `engineFor` de aura/Ambient
cachea por `WeakMap<SceneDom>` y clava la fuente del primer montante (paridad estructural);
(4) `data-motion` tiene TRES dueños (reduce · navigation-menu from/to · tabs fade|slide) —
el rename es de esos ejes; (5) el idioma `{ get: () => (eidos.reducedMotion ? 'reduce' :
'allow') }` está en CINCO sitios (aura-indicator, Ambient, menu-dial, metrics, onion-menu):
un sexto merece un helper en `ActiveEidos` (p. ej. `eidos.motionSource`), no un sexto literal;
(6) la bolsa `sources` de soma se construye a mano en 96 sitios y los 93 harnesses castean `as
unknown as Soma` — el tipo era ciego ahí (60 rojos al hacer `motion` obligatorio); el casteo es
una deuda de los harnesses, no de este lote; (7) **el boot compila SIEMPRE el esquema por
defecto** (`boot.ts`): un app que REDEFINE un eje resuelve distinto antes y después de hidratar
⇒ flash; latente hoy (nadie en `web/` llama a `renderUixBootScript`), pero es el hueco hermano de
C y se cierra pasando al boot el esquema del app (o su serialización); (8) **D EJECUTADO `2ed9c5493`** (firmado «arregla también /blocks»): el shell de `/blocks` y el boot de
cada preview (`BootUix`) leen `mode`/`density` de `uix.prefs`, sus toggles escriben `setIntent`,
muere el segundo motor de densidad (`writeStyle` a mano → `data-density` + escalas del framework
0.84/0.9·1.16/1.12), migración única del blob `uix-blocks-prefs` (queda `{language, dir}`), y la
preview nace con `intent` desde la URL y `storage: false` (ya no hidrata ni escribe el sobre
compartido del padre — contaminación cruzada cerrada). Verificado en Chrome por el coordinador:
SO oscuro sin blob ⇒ coherente; toggles de modo y densidad mueven `<html>` y wrapper; blob viejo
migrado; preview `?mode=light` clara con el sobre en `dark` intacto. Ledger 93 → 89. Queda SOLO
`alpha/+layout@` (TERMINATED: no se toca). ⚠ Nombrado por D: `ActiveUixOptions.prefs` no es
genérico ⇒ `intent` no es puerta de tipo (una errata se sanea en silencio mientras `setIntent`
lanza) — asimetría en `src/`, fila; `density` no llega a la preview (BlockDemo no la serializa);
las sondas del `AUDIT-blocks-ledger.md` que esperaban `#uix-blocks-display` quedan sin puerta;
(9) el SSR del docs sirve `light` y la hidratación corrige — su cura es el boot (previo a #5).
⚠ Lecciones del día: el brief de A llevaba una cifra mía mal medida (`grep -rh | grep -v
generated` filtra LÍNEAS, no el directorio) — el constructor la re-midió porque el brief lo
exigía; DOS constructores cortados por límite y reanudados tras releer su diff entero; el
constructor B recortó en silencio el README de prefs (cazado por el adversarial: «nunca recortar
alcance en silencio»); yo edité `CONTINUE-sema-audit.md` con un adversarial en vuelo tras
decirle «árbol quieto» — la ley es mía también.
Orden del día que queda: decisión del autor sobre `esbuild` como devDependency (servidores
parados) · `.prettierignore` con `src/uix/active-uix/generated/` (una línea; `lint` fuera del
gate) · test propio de `render.ts` (`pins` + escape de `</script>`) · las filas de arriba ·
push (autor). Luego P2 sema (§3.9 + D-full + canales antes de deltas + stack de persistencia +
coincident + `engine.explain`) · distribución + LangBase + wrappers · axe-core «sí, pero no
ahora» · AT · P3. Menores: `LangNode` import muerto en `active-uix.svelte.ts:53`; el bundle del
boot compila el mismo esquema que la raíz (no recortar).
**ACTUALIZACIÓN 2026-08-26 tarde (sesión vicen-42)**: los items 1 y 2 de «Qué
queda» EJECUTADOS. Los 8 guards de contracts.test adjudicados y verdes
(`461078138`, método del precedente; detalle en el cuerpo del commit) — con
tres actas: (a) THEME-SYS-1 NO se adjudicó: el peldaño de header-z es decisión
del AUTOR (`docs/next-features.md`, objeción fundada del eje theming aceptada)
y quedó como deuda NOMBRADA `MAGIC_Z_DEBT` en el guard; (b) contracts.test
consume ahora `scripts/eidos-only-attrs.ts` (tercer instrumento sobre el
registro compartido); (c) data-ready lo resolvió theming en `1cb07c1e1`
(declarado en los morfos, registro entero viajó con su commit, opción A).
La primera pasada del árbitro destapó además 48 asserts de TIPO en las 12
suites re-firmadas por agentes de C2c (runtime verde, tipos rojos — el
«check con src/ a 0» de abajo era FALSO) → re-firmados a `toMatchObject`.
**Política de flakies FIRMADA por el autor** (`d09c4919c`): los 4 flaky eran
UNA clase — defaults de vitest (5s test/10s hook) midiendo transform/import
bajo carga (5210s agregados vs ~1s solitario; el de orca era el hook
beforeEach muriendo en un `await import`) — resuelta con
testTimeout/hookTimeout 30_000 explícitos y con acta en vite.config.ts.
**HOOK PRE-PUSH ARMADO** (byte-idéntico, peer theming avisado). ⚠ Hallazgo
residual de clase NUEVA, adjudicación aparte (chip lanzado): gesture.test.ts
rotate-detent es un assert sensible a carga (90 vs 270 — `projectAngle` come
velocidad de timestamps reales); verde ×2 en solitario. Siguiente: P1 (item 3
de abajo).
**Estado**: **P0 = A + B + C, EJECUTADO Y VERIFICADO.** Fase A cerrada (árbol en
verde con instrumento afinado) · fase B cerrada (la puerta existe, sabe fallar y
pasó en verde sobre HEAD; hook con fuente commiteada) · fase C cerrada entera
(C2a bolsa=contrato · C2b renderProps muerto · C2c efecto muerto — **una sola
tubería de attrs**). **D (desacoplar emit del handler) quedó FUERA de P0 por
decisión de diseño**: la versión barata (D-lite) creaba un contrato condicional
(`TriggerResult.id` a-veces-definido) y se retiró; la correcta es **D-full**
(`emit()` → `{id, settled}`) y reabre el eje sema con su handoff — ver §D abajo.
**EJE FOCUS-FIRST — EJECUTADO ENTERO 2026-08-26 noche** (mandato del autor:
«ejecuta el eje focus-first completo»; plan y leyes en
`docs/process/PLAN-focus-first.md`). Nació de la inversión del autor sobre el
hallazgo del corpus: el error no era `Morfo.focus` sin lectores — era la flota
que tiene foco y no lo declaraba. Resultado: **la declaración es ahora
CARGANTE** (FocusScope la consume como fallback de política:
`trapFocus ?? modal ?? policy.trap`) y el censo la vigila en AMBAS direcciones.
- **Contrato**: `MorfoFocus = MorfoFocusTrap | MorfoFocusRoving` (unión
discriminada por `kind`; declaración = DEFAULT DE ESPECIE, la prop
per-instancia gana — patrón `dir`). Esquema: `grid` sin
`executor.custom` firmado LANZA (el primitivo compartido es 1D).
- **28 morfos declaran**: 9 trap (popover corregido a `trap:false` — la
verdad embarcada manda, con acta) · 19 roving (radio-group adjudicado
`'both'` por APG: su propio keyboard[] declaraba las 4 flechas; grids de
calendario ×4 con custom firmado «2D excede el primitivo»).
- **Dos ejecutores reales**: FocusScope con `policy` (7 overlays cableados) ·
`RovingFocusGroup` re-clavado a ELEMENTOS (era id-keyed; defecto medido:
candidatos sin id colisionaban en `''`) con membresía viva — 9 providers
migrados (lotes A+B por agentes, 41/41 verdes): toolbar (adopción total),
tabs/toggle-group/tag-group (primitivo = ejecutor de teclado; el tab-stop
sigue a la selección), menubar, listbox, stepper (`:not([aria-disabled])`),
tags-input (input = stop de reposo, desviación firmada), radio-group.
- **Censo `focus-census.test.ts` 4/4** con red endurecida: trap⇒scope+policy ·
roving⇒primitivo o custom firmado · INVERSA (maquinaria sin declaración
falla). La red inversa destapó 11 más; adjudicados VERIFICANDO providers:
4 declaran (calendarios: roving-tabindex real 2D) y 7 son ESPECIES fuera
de la unión con acta in-file (accordion=walk sin stop único ·
nav-menu=disclosure (eje vivo ajeno) · rating=slider (flechas mueven VALOR) ·
select=activedescendant · date/time/color-field=segmentos todos tabulables).
**Kinds candidatos (`walk`/`activedescendant`/`slider`/`segments`) = decisión
del AUTOR pendiente**, registrada, jamás dodge del censo. Mentira plantada
y MORDIDA en las 3 redes (toolbar trap:true falso + avatar roving falso →
2 rojos exactos; restore byte-idéntico verificado).
- **Docs**: morfo.md (fila runtime + ejemplo + paso 5 con ambas familias +
fila 2-de-3 firmada) · eidos.md (fila falsa `focus.trap` retirada) ·
`FocusPolicyTable.svelte` renderiza la política en 27 páginas de demo
(agente 25 + 2 de referencia; **chronos SIN sección Accessibility** —
hueco de página anotado, no inventado).
- **Verificación**: suite completa **5069/5069** · gate verde · censo con
mentira demostrada · morfo-check **161/161 en caliente** (primera pasada
158/161: los 3 eran la clase «compilación fría» documentada abajo) ·
perm-check 447/454 pasos con los 10 fallos ADJUDICADOS AJENOS al eje:
barcode (clase «navega bajo el eval») · image (red externa) · calendar+fab
(falsos positivos: perm-check NO consume `eidos-only-attrs.ts` — hueco de
instrumento de P0, chip lanzado) · tag-group/tags-input/file-upload =
clase preexistente «commit-remove corre contra su propio desmontaje»
(`soma::runtime.target` tras el rAF del emit; el catch SEM-2 la convierte
en log; diff de migración con CERO toques a commit-remove y file-upload
INTACTO falla idéntico — chip lanzado con los 3 cierres candidatos, la
elección es del autor).
- **Sin migrar con acta**: los 7 de especies nuevas esperan sus `kind`s
(decisión de autor) · chronos sin sección a11y en su página · el eje
navigation-menu migrará su walk cuando toque (su eje vivo).
**P1 EN EJECUCIÓN (2026-08-26 noche, tras cerrar focus-first)** — orden del
autor «continua»; agentes Opus 5 ejecutan, la sesión coordina y verifica
(orden de agentes FIRMADA esta noche tras corrección del autor).
- **Hecho**: logger en Soma.runtime() (ya estaba: `3f8777332`) ·
**part-present desde el REGISTRO VIVO** (`e5cc05a02`: bindings.parts se
llena del registro cuando la respuesta es inequívoca; callback a mano =
override; hermanos repetidos silencian el registro; `partsRevision`
($state) hace reactiva la membresía de arrays planos; 4 tests; soma
1304/1304) · **morfo-check con pase ARIA ADVISORY** (presencia
required-sin-condición, role declarado↔renderizado, aria-\* sin declarar
con la tría A-85 exenta; imprime y cuenta aparte, NO gatea hasta
adjudicar la primera cosecha — pendiente de correr con servidor limpio).
- **Censo keyboard MEDIDO** (agente Opus, tablas completas en el resultado
del task; ficheros de trabajo en scratchpad kb6/inv/mod.tsv): 460
entradas / 68 morfos declarantes / 37 valores de key. El mecanismo del
morfo (`runtime.keydown`+`actions`) ejecuta solo 26 entradas (5,7 %, 6
componentes); 42 páginas de docs renderizan keyboard[] crudo. Dos
poblaciones confirmadas: 451 spec ejecutables + 9 no-ejecutables.
- **Adjudicaciones firmadas del wave keyboard** (ejecutor Opus en vuelo):
palabras Ctrl+B..Y = spec MAL CASEADO (minúscula), no display ·
'0-9'/'<'/'>'/'/' = display en el tipo (`kind`) · range-calendar Escape
se IMPLEMENTA (única entrada sin ejecutor) · Home/End de calendarios =
SEMANA (la declaración+APG arbitran, precedente radio-'both'; cambio de
conducta intencional con re-firma de tests) · table 'select'→'toggle-sort'
(la mentira era el nombre) · combobox/menu-dial mudan la entrada a la
parte verdadera · condición nueva `prop-equals` (orientación de los «4
declaradas / 2 vivas» ×7 y activationMode de tabs) · catch-up:
color-picker, gradient-builder, field-langs, drawer/tooltip trigger ·
typeahead / dígitos / hotkeys de host / modificadores multi-select =
CLASES firmadas del censo nuevo keyboard-census.test.ts.
- **Deudas NOMBRADAS del censo keyboard (no ejecutadas, decisión de autor
o verificación pendiente)**: (a) float-panel Escape kb-move-cancel vs
Dismissal en document COMPITEN — AMBIGUO, necesita navegador; (b)
palabras 10 entradas en colisión (Enter×3, Tab×3…) desambiguadas por
`currentBlock`, que el morfo no modela — excepción firmada; (c) sidebar
Escape declarado en menu-sub, ejecutado en menu-item — límite del
mecanismo `actions` a nivel runtime (no despacha por parte repetida),
firma en sidebar-provider:757-762; (d) LA PREGUNTA DE ARQUITECTURA: tres
ecosistemas de ejecución conviven (morfo-actions 26 · primitivos
compartidos ~115 · artesanal 307) — si el mecanismo del morfo debe
crecer, morir o quedarse como está es decisión del AUTOR con estos
números delante.
- **En vuelo además**: saneo svrs/auth (split crypto real, entry testing
con guard, verifyPassword endurecido, test de frontera server-only,
docs; la medición completa está en el task del agente) · capa
**HideOthers** (inert ref-count, dialog/drawer/alert-dialog; deuda
nombrada: contenido añadido al body con modal abierto queda fuera —
MutationObserver no entra en esta iteración).
- **Preparado, en cola tras el wave keyboard** (contención types/schema):
wave ARIA — split attrs[]/aria[] (989 entradas medidas: 847 aria, 8
role, 134 impostores — type×130, tabindex×2, for, contenteditable) +
vocabulario cerrado al spec 1.2 (39 nombres en uso; grabbed/dropeffect
deprecados con acta). Brief completo: scratchpad/brief-aria-wave.md.
- **COSECHA ADVISORY MEDIDA (2026-08-27 madrugada, servidor propio, dos
pasadas)**: **258 hallazgos en 32 morfos** (58 missing + 200 undeclared).
Ficheros completos: scratchpad harvest-pass1/2.txt. Concentración:
chronos 120/258 (aria-roledescription ×59 + aria-dropeffect ×42 —
DEPRECADO — por celda sin declarar) · card-group 17 · color-field 16 ·
chart 15 · chat-list 12 · time/date-field 10+10 · radio-cards 8 ·
calendar 8. Clases para el wave de adjudicación (siguiente ejecutable,
agentes Opus): (a) soma escribe aria sin declarar (la clase A4:
declaración alcanza o escritura se retira) — controls/expanded/pressed/
disabled/selected/orientation; (b) role="presentation" decorativo ×12
sin declarar; (c) tríos de valor aria-valuemin/max/now/text declarados
required SIN condición en sliders/knobs/meters (×27 missing — la
condición falta en la declaración, no el attr en el DOM); (d)
aria-hidden declarado y no emitido ×18; (e) chronos entero = caso propio
(¿roledescription por celda es diseño? dropeffect deprecado ¿se retira o
se declara con acta como drag-drop?). El FLIP a gate va al final de ese
wave. ORDEN acordado por canal: el wave espera a que vicen-e1 aterrice
su C2 (4 providers) — árbol asentado, cero interleaving.
- **DECISIÓN 1/3 FIRMADA por el autor (2026-08-27, «ok» a C+)** — mecanismo
keyboard: la declaración queda como contrato único (gramática+censo) y la
ejecución converge en **PRIMITIVOS POR ESPECIE** (el patrón del eje focus
aplicado a teclas): `grid-walk` (4 calendarios, ~90 % de máquina
compartida) · `segment-walk` (date/time/color-field) ·
`highlight-typeahead` (select/command/combobox/listbox, consolidando
list-selection) · `value-step` (slider/knob/rating/media-player). Además:
sanear el hueco medido del dispatcher (`actions` POR REGISTRACIÓN, no por
runtime — la firma de sidebar:757-762 es defecto estructural de un
mecanismo sancionado) y el censo pasa a verificar especie→primitivo.
Artesanal queda solo lo genuinamente único (palabras), con firma. La
premisa que corrigió la adjudicación: «lo mejor para la arquitectura
INDEPENDIENTEMENTE de lo que cueste» — la C original sobrevivía por
barata. ORDEN: wave C+ detrás del wave de la cosecha (y este detrás del
C2 de vicen-e1) — cero entrelazado.
- **DECISIÓN 2/3 FIRMADA por el autor (2026-08-27, «ok» a la opción a)** —
barrera server-only: `*.server.ts` NATIVO de SvelteKit en la PUERTA de
cada motor svrs (entrada renombrada `index.server.ts` / `testing.server.ts`;
el ALIAS absorbe el rename — ningún consumidor cambia; SvelteKit rompe el
BUILD si el módulo entra al grafo cliente). El censo
`server-boundary.test.ts` se QUEDA como segunda capa (contextos
no-SvelteKit); la convención se escribe como LEY DEL TIER en la doctrina
de svrs — la puerta, no cada habitación. Encolada tras el wave C+.
- **DECISIÓN 3/3 FIRMADA por el autor (2026-08-27, «ok»)** — la unión de
foco se EXTIENDE con los cuatro kinds medidos: `walk` (accordion,
nav-menu — tabulables nativos, flechas pasean, sin stop único) ·
`activedescendant` (select; command/combobox cuando la red los vea —
highlight virtual, stop en el ancla) · `slider` (rating — flechas mueven
VALOR) · `segments` (date/time/color-field — todos los segmentos
tabindex 0). Convergencia firmada: la taxonomía de foco CALCA la de los
primitivos C+ (segments↔segment-walk, activedescendant↔highlight-typeahead,
slider↔value-step, walk↔paseo direccional) — dos contratos, una
taxonomía. Cada kind gana verificación bidireccional en el censo;
INVERSE_EXCEPTIONS queda vaciada a lo composicional; FocusPolicyTable
renderiza las especies nuevas.
- **WAVE 1 COSECHA EJECUTADO `338c49d67`** (2026-08-27): 232/258 leyendo
fuente (183 declarados A4 · 32 declaraciones ganan su condición real ·
17 fuentes corregidas) · 26 AMBIGUO con 6 causas verificadas (insumo
completo scripts/\_\_wave1-aria-resultado.md). **HALLAZGO MAYOR ABIERTO**:
74 % de la cosecha = ARIA de componente COMPUESTO sobre el elemento
compartido de la parte anfitriona — el espejo duplica contratos y no
escala (6 aria-controls innombrables lo prueban); espejo-vs-instrumento
composition-aware = fila cero del dossier. FLIP advisory→gate CONGELADO
hasta ese juicio.
- **HOTFIX de regresión del wave 1 `f48acab37`** (2026-08-27): el espejo de
aria-expanded en chat-message.reaction-add emitía 'false' CONSTANTE
(stateRef sin fuente + modo aria-boolean «NEVER removed» + bolsa última
en el merge = pisaba el valor vivo del Popover compuesto). RETIRADO con
acta; verificado en navegador real (clic → popover montado + 'true').
Gemelas LATENTES auditadas (chat-list.item, chronos peek — salvadas por
orden de merge); card-group inerte (eidos-scope). **LEY DE WAVE nueva**
(cabecera del dossier + memoria): declarar value: sin registrar la
fuente EN EL MISMO MOVIMIENTO fabrica fila del censo, y la variante
aria-boolean EMITE mentira si la bolsa gana el merge.
- **DOSSIER UNIFICADO COMPLETO (2026-08-27, vicen-e1 `168a0fba0`)**: fila
cero con 3 exhibits (mi hotfix = exhibit A cerrado, primer precedente
composition-aware ejecutado), recomendación (b)-forma-(ii)-de-§15 con
(c) puente; PRESENTADO al autor. Al firmar: veredicto+reparto de lotes
se me trasladan y mi descongelamiento se CONFIRMA con mi autor en mi
sesión (protocolo de firmas cruzadas de esta noche).
- **FILA CERO EJECUTADA Y CERRADA (2026-08-27, 4 commits P1)**: retirada de
los 35 espejos `216ac0116` (32 del dossier + 3 que su mirrors.mjs no veía
— ciego a la forma attr:'role'; comp:true en su propio compo-final) ·
filas verificadas + L5 vía precedente aria-rowindex `c0c03818c` (ref
computada registrada, mano retirada; «value opcional» = decisión de autor
NO tomada) · instrumento de la ARISTA + residuo a cero + **FLIP: el pase
ARIA GATEA** `5027c135f`. Cosecha 258 → 16 → CERO (composición cayó 100 %
por construcción; confirmación gateada 161/161 exit 0). Forma magra
firmada por ambos carriles: la arista se resuelve desde los ATTRS
CO-LOCALIZADOS (la identidad ya viaja por la tubería única); disparador
NOMBRADO para el marcador explícito (si el flip bidireccional del censo
de fuentes destapa filas no-adjudicables en estático). Causa raíz de
card-group = STRIP del attr identificador como toggle de estilo (especie
nueva archivada; la arista TAMBIÉN en CSS: `:not([data-button])` +
selector de dos attrs). Excepciones firmadas ×2 con dueño de cura
(search-field demo-harness · palabras.status 4.1.3). Maquinaria advisory
= patrón de rollout de la casa, documentado en el tipo Issue.
- **C+ TANDA DE ESPECIES COMPLETA (2026-08-27)** — 4 primitivos, 4
commits, mismo molde (puro, porqués leídos, false en tecla ajena,
AMBIGUO con acta antes que unificación silenciosa, población VERIFICADA):
grid-walk `b10542110` (4 calendarios, −58; Home/End de rejillas = PÁGINA
por declaración) · segment-walk `271c79dea` (3 fields, −161; Home/End =
VALOR en la especie; Backspace-retrocede = callback del valor) ·
highlight-walk `4cad87c24` (trío activedescendant, −39; listbox FUERA
con evidencia; sin puerto de foco; nextIndex CONSUMIDO con tipo
ensanchado) · value-step `5436fa297` (slider/knob/rating/
GRADIENT-BUILDER — destapado por censo, −49; media-player REFUTADO: dos
pares mueven dos valores; number/css-field aplazados EN PAR). Cobertura
estrenada en 3 huecos preexistentes (month/year-grid, gradient-builder
sin NI UN test). Queda del wave: saneo del dispatcher (constructor en
vuelo: actions POR REGISTRACIÓN — cierra la firma-workaround de
sidebar:757 — + compile recoge deps de condiciones keyboard, la deuda
que desbloquea la retirada de la unión AST de vicen-e1) + párrafo de
especies en morfo.md.
- **BOLSA DE DECISIONES DE AUTOR (presentación CONJUNTA al cerrar C+,
acordada con vicen-e1 — doble vía: lo firmado antes en cualquier sesión
se ejecuta y se releva)**: (1) deriva Home/End set-min/max vs
first-item/last-item ×6 morfos, misma conducta; (2) rating: dígitos sin
declarar + Number(' ')=0 ⇒ ESPACIO LIMPIA con min:0; (3)
gradient-builder par horizontal físico sin firma RTL; (4) migración
number+css-field a value-step cuando el par se libere; (5) puertas de
borde divergentes calendar vs range-calendar (min/maxViewValue +
announceNavigation solo en uno); (6) Shift+PageUp/Down ±1 año sin
declarar; (7) ensanchar `value` a opcional en entradas aria (cura L5
inexpresable); (8-9 de vicen-e1) time-range-picker.invalid (tres
formas) · visual tooltip-disabled vs archetype.
- **C+ WAVE COMPLETO Y VERIFICADO (2026-08-27)**: 6 commits — 4 especies
(`b10542110` · `271c79dea` · `4cad87c24` · `5436fa297`, neto −307 de
ejecutores artesanales) + saneo dispatcher `3a394c1d4` (actions POR
REGISTRACIÓN, workaround de sidebar muerto; compile recoge deps de
condiciones — vicen-e1 retiró su unión AST en `445ca917e` con prueba
deadPerPart=0) + fila keyboard de morfo.md `0da97636b`. Barrido final:
**gate 443 ficheros / 5207 tests VERDE** (+138 esta noche) ·
morfo-check doble pasada **161/161 con el gate ARIA armado dentro**.
- **⚠ WATCH-ITEM DEL LOTE TOOLTIP (medido por vicen-e1, verificar ANTES de
commitear ese lote)**: retirar las DOS entradas del morfo (aria-disabled
y data-disabled — la que atenúa es la SEGUNDA, `archetypes.css:86-89`,
no el aria) deja la fuente `disabled` registrada en
tooltip-provider:349 SIN LECTORES ⇒ huérfana nueva que muerde el gate de
huérfanas del censo de fuentes, que va en `npm run gate` y en el hook
pre-push COMPARTIDO. La cura es retirar el REGISTRO en el mismo
movimiento (jamás una fila de ORPHAN_EXCEPTIONS: sería excepcionar un
acierto). `isDisabled` (:189-191) SOBREVIVE — `handleOpen` (:213) lo
usa. El acta de :339-348 queda obsoleta entera. Y la trampa del
auditor: `component-audit.ts:866` decide por un `includes` sobre EL
TEXTO DEL MORFO, así que el acta de retirada NO puede escribir esa
cadena literal dentro de `tooltip.ts` o R-1.2 revive y exige una fila de
excepción que el README no tiene.
- **RESERVA LEVANTADA — `5a1492952`** (vicen-e1 firmó la guarda del censo
de fuentes; su fichero ya no está en obra: un rojo ahí es señal real).
Tres datos que tocan la decisión 7(a) ejecutada en esta ola: la trampa
era DOBLE (fallaban `attrs[]` y `aria[]`, no solo aria) · el uso
fantasma de la condición **TAPABA el gate de huérfanas** (medido sobre
accordion: retirado el value, la registración viva de
accordion-provider:318 se quedaba VERDE — el gate de componente y el de
parte iban DESINCRONIZADOS desde antes; **7(a) no creó el agujero, lo
hizo visible**) · el negativo se vio ROJO en el árbol pre-guarda, y lo
que reventaba era la mitad del censo, no la del compilador (que siempre
tuvo la suya): el negativo no es vacuo.
La regla de las dos manos sigue vigente para lo que quede en obra: cada
uno tapa lo suyo, nadie repara el fichero del otro.
- **⚠ AVISAR a vicen-e1 si el lote L3/chronos mueve registraciones**: la
cifra de ⚠ UNCERTAIN de la cabecera de su censo pasó de SEIS a TRES con
la retirada de espejos (`216ac0116`) y volvería a moverse; el sitio
donde apuntarlo ya existe, pero el fichero es suyo.
- **CORRECCIONES PENDIENTES EN MI PROPIO INSTRUMENTO Y ACTA** (aplicar al
commitear la ola; medidas por vicen-e1 y verificadas por mí):
1. **Proveniencia FALSA** en `schema.ts` (JSDoc de
`validateSourcelessEntry`), en el JSDoc del flag de `types.ts` y —lo
peor— en el MENSAJE QUE SE LANZA: citan la regresión `f48acab37`
como la clase que el invariante cierra, y NO la cierra (aquella
entrada TENÍA value ⇒ retorno anticipado). El invariante cierra la
puerta de la FORMA; la regresión entró por la de la REGISTRACIÓN,
que el esquema no ve porque no ve al provider. Dato añadido: las 114
apariciones de `ariaBoolean` llevan TODAS value ⇒ hoy el invariante
no muerde ninguna (sigue siendo higiene correcta). Mismo género que
la fe de erratas `43bb676d4`.
2. **`continue` MUDO** en `scripts/morfo-check.ts` (~:236): cuando una
parte no monta en su demo, ni se cuenta ni se imprime — y el informe
dice «161/161». Es la clase «un guard sobre VACÍO pasa» dentro de mi
propio instrumento: hay que TALLARLO y reportarlo («N partes no
montaron, no validadas»). El hueco real NO son los 7 morfos sin demo
ruteada (solo 2 declaran ARIA): son las partes que no montan dentro
de demos que sí existen. **La talla debe contar COMPONENTES DISTINTOS,
no solo partes** (afinado por vicen-e1): concentradas en pocos ⇒
problema de demos concretas; repartidas ⇒ el 161/161 mentía POR
DISEÑO, y lo citamos toda la semana como evidencia.
3. **Prosa caduca**: `morfo-check.ts` ~:416-417 y ~:456 siguen diciendo
«not gating yet» cuando el pase ARIA ya gatea.
4. Framing correcto para la fila 13: en el pase (d), **`severity` es el
filtro DOMINANTE y `condition` el marginal** — quien abra ese trabajo
creyendo que el agujero es la condición optimizará lo minúsculo. ⚠ La
cifra «115 de 265» que circuló NO es reproducible (tres denominadores
distintos en cuatro medidas): NO resucitarla; verificado que nunca
entró en briefs ni en este handoff.
- **LA BOLSA DE 12 — FIRMADA UNA POR UNA Y EJECUTADA (2026-08-27)**: ola de
8 lotes de ficheros disjuntos (workflow, 16 agentes Opus: constructor +
adversarial por lote, 0 errores). Hashes: `0556b9a1a` contrato (dec 7) ·
`0d7a97cb7` demos/LA MINA · `04ac452a1` runtime (dec 8+9) · `fe31a3f16`
valor (dec 1-4) · `2503e7f09` calendario (dec 5+6) · `2d7d2d698` picker
(dec 10) · `c1f5b4925` tooltip (dec 11) · `cb70927e9` L3+chronos (dec 12,
lleva el censo del peer con ATRIBUCIÓN declarada y su firma escrita).
**Gate entero VERDE con la ola dentro: 443 ficheros / 5224 tests.**
Los 8 veredictos adversariales: ni un defecto de corrección; los
documentales (cifra 91→97 medida, dos comentarios que el propio lote
volvió falsos, un acta que afirmaba una imposibilidad refutada por
sidebar-menu-button) corregidos antes de anclar.
**LA MINA**, que va antes que la puerta: 49 páginas de demo
desreferenciaban `{a.value.kind}` PELADO con el gate de tipos CIEGO por
construcción (castean la parte cruda) — la primera entrada sin value
habría reventado esas tablas con TypeError. Endurecidas al patrón de la
casa. Hoy hay 0 entradas sin value: se desarmó una bomba, no se curó un
defecto.
⚠ **TROPIEZO REPARADO**: el commit de demos entró con `git add web/routes`
y se llevó `web/routes/alpha/**` (TERMINATED, regla de no commitear).
Detectado en el acto, verificado que los 3 commits de arriba eran míos y
sin nada del peer encima, y rehechos en UNA invocación con listas
explícitas. LEY: `git add <directorio>` no existe en árbol compartido.
- **FILA 13 pendiente de firma** (nacida de la ejecución de la 7): qué
significa una entrada declarada SIN fuente que además lleva `condition` o
`severity: optional`. Hoy queda invisible en los TRES registros (el
compilador la salta con la entrada, el resolver no la evalúa, y el pase
(d) del instrumento la exime). Opciones: (a) exigir emisión siempre para
las sin-value; (b) prohibir `condition` en ellas; (c) documentar los tres
estados (sin value + required + sin condición ⇒ (d) exige; sin value +
optional o condicionada ⇒ contrato-only). Mi lectura es (c) — «sin value
- condición» es la ÚNICA forma de decir «el provider lo posee Y es
condicional» — y el peer la suscribe sobre sus dos. Dato para no
optimizar lo minúsculo: en ese pase `severity` es el filtro DOMINANTE y
`condition` el marginal.
- **CANDIDATO NUEVO (autoría vicen-e1, medido)**: `data-perm-step` está en
27 de 178 demos ⇒ el 84 % de los componentes no se observa NUNCA en un
segundo estado; es la causa raíz de que la regresión de anoche
necesitara un humano con navegador. Encaja con el `continue` mudo que
este handoff ya nombra: un contrato verificado en UN estado es medio
contrato. Eje candidato «instrumentar demos» — del autor.
- **LOS DOS ÚLTIMOS WAVES DE LA COLA — EJECUTADOS (2026-08-27)**:
`8178c4152` kinds de foco (contrato: la unión pasa a SEIS especies —
walk/activedescendant/slider/segments—, 9 componentes declaran,
INVERSE_EXCEPTIONS vaciada a lo composicional, censo 4→9 tests con regla
por especie, **prueba negativa de 6 mentiras: 6 rojos y 6 reversiones
byte-idénticas**; barrido 168 morfos / 37 declarantes) · `35d0005de`
kinds docs (tabla de 6 especies en 9 páginas; el `<h4>` huérfano cerrado
por el TIPO — añadir una séptima especie deja de compilar, porque una
rama de fallback era código muerto demostrable) · `64ead8407` server-only
(`*.server.ts` en la PUERTA de cada motor, el alias absorbe el rename,
**`npm run build` VERDE** como prueba de que el grafo cliente sigue
limpio; el censo de frontera queda de segunda capa; ley del tier escrita).
**Gate entero VERDE: 443 ficheros / 5239 tests.**
⚠ INSTRUMENTO CORREGIDO en el camino: `docs-check` modelaba los alias como
de UN segmento y declaró rota una importación VÁLIDA (`$svrs/auth/testing`,
ahora clave exacta), poniendo rojo el gate y el hook pre-push COMPARTIDO.
Ahora resuelve como Vite (clave más larga primero). El doc tenía razón y
el instrumento estaba mal.
⚠ LEY DE COORDINACIÓN (vicen-e1, pagada por los dos): «el árbol está
quieto» es una OBSERVACIÓN, no un estado — caduca al escribirla. Quien
vaya a medir algo que dependa del árbol ENTERO lo dice ANTES. Y `git mv`
ESTAGEA: dejó dos renames míos en el índice compartido (despejados con
`git reset -- <rutas>` sin tocar el disco).
- **⛔ LEY: `web/routes/**`CONGELADO** (2026-08-27; regla del autor
trasladada por vicen-e1 y adoptada aquí como norma de trabajo — solo
restringe, y el autor la corrige cuando quiera): ese árbol se
RECONSTRUIRÁ, así que todo trabajo sobre él se tira el día de la
reconstrucción. Leerlo como evidencia sigue valiendo; ESCRIBIR no.
Medido de paso, sin tocar nada:`/`da 404 (no hay`+page.svelte`raíz)
y las páginas de componente responden 200 con marcado real.
**CONSECUENCIA MAYOR PARA TODO ANÁLISIS FUTURO**:`morfo-check`,
`permutation-check`y`component-audit`validan NAVEGANDO a`web/routes/uix/components/<kebab>/+page.svelte`. El «161/161» que este
handoff cita como evidencia mide **un árbol que se va**: no es evidencia
sobre el contrato, y lo será menos cada día. La ley que se deduce, y que
el peer alcanzó por otra vía: **el guard debe vivir donde NACE el valor
—morfo, compile, runtime— y no donde se observa.** Lo construido en
`src/` sobrevive a la reconstrucción; lo apoyado en la ruta, no.
Consecuencias inmediatas: el candidato «instrumentar demos»
(`data-perm-step`27/178) queda RETIRADO por ser trabajo de rutas; el
lote`0d7a97cb7` (endurecimiento de tablas) vive ahí y no se toca.
- **P2 ARRANCADO (eidos ops) y DOS LEYES DE COORDINACIÓN nuevas**:
1. **La regla de las dos manos NO ESCALA A TRES.** Con dos sesiones «cada
uno tapa lo suyo» basta porque cada uno sabe qué es suyo; con tres,
nadie tiene la lista completa — hoy dos carriles tocaron
`theming-sentinel.ts` con UN commit de diferencia y no se perdió nada
por ZONAS DISJUNTAS, es decir por suerte, no por protocolo. Ley: antes
de entrar en superficie caliente se pregunta a TODOS los carriles vivos
(`ListAgents`), no solo al que uno conoce. Aplicada hoy: paré la ola de
P2 antes de escribir y el reparto se negoció por canal (vicen-0b tiene
§17/§18 vivas; vicen-42 custodia contracts.test + eidos-only-attrs).
2. **ACOPLAMIENTO POR DERIVACIÓN, no por fichero** (vicen-0b, medido): su
guard `src/uix/value-channels.test.ts` deriva el vocabulario de sistema
RESTANDO el contrato a lo que emite `renderGeneratedBaseEidosCss()`.
Si una cura cambia QUÉ nombres emite el generador o EN QUÉ ORDEN se
compone la hoja, su lista derivada cambia y su guard enrojece sin que
nadie haya escrito un nombre nuevo; si solo cambia CUÁNDO se emite
(memoizar el mismo resultado), no le afecta. Verificación obligatoria
de mi lote: `value-channels.test.ts` + `generated-css.test.ts` (esta
aserta la hoja byte a byte). Rojo ahí ⇒ ping, no tocar.
3. **LEY DEL «SE ANDUVO»** (vicen-0b, y es la misma familia que el
`continue` mudo de mi propio morfo-check): al declarar que algo se
emite UNA VEZ, asertar también que **se anduvo** — que cada raíz
declarada resolvió a ficheros de verdad. Una raíz que resuelve a cero
es la puerta que nadie habría visto, y **un contador a cero se lee
igual de verde estando roto**.
4. Matiz de rutas (vicen-e1/`dentro`): una cura puede vivir en `src/` y
tener su VERIFICACIÓN natural en una demo — medir ahí vale, escribir
no. Afecta a la emisión SSR y al script de pre-hidratación.
- **P2 EIDOS OPS — TRES LOTES EJECUTADOS (2026-09-13)**, workflow de 2
agentes Opus (constructor + adversarial), tras el fallo del 2026-08-27 por
límite semanal. Hashes: memo `cc586612a` · split `598ea460e` · color-scheme
`39edbc8db`. El adversarial confirmó lo sustantivo con métodos propios
(`index.css` byte a byte contra HEAD normalizando CRLF · `base.css` +2
líneas · **5751 nombres únicos / 8034 ocurrencias antes y después**,
lost=[] gained=[] · memo === render directo, y la clave falla por la vía
REAL de breakpoints · 452/452 · check 71 · eidos:lint 0) y declaró LOTE
CON 4 DEFECTOS: uno de código (`dispose()` no soltaba el memo, ~431 KB —
cerrado antes de commitear) y tres del informe (los consumidores de
`index.css` son **39**, no 8, todos bajo `web/routes/**`; las «3
construcciones en el arranque de /uix» de la medida NO se reprodujeron —se
mide UNA; la ganancia es del camino caliente, 16,0→4,1 ms/llamada—; y una
afirmación de UI sin navegador). El navegador lo puse yo: en `/uix`
(servidor PROPIO 5201; la navegación por herramienta a localhost está
DENEGADA en este entorno — `location.assign` desde dentro de la página sí
funciona) las únicas reglas con `color-scheme` del documento son las dos
de eidos y el computado de `<html>` bajo `data-theme=base-dark` es `dark`;
el popup nativo del `<select>` no sale en screenshot.
· **COSTURA CERRADA `e2c37f892`** (firmada «sí» 2026-09-13 tras la pregunta
del autor «¿es lo más profesional o es un puto parche?» — mi (a) inicial era
un 70 %: dejaba la apariencia como OPCIÓN del que renderiza, con el sufijo
`-light|-dark` del id como SEGUNDA fuente de verdad, y rebajaba el campo a
opcional «para no tocar routes». Ley del autor: **routes es provisional y no
puede afectar al diseño ni a la arquitectura**). Forma ejecutada:
`ThemeDefinition.appearance: ThemeEffective` OBLIGATORIO; `renderThemeCss` lo
lee del TEMA (la opción de `39edbc8db` murió sin shim; `apply()`,
`renderCss()` y el generador lo emiten gratis); el sufijo baja a convención
de BÚSQUEDA y el validador cierra la contradicción (gate de forma + gate
semántico); el bloque del seed (`applyColorScheme`, donante forzable) declara
la apariencia del DONANTE; documento persistido v1→v2 sin migración;
`web/routes` pierde el tipo por diseño → ledger +2 (solo
`alpha/lib/docs-theme.ts`) con la excepción ESCRITA en la cabecera de
`check-debt.ts` (crece SOLO cuando el framework se mueve bajo el árbol
provisional). `generated/` byte-idéntico, 5751/8034 intactos, 460/460, check
73 (src/ a cero), gate OK. Adversarial: LOTE SANO EN CÓDIGO, 5 defectos (3
docs, 2 informe) corregidos antes de commitear. ⚠ Latentes ANOTADAS: el
ledger ENMASCARA la deuda de `appearance` en `theme-variants.ts`/`grafito.ts`
(TS suprime el error exterior tras el anidado; comentado en el ledger) ·
`renderThemeCss` cruda sobre config NO validada emite `color-scheme:
undefined` (cerrado por el tipo y por la no-exportación de `index.ts:74-79`;
observación, no defecto) · el donante se resuelve dos veces por render (mismo
camino). ⚠ Lección: el constructor fue CORTADO por límite de sesión a mitad
de la MUTACIÓN — revisar el diff completo ANTES de reanudarlo (podía haber
dejado la cura quitada; esta vez no).
· Deuda de rutas anotada: `web/routes/active/styles.css:8,65` declara lo
mismo a mano — se retira en la reconstrucción. · El XOR de
`recipe-css-contract.test.ts:251` solo mira `index.css`: con dos entradas
queda COMPENSADO por `index-runtime-css.test.ts`, no cubierto.
· Quedan de P2 eidos: #2 (attrs de tema pre-hidratación: exige inventar un
contrato de persistencia) y #5 (`data-motion='allow'`: bloqueado por la
doctrina de `$prefs`) — ambos a firma.
- **P2 #2 EJECUTADO `e3c0899dd` (2026-09-14) — «un motor, un sobre, un código»**, firmado
tras DOS rondas de «¿es lo más profesional o un puto parche?» (mis dos rebajas cazadas:
el hook de Kit DENTRO del framework — `src/uix` tiene CERO `$app/*` — y un script de
arranque REPLICADO con «test de paridad» = dos verdades). Forma: `mode` dimensión de
prefs junto a `motion` (`resolveTheme` de `$libs/theme` ya era su gemelo; el entorno ya
traía `colorScheme`); `theme/density/scaling` declaradas por la RAÍZ en módulo puro
(`active-uix/prefs-schema.ts`); eidos lee `uix.prefs` por su puerto
(`eidos/prefs-source.ts`) como ya consume `uix.langs/dom/motion`; REVOCADA la frase de
`arts/prefs/README.md:185`. Sobre `uix.prefs-intent` v1 (`libs/prefs/document.ts`),
adaptador localStorage canónico en la raíz standalone, **hidratación SÍNCRONA** (el
bridge awaitea `load()` aunque sea síncrono → primera resolución con defaults y salto un
tick después = el flash movido; la raíz lee el sobre ANTES de crear el motor). Boot
COMPILADO con esbuild desde los módulos puros (`active-uix/boot/boot.ts` →
`generated/boot.js`, sincronía como `base.css`), `renderUixBootScript` Kit-agnóstico,
NUEVE attrs → delta cero probado con la raíz REAL y tres mutaciones. Hallazgo del test
de delta cero: la raíz NO aplicaba el entorno del navegador (esperaba `onMount` del app)
— ahora lo siembra y lo observa ella. Cifras: 57/593 · suite 453/5306 · check 73 (src/ a
cero) · gate OK. ⚠ INTERINO: el throw `uix`+escalar (forma firmada primero) rompe todas
las demos → (c′) mañana (bloque «PRIMERO MAÑANA» arriba). ⚠ Lección de coordinación:
DOS cortes de agente en una noche (red y límite de sesión) — revisar el diff entero
antes de reanudar, y el adversarial cortado NO cuenta como verificación.
- **FILA 15 — axe-core NO es «instalar o no»: es CUÁNDO** (deliberación a
tres bandas, 2026-08-27; la instalación se RETIRÓ antes de tocar el lock
para no convertir doctrina en hecho consumado). Tres caras:
· **A FAVOR, y es un alegato que se ganó a pulso HOY**: nuestros
instrumentos propios enseñaron en una jornada ocho agujas ciegas, una
etiqueta que miente, un pase que observa un solo estado y un «161/161»
que callaba lo que no pudo mirar. Una implementación casera de reglas
de a11y envejecería igual de mal.
· **EN CONTRA**: el corpus de tests pasaría a apoyarse en su vocabulario
de reglas — puerta de entrada fácil, salida cara. (La otra mitad de la
objeción, «viola la dependencia cero», quedó RETIRADA al medir: el
repo embarca 2 dependencias y tiene 23 de andamio, así que la regla
gobierna lo que se EMBARCA.)
· **EL TIMING, que decide** (vicen-e1/`dentro`): axe mide el DOM
MONTADO, y el DOM montado vive en `web/routes` — el árbol congelado
que se reconstruye. Instalarlo hoy es comprar un auditor para
certificar un árbol que desaparece; y sin conducción de estados
(`data-perm-step` 27/178) sólo vería el estado de REPOSO, donde una
mentira dependiente de estado es indistinguible de la verdad. Daría
VERDE justo sobre lo que nadie ha mirado, y ese verde se cita como
evidencia: peor que no tener instrumento.
**Forma recomendada de la firma: «sí, pero no ahora»**, con condición de
entrada escrita — tras la reconstrucción de rutas y con conducción de
estados. Fija el criterio en vez de aplazar sin él.
- **PRECONDICIÓN OPERATIVA, acotada a lo cierto** (vicen-42): parar los
servidores de dev antes de un comando que ESCRIBE en `node_modules`
(`npm i`, `npm ci`, `npm prune`, cambiar de rama con deps distintas) —
leer no. El mecanismo es de WINDOWS: bloqueo obligatorio de ficheros
abiertos, un vite con el árbol de módulos mapeado impide renombrar o
borrar dentro y npm cae con EPERM/EBUSY dejando el árbol a medias. En
Linux/macOS el mismo comando pasa sin ruido: **si algún día corre CI en
Linux no verá este fallo JAMÁS, y ese silencio no es prueba de que aquí
no exista.** Y el
lock está LIMPIO pese al ` M` de `git status` (verificado por hash de
blob: disco = índice = HEAD): es normalización de finales de línea, así
que cualquier delta futuro será del instalador — pero ojo a la trampa
CRLF al commitear (3.552 líneas; exigir delta pequeño en `--stat`).
- **P1 QUEDA CERRADO**. Pendiente de decisión del autor: push (la rama
acumula todo sin subir) · axe-core (toca package.json, pide árbol quieto)
· pasada AT de los 20 top (humano + lector de pantalla) · el candidato
«instrumentar demos» (`data-perm-step` en 27 de 178 — el 84 % de los
componentes no se observa NUNCA en un segundo estado). Luego: P2 → P3.
- **GOBERNANZA UNIFICADA (autor confirma en esta sesión, 2026-08-27)**:
la cosecha ARIA (258) y el advisory de fuentes de vicen-e1 (78) se
adjudican JUNTOS — dossier unificado en construcción por vicen-e1
(read-only, cruce por componente, una recomendación por fila, lotes a
firma del autor); la ejecución posterior = UNA wave y el reparto de
coordinación lo presenta el dossier al autor. **Mi cola C+ → kinds →
server-only queda EN PAUSA hasta el dossier** (las 3 firmas intactas;
el juicio de composición da suelo a C+ (highlight-typeahead compone
Popover) y a los kinds (activedescendant)).
Además: axe-core en proyecto client (devDep — APLAZADO
deliberadamente: package.json/lock compartidos con 3 sesiones vivas;
instalarlo pide árbol quieto) · pasada AT documentada sobre los 20 top
(requiere humano+AT, decisión de autor).
**Origen**: informe de auditoría (artifact `Auditoría ActiveUix`,
https://claude.ai/code/artifact/02076a7a-9a51-4c89-a610-dfd2f6948954) — 70
hallazgos, 10 verificados adversarialmente (7 confirmados / 3 refutados), dos
comparativas de mercado. El plan P0→P3 vive en su §5.
## Commits de este eje (todos en `alpha-0.1-background`, sin push)
- `58fa3021c` — fase A: `close` huérfano de connection (4 errores) · stepper
`'completed'`→`'complete'` (el test pineaba la deriva) · langs stop/record.
- `2ec018107` — fase B: `check:gate` (src/=0 duro; web/ contra ledger menguante
`scripts/check-debt.ts`, 34 ficheros/71 errores congelados) · `eidos:lint` ·
`gate` (SIN `lint` — deliberado, ver deuda) · fuente del hook en
`scripts/hooks/pre-push` · `.prettierrc` endOfLine:auto.
- `3ca138e91` — C2a: `partPropsForRegistration` resuelve el contrato entero;
nace `ssr-contract.test.ts` (rojo antes, 6/6 después); pin viejo re-firmado.
- `9dffa541e` — fase A cierre: los 6 rojos del censo adjudicados (2 escrituras
«soma posee el valor» · menu-dial estampa olvidada · calendar/fab al allowlist
compartido nuevo `scripts/eidos-only-attrs.ts` que consumen morfo-check Y
eidos-lint · palabras.handle declarado). morfo:check 160/161→**161/161**.
- `<C2b>` — codemod `renderProps()`→`.props` (83 ficheros, 262 call sites) +
borrado del método/tipo, sin shim; comentarios re-nombrados a «render bag».
- `<C2c pendiente de commit al cierre>` — efecto `syncPartAttrs` + flag
BORRADOS; ~71 registraciones limpias; suite del runtime re-firmada de
asertos-DOM a asertos-de-bolsa; 5 tests de provider (dialog, drawer,
pagination×2, popover) re-firmados igual. **tabs-provider y
radio-group-provider viajaron en `8ac23047f` del eje theming** con la
retirada declarada en su cuerpo (coordinación por canal, opción A).
## Verificación con la que cerró C2c
runtime 49/49 · ssr-contract 6/6 · soma-attr-audit verde · **morfo:check
161/161 post-strip** («All routed morfos validate» — el detector definitivo:
una parte dependiente del efecto habría perdido sus attrs requeridos) ·
las 4 suites re-firmadas 13/13 · check con src/ a 0 (71 = ledger exacto).
**La PRIMERA pasada de la suite COMPLETA de la historia** (nadie la corría —
el hallazgo original demostrándose) destapó y se adjudicó así:
- **51 asserts de la clase C2c** en ~29 tests de provider jsdom (elementos
sueltos + getAttribute esperando el efecto retirado → null). Re-firmados a
asserts de bolsa en serie (3 agentes con contrato de conversión; los 5
primeros a mano: dialog, drawer, pagination×2, popover). REGLA: prewrite
(`data-last-action`) SIGUE siendo assert de DOM; el resto, bolsa.
- **1 preexistente real**: `lint.test` — `skin-media-player` nació (bf12b8a57)
sin registrarse en `COMPETING_ROOTS` (viste el contrato de media-player,
clase audio-player). Registrado con razón.
- **4 flaky de CARGA** (pasan en solitario): cookie-adapter y orca (timeouts
10 s) · soma-attr-audit y recipe-css-contract (5 s justos bajo CPU
saturada). Ley: un timeout con la máquina cargada NO es un rojo — re-correr
en solitario antes de adjudicar.
## Los 8 guards rojos de `contracts.test.ts` (preexistentes, PRIMERA tarea de mañana)
FE DE ERRATAS (lectura del corpus 2026-08-26): «nunca los vio nadie» era
FALSO — vistos y contabilizados en 2026-06-27 (gradient-builder/CONTINUE.md:299,
«~12 PRE-EXISTING failures from other sessions' WIP») y reducidos de 12 a 3
por `cb554ae2e`; lo que no existía era una PUERTA que los detuviera, y
volvieron a crecer sin que nadie corriera la suite — que es exactamente el
hallazgo que este handoff vindica. (Mi patrón de extracción tampoco capturaba
el fichero raíz.) Sujetos verificados AJENOS a los diffs de P0:
1. Barrel de soma: falta `waveform -> Waveform` en el export público.
2. Filenames de provider filtrándose a un barrel público (1).
3. **MOR-4**: `audio-player` y `skin-media-player` sin morfo — invisibles a
component-audit/eidos-lint (los competing roots necesitan su propia
respuesta en la enumeración, como ya la tienen en lint.test).
4. **THEME-SYS-1**: `recipes/base.ts` `'header-z': '2'` z-index crudo ×2 —
territorio theming.
5. Soma importando su propio alias público (1).
6. Escritura DOM mutable directa en soma (1).
7. **Hardcoded data attrs (8)**: aura-status ×2 · media-player sliders
`data-size` ×2 · waveform played/remaining ×2 · **radio-group y tabs
`data-ready`** — proveniencia CORREGIDA por el eje theming (midió con
`git show`): los introdujo `76f18a5e8` (cuyo propio mensaje avisaba
«aparece un punto ciego del guard»), NO `8ac23047f`. ACEPTADOS como item 0
de su cola (su handoff `e8a54ac45`), adjudicación pre-masticada:
`data-ready` es contrato REAL (lo leen tabs.css:329 y sliding-indicator.css
como puerta anti-flash) → se DECLARA en los dos morfos, ni exención ni
retirada. Los 6 restantes siguen siendo de este eje.
8. Namespaces de traducción no-kebab (4).
Cada uno se adjudica leyendo (¿guard exige razón nueva, o deriva real?) —
la clase skin-media-player/lint.test de ayer es el precedente del método.
⚠ El hook NO se armó por esto (promesa: solo en verde). Armarlo = cerrar
estos 8 + política para los 4 flaky de carga (¿timeouts subidos? ¿retry?).
## Qué queda mañana (en orden)
1. **Los 8 guards de contracts.test** (sección de arriba) — adjudicar uno a
uno; con ellos verdes y la política de flaky decidida, el `gate` entero
queda verde por primera vez.
2. **Re-armar el hook**: `cp scripts/hooks/pre-push .git/hooks/pre-push` y
AVISAR a la sesión del eje theming por SendMessage (prometido dos veces;
está conforme). Solo con fase A verde en el árbol.
3. **P1 del plan** (informe §5): ejes muertos de morfo (focus: ejecutar desde
`compiled.focus` o amputar+censo · keyboard: censo declaración↔implementación
· ARIA: vocabulario cerrado de nombres/roles + separar `attrs[]` de `aria[]`
· `part-present` desde registro vivo) · **logger en `Soma.runtime()`** (una
línea que resucita TODO el diagnóstico — la más barata de P1) · a11y
auditable (capa inert/hide-others para modales, axe en el proyecto client,
morfo:check con role/aria) · saneo de `svrs/auth` (adaptadores test a
`$svrs/auth/testing` con guard de producción — el patrón ya existe en
memory-store; `crypto-web` real sin node:crypto; guard de longitudes en
verifyPassword; barrera `*.server.ts`; snippet del docs-site).
4. **D-full como eje propio — EJECUTADO `035a74845` 2026-09-16 (ver bloque PRIMERO)**: `emit()` → `{id, settled}`; reabre sema
(S-07 intocable, handoff CONTINUE-sema-audit), re-firma su suite (~40
asserts) y la prosa doctrinal (engine.ts:639 · morfo.md:801 · sema.md:652).
El test A-36 pinea `open=post` y NO estorba. El argumento completo del
refutador (por qué el hold no sirve a su razón declarada) está en el informe.
5. **P2/P3**: eidos ops (HECHOS 2026-09-13: memo `cc586612a` · foundation una
vez `598ea460e` · color-scheme `39edbc8db` ·
apariencia en el TEMA `e2c37f892` · boot+prefs `e3c0899dd` · (c′) pines
`f875f60b9` · #5 una fuente de motion + ejes visuales `6e0decb9e` (tres adversariales SANO, gate verde); SIGUIENTE: las filas 1-9 del bloque PRIMERO, luego P2 sema) · sema tubería (canales antes
de deltas, stack de persistencia, coincident real o borrado, háptica sin
reduced-motion, engine.explain) · distribución + LangBase + generación de
wrappers.
## Deudas EXPLÍCITAS abiertas (nunca silenciosas)
- **`lint` fuera del gate**: ~2.300 ficheros de deuda de formato preexistente
(bump de prettier-plugin-svelte 3.4→3.5 + clases nunca formateadas; el pin a
3.4.1 NO movió el número — no es el plugin, es histórico). One-shot pendiente:
`npm run format` en árbol QUIETO como commit propio + re-añadir
`npm run lint &&` al `gate`. Jamás sobre rama con sesiones vivas.
- **Ledger `check-debt.ts`**: 71 errores de `web/` congelados; solo mengua.
`web/routes/alpha/**` está TERMINATED (memoria) — si se borra, sus 16
entradas salen del ledger.
- **`gradient-builder.track` `data-kind`**: emite `provider.kind` crudo, que
incluye `'mesh'` fuera del enum del morfo `[linear|radial|conic]` — misma
exposición que el preview (paridad deliberada). Si una demo monta mesh,
morfo-check lo flagueará: decidir entonces (ensanchar enum o guardar).
- **`natural-time-picker` en morfo:check**: clase flaky «demo que navega bajo
el eval» (como `link`); si reincide, endurecer el script (reintento por ruta),
no el componente. En la última pasada fue verde.
## Leyes de este eje (caro de re-aprender)
- ⚠⚠ **Rama compartida con 2-3 sesiones VIVAS**: add selectivo por lista
explícita + `git diff --cached --stat` + commit EN LA MISMA invocación;
selección por contenido con `git diff -G"patrón" --name-only` cuando el
status mezcla ejes; JAMÁS stash ni checkout de ficheros ajenos.
- ⚠ **Coordinación entre sesiones por SendMessage FUNCIONA**: avisar ANTES de
tocar ficheros de otro eje; el peer re-lee del disco; inclusión-declarada
(opción A) para cambios triviales mezclados — precedente `8ac23047f`.
- ⚠ **Un gate se arma DESPUÉS del verde, nunca antes** — armado en caliente
bloqueó el push legítimo del peer (y de paso demostró que sabe fallar).
- ⚠ **La bolsa entrega valores CRUDOS** (número 20, no '20'); Svelte
stringifica al render. Asertos de bolsa ≠ asertos de DOM.
- ⚠ **El único escritor imperativo sancionado es el prewrite** de `trigger`
(commit declarado por el morfo). Todo lo demás: bolsa. FE DE ERRATAS
(corpus 2026-08-26): la ley tiene UNA excepción abierta en el árbol —
textarea-provider:164/191 escribe `style: height` imperativamente para el
autosize (geometría medida, con bug documentado en su README:96-103: pisa
el overflow). El cierre correcto es (b): altura por custom property que
eidos lee (GESTURES.md:404 regla 2), que arregla el bug de paso — encolado,
no ejecutado.
- ⚠ `npm run dev` lleva `--force`: primer morfo:check tras arrancar servidor
paga compilación fría → timeouts falsos (accordion/background/link).
Pasarlo DOS veces o precalentar. `morfo-check`/`perm`/`smoke` aceptan la URL
como argv[2] — usar el servidor PROPIO (5201), nunca el de otra sesión.
- ⚠ `| tail` enmascara exit codes TAMBIÉN en los scripts del gate (medido en
vivo: check con 76 errores salió 0 por un tail).
## P2 #5 — brief (EJECUTADO `6e0decb9e` 2026-09-15; se conserva como crónica — la forma final difiere: lotes A/B/C + adenda soma, ver bloque PRIMERO)
LOTE P2 #5 — motion «allow» REAL: eidos y el motor JS leen el valor EFECTIVO, nunca el SO
(firmado por el autor 2026-09-14 como parte de «un motor, un sobre, un código»; se ejecuta DESPUES de que el boot (#2) esté commiteado, porque «solo atributo» exige que data-motion exista desde el primer pintado.)
MEDIDO 2026-09-14:
- Doctrina prefs: resolveMotion (src/libs/motion/resolve.ts:24) — intent 'allow'|'reduce' GANA al hint del SO; 'system' deriva del hint. La proyección estampa el efectivo en data-motion (PREFS_DOM_ATTRS.MOTION).
- eidos CSS: 94 bloques `@media (prefers-reduced-motion: reduce)` en 64 ficheros \*.css bajo src/uix/eidos (32 con !important) + 65 selectores `[data-motion='reduce']` ya existentes (conviven: dos fuentes).
- Generador: render-css.ts emite el mismo media en 4 sitios (:534, :1193, :1333, :1376) → 29 bloques en generated/base.css.
- Motor JS: engine-motion.ts:131 `reduced = opts?.reduced ?? dom?.prefersReducedMotion.matches ?? false` — lee el SO por el puerto DOM, no el efectivo de prefs: un 'allow' explícito tampoco llega al JS.
- Colisión de NOMBRE: navigation-menu usa data-motion='fade|slide|from-start|from-end|to-start|to-end' en sus propios elementos (motion.css:14 lo anota como «dos dueños»). Valores distintos ⇒ sin clash funcional con `[data-motion='reduce'] X`; el rename es del eje navigation-menu (vivo, ajeno): FLAGUEAR, no tocar.
FORMA:
1. CSS: cada bloque `@media (prefers-reduced-motion: reduce) { SEL { decl } }` pasa a `[data-motion='reduce'] SEL { decl }` (o `SEL[data-motion='reduce']` cuando SEL es el propio html/raíz). Misma declaración, misma especificidad + 1 attr; revisar los 32 !important uno a uno: si el !important solo compensaba la especificidad del media, cae; si bate estilos inline, queda con acta. Los 65 selectores `[data-motion='reduce']` existentes se unifican con los nuevos (sin duplicar reglas).
2. Generador: los 4 emisores de render-css.ts emiten `[data-motion='reduce']` en vez del media; `npm run generate:eidos-css` en el mismo movimiento; generated-css.test verde; value-channels: los `--nombres` emitidos NO cambian (5751/8034) — comprobarlo.
3. GUARD: test vitest (junto a recipe-css-contract.test.ts) que falla si aparece `prefers-reduced-motion` en cualquier \*.css de src/uix/eidos o en la salida de renderGeneratedBaseEidosCss(); con anti-vacío (número de ficheros inspeccionados > 60) y probado por mutación.
4. Motor JS: arts/motion NO importa arts/prefs (puertos). El puerto pasa a ser un `reduced` inyectado por la RAÍZ desde el efectivo de prefs (createActiveUix: motion recibe una fuente `() => prefs.motion.effective === 'reduce'` o suscripción), y `dom?.prefersReducedMotion` deja de ser la fuente por defecto en modo compuesto; standalone (sin raíz) conserva el fallback del SO con acta. Test: intent 'allow' con SO reduce ⇒ el motor NO degrada; intent 'reduce' con SO no-preference ⇒ degrada.
5. Docs: docs/theming/motion.md o el capítulo que documente reduced-motion (grep) + arts/motion/README.md + changelog: «eidos no lee medios: lee atributos; el efectivo lo decide prefs».
VERIFICACIÓN: vitest eidos + arts/motion + active-uix + value-channels; check (src/ a cero; ceiling web/ del ledger); check:gate; eidos:lint; prettier de lo tocado; adversarial con re-medida (grep cero media en eidos, cuenta de reglas antes/después, !important antes/después con acta por cada uno que queda).

Powered by TurnKey Linux.