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

10 KiB

CONTINUE — auditoría del sistema (2026-07-29)

Punto de arranque, no diario. Lo que hay que saber para seguir mañana.

Estado de los guards

Guard Estado
npm run check 0 errores en src/ · los 64 restantes son de web/ (línea base preexistente)
npx vitest run --project=server 3701 / 3701 · 262 ficheros
npm run docs:check 0 errores, 0 warnings
npm run component:audit 161 PASS · 2 NEEDS-WORK · 0 BROKEN
npm run translations:check corre; 0 errores fuera del track palabras
arts · packs · blocks · agent · eidos-lint limpios

Lo que decidió el usuario y quedó hecho

  • D-1 · tooltip → cablearlo. Sus 3 eventos emerge emiten ya; el pack sema (silencioso por doctrina D.7) está vivo por primera vez. present pasó a sequence: 'post' — con 'pre' el hold de ~240 ms gateaba el montaje.
  • D-2 · card → debe poder emitir. Se le dio un puente headless en soma/components/card/ (la forma de menu-dial / onion-menu): el root de eidos posee estado y render, soma sólo posee el SomaRuntime que emite commit-select. Registrado en SOMA_INTERNAL_PRIMITIVES.
  • Colapso de tipos de props → migración completa. Ver abajo.

Decisiones pendientes (bloquean trabajo)

  • D-3 · AgentTimersPort.once — RETIRADA 2026-07-29, era un falso hallazgo. El puerto SÍ está satisfecho: defineActiveAgent() (arts/active-app/service-factories/agent.ts) adapta core.timers.schedule a once en la raíz de composición, que es exactamente el patrón previsto. Type-checkea. No hay nada que decidir.
  • D-4 · El grandfather de component:audit. 134 componentes exentos de las reglas F-1.x desde 2026-07-07 (scripts/component-audit.grandfather.json), sin que completion-checklist.md lo mencione. O se declara en el checklist, o se quema por lotes.
  • Card.intent. El morfo declara intent: { fromProp: 'intent' } y Card no tiene esa prop — el evento sale siempre neutral. Añadir prop pública (ampliación de API, exige tu firma por A32) o quitar el fromProp. Derivarlo de color está descartado: el canon prohíbe inferir semántica.
  • READMEs de backdrop y mockup (los 2 NEEDS-WORK). Exigen tabla comparativa con ≥3 referencias, y A32 reserva esa firma para el usuario.
  • CardGroup.Item.rounded. Prop documentada e inerte: dentro de un grupo el radio lo fija [data-card-group] [data-card] (0,2,0) para mantener el anidamiento concéntrico. O se retira la prop, o el grupo respeta el ítem.

El hallazgo grande: colapso de uniones de props

Un Props de eidos es { …props propias… } & <atributos nativos>. Cuando el elemento declara un atributo homónimo, la intersección funde ambos y una unión estrecha contra el string nativo colapsa a string.

Causa: Without<T, U> = Omit<T, keyof U> invocado como Without<T, {}> (Omit<T, never>, un no-op) 433 veces en soma; sólo 3 con un argumento real.

Estaba invisible para svelte-check: ensanchar un tipo no es un error, es una garantía perdida.

  • Medido: 95 props en 72 componentes. <Avatar color="nonsense"> compilaba. ComboboxInput.size chocaba con el <input size> numérico y era inusable.
  • Migrado con codemod sobre AST (nunca regex), restringido a los nombres que el sistema reclama (color, size): 92 tipos en 73 ficheros + 2 a mano.
  • Resultado: 95 → 0, con check sin moverse.
  • Garantía: src/uix/eidos/prop-surface.test.ts (PROP-1) compara los literales de la anotación del autor contra los de la propiedad pública. Verificado que falla reintroduciendo el defecto.

Lo que queda de esta línea

Las 27 colisiones heterogéneas que el codemod detectó y NO tocó: class ×9, children ×5, style, role, src, viewBox, onload/onerror, y props de layout (gap, align, justify, columnGap, rowGap, grow, direction). No son props enum, así que PROP-1 no las ve. Restar style o class de la superficie de paso no es obviamente correcto como sí lo era restar color: piden revisión caso a caso.

El patrón de fondo de toda la auditoría

El framework valida que lo escrito sea válido, no que lo declarado se cumpla. Y sus guards fallaban ABIERTOS. Cuatro instancias, todas arregladas:

Guard Cómo fallaba
translations:check crasheaba en cada ejecución de su historia (un stripper de comentarios borraba // dentro de strings)
soma-attr-audit agotaba el timeout de 5 s → sin veredicto, verde por omisión
component-audit D-7.4 continue mudo cuando el tipo no resolvía
component-audit R-1.1 el regex /\[data-motion\b/ casa con [data-motion='reduce'] → PASS por el motivo equivocado (documentado como excepción en motion/README.md)

Regla adoptada: un guard que no puede evaluar tiene que decirlo. El informe de component:audit tiene ahora un bloque «Not verified» y un recuento en el resumen. Precedente de la casa: el SKIP explícito de morfo:check.

Herramienta nueva reutilizable

scripts/prop-unions.ts — resuelve uniones de props preguntándole a TypeScript en vez de con regex sobre el fuente. Un Program por ejecución (~3 s), compartido. Maneja dos trampas medidas:

  1. atributos HTML nativos (sólo cuentan las props declaradas por el autor);
  2. colapso por intersección (resuelve desde la anotación propia, no desde la propiedad fundida).

Con él, los puntos ciegos de D-7.4 pasaron de 124 a 3.

Trampas del entorno (leer antes de verificar nada)

  • El pane del IDE corre como pestaña en segundo plano: visibilityState: hidden congela IntersectionObserver y rAF. Las revelaciones por viewport no se pueden verificar ahí — usar Playwright.
  • Un dev server frío puede no servir el entry cliente. Me dio un falso «0 revelados» y estuve a punto de reportar una regresión inexistente. Verificar contra un servidor caliente y comprobar document.querySelector('script[type=module]').
  • Árbol compartido entre sesiones. El dev server ajeno provoca EBUSY en la caché SSR de Vite y hace fallar suites largas de forma intermitente (panel-schema.test.ts falló y pasó aislado). Stagear siempre rutas explícitas, nunca git add -A.

Auditoría por componente — hallazgos abiertos

De los 165 morfos, 138 limpios. Lo que sigue sin cerrar:

  • scope ↔ realidad (5) — RETIRADO 2026-07-29, eran falsos positivos. avatar-group, path-trace y rotate-align SÍ están implementados en eidos: el framework co-loca sub-componentes dentro del directorio del padre (avatar/avatar-group.svelte, proof-of-human/path-trace.svelte). Mi heurística asumía «directorio == kebab» y los leyó como ausentes. Queda un caso real y menor: metrics declara ['sema','eidos'] teniendo puente soma, mientras menu-dial / onion-menu / card — misma forma de puente — sí declaran 'soma'. El comentario de contracts.test.ts sugiere que la omisión fue deliberada; decidir si se unifica.
  • kind: 'public' mal aplicado (29 morfos) — RETIRADO 2026-07-29, el recuento no vale. Medía si existe <Componente.Parte> y trataba las props/snippets como si no compusieran. Pero un primitivo de API plana controla sus partes así: <Badge dot> gobierna la parte Dot, icon la parte Icon, children la Label. Esas partes SON públicas. Mismo error que los otros dos falsos positivos del día: heurística de una sola vía dada por hecho sin abrir el código. Lo que queda por mirar es mucho más estrecho: partes que NINGÚN mecanismo del consumidor alcanza — candidatas vistas a ojo: Spinner.Track, Sticky.Sentinel, ScrollFrames.Viewport/Media/Loader, Mockup.Bar/Dot. Sin recuento fiable: hace falta cruzar partes contra props + snippets + sub-componentes, no sólo lo último. Hallazgo aparte y real: el morfo de pin-input declara una parte Input mientras eidos expone Cell — desajuste de nombre, no de visibilidad. Causa documental (corregida): morfo.md §Step 2 omitía 'private'. El criterio que quedó escrito ahí es el consumidor decide la parte, con independencia de la sintaxis; no «existe el sub-componente».
  • READMEs ausentes: soma menu-dial, metrics, onion-menu.
  • 13 providers soma sin test, y el guard NO_MISSING_PROVIDER_TESTS que dos documentos afirmaban no existe (los docs ya están corregidos).

npm run lint no da señal (hallazgo 2026-07-29)

lint es prettier --check . y está rojo, y lo ha estado siempre:

  • En un checkout Windows (core.autocrlf=true) fallan ~2478 ficheros por finales de línea. Ese ruido es artefacto de entorno, no deriva.
  • Neutralizando el ruido (--end-of-line auto) quedan 1544 ficheros con deriva REAL de contenido: markdown que Prettier quiere con _cursiva_ en vez de *cursiva* y tablas reformateadas, más anchos de línea en TS.

O sea: el repo nunca ha pasado su propio formateador. No es un guard que falle abierto — falla a gritos, y por eso nadie lo mira; el efecto neto es el mismo. Decisión del usuario, porque el arreglo (prettier --write) toca 1544 ficheros de golpe sobre un árbol compartido entre sesiones. La alternativa menos invasiva es endOfLine: 'auto' en .prettierrc (mata sólo el ruido CRLF) o un .gitattributes con * text=auto eol=lf.

Corregido de paso, por ser una clase acotada: los 10 morfos que escribían expression: "family-default" con comilla doble en un corpus de 173 que usa comilla simple.

Por dónde empezaría mañana

Los dos primeros puntos de la versión original de este documento eran falsos (ver tachados arriba). Lo que queda, en orden:

  1. Los 29 morfos con kind: 'public' irreal — ahora que §Step 2 documenta 'private', decidir componente a componente entre degradar a private o exponer el sub-componente. Empezar por los pequeños y cerrados (badge, mockup, scroll-frames, spinner, sticky), dejar palabras y chronos fuera (track activo).
  2. La decisión de lint (arriba) — sin ella el formateador no da señal.
  3. D-4, el grandfather de 134 componentes.

Y la lección de método, porque me pasó a mí escribiendo este documento: un analizador heurístico que no puede resolver algo tiene que decir «no verificado», no emitir un hallazgo. Tres de los puntos de la primera versión salieron de emparejar nombres de directorio y los di por hechos sin abrir el código. Es exactamente el fallo que la auditoría denuncia en los guards.

Powered by TurnKey Linux.