18 KiB
Plan de ataque — fixes de la auditoría clean-room (2026-07-10)
Handoff turnkey + plan aprobable. Creado: 2026-07-11. Rama alpha-0.1-sec-dom.
Fuente: la tabla §9 de
docs/audit-new-10-july/audit-active-uix.md— leerla ENTERA antes de tocar nada. El programa anterior está COMPLETO (cierre endocs/audit/components/_cierre.md). Este plan cubre todas las IDs de la §9 (P1/P2/P3 + GAP-*); nada queda sin fase asignada — la tabla de cobertura del final es el checklist.
Reconciliación de estado (la clean-room es snapshot del 2026-07-10)
- SOM-5 (P1) ✅ CERRADO post-snapshot: field
commit-submital Enter (33f346ed) + float-panel kb-gestos por handle-events (bdd17f04). - SOM-3 (P2) ◐: float-panel ya en
uix.timers(bdd17f04); media-provider es PUERTO Svelte-agnóstico exento por diseño. Quedan ~6setTimeout(image-provider, safe-polygon, dismissal, datetime/announcer, tags-input, announce/global). - Badge R-4.3 (única violación R-4.x viva en su §5.3) ✅ cerrada en el censo
R-1.x (
ee9786bc). Su matriz (102/32/0) es previa: hoy 134/0/0.
Principios de secuencia
- La máquina primero. Los fixes que amplían el enforcement (THM-1, MOR-4, guard de enumeración) van ANTES que el resto: destapan trabajo que las fases posteriores deben ver en el tablero.
- Rojo = parar la línea. Una suite/guard en rojo (hoy: THM-3) se arregla antes de cualquier otra cosa de la sesión. Ninguna sesión cierra en rojo.
- No-cascada: un fix → gates → el siguiente. Fix sistémico ⇒ guard que lo fije (doctrina del programa 2026-06-26).
- Iniciativas ≠ fixes. THM-2, SEM-4, DOC-1/2 y los GAP-* requieren decisión de alcance del usuario ANTES de ejecutar — este plan las dimensiona y propone, no las arranca en frío.
F0 — La máquina, en verde y completa ✅ EJECUTADA 2026-07-11
Resultado: matriz 135/0/0 (card-group dentro) · contracts 38/38 (guard MOR-4 nuevo) · suite eidos 306/306 · baseline 57 · vocab exit 0. Verificación en navegador: shake
commit-blockvivo (block-shake,--duration-moderate, stampcommit · risk) + chevrons a--opacity-muted(0.65 computado). Extras que destapó la ejecución:
- El spectrum de color-picker había sido creado para evadir los escáneres (su header lo admitía; reescrito con doctrina honesta) y
recipe-css-contract.test.tstenía el MISMO agujero de enumeración — cerrado también (concatena hermanos;color-pickerenFIXED_TONE_COMPONENTS); R-2.1 ahora caza hsl/hwb/lab/lch/oklab/oklch y escanea texto sin comentarios.commit-blockpromovido a firma global deEidosConfig.motion(block-shake+ reduce) — completaba un peldaño ausente (el ladder tenía fulfill/affirm/threat): ahora TODO rechazo de ToggleGroup lleva la firma de denegación en vez del settle de éxito.- Hallazgo sistémico (pre-existente, registrado abajo): las reglas de cascada de entrada (
[data-stagger][data-state='open'] > [data-animation-style], (0,3,0)) retienenanimationpara SIEMPRE y sombrean cualquier firma de evento de igual/menor especificidad — el shake shipped de card-group YA estaba muerto en grupos conmotion. Card-group lo resuelve re-afirmando a (0,4,0); el arreglo sistémico queda en cola.morfo/internal/existe (picker-shell.ts vive ahí); el guard MOR-4 acepta ambas raíces.icon-button(alias puro de Button, 0 css) ysvg(glifos) exentos con auto-vigilancia (si les aparece css, el guard exige morfo);spin-fieldqueda como única capa sin contrato (F1). Gap real registrado en el dossier de card-group:disabledno llega al Collapsible (la tabla de props promete selection+toggle).
| Ítem | Qué | Tamaño |
|---|---|---|
| THM-3 P2 | Voltear el canario vencido: toggle ya lee 0 class-hooks → lint.test.ts:110 a toBe(0). Suite eidos 305/306 → 306/306. |
S |
| THM-1 P1 | Extender component-audit.ts:698,756 a todos los *.css del directorio del componente (hoy solo {kebab}.css). Pagar lo que destape: hsl() ×8 en spectrum (anotar /* literal: physically-fixed */), opacity: 0.6 (calendar-select.css:27), fallback mágico var(--calendar-month-gap, 36px). Mismo espíritu que la válvula R-2.1: máquina primero, luego los fixes, no-cascada. |
M |
| MOR-4 P1 | Morfo para card-group (doctrina morfo-first, scope:['eidos'] si procede): leer CSS+wrapper existentes para derivar parts/attrs, recordar la decisión de cardinalidad 2026-06-27 (ToggleGroup OWNS cardinality) en el docblock. Entra en la matriz (→135) con dossier F-1.x y R-* limpias (incluida su opacidad literal, TOK-3). |
M |
| Guard de enumeración (contramedida) | La matriz enumera por MORFO_DIR — un dir de eidos sin morfo es invisible (así se escondió card-group). Guard nuevo: eidos/components/* ↔ morfos, con exenciones explícitas (picker-shell, spin-field = capas compartidas). |
S |
Salida de F0: matriz 135/0/0 · contracts 38/38 · suite eidos 306/306.
F1 — Coherencia P1: foco y voz (≈1 sesión)
| Ítem | Qué | Tamaño |
|---|---|---|
| EID-1 P1 | Anillo de foco de la fundación (archetypes.css:162-167) en box-shadow vs §32 que canoniza outline. Propuesta: retirar el condicionante (el outline: none global de web/routes/layout.css — inversión de dependencia app→fundación), migrar el anillo a outline con --focus-ring-*, conservar la vía forced-colors. Verificación visual obligatoria (focus visible en varios componentes, light/dark; 57/130 recetas caen a este anillo). Alternativa (si el usuario prefiere): anotar la excepción en §32. DECISIÓN #2. |
M |
| EID-2 P2 | Misma pasada, mismo fichero: opacity: 0.5/0.85 literales en la fundación → var(--opacity-disabled) / token o anotación. |
S |
| Fundación bajo R-* (contramedida) | La fundación violaba su propia disciplina porque nadie la escanea. Añadir archetypes.css + capas compartidas sin morfo (picker-shell/*.css, spin-field.css) al alcance del escáner R-2.x/R-4.x o test dedicado. Nota THM-1 (2026-07-11): recipe-css-contract.test.ts ya cubre esas capas para color/typo/phantoms (enumera por directorio y ahora concatena hermanos); lo que falta aquí es la vía component-audit (enumera por morfo). |
S |
| SEM-1 P1 | Unificar la prioridad de live-region: runtime (family==='signal' ∧ intent∈{threat,loss} → assertive, runtime.svelte.ts:725) vs AnnounceChannel (solo intent, announce.ts:55). Propuesta: canónica = intent (lo que sugiere el libro); alinear el runtime + test que fije la política. DECISIÓN #1. |
S–M |
| AUX-1 P3 | Mismo territorio: párrafo de doctrina que ordene los TRES mecanismos de live-region (uix.announce zwsp-toggle · fallback del canal · <Announce> soma) — quién es para qué capa. |
S |
F2 — Lote mecánico P2/P3 (≈1 sesión, sin decisiones de usuario)
Batch de fixes pequeños, cada uno con su test donde aplique; gates al final:
- DEP-2 P2 — eliminar
ogldepackage.json(0 imports; doctrina zero-dependence en su forma más barata). - DEP-1 P2 —
clsxfantasma (soma/props/props.ts:1): inlinear implementación propia mínima (doctrina zero-dependence) y cerrar el "pending decision" dearchitecture/soma.md§12. - MOR-1 P2 —
semaSelector: escape propio de valores (isomorfo, sinCSS.escapedel DOM) + validación de nombres de attr en matchers string (selectors.ts:141-155); tests con"y]. - MOR-2 P3 — des-duplicar la convención de marker (
selectors.ts:121re-implementa lo que el compilador ya posee): exportarla de un solo sitio. - MOR-3 P3 —
_resetCompileCache()no-op: implementar (WeakMap reasignable) o eliminar según uso real. - SOM-1 P2 —
await handler()vs contrato "handler síncrono": censo de handlers async registrados; si 0 → quitar el await (contrato cumplido, timing más limpio incluso para síncronos); si hay async → DECISIÓN #3. - SOM-2 P2 —
keydowncorta en el primer match sin handler (runtime.svelte.ts:562):continueen vez dereturn false+ test con dos planes de la misma tecla. - SEM-2 P2 — política de errores: logging por defecto (dev) en fallos de
canales sin
loggerinyectado; el caminovoid trigger(...)no puede producir unhandled rejection en producción (catch+log en la vía void, throw intacto para llamadores que awaitean). - SEM-3 P3 — motor: fallback muerto de
applyDominance(engine.ts:518)- cancelar el timer tope de
awaitExpressioncuandofinishedgana (callback fantasma ≤1500 ms).
- cancelar el timer tope de
- SEC-1 P2 — endurecer el serializador de
renderCssVariables: rechazar;/}embebidos en valores + test. - THM-7 P2 — ejemplo de
sema.md§packs con selector manual → reescribir consemaSelector(el código real ya está limpio). - THM-6 P3 —
var(--radius-full, 1px)en menu-dial.css:293 →9999px. - EID-4 P3 — comentarios "31-scale" → 33 (
active-eidos.svelte.ts:653,arts/color/README.md:65).
F3 — Censos con guard (≈2 sesiones; SOM-4 puede parir lotes)
| Ítem | Qué | Tamaño |
|---|---|---|
| SOM-3 P2 ◐ | Migrar los ~6 setTimeout restantes a uix.timers.schedule (key+meta, patrón float-panel) y retirar sus exenciones del guard de contracts. |
S–M |
| THM-5 P2 | Doctrina para los 17 !important: anotación en línea (/* important: <razón> */) + regla de máquina R-* nueva que marque los no anotados. color-picker (4) y group (8) ya tienen razón — anotarla; drawer/float-panel/tabs/select: revisar (fix o anotar). |
S–M |
| SOM-4 P2 | Censo de providers que re-declaran role/aria-* en props getters en vez de renderProps() ("most existing providers", guía §Part props). Entregable: tabla censo + guard (WARN) + migración de los mecánicos; si el volumen es grande → plan por lotes tipo S4 con sizing para aprobación. |
M (censo) |
| THM-4 P2 | Doctrina para los 1150 selectores de contrato sin consumidor CSS: criterio en eidos.md (attr de comportamiento puro = legítimo y declarado; resto = consumir o podar del contrato) + quemar hotspots (media-player 47 · stepper 30 · color-picker 29 · avatar 28 · trp 26; words 149 excluido). |
M |
F4 — Iniciativas con decisión de usuario (cada una, su sesión)
F4-A · THM-2 P1 — paleta 33 por instancia (2/17 superficies)
Es un ROLLOUT, no un fix. Triple acción de la clean-room + sizing previo:
- Medir el delta de CSS generado por superficie (33 escalas × 15
componentes en
generated/base.css— el peso decide el alcance). - DECISIÓN #4: rollout completo (15), subconjunto razonado, o
re-redactar la promesa de demo-authoring §6 ("acepta las 33") al estado
real. Mecanismo real:
universalPaletteDecls(render-css.ts:1766) — el doc cita un helper inexistente (paletteScaleDecls): corregirlo. - Tracker con hogar: el registro de rollout borrado renace en
docs/theming/(contramedida: borrar un tracker exige puntero sucesor).
F4-B · SEM-4 P2 — firma perceptiva declarada y no emitida
Deuda "sin urgencia" desde 2026-05/06 — ponerle fecha (DECISIÓN #5):
- close polimórfico inerte (5 pickers togglean
opensin disparar el evento): cablearruntime.trigger('close-*')con el patrón dialog/popover — el intent per-evento ya está declarado. - packs dormant (6 morfos menú/árbol sin emitir): cablear triggers en providers (D.6).
- flag
emission(D.2) en el morfo para que esto sea auditable por la máquina (cumple la regla 2-de-3: sema + auditoría).
F4-C · Corpus documental (DOC-1/2 P1 · DOC-3/4 P2 · DOC-5/6, EID-3, AUX-2 P3)
- DOC-1 — migrar el contenido canonizado FUERA de audit/deprecated:
registro de intent-assignment (CANON §3 ←
_veredictos.md) y build-contract (component-guide ← ARCHETYPE_COHERENCE §13) a E1/E2; los audits quedan como historia. DECISIÓN #6 (hogares exactos). - DOC-2 — ancla editorial: existe edición FINAL sin trackear
(
docs/Disenando_lo_que_ocurre_FINAL.{pdf,docx}, 426 págs.) y los punteros apuntan a ficheros inexistentes (README.md:138 · CANON.md:295). DECISIÓN #7: ¿commitear los binarios (hoy excluidos por norma) o punteros a edición externa? Reconciliar en cualquier caso. - Barrido editorial (una sesión): DOC-3 enlaces muertos ×15 · DOC-4
secciones stale (cadena de hold sin
SEMA_MAP.families[*].hold,event:*obsoleto en notes.md,gradientomitido en reference §10 + channels §5,paletteScaleDecls) · DOC-5 recuentos fósiles (95/62/25/66/31 → reales) · DOC-6 menudencias (§4.11 duplicado, TODO(reconcile) huérfanos, stub TSC, enlace air-old) · EID-3 párrafo de excepción de placement físico en el mandato logical-RTL · AUX-2 documentardisabledDommixto en la tabla de contratos. - Contramedida: tras limpiar,
docs:checkenlaces muertos WARN→error (que nunca vuelva a tolerarse ×15).
F4-D · GAP-* — menú de personalización (§7; el usuario elige)
| Iniciativa | Contenido | Tamaño |
|---|---|---|
| Selection/caret/cursor | La fundación no emite ::selection, caret-color ni tokens de cursor (cableados por arquetipo) — los 3 huecos sistémicos visibles. |
M |
| Scrollbar global | scrollbar-color/webkit sistémico fuera de scroll-area. |
S–M |
| Layer 4 | --{c}-{role}-{slot} sin consumidores; la FAQ la marca candidata a colapso "si en 6 meses nadie la usa" → decidir: consumir o colapsar. |
decisión |
| Jaulas abiertas | scrim sin regla consumidora · --depth-{plane}-z 0 consumidores · container queries 0 consumidores — fecha de consume-o-poda para que no fosilicen. |
decisión |
| Assets variables | Motor de fuentes variables listo, assets aún TTF estáticos (rfc-typography §5). | M |
| Límites a registrar (sin código) | Sonido por tema = app/dev (D.7) · icon-set no temable · touch-target constante (§37) · ThemeDefinition sin recipes (§19). Registrarlos como decisiones en el gap-log y cerrar. |
S |
Tabla de cobertura (toda ID de §9 → fase)
| Fase | IDs |
|---|---|
| ✅ hecho | SOM-5 (33f346ed+bdd17f04) · badge R-4.3 (ee9786bc) |
| F0 | THM-3 · THM-1 · MOR-4 (+guard enumeración) |
| F1 | EID-1 · EID-2 (+fundación bajo R-*) · SEM-1 · AUX-1 |
| F2 | DEP-1 · DEP-2 · MOR-1 · MOR-2 · MOR-3 · SOM-1 · SOM-2 · SEM-2 · SEM-3 · SEC-1 · THM-7 · THM-6 · EID-4 |
| F3 | SOM-3◐ · SOM-4 · THM-5 · THM-4 |
| F4-A | THM-2 |
| F4-B | SEM-4 |
| F4-C | DOC-1 · DOC-2 · DOC-3 · DOC-4 · DOC-5 · DOC-6 · EID-3 · AUX-2 |
| F4-D | GAP-* (§7) |
Decisiones que solo puede tomar el usuario
- SEM-1: política canónica de assertive — propuesta: intent ∈ {threat, loss} (el libro), alineando el runtime.
- EID-1: migrar fundación a outline + retirar el reset del app (propuesta) — o anotar excepción en §32.
- SOM-1: solo si el censo encuentra handlers async reales (si 0, se quita el await sin preguntar).
- THM-2: alcance del rollout de paleta (15 / subconjunto / re-redactar promesa), tras la medición de peso.
- SEM-4: prioridad y fecha (close ×5 · packs ×6 · flag
emission). - DOC-1: hogares E1/E2 exactos del contenido migrado.
- DOC-2: destino de los binarios del libro (commit vs puntero externo).
- F4-D: qué iniciativas GAP se arrancan y cuáles se registran como límite deliberado.
- Contramedidas: adoptar rojo=parar · fundación bajo R-* · guard de enumeración · trackers con sucesor · deuda con fecha (book-deviations) · docs:check en error · clean-room periódica al cierre de cada programa.
Protocolo de verificación (invariantes)
npx tsx scripts/component-audit.ts→ 134/0/0 debe MANTENERSE (→135 al entrar card-group; nunca hacia abajo).npx vitest run src/uix/contracts.test.ts→ 37/37 (→38 con el guard de enumeración).npm run checkfuera dealpha|words|palabras|chronos→ 57.npx tsx scripts/morfo-vocabulary-check.ts→ exit 0 (WARN cropper conocido).- Suite eidos → 306/306 tras F0 y se mantiene.
- Cambios observables → verificación en navegador (gesto/tecla/inspección
real). Gotchas de entorno en la memoria del proyecto (rAF congelado en tab
desenfocado, dev server flaky, HMR de
.svelte.ts).
Exclusiones DURAS (sin cambios)
words/**·palabras/**·chronos(ACTIVE_DEV_TRACK) ·web/routes/alpha/**.- Demos: NO son fix — frame nuevo de sistema pendiente de diseño
(decisión 2026-07-10,
e53bf0e8); las D-* son verdict-neutral. - Nada a git sin petición; commits con paths explícitos (excluir
.claude/,rt.css, README stub, binarios del libro — salvo que la DECISIÓN #7 los incorpore).
Cola lateral (no bloqueante, registrada)
- Cascada de entrada vs firmas de evento (sistémico, shipped): las
reglas generadas
[data-stagger][data-state='open'] > [data-animation-style='…'](0,3,0) siguen reclamandoanimationtras terminar la entrada (data-state persiste) y sombrean las firmas de evento (0,2,0) en cualquier ítem escalonado — commit-settle/announce-* incluidos. Card-group re-afirma la suya a (0,4,0) como parche local; el arreglo de sistema (¿liberaranimationdurantedata-event-phase='active'? riesgo: re-disparo de la entrada al soltar) es diseño de motion — adjudicar en F2/F4-B. spin-field: única capa compartida sin morfo/contrato (guard MOR-4 la exime con nota) — darle contrato en F1 junto a la fundación.card-group.disabledno alcanza el Collapsible compuesto (promesa de la tabla de props a medias) — dossier## Gapscon «implementar»; toca wrapper eidos de collapsible (reenvío) o corregir el doc.- class-hooks preexistentes: color-picker(2) · time-picker(8) ·
time-range-picker(12) — extensión del codemod S1 cuando cuadre; nota:
.range-time/.picker-time-row(CSS secundarios, ahora escaneados) pertenecen al mismo censo. task_d66356de(trpdata-sizea endpoints) · Task #31 (pickers →PickerProvider<TValue>) ·FieldLangsSnippetPropssin uso.- Hallazgos trascendentes del programa anterior (en
_cierre.md): holiday/event de range-calendar · naming gemelos tree · invalid de rango.