70 KiB
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 porkind; declaración = DEFAULT DE ESPECIE, la prop per-instancia gana — patróndir). Esquema:gridsinexecutor.customfirmado 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) ·RovingFocusGroupre-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.ts4/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.trapretirada) ·FocusPolicyTable.svelterenderiza 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.targettras 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
kinds (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 nuevaprop-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 mecanismoactionsa 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 (actionsPOR 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.tsNATIVO de SvelteKit en la PUERTA de cada motor svrs (entrada renombradaindex.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 censoserver-boundary.test.tsse 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-rowindexc0c03818c(ref computada registrada, mano retirada; «value opcional» = decisión de autor NO tomada) · instrumento de la ARISTA + residuo a cero + FLIP: el pase ARIA GATEA5027c135f. 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-walk271c79dea(3 fields, −161; Home/End = VALOR en la especie; Backspace-retrocede = callback del valor) · highlight-walk4cad87c24(trío activedescendant, −39; listbox FUERA con evidencia; sin puerto de foco; nextIndex CONSUMIDO con tipo ensanchado) · value-step5436fa297(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
valuea 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 dispatcher3a394c1d4(actions POR REGISTRACIÓN, workaround de sidebar muerto; compile recoge deps de condiciones — vicen-e1 retiró su unión AST en445ca917econ prueba deadPerPart=0) + fila keyboard de morfo.md0da97636b. 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 fuentedisabledregistrada en tooltip-provider:349 SIN LECTORES ⇒ huérfana nueva que muerde el gate de huérfanas del censo de fuentes, que va ennpm run gatey 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:866decide por unincludessobre EL TEXTO DEL MORFO, así que el acta de retirada NO puede escribir esa cadena literal dentro detooltip.tso 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 (fallabanattrs[]yaria[], 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í):
- Proveniencia FALSA en
schema.ts(JSDoc devalidateSourcelessEntry), en el JSDoc del flag detypes.tsy —lo peor— en el MENSAJE QUE SE LANZA: citan la regresiónf48acab37como 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 deariaBooleanllevan TODAS value ⇒ hoy el invariante no muerde ninguna (sigue siendo higiene correcta). Mismo género que la fe de erratas43bb676d4. continueMUDO enscripts/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.- Prosa caduca:
morfo-check.ts~:416-417 y ~:456 siguen diciendo «not gating yet» cuando el pase ARIA ya gatea. - Framing correcto para la fila 13: en el pase (d),
severityes el filtro DOMINANTE yconditionel 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.
- Proveniencia FALSA en
- 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:
0556b9a1acontrato (dec 7) ·0d7a97cb7demos/LA MINA ·04ac452a1runtime (dec 8+9) ·fe31a3f16valor (dec 1-4) ·2503e7f09calendario (dec 5+6) ·2d7d2d698picker (dec 10) ·c1f5b4925tooltip (dec 11) ·cb70927e9L3+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ó congit add web/routesy 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
conditionoseverity: 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) prohibirconditionen 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
severityes el filtro DOMINANTE yconditionel marginal.
- 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
- CANDIDATO NUEVO (autoría vicen-e1, medido):
data-perm-stepestá 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 elcontinuemudo 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):
8178c4152kinds 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) ·35d0005dekinds 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) ·64ead8407server-only (*.server.tsen la PUERTA de cada motor, el alias absorbe el rename,npm run buildVERDE 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-checkmodelaba 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. Ygit mvESTAGEA: dejó dos renames míos en el índice compartido (despejados congit 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.svelteraíz) y las páginas de componente responden 200 con marcado real. CONSECUENCIA MAYOR PARA TODO ANÁLISIS FUTURO:morfo-check,permutation-checkycomponent-auditvalidan NAVEGANDO aweb/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 ensrc/sobrevive a la reconstrucción; lo apoyado en la ruta, no. Consecuencias inmediatas: el candidato «instrumentar demos» (data-perm-step27/178) queda RETIRADO por ser trabajo de rutas; el lote0d7a97cb7(endurecimiento de tablas) vive ahí y no se toca. - P2 ARRANCADO (eidos ops) y DOS LEYES DE COORDINACIÓN nuevas:
- 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.tscon 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). - ACOPLAMIENTO POR DERIVACIÓN, no por fichero (vicen-0b, medido): su
guard
src/uix/value-channels.test.tsderiva el vocabulario de sistema RESTANDO el contrato a lo que emiterenderGeneratedBaseEidosCss(). 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. - LEY DEL «SE ANDUVO» (vicen-0b, y es la misma familia que el
continuemudo 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. - Matiz de rutas (vicen-e1/
dentro): una cura puede vivir ensrc/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.
- 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
- 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· split598ea460e· color-scheme39edbc8db. El adversarial confirmó lo sustantivo con métodos propios (index.cssbyte 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 deindex.cssson 39, no 8, todos bajoweb/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.assigndesde dentro de la página sí funciona) las únicas reglas concolor-schemedel documento son las dos de eidos y el computado de<html>bajodata-theme=base-darkesdark; el popup nativo del<select>no sale en screenshot. · COSTURA CERRADAe2c37f892(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|-darkdel 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: ThemeEffectiveOBLIGATORIO;renderThemeCsslo lee del TEMA (la opción de39edbc8dbmurió 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/routespierde el tipo por diseño → ledger +2 (soloalpha/lib/docs-theme.ts) con la excepción ESCRITA en la cabecera decheck-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 deappearanceentheme-variants.ts/grafito.ts(TS suprime el error exterior tras el anidado; comentado en el ledger) ·renderThemeCsscruda sobre config NO validada emitecolor-scheme: undefined(cerrado por el tipo y por la no-exportación deindex.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,65declara lo mismo a mano — se retira en la reconstrucción. · El XOR derecipe-css-contract.test.ts:251solo miraindex.css: con dos entradas queda COMPENSADO porindex-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/uixtiene CERO$app/*— y un script de arranque REPLICADO con «test de paridad» = dos verdades). Forma:modedimensión de prefs junto amotion(resolveThemede$libs/themeya era su gemelo; el entorno ya traíacolorScheme);theme/density/scalingdeclaradas por la RAÍZ en módulo puro (active-uix/prefs-schema.ts); eidos leeuix.prefspor su puerto (eidos/prefs-source.ts) como ya consumeuix.langs/dom/motion; REVOCADA la frase dearts/prefs/README.md:185. Sobreuix.prefs-intentv1 (libs/prefs/document.ts), adaptador localStorage canónico en la raíz standalone, hidratación SÍNCRONA (el bridge awaiteaload()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 comobase.css),renderUixBootScriptKit-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 (esperabaonMountdel app) — ahora lo siembra y lo observa ella. Cifras: 57/593 · suite 453/5306 · check 73 (src/ a cero) · gate OK. ⚠ INTERINO: el throwuix+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 enweb/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-step27/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 alMdegit 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-stepen 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:closehué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 menguantescripts/check-debt.ts, 34 ficheros/71 errores congelados) ·eidos:lint·gate(SINlint— deliberado, ver deuda) · fuente del hook enscripts/hooks/pre-push·.prettierrcendOfLine:auto.3ca138e91— C2a:partPropsForRegistrationresuelve el contrato entero; nacessr-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 nuevoscripts/eidos-only-attrs.tsque consumen morfo-check Y eidos-lint · palabras.handle declarado). morfo:check 160/161→161/161.<C2b>— codemodrenderProps()→.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>— efectosyncPartAttrs+ 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 en8ac23047fdel 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-playernació (bf12b8a57) sin registrarse enCOMPETING_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:
- Barrel de soma: falta
waveform -> Waveformen el export público. - Filenames de provider filtrándose a un barrel público (1).
- MOR-4:
audio-playeryskin-media-playersin 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). - THEME-SYS-1:
recipes/base.ts'header-z': '2'z-index crudo ×2 — territorio theming. - Soma importando su propio alias público (1).
- Escritura DOM mutable directa en soma (1).
- Hardcoded data attrs (8): aura-status ×2 · media-player sliders
data-size×2 · waveform played/remaining ×2 · radio-group y tabsdata-ready— proveniencia CORREGIDA por el eje theming (midió congit show): los introdujo76f18a5e8(cuyo propio mensaje avisaba «aparece un punto ciego del guard»), NO8ac23047f. ACEPTADOS como item 0 de su cola (su handoffe8a54ac45), adjudicación pre-masticada:data-readyes 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. - 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)
- 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
gateentero queda verde por primera vez. - Re-armar el hook:
cp scripts/hooks/pre-push .git/hooks/pre-pushy AVISAR a la sesión del eje theming por SendMessage (prometido dos veces; está conforme). Solo con fase A verde en el árbol. - P1 del plan (informe §5): ejes muertos de morfo (focus: ejecutar desde
compiled.focuso amputar+censo · keyboard: censo declaración↔implementación · ARIA: vocabulario cerrado de nombres/roles + separarattrs[]dearia[]·part-presentdesde registro vivo) · logger enSoma.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 desvrs/auth(adaptadores test a$svrs/auth/testingcon guard de producción — el patrón ya existe en memory-store;crypto-webreal sin node:crypto; guard de longitudes en verifyPassword; barrera*.server.ts; snippet del docs-site). - D-full como eje propio — EJECUTADO
035a748452026-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 pineaopen=posty NO estorba. El argumento completo del refutador (por qué el hold no sirve a su razón declarada) está en el informe. - P2/P3: eidos ops (HECHOS 2026-09-13: memo
cc586612a· foundation una vez598ea460e· color-scheme39edbc8db· apariencia en el TEMAe2c37f892· boot+prefse3c0899dd· (c′) pinesf875f60b9· #5 una fuente de motion + ejes visuales6e0decb9e(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)
lintfuera 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 formaten árbol QUIETO como commit propio + re-añadirnpm run lint &&algate. Jamás sobre rama con sesiones vivas.- Ledger
check-debt.ts: 71 errores deweb/congelados; solo mengua.web/routes/alpha/**está TERMINATED (memoria) — si se borra, sus 16 entradas salen del ledger. gradient-builder.trackdata-kind: emiteprovider.kindcrudo, 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-pickeren morfo:check: clase flaky «demo que navega bajo el eval» (comolink); 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 congit diff -G"patrón" --name-onlycuando 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 escribestyle: heightimperativamente 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 devlleva--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/smokeaceptan la URL como argv[2] — usar el servidor PROPIO (5201), nunca el de otra sesión. - ⚠
| tailenmascara 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:
- CSS: cada bloque
@media (prefers-reduced-motion: reduce) { SEL { decl } }pasa a[data-motion='reduce'] SEL { decl }(oSEL[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). - Generador: los 4 emisores de render-css.ts emiten
[data-motion='reduce']en vez del media;npm run generate:eidos-cssen el mismo movimiento; generated-css.test verde; value-channels: los--nombresemitidos NO cambian (5751/8034) — comprobarlo. - GUARD: test vitest (junto a recipe-css-contract.test.ts) que falla si aparece
prefers-reduced-motionen 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. - Motor JS: arts/motion NO importa arts/prefs (puertos). El puerto pasa a ser un
reducedinyectado por la RAÍZ desde el efectivo de prefs (createActiveUix: motion recibe una fuente() => prefs.motion.effective === 'reduce'o suscripción), ydom?.prefersReducedMotiondeja 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. - 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).