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/src/uix/auditoria-kimi.md

13 KiB

Auditoria Arquitectonica UIX — Kimi (2026-05-12)

Alcance

Auditoria de las capas ActiveUix, Morfo, Soma, Sema y Eidos. NO se analizaron los componentes internos de Soma ni Eidos.


Estado Codex 2026-05-13

Esta auditoria queda parcialmente cerrada por fases en la rama active-uix:

  • ActiveUix ya no expone ni crea Eidos; ActiveEidos se crea aparte.
  • ActiveEidos no crea ActiveDom propio y falla si necesita DOM real y no se le entrega una superficie valida.
  • Provider abstract class fue eliminada; queda solo el contrato de opciones.
  • Morfo.langs se renombro a Morfo.translations.
  • VisualChannel posee la proyeccion data-event-*; EngineSemantic solo orquesta prepare/handle/cleanup de canales.
  • Sema ya no transporta motion, color ni presence en el mapa canonico.
  • SoundChannel no registra listeners globales en el constructor.
  • ActivePrefsDomProjection escribe dir, data-motion, data-sound y data-haptic; ActiveEidos escribe data-theme, data-mode y data-density.
  • La auditoria DOM P1 queda documentada en src/uix/dom_audit.md.

Pendiente vivo: terminar la generacion/retirada de CSS legacy de Eidos. createAttrs queda como selector helper, registerContract ya no se usa desde Soma, y arts/frontend queda como servicio legacy opt-in fuera del boot UIX.


1. Errores arquitectonicos criticos

1.1. Sema viola su propia frontera: conoce DOM y concerns visuales

Archivo: src/uix/sema/engine.ts (lineas 113, 202, 232)

Problema: EngineSemantic crea un DomSignalProjector por defecto y es el quien escribe data-event-* en el DOM durante emit. La doctrina dice que Sema es "registry + dispatch" y que VisualChannel materializa la senal, pero en el codigo el engine proyecta antes de llamar a los canales. El VisualChannel solo hace sleep(hold); no controla la proyeccion.

Impacto: El canal visual no es autonomo. El engine esta acoplado a la idea de "escribir en el DOM", lo que rompe la regla Sema no conoce DOM.

Fix propuesto: Mover projector.project() y handle.cleanup() dentro de VisualChannel.handle(). El engine solo despacha la senal; el canal visual gestiona su propio plano DOM.


1.2. Acoplamiento historico de Eidos/Semantic en ActiveUix

Estado 2026-05-13: resuelto por retirada de ambos acoplamientos. ActiveUix ya no expone eidos ni el alias semantic; el motor perceptivo se expone como events?: EngineSemantic y ActiveEidos.create(...) crea el scope visual. ActiveEidos solo es necesario para CSS runtime o authoring de configuracion.

Archivo: src/uix/active-uix/types.ts (linea 115), src/uix/active-uix/active-uix.svelte.ts (lineas 419-425)

Problema historico: En types.ts, semantic: EngineSemantic | undefined (opcional), pero el bridge visual era obligatorio. Sin embargo, createActiveUix({ eidos: false }) era valido, lo que hacia que leer el bridge visual fallase en runtime. Esto contradecia la degradacion graceful que se aplicaba al motor perceptivo.

Impacto historico: Un componente que intentaba leer el bridge visual en modo headless (sin CSS) crasheaba; uno que leia el motor perceptivo simplemente saltaba la senal.

Fix aplicado: retirar ambos acoplamientos de ActiveUix: events queda como motor perceptivo opcional y ActiveEidos se crea aparte cuando la app lo necesita.


1.3. ActiveEidos puede crear su propio ActiveDom sin permiso de ActiveUix

Archivo: src/uix/eidos/active-eidos.svelte.ts (lineas 258-263)

Problema: ActiveEidos.#getDom() llama createActiveDom() si no recibe dom. Esto crea un segundo servicio DOM con sus propios listeners de resize/scroll, rompiendo el principio de que ActiveUix es el unico dueno del DOM.

Impacto: En modo dom: false o attach sin dom, ActiveEidos silenciosamente instancia DOM real en lugar de usar el disabledDom compartido.

Fix propuesto: ActiveEidos debe recibir siempre un dom explicito (incluso si es disabledDom). Si falta, debe lanzar error o degradar a no-op, nunca crear uno propio.


1.4. Provider abstract class sigue vivo a pesar de que la doctrina dice "dropped"

Archivo: src/uix/soma/provider/provider.svelte.ts Documento: src/uix/soma/SOMA_ARCHITECTURE.md §15

Problema: SOMA_ARCHITECTURE.md dice "Provider inheritance dropped", pero provider.svelte.ts sigue exportando abstract class Provider<S>. Esto genera dos patrones conviviendo: el legacy (herencia) y el nuevo (SomaRuntime directo).

Impacto: Confusion para nuevos desarrolladores. Doble mantenimiento.

Fix propuesto: Depreciar/Eliminar Provider. Todos los componentes deben migrar a SomaRuntime + funciones helper puras.


1.5. Duplicacion de logica de prefs entre active-uix/prefs.ts y eidos/active-eidos.svelte.ts

Archivo: src/uix/active-uix/prefs.ts (lineas 55-69, 71-78) Archivo: src/uix/eidos/active-eidos.svelte.ts (lineas 364-375)

Problema: readPrefsSlot, createPrefsPreferenceSource, readActiveUixPrefsSlot y createLocaleSourceFromPrefs hacen esencialmente lo mismo: leer slots de ActivePrefs de forma defensiva.

Impacto: DRY violation. Si cambia la forma de ActivePrefs, hay que tocar dos sitios.

Fix propuesto: Extraer un prefs-bridge.ts compartido en $libs/prefs o $active-uix.


1.6. Morfo incluia langs — mezcla de contrato estructural con datos de i18n

Archivo: src/uix/morfo/types.ts (linea 604), src/uix/morfo/registry.ts (lineas 33-47)

Actualizacion 2026-05-13: el campo publico se ha renombrado a Morfo.translations y el registro a connectMorfoTranslations, para distinguir catalogo declarativo de servicio runtime langs.

Problema original: Morfo.langs era un catalogo de traducciones embebido en el contrato. Morfo deberia ser "DNA" puramente estructural. Las traducciones son volatiles y no forman parte del contrato DOM.

Impacto: Cada vez que cambia una traduccion, el fingerprint del morfo cambia, disparando re-registros innecesarios en connectMorfoTranslations.

Fix propuesto: Separar MorfoLangCatalog en un registry aparte. El morfo puede tener una referencia (langCatalogId), pero no el arbol de strings.


2. Optimizaciones identificadas

2.1. readBindings en SomaRuntime genera garbage objects en cada tick

Archivo: src/uix/soma/runtime.svelte.ts (lineas 213-239)

function readBindings(reg, sources): MorfoBindings {
	const states = {};
	const props = {};
	const parts = {};
	// ... for-in + asignacion en cada $effect
}

En un $effect reactivo, esto corre en cada cambio de estado. Para 20 partes, son ~60 objetos nuevos por frame.

Optimizacion: Usar un objeto de bindings mutable reutilizado (pool) o cambiar readBindings a lectura lazy por plan, evaluando solo las fuentes que el evalAttrPlan necesita.


2.2. EngineSemantic clona SEMA_MAP en cada instancia

Archivo: src/uix/sema/engine.ts (linea 107)

this.map = applyMapOverrides(SEMA_MAP, opts.overrides?.runtime);

En tests con muchas instancias, deep-clone del mapa perceptivo completo es costoso.

Optimizacion: Hacer SEMA_MAP inmutable y aplicar overrides como un layer de proxy/lookup en tiempo de resolucion, no en construccion.


2.3. SoundChannel registra listeners globales en el constructor incluso si nunca se usa

Archivo: src/uix/sema/chans/sound.ts (eager-init)

Eager-init de AudioContext es correcto para la politica de autoplay, pero registrar un listener capture-phase en document al crear EngineSemantic es side-effect agresivo.

Optimizacion: Mover el registro del listener a la primera llamada a emit() que requiera sonido, o usar un singleton lazy para SoundChannel.


2.4. stableStringify en registerMorfo para fingerprint de langs

Archivo: src/uix/morfo/registry.ts (lineas 97-109)

function fingerprintLangNode(node: LangNode): string {
	return stableStringify(node); // JSON-like recursivo
}

Esto se ejecuta en cada registerMorfo. Para catalogos grandes, es O(n) en cada registro.

Optimizacion: Usar WeakMap + referencia de objeto si los catalogos son estaticos (lo son), o cachear fingerprints por identidad de objeto.


3. Propuesta de mejor arquitectura

3.1. Contrato minimo tipado por capa

El documento active_architecture.md admite que falta la tabla de contratos. Propongo definirla como tipos en src/uix/contracts.ts:

export interface UixServiceContract {
	langs: ActiveLangs; // obligatorio
	dom: ActiveDom; // obligatorio (puede ser disabledDom)
	events?: EventEngineEmitter; // ornamental
	eidos?: ActiveEidos; // ornamental
	format?: ActiveFormat; // ornamental
	prefs: ActivePrefs; // obligatorio
}

ActiveUix deberia implementar esta interfaz y createActiveUix deberia validar contra ella en lugar de logica if/else dispersa.


3.2. Sema debe purgarse de dimensions visuales (motion, color, presence)

La doctrina de Phase 5 del codex refactor es correcta pero incompleta. Propongo:

  • SemaChannelSignatures solo debe tener: sound, haptic, y canales futuros (a11y, voice).
  • El hold visual debe vivir en VisualChannel como hold: number | 'brief' | 'noticed'..., resuelto desde SEMA_MAP.families[*].hold (ya existe), no desde effective.motion.duration.
  • motion, color, presence se eliminan del EffectiveSignature. Eidos reacciona a data-event-family + data-event-intent directamente en CSS.

Esto reduce drasticamente el acoplamiento Sema→Eidos.


3.3. Unificar scopes redundantes

Estado 2026-05-13: el adaptador Eidos ya fue eliminado. ActiveEidos queda como scope visual real y no existe un engine visual separado. Soma sigue como scope headless mientras se decide si debe conservarse o aplanarse.

La deuda historica era:

Soma y el antiguo adaptador visual repetian una superficie casi identica: uix, dom, events, langs y helpers de capa. Esa duplicacion ya no debe volver.

Propuesta viva: eliminar solo los adaptadores que no aporten contrato propio. Los componentes no deben leer la superficie completa de ActiveUix; deben consumir scopes o vistas estrechas:

// En un provider headless
const uix = getActiveUix();
const runtime = uix.runtime(morfo, sources);

// En un wrapper visual
const uix = getActiveUix();
const eidos = ActiveEidos.require();

Si se quiere evitar que los componentes toquen ActiveUix completo, usar interfaces de capa:

export interface SomaServices {
	dom: ActiveDom;
	events?: EventEngineEmitter;
	langs: ActiveLangs;
}
export interface EidosServices {
	dom: ActiveDom;
	eidos?: ActiveEidos;
	langs: ActiveLangs;
}

Esto elimina una clase entera de indireccion y la dependencia a runed/Context extra.


3.4. SomaRuntime desacoplado de Svelte

Nota 2026-05-13: la API publica registerPart fue eliminada. La superficie canonica de registro queda en SomaRuntime.part(...); esta observacion queda como contexto historico de la auditoria.

registerPart usa $effect directamente. Para testear el runtime sin un entorno Svelte, o para reutilizarlo en otro framework, propongo inyectar un Scheduler:

export interface EffectScheduler {
    run(fn: () => void): () => void; // return cleanup
}

export function createSomaRuntime(
    morfo: Morfo,
    sources: SomaRuntimeSources,
    scheduler: EffectScheduler = svelteScheduler
): SomaRuntime { ... }

Esto permite testear con nanostores, mobx, o incluso manual callbacks.


3.5. Eliminar CSS legacy (contracts/, tokens/, themes/base/)

Estos directorios son deuda de migracion. ActiveEidos ya puede generar todo el CSS estatico y de theme desde la configuracion viva. Mantenerlos:

  • Duplica fuentes de verdad (engine vs CSS manual).
  • Hace que eidos-lint sea necesario.
  • Incrementa bundle size.

Estado 2026-05-13: existe el primer artefacto generado, src/uix/eidos/generated/base.css, producido con npm run generate:eidos-css desde EidosConfig base. Aun no se borran contracts/, tokens/ ni themes/base/ porque varias recipes y aliases historicos dependen de ellos. El siguiente paso es separar recipes estables de aliases/contratos retirables.


4. Preguntas para priorizar

  1. ¿Prioridad inmediata? ¿Cerrar primero la tabla de contratos minimos (active_architecture.md §102) antes de cualquier refactor?
  2. ¿Eliminacion de Provider legacy? ¿Hay componentes que aun no se migraron al patron SomaRuntime directo? ¿Se puede deprecar Provider abstracto ya?
  3. ¿Sema visual cleanup? ¿Aceptar que sea un cambio grande? Implica mover motion/color/presence fuera de SEMA_MAP y ajustar VisualChannel.
  4. Eidos opcional: resuelto. ActiveUix no expone el bridge visual; ActiveEidos se crea aparte.
  5. ¿Scopes Soma/Eidos? ¿Eliminar ambas clases y dejar que los componentes lean getActiveUix() directamente, o mantener la separacion simbolica?

Auditoria generada el 2026-05-12. Estado de referencia: rama active-uix.

Powered by TurnKey Linux.