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 eventosemergeemiten ya; el pack sema (silencioso por doctrina D.7) está vivo por primera vez.presentpasó asequence: 'post'— con'pre'el hold de ~240 ms gateaba el montaje. - D-2 ·
card→ debe poder emitir. Se le dio un puente headless ensoma/components/card/(la forma demenu-dial/onion-menu): el root de eidos posee estado y render, soma sólo posee elSomaRuntimeque emitecommit-select. Registrado enSOMA_INTERNAL_PRIMITIVES. - Colapso de tipos de props → migración completa. Ver abajo.
Decisiones pendientes (bloquean trabajo)
D-3 ·— RETIRADA 2026-07-29, era un falso hallazgo. El puerto SÍ está satisfecho:AgentTimersPort.oncedefineActiveAgent()(arts/active-app/service-factories/agent.ts) adaptacore.timers.scheduleaonceen 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 reglasF-1.xdesde 2026-07-07 (scripts/component-audit.grandfather.json), sin quecompletion-checklist.mdlo mencione. O se declara en el checklist, o se quema por lotes. Card.intent. El morfo declaraintent: { fromProp: 'intent' }yCardno tiene esa prop — el evento sale siempreneutral. Añadir prop pública (ampliación de API, exige tu firma por A32) o quitar elfromProp. Derivarlo decolorestá descartado: el canon prohíbe inferir semántica.- READMEs de
backdropymockup(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.sizechocaba 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
checksin 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:
- atributos HTML nativos (sólo cuentan las props declaradas por el autor);
- 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: hiddencongelaIntersectionObservery 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
EBUSYen la caché SSR de Vite y hace fallar suites largas de forma intermitente (panel-schema.test.tsfalló y pasó aislado). Stagear siempre rutas explícitas, nuncagit add -A.
Auditoría por componente — hallazgos abiertos
De los 165 morfos, 138 limpios. Lo que sigue sin cerrar:
— RETIRADO 2026-07-29, eran falsos positivos.scope↔ realidad (5)avatar-group,path-traceyrotate-alignSÍ 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:metricsdeclara['sema','eidos']teniendo puente soma, mientrasmenu-dial/onion-menu/card— misma forma de puente — sí declaran'soma'. El comentario decontracts.test.tssugiere que la omisión fue deliberada; decidir si se unifica.— RETIRADO 2026-07-29, el recuento no vale. Medía si existekind: 'public'mal aplicado (29 morfos)<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,iconla parte Icon,childrenla 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 depin-inputdeclara una parteInputmientras eidos exponeCell— 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_TESTSque 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:
- Los 29 morfos con
kind: 'public'irreal — ahora que §Step 2 documenta'private', decidir componente a componente entre degradar aprivateo exponer el sub-componente. Empezar por los pequeños y cerrados (badge,mockup,scroll-frames,spinner,sticky), dejarpalabrasychronosfuera (track activo). - La decisión de
lint(arriba) — sin ella el formateador no da señal. - 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.