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-cleanroom-fixes-20...

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 en docs/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-submit al 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 ~6 setTimeout (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

  1. 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.
  2. 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.
  3. No-cascada: un fix → gates → el siguiente. Fix sistémico ⇒ guard que lo fije (doctrina del programa 2026-06-26).
  4. 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-block vivo (block-shake, --duration-moderate, stamp commit · 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.ts tenía el MISMO agujero de enumeración — cerrado también (concatena hermanos; color-picker en FIXED_TONE_COMPONENTS); R-2.1 ahora caza hsl/hwb/lab/lch/oklab/oklch y escanea texto sin comentarios.
  • commit-block promovido a firma global de EidosConfig.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)) retienen animation para SIEMPRE y sombrean cualquier firma de evento de igual/menor especificidad — el shake shipped de card-group YA estaba muerto en grupos con motion. 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) y svg (glifos) exentos con auto-vigilancia (si les aparece css, el guard exige morfo); spin-field queda como única capa sin contrato (F1). Gap real registrado en el dossier de card-group: disabled no 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 ogl de package.json (0 imports; doctrina zero-dependence en su forma más barata).
  • DEP-1 P2 — clsx fantasma (soma/props/props.ts:1): inlinear implementación propia mínima (doctrina zero-dependence) y cerrar el "pending decision" de architecture/soma.md §12.
  • MOR-1 P2 — semaSelector: escape propio de valores (isomorfo, sin CSS.escape del 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:121 re-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 — keydown corta en el primer match sin handler (runtime.svelte.ts:562): continue en vez de return 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 logger inyectado; el camino void 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 awaitExpression cuando finished gana (callback fantasma ≤1500 ms).
  • 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 con semaSelector (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:

  1. Medir el delta de CSS generado por superficie (33 escalas × 15 componentes en generated/base.css — el peso decide el alcance).
  2. 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.
  3. 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 open sin disparar el evento): cablear runtime.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)

  1. 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).
  2. 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.
  3. 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, gradient omitido 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 documentar disabledDom mixto en la tabla de contratos.
  4. Contramedida: tras limpiar, docs:check enlaces 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

  1. SEM-1: política canónica de assertive — propuesta: intent ∈ {threat, loss} (el libro), alineando el runtime.
  2. EID-1: migrar fundación a outline + retirar el reset del app (propuesta) — o anotar excepción en §32.
  3. SOM-1: solo si el censo encuentra handlers async reales (si 0, se quita el await sin preguntar).
  4. THM-2: alcance del rollout de paleta (15 / subconjunto / re-redactar promesa), tras la medición de peso.
  5. SEM-4: prioridad y fecha (close ×5 · packs ×6 · flag emission).
  6. DOC-1: hogares E1/E2 exactos del contenido migrado.
  7. DOC-2: destino de los binarios del libro (commit vs puntero externo).
  8. F4-D: qué iniciativas GAP se arrancan y cuáles se registran como límite deliberado.
  9. 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 check fuera de alpha|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 reclamando animation tras 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 (¿liberar animation durante data-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.disabled no alcanza el Collapsible compuesto (promesa de la tabla de props a medias) — dossier ## Gaps con «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 (trp data-size a endpoints) · Task #31 (pickers → PickerProvider<TValue>) · FieldLangsSnippetProps sin uso.
  • Hallazgos trascendentes del programa anterior (en _cierre.md): holiday/event de range-calendar · naming gemelos tree · invalid de rango.

Powered by TurnKey Linux.