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/audit-new-10-july/audit-active-uix.md

47 KiB

title type audience authority status método
Auditoría clean-room del ecosistema active-uix — theming y tokens en foco audit human + agent snapshot de análisis (2026-07-10) — registra hallazgos, no doctrina; los docs E1/E2 siguen siendo la verdad clean-room 2026-07-10 lectura completa del corpus (sin auditorías previas) + auditoría de código por capas + guards del repo + barridos propios

Auditoría clean-room del ecosistema active-uix (2026-07-10)

Encargo: auditar todo el ecosistema active-uix buscando debilidades, correcciones, vulnerabilidades y carencias, con doble foco en (a) la coherencia del sistema de theming y los tokens de componentes y (b) las carencias de personalización. Clean-room: no se ha leído ninguna auditoría previa ni documento de trabajo pendiente (docs/audit/**, docs/old-deprecated/**, docs/process/**, next-features.md, theming/changelog.md). Sin agentes. web/ fuera del análisis (solo se verificó la existencia de rutas que los docs citan). words/ · palabras/ · chronos = pista activa separada, fuera del alcance de hallazgos.


1. Resumen ejecutivo

El ecosistema está en un estado de coherencia estructural notablemente alto donde hay guard mecánico, y con deriva concentrada exactamente donde no lo hay — la tesis que el propio repo defiende ("canons defended by a mechanical guard drift ~0%") se confirma en frío. Los datos duros de esta pasada:

  • Disciplina de tokens casi perfecta en el catálogo mantenido: 0 hex sin anotar, 0 var(--scale-*)/var(--primitive-*) en CSS de componente, 0 prefijos prohibidos, 0 z-index crudos, 0 ejes físicos en claves de receta, box-shadow 100 % tokenizado, 0 data-soma-*.
  • Drift morfo↔CSS = 0: eidos-lint-all clasifica 0 selectores inválidos en todo el catálogo; los 63 packs sema construyen sus 244 selectores con semaSelector (0 strings manuales).
  • Matriz de aceptación: 134 componentes → 102 PASS · 32 NEEDS-WORK · 0 BROKEN. El grueso de lo pendiente es documental/demo (README F-1.x, chips D-7.x, trazas D-1.5/D-4.3), no de contrato visual.
  • La suite eidos corre 305/306 (1 canario de censo desfasado).

Las debilidades reales no están en la disciplina de valores sino en cuatro frentes:

  1. Huecos de enforcement — el escáner de recetas solo lee {kebab}.css (los 6 CSS secundarios escapan a R-*, y dentro hay violaciones reales); un componente sin morfo (card-group) es invisible para toda la matriz; la propia fundación (archetypes.css) usa literales de opacidad que prohíbe a las recetas.
  2. Contrato sema declarado pero no ejecutado — deuda ya registrada en book-deviations.md (D.6/D.11) pero sustantiva: 6 superficies de menú/árbol con packs dormant y 5 pickers con close polimórfico inerte; a lo que esta pasada añade field (interactivo con 0 eventos, error A-3.1) y float-panel (28 acciones de teclado vs 6 eventos, A-3.7).
  3. Incoherencias puntuales de a11y/theming — dos políticas divergentes de prioridad de live-region (runtime vs AnnounceChannel); el anillo de foco de la fundación sigue en box-shadow cuando §32 canonizó outline para todo el catálogo hace 3 días — condicionado por un outline: none de la capa de aplicación.
  4. La promesa de personalización más visible a medias: la paleta de 33 escalas por instancia (<X color="teal">) solo está cableada en 2 de las 17 superficies con data-color (button, toggle), y su tracker de rollout fue borrado sin nuevo hogar.

En vulnerabilidades de seguridad clásicas el resultado es tranquilizador: no hay {@html}/innerHTML inyectable en el núcleo, el clipboard es write-only, la persistencia de config valida contra contrato versionado, y setCssVariables es transaccional y validado. La única superficie de inyección observada es de nivel autor (matchers de semaSelector sin escape), no de usuario final.


2. Alcance y método

Fase Qué se hizo
1 Lectura completa del corpus: E0–E4 + RFCs ×7 + book-deviations.md + READMEs in-code (uix, eidos/components, arts del grafo: color, prefs, active-app, mapa arts). ~45 documentos.
2 Auditoría de código del núcleo: morfo (compile, selectors, tipos), sema (engine, canales visual/sound/announce, holds), soma (runtime completo, Soma), active-uix (boot dual, impl, services), eidos (ActiveEidos, archetypes/events.css, themes/base, spot-checks de render-css).
3 Guards del repo (eidos-lint-all, component:audit, vitest src/uix/eidos, docs:check) + barridos grep propios sobre los 130 CSS mantenidos + matriz transversal por componente.
4 Gap analysis de personalización por eje (síntesis en §7).

Límites declarados: morfo:check, perm:check y smoke no se ejecutaron (requieren dev server activo); no hubo verificación visual en navegador. Los hallazgos de comportamiento provienen de lectura de código y tests existentes, no de reproducción en vivo.

Inventario real del ecosistema: 140 morfos · 65 packs sema · 139 ficheros CSS eidos (131 recetas principales; 130 mantenidas tras excluir pistas) · 134 componentes enumerables por la matriz de aceptación · 22 arts.


3. Lo que está sano (verificado, no asumido)

Merece constar porque delimita dónde NO hay que gastar esfuerzo:

  • TSC operativa: álgebra de scopes + detección cross-axis en render-css.ts (scopeCovers, fase 3 de colisiones), guard de palette-derived-at-root en tests. El bug clase "toggle pre-TSC" no puede volver silenciosamente.
  • Pipeline de color de referencia: OKLCH default-on con fallback hex (appendColorScaleDeclarations), on-solid APCA con doble suelo y un solo criterio para rol e instancia (eidos/lib/on-solid.ts), alpha compositing-inverse, deriveScheme M3 + temper para intents. Coincide con lo prometido en rfc-color-engine §6–8.
  • Runtime de theming completo y seguro: setCssVariables valida contra getCssContract() y solo comita si el render valida (transaccional, active-eidos.svelte.ts:626-639); resolveToken resuelve color en JS puro sin getComputedStyle (497-517); los 7 builders (applyColorScheme / applyTypeScale / applyDepth / applyShape / applySpacing / applyGradients / applyTheme) existen con sus clear*() y el capstone escribe atómico.
  • Teardown disciplinado: EngineSemantic.dispose limpia persistentes + timers de frecuencia/dominancia + canales; SoundChannel cierra AudioContext y su listener de unlock; AnnounceChannel retira sus regiones; ActiveUixImpl.dispose desmonta en orden inverso y solo lo que posee (attach no toca el app, salvo el motion-fallback propio).
  • SSR/degradación coherente con la tabla de contratos: sema es ornamental de verdad (el runtime salta el emit sin engine), dom:false inyecta un disabledDom compartido, applyDom:false deja a Eidos como generador puro.
  • A11y de fundación: @media (forced-colors: active) emite outline de foco (generated/base.css:6488) y prefers-contrast: more refuerza bordes y texto — tal como promete §28.
  • Hold-como-suelo implementado: VisualChannel.awaitExpression espera las animaciones del target con tope MAX_EXPRESSION_WAIT_MS = 1500 — el antipatrón de truncado (D.12) está efectivamente muerto en código.

4. Hallazgos por capa

Formato: ID · severidad (P0 rompe contrato/usuario · P1 incoherencia con impacto real · P2 deuda/corrección · P3 menor/observación). Evidencia en ruta:línea.

4.1 morfo

  • MOR-1 · P2 — semaSelector no escapa ni valida los matchers de string. state.attr/value, aria.attr/value, eventNamePrefix, ancestor y pseudo se interpolan tal cual (src/uix/morfo/selectors.ts:141-155). Un valor con " o ] produce un selector inválido que revienta en target.matches() durante el emit (excepción dentro de la cascada sema). Es superficie de error de autor (no de usuario final), el propio doc lo marca "future iteration", pero el builder existe precisamente para que el compilador ataje esto: falta CSS.escape/validación de nombre de attr.
  • MOR-2 · P3 — duplicación de la convención de marker. selectors.ts:121 re-implementa la regla kebab === 'provider' → data-{c} que ya posee el compilador (el comentario lo admite: "Mirrors what compileMorfo emits"). Dos fuentes para la misma convención = deriva futura.
  • MOR-3 · P3 — _resetCompileCache() es un no-op con nombre engañoso (compile.ts:274-277). O se implementa (reasignando el WeakMap) o se elimina.
  • MOR-4 · P1 — un componente público sin morfo: card-group. eidos-lint-all lo salta ("no morfo file — skipped") y component:audit ni lo lista (enumera por MORFO_DIR, scripts/component-audit.ts:192) → invisible para toda la maquinaria de aceptación, con violaciones dentro (opacidad literal, ver TOK-3). Contradice la doctrina "morfo first para todo componente, incluso scope: ['eidos']" (guía §5 / regla F-1.5). picker-shell y spin-field también se saltan, pero esas son capas compartidas y la doctrina las exime; card-group no es una capa.

4.2 sema

  • SEM-1 · P1 — dos políticas divergentes de prioridad de live-region. El runtime marca assertive solo si family === 'signal' ∧ intent ∈ {threat, loss} (soma/runtime.svelte.ts:725-728); AnnounceChannel mira solo el intent (sema/chans/announce.ts:55-56). Un commit.fail + threat se anuncia polite por la vía sources.announce y assertive por la vía canal. Mismo concepto, dos reglas — hay que unificar (y decidir cuál es la canónica; el libro sugiere intent).
  • SEM-2 · P2 — política de errores frágil en los bordes. (a) Los fallos de canales no visuales son silenciosos si no se inyecta logger (engine.ts:79-80, documentado); (b) un rechazo del canal visual en void runtime.trigger(...) es unhandled rejection ("política consciente"); (c) trigger lanza SomaRuntimeTargetError si el target no está montado con engine presente (runtime.svelte.ts:670-671) — un void trigger sobre una parte recién desmontada produce un unhandled rejection real en producción. Merece al menos un logging por defecto.
  • SEM-3 · P3 — restos menores en el motor. applyDominance tiene un fallback muerto signal.id ?? sig-N++ (engine.ts:518) que, de alcanzarse, desalinearía los ids; awaitExpression no cancela el timer de tope cuando finished gana (callback fantasma de hasta 1500 ms en uix.timers, chans/visual.ts:113-126).
  • SEM-4 · P2 — deuda declarada pero perceptualmente relevante: los packs de menú/árbol están dormant (los 6 morfos no emiten vía runtime.trigger; book-deviations.md D.6 caveat) y los 5 pickers tienen el close polimórfico declarado pero los providers solo togglean open (D.11 "eventos inertes"). Resultado: la firma perceptiva de ~11 componentes existe en el contrato y no en runtime. Está registrado "sin urgencia" desde 2026-05/06; a fecha de hoy sigue igual — el flag emission (D.2) que lo haría auditable tampoco existe aún.

4.3 soma

  • SOM-1 · P2 — trigger espera handlers que el contrato declara síncronos. EventHandler documenta "Returning a value is allowed; it is ignored" pero el código hace await handler() (runtime.svelte.ts:743,753): un handler async SÍ se espera, cambiando el timing (a11y y TriggerResult se retrasan). O se cumple el contrato (no-await) o se corrige el contrato.
  • SOM-2 · P2 — keydown corta en el primer match sin handler. Si el primer plan que casa la tecla no tiene handler registrado, return false aborta el bucle y nunca se prueban otros planes de la misma tecla con distinta condición/acción (runtime.svelte.ts:562-564). Debería continue.
  • SOM-3 · P2 — 8 desviaciones de la doctrina de recursos gestionados: 7 setTimeout crudos (image-provider, safe-polygon, dismissal, datetime/announcer, tags-input, float-panel, announce/global) y 1 addEventListener directo (media-player/media-provider.ts) frente a la regla "timers via uix.timers / listeners via dom.listen". Con cleanup o sin él, quedan fuera de la observabilidad/cancelación del scheduler.
  • SOM-4 · P2 — backlog reconocido de re-declaración de attrs morfo. La guía lo dice textualmente ("Most existing providers still do this — a documented migration backlog", component-guide §Part props): providers que re-declaran role/aria-* en sus props getters en vez de renderProps(). Es duplicación morfo↔soma latente en gran parte del catálogo, sin censo ni guard.
  • SOM-5 · P1 — contrato sema infra-declarado en dos componentes (evidencia fresca de component:audit): field es interactivo con 0 eventos (error A-3.1) y float-panel declara 6 eventos para 28 acciones de teclado mutantes (error A-3.7). Junto a SEM-4, son los huecos de contrato perceptual más claros del catálogo.

4.4 active-uix

  • AUX-1 · P3 — tres mecanismos de live-region conviven: el de ActiveUixImpl.announce (zwsp-toggle, active-uix.svelte.ts:444-492), el fallback propio de AnnounceChannel (clear-then-set) y el componente soma <Announce>. La capa está razonada (uix = low-level compartido), pero las técnicas de re-anuncio difieren y la prioridad diverge (SEM-1). Vale un párrafo de doctrina que los ordene.
  • AUX-2 · P3 — disabledDom mixto: escrituras no-op silenciosas pero measure/raf/getDocument lanzan (active-uix.svelte.ts:242-329). Coherente con "reads that must return throw", pero no está documentado en la tabla de contratos — un componente que mida en dom:false muere con ActiveUixDomDisabledError sin que la tabla lo avise.
  • AUX-3 · OK verificado — el resto de la tabla de degradación (attach exige langs+dom, opcionales fallan explícito, dispose selectivo) se cumple tal cual la documentan contracts.ts y architecture/active-uix.md.

4.5 eidos (núcleo)

  • EID-1 · P1 — el anillo de foco de la fundación contradice el §32 canonizado. archetypes.css:162-167 implementa el anillo universal con box-shadow, con esta justificación en comentario: "the project's layout.css sets outline: none !important globally". Pero theming/reference.md §32 canonizó (2026-07-07) outline como modelo único de todo el catálogo (HCM-safe, sin flicker de segmentos, paridad con referencias). El agujero de HCM está parcheado (forced-colors emite outline, generated/base.css:6488), pero: (a) la fundación es ahora la excepción del modelo que el canon declara único; (b) el condicionante es CSS de la capa de aplicación (web/routes/layout.css) — una inversión de dependencia: la fundación del framework moldeada por un reset del app. Decisión pendiente: migrar el anillo de fundación a outline y retirar el reset, o anotar la excepción en §32.
  • EID-2 · P2 — la fundación viola su propia disciplina de opacidad. archetypes.css usa opacity: 0.5 (líneas 52, 111, 222) y opacity: 0.85 (129) sin token ni anotación — exactamente lo que R-4.2 declara error en recetas ("nunca 0.4/0.5/0.6 a mano; var(--opacity-disabled)"). Además ignora --opacity-disabled existiendo el token.
  • EID-3 · P3 — ✅ RESUELTO 2026-08-15. La excepción se cerró con doctrina explícita en vez de con una nota: hay DOS rejillas nombradas en eidos/lib/types.ts — Position (física, no espeja) y LogicalPosition (start/end, espeja) —, ambas generadas a canon/vocabularies.md, y la regla de elección es de comportamiento: ¿tiene que voltearse para un lector de derecha a izquierda? El hallazgo original, para el registro:
  • EID-3 · P3 — data-side físico vs mandato "logical RTL". dialog (24), drawer (16), toast (6), box (6) y media-player (4) usan left:/right: físicos como implementación de APIs de colocación por lado físico de viewport (convención floating-ui). No es deuda de flujo de contenido (paddings/margins están en lógicos), pero convendría que el mandato "logical RTL — never left/right" (cabecera de component-guide) explicitara la excepción de placement.
  • EID-4 · P3 — comentarios con recuento de paleta desactualizado: "the 31-scale palette" (active-eidos.svelte.ts:653, arts/color/README.md:65) vs las 33 canónicas de PALETTE_SCALES/docs:check.

4.6 arts y dependencias

  • DEP-1 · P2 — clsx es dependencia fantasma. Importada en src/uix/soma/props/props.ts:1 y ausente de package.json (resuelve como transitiva). Deuda ya documentada en architecture/soma.md §12 ("pending decision") — sigue sin decidirse: declararla o inlinear las ~30 líneas.
  • DEP-2 · P2 — ogl es dependencia runtime muerta. Declarada en package.json:60 y cero imports en src/ y web/ (solo un comentario en la demo aurora la menciona como convención). Contradice la doctrina "zero dependence" en su forma más barata de arreglar: eliminarla.
  • ARTS-OK — $color cumple lo prometido (puro, isomorfo, sin DOM); prefs mantiene la partición theme/mode/density fuera del preset core tal como el doc; motion como servicio con gating JS en Presence coincide con motion.md §9.

5. Theming: coherencias e incoherencias

5.1 Lo coherente (contrastado doc↔código)

  • Las 7 capas de token existen como se documentan; los slots son los 12 de DEFAULT_COLOR_ROLE_SLOT_STEPS con border = 7 (§28) y el retiro de border-hover está reflejado en RFC y código.
  • THEME_BASE_COLOR_ROLES coincide con §4 (primary purple · tertiary indigo reservado y anotado como no-muerto · loss plum · risk orange) — las colisiones históricas (primary≡loss, tertiary≡neutral) están corregidas.
  • Densidad ⊥ tipografía y scaling por mapa de participación (STATIC_SCALING_PARTICIPATION) están implementados como el RFC de scaling; la reconciliación del drift de radius (commit 845d6579) quedó registrada en el propio RFC.
  • Los guards de valor-referencia de 2026-07-07 (no phantom refs, privates con scope cubriente) están en recipe-css-contract.test.ts y la suite los ejercita.
  • El gap trigger→panel, el state layer y el radius-factor son config data (primitives.floating/state) como declara §36/§38/§40.

5.2 Incoherencias y huecos (los que importan)

  • THM-1 · P1 — enforcement parcial: los CSS secundarios no se escanean. component-audit lee únicamente {kebab}.css (scripts/component-audit.ts:698,756). Quedan fuera de TODAS las reglas R-_: calendar/calendar-select.css, color-picker/color-picker-spectrum.css, date-range-picker/date-range-picker-time.css, field/field-control-trigger.css, field/field-segment-state.css, picker-shell/picker-time-row.css. Y no es teórico: dentro hay hsl() ×8 sin anotar (spectrum — físicamente fijo, pero la regla exige anotación), opacity: 0.6 (calendar-select.css:27) y un fallback mágico var(--calendar-month-gap, 36px) (date-range-picker-time.css:9). El escáner debería iterar _.css del directorio.
  • THM-2 · P1 — la paleta por instancia está al 12 % del catálogo. De las 17 superficies que emiten cascada data-color (avatar+badge, badge, button, card, checkbox, editable, file-upload, radio-group, select-trigger/content, stepper, switch, tag-group, tags-input, toggle-group, toggle), solo button y toggle generan la cascada de las 33 escalas ([data-{c}][data-color='teal'] en generated/base.css). El resto acepta únicamente roles. La promesa "el color prop acepta cualquiera de las 33 escalas" (demo-authoring §6, vocabularies) es hoy una capacidad de 2 componentes; su tracker de rollout fue borrado sin sustituto (TODO(reconcile) en demo-authoring §6) y el helper citado (paletteScaleDecls en recipes/base.ts) no existe — el mecanismo real es universalPaletteDecls (render-css.ts:1766). Triple acción: decidir el rollout, restaurar el registro, corregir el doc.
  • THM-3 · P2 — la suite eidos está en rojo por un canario vencido. lint.test.ts:106-111 exige que toggle siga teniendo ≥1 class-hook "hasta que aterrice el codemod P5, y CERO después" — toggle ya lee 0 y el guard no se volteó a toBe(0). Trivial, pero significa que npm test del scope eidos no está verde en la rama.
  • THM-4 · P2 — 1150 selectores de contrato sin consumidor CSS (columna unused de eidos-lint-all; hotspots: words 149 — excluido —, media-player 47, stepper 30, color-picker 29, avatar 28, time-range-picker 26). No son errores per se (muchos attrs son de comportamiento puro), pero el volumen merece un criterio: o el attr tiene consumidor previsto (documentarlo), o sobra del contrato. Hoy no hay severidad ni doctrina para "declarado y jamás estilizado".
  • THM-5 · P2 — !important sin doctrina. 17 usos en el catálogo mantenido. Los de color-picker (4) están justificados en comentario (inline styles de soma) y los de group (8) implementan la fusión de radios; drawer/float-panel/tabs/select van sin anotación. No hay regla R-* que los gobierne — conviene o anotarlos como los literales o darles guard.
  • THM-6 · P3 — fallback sospechoso: var(--radius-full, 1px) en menu-dial/menu-dial.css:293 (sus hermanos usan 9999px; 1px como fallback de full huele a errata).
  • THM-7 · P2 — el ejemplo del doc contra su propia regla: sema.md §packs ilustra el pack de dialog con un selector manual ('[data-dialog-content][data-event-intent="threat"]') cuando la regla del mismo documento exige semaSelector. El código real está limpio; el ejemplo enseña el antipatrón.

5.3 Matriz transversal (130 componentes mantenidos)

Lectura correcta: heredar de la fundación es cumplimiento — la columna mide uso propio en la receta, no cobertura efectiva.

Sistema Recetas que lo consumen en propio CSS Lectura
state-layer (var(--state-*)) 20/130 El resto lo hereda de archetypes.css vía arquetipo; el único hover fuera de contrato que queda es badge (R-4.3, color-mix(currentColor)), confirmado por la matriz de aceptación.
depth/shadow tokens 10/130 Coherente con el modelo data-depth (el bundle lo pinta la fundación); no hay sombras literales.
focus propio 73/130 El resto cae al anillo de fundación (ver EID-1). 7 warns R-1.5 pendientes en la matriz.
data-size en CSS 78/130 Consistente con los subsets declarados; 10 fallos de paridad de chips (D-7.4) son de demo, no de receta.
motion tokens propios 37/130 El resto usa presets/signatures generados — el diseño esperado.
props físicas left/right 5/130 Todas son APIs de colocación (EID-3).

Estado por reglas de la matriz de aceptación (fresco, 2026-07-10): R-4.x prácticamente limpio (1 sola violación viva: badge R-4.3) y R-2.7 con 2 warns (format-date font-size 1em ×2 · relative-time line-height 1.2 — probablemente identidades anotables). El backlog real de los 32 NEEDS-WORK es demo/README: D-4.3 ×22 (botones ▶ del tab Sema), F-1.1/F-1.4 ×10 (secciones Baseline/Gaps), D-7.4 ×10 (paridad de chips), D-1.5 ×10 (traza MutationObserver), R-1.3 ×9 (data-readonly sin estilo), R-1.5 ×7, D-3.1 ×7, F-1.2 ×6, F-1.5 ×5, D-1.2 ×4, R-1.4/R-1.2/A-1.4 ×3.


6. Vulnerabilidades (lectura de seguridad)

No se encontró superficie explotable por usuario final en el núcleo:

  • Inyección: sin {@html}/innerHTML dinámicos en morfo/sema/soma/ active-uix core; los live-regions escriben textContent. La única interpolación sin escape es MOR-1 (selectores construidos por el AUTOR; se vuelve relevante solo si una app construye cascade rules desde datos de usuario — p. ej. un editor de temas sonoros: conviene cerrar con CSS.escape antes de que exista ese consumidor).
  • CSS injection vía theming runtime: setCssVariables valida nombres contra el contrato (strict por defecto) y es transaccional; los VALORES no se sanean (un valor podría cerrar el bloque }... — mitigado porque el render pasa por el serializador propio; no se observó escape explícito de } en valores: recomendación de endurecer el serializador de renderCssVariables rechazando ;} embebidos, mismo rango P2).
  • Persistencia: EidosConfigDocument valida kind/version/options sin compat silenciosa — hidratar JSON manipulado degrada a error de validación, no a estado a medias.
  • Unhandled rejections (SEM-2) son la debilidad de robustez más real en producción (pueden tumbar procesos en SSR/node si un trigger void falla con engine presente y target desmontado).

7. Carencias de personalización (gap analysis por eje)

Veredicto global: la superficie de personalización es excepcionalmente ancha (pocas librerías exponen color+tipografía+espacio+forma+profundidad+ gradientes con builders runtime y contrato introspectable). Las carencias son periféricas pero reales:

Eje Estado Carencia concreta
Color (escalas/roles/slots/alphas) ✅ completo + runtime + CSS-only themes THM-2: paleta 33 por instancia solo en 2/17 componentes. Layer 4 (--{c}-{role}-{slot}) sigue sin consumidores — la propia FAQ la marca candidata a colapso "si en 6 meses nadie la usa": toca decidir.
Tipografía ✅ fluid + tracking/leading/features/measure + fonts config Motor de fuentes variables listo pero assets aún TTF estáticos (capacidad no ejercida, rfc-typography §5); --size-{k}-font-letter-spacing emitido y sin consumir por recetas de control (pendiente declarado); divergencia de 2 px en label lg segmentado (pendiente documentado en §5).
Espaciado / densidad / scaling ✅ 3 ejes compuestos + applySpacing Densidad = 3 niveles fijos (retunables en magnitud, no ampliables en número); cambiar la participación de scaling exige config+regeneración (no runtime) — ambas son decisiones de diseño declaradas, no bugs.
Forma ✅ smoothing/familias/nesting + applyShape Nesting a full solo esquinas superiores (límite geométrico documentado).
Profundidad ✅ planes config-driven + frost + applyDepth Token scrim emitido sin regla que lo consuma (backdrop sigue per-component); --depth-{plane}-z expuesto con 0 consumidores (open cage declarado — vigilar que no fosilice).
Motion ✅ dos momentos + presets tipados extensibles + loops + reduce Presets JS no serializables (registro directo, documentado). Sin carencia real.
Sonido / háptica ✅ tunings + packs + preferences reduce/off Un tema no puede re-sonorizar sin código: los packs son TS, no config del tema (coherente con D.7 "samples = recursos", pero significa que la personalización sonora es de app/desarrollador, no de theme distribuible).
Focus ring ✅ --focus-ring-* parametrizado EID-1 (modelo dividido fundación vs canon §32).
State layer ✅ config primitives.state —
Touch target Constante 44 px No temable por diseño declarado (§37: "hard ergonomic constant"). Coherente; anotar en gap solo como decisión.
Z-index / bandas ✅ config + banda overlay —
Breakpoints / container ✅ config vía ActiveDom → tokens + @container Container queries: eje temable con 0 consumidores ("open cage", tsc.md §container) — mismo riesgo de fosilización que scrim/depth-z.
Gradientes ✅ tokens + angles + applyGradients + capstone Solo deriva documental: reference §10 y channels.md §5 omiten el eje gradient que ThemeSeed ya compone (active-eidos.svelte.ts:219-232).
Iconografía ✅ --icon-size-*/stroke + glifos spin-field temables No hay noción de "icon set" temable por config (el tema no puede sustituir el set de iconos; decisión razonable, registrar como límite).
Scrollbars Parcial scroll-area tiene receta propia, pero no hay theming sistémico del scrollbar nativo global (colores de scrollbar-color/webkit fuera del contrato).
Selección de texto / caret / cursor ❌ La fundación no emite ::selection, caret-color ni tokens de cursor; los cursores están cableados por arquetipo (pointer/grab/not-allowed). Si "todos los aspectos" es la vara: son los tres huecos sistémicos visibles.
Dark/light/HCM/contraste ✅ —
RTL ✅ lógico en flujo + getDirectionalKeys EID-3 (placement físico — pendiente de doctrina explícita) → cerrado 2026-08-15: dos rejillas nombradas (Position física / LogicalPosition lógica) con regla de elección en sus typedoc y en el canon generado.
Tema distribuible ✅ CSS-only vía contrato Límite deliberado (§19): ThemeDefinition no expone recipes — un tema no puede ajustar tokens per-component (solo el app en boot). Está razonado (portabilidad perceptual); consecuencia práctica: un "marketplace de temas" no podría re-tunear componentes sin pasar por el app.

8. Documentación: deriva y arquitectura del corpus

El corpus es de una calidad inusual, y precisamente por eso sus grietas son sistemáticas y vale la pena listarlas:

  • DOC-1 · P1 — el canon depende de material de auditoría/deprecated. CANON.md (§3 intent-assignment) y sema.md (§expression) citan docs/audit/components/_veredictos.md como registro canónico de veredictos; component-guide.md (cabecera) y src/uix/eidos/components/README.md declaran que la fuente de verdad del build-contract es el §13 de docs/old-deprecated/ARCHETYPE_COHERENCE_AUDIT_2026-06-19.md. La regla del propio corpus es "process/audit ≠ source of truth": el contenido canonizado debe migrar a E1/E2 y dejar el audit como historia.
  • DOC-2 · P1 — el ancla editorial no existe en el repo. docs:check (WARN I6): README.md:138 → Disenando_lo_que_ocurre_HOMOGENEIZADO.pdf no resuelve, y CANON.md:295 cita v2_3.md que su propio frontmatter declara inexistente. En el working tree hay docs/Disenando_lo_que_ocurre_FINAL.{docx,pdf} sin trackear — hay una edición FINAL (426 págs., la que book-map.md ya referencia) sin reconciliar con los punteros del corpus.
  • DOC-3 · P2 — enlaces muertos tolerados: THEMING_AUDIT_2026-06-01.md citado 4 veces (reference.md:1206,1418 + changelog ×2) y borrado; sema.md:616 → audit/sema-findings.md; 7 rutas de demos de animación en MOTION_SERVICE_RFC.md. El guard existe pero a severidad WARN — nunca bloquea.
  • DOC-4 · P2 — secciones desfasadas puntuales: la cadena de resolución de hold de sema.md sigue citando SEMA_MAP.families[*].hold (eliminado; sema-map.ts:38-43 lo confirma); notes.md FAQ defiende y deja como "pendiente" el scope event:* que reference §13/§14 y motion.md declaran obsoleto; reference §10 y channels.md §5 omiten gradient en el capstone; demo-authoring cita el helper inexistente paletteScaleDecls.
  • DOC-5 · P3 — recuentos fósiles: "~95 components" y "62 exhaustive" (reference §11), "all 25 soma components" y "66 morfos" (component-guide), "31 scales" (color README / comentario ActiveEidos) frente a los reales (134 auditables, 140 morfos, 33 escalas). La regla anti-números-frágiles de authoring.md no se aplicó retroactivamente.
  • DOC-6 · P3 — menudencias: numeración duplicada §4.11 en component-audit.md; su audit-log congelado en 2026-05-22 con "Known gaps" pendientes dentro de una guía binding; dos TODO(reconcile) huérfanos (normas N-6/N-7 de PENDIENTES.md borrado; tracker pendiente_color_demos.md borrado); authoring.md tabla E2 apunta a eidos/TSC.md (stub) en vez de canon/tsc.md; eidos/components/README enlaza ../../air-old/ (rama muerta).

9. Tabla de severidades

ID Severidad Resumen Dónde
MOR-4 P1 card-group sin morfo → invisible a toda la maquinaria de aceptación eidos/components/card-group/ · component-audit.ts:192
SEM-1 P1 Dos políticas de prioridad de live-region divergentes runtime.svelte.ts:725 vs chans/announce.ts:55
SOM-5 P1 field 0 eventos (A-3.1) · float-panel 28 acciones/6 eventos (A-3.7) matriz de aceptación 2026-07-10
EID-1 P1 Anillo de foco de fundación en box-shadow vs §32 outline canónico; condicionado por reset del app archetypes.css:138-167
THM-1 P1 6 CSS secundarios fuera del escaneo R-* (con violaciones dentro) component-audit.ts:698,756
THM-2 P1 Paleta 33-escalas por instancia solo en button+toggle (2/17) generated/base.css (census)
DOC-1 P1 Canon E1/E2 dependiente de docs audit/deprecated CANON §3 · component-guide cabecera
DOC-2 P1 Ancla editorial (PDF del libro) inexistente; edición FINAL sin reconciliar docs:check I6 · git status
SEM-2 P2 Errores de canales silenciosos sin logger; unhandled rejections en void trigger engine.ts · runtime.svelte.ts:670
SEM-4 P2 Packs dormant (6) + close polimórfico inerte (5) — firma declarada, no emitida book-deviations D.6/D.11
SOM-1 P2 await handler() contradice el contrato de handler síncrono runtime.svelte.ts:743
SOM-2 P2 keydown corta en match sin handler runtime.svelte.ts:562
SOM-3 P2 7 setTimeout + 1 addEventListener fuera de la doctrina gestionada soma layers/providers
SOM-4 P2 Backlog de providers re-declarando attrs morfo, sin censo ni guard component-guide §Part props
MOR-1 P2 semaSelector sin escape/validación en matchers string selectors.ts:141-155
EID-2 P2 Literales de opacidad en la fundación sin token/anotación archetypes.css:52,111,129,222
THM-3 P2 Suite eidos en rojo (canario censo toggle sin voltear) lint.test.ts:110
THM-4 P2 1150 selectores de contrato sin consumidor CSS, sin doctrina eidos-lint-all
THM-5 P2 17 !important sin regla que los gobierne catálogo
THM-7 P2 Ejemplo de sema.md con selector manual contra su propia regla sema.md §packs
DEP-1 P2 clsx fantasma (importado, no declarado) soma/props/props.ts:1
DEP-2 P2 ogl declarado como dependencia y sin un solo import package.json:60
SEC-1 P2 Endurecer serializador de valores en renderCssVariables (;} embebidos) lib/ render de variables
DOC-3/4 P2 Enlaces muertos ×15 (WARN tolerado) + secciones stale (holds, event:*, gradient, paletteScaleDecls) docs:check · sema.md · notes.md
MOR-2/3 · SEM-3 · AUX-1/2 · EID-3/4 · THM-6 · DOC-5/6 P3 Menores: duplicación marker, no-op cache, timer fantasma, triple live-region, disabledDom mixto, placement físico, recuentos fósiles, radius-full 1px, §4.11 duplicado, TODOs huérfanos ver secciones
GAP-* — Carencias de personalización de §7 (selection/caret/cursor/scrollbar global, sonido por tema, layer-4, container/scrim/depth-z sin consumidores, assets variables) §7

Sin P0. Nada de lo encontrado rompe el contrato público ni expone al usuario final; los P1 son incoherencias internas con impacto real en a11y, enforcement o promesas de personalización.


10. Nota final de método

Todo lo afirmado tiene evidencia en ruta:línea o en salida literal de los guards ejecutados el 2026-07-10 (component:audit 102/32/0 · eidos-lint-all 0 inválidos · vitest src/uix/eidos 305/306 · docs:check 0 errors/15 warns). Este documento analiza y no prescribe: la priorización de qué corregir, en qué orden y con qué alcance es decisión del autor del framework.

Powered by TurnKey Linux.