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.

25 KiB

AUDIT_OPENCODE

Resumen ejecutivo

El ecosistema Active es un framework propio sólido y bien diseñado (8.2/10). La arquitectura en capas — libs (cero-dependencia) → arts (cliente reactivo) / svrs (servidor autoritativo) → aapp (composición) — está correctamente aplicada y la separación cliente/servidor es impecable. El patrón Engine/Active con runes de Svelte 5 se sigue consistentemente, la seguridad es adecuada (CSRF con double-submit cookie + HMAC, scope isolation en caché, generación guard en permisos), y la política de tree-shaking con barrel exports está bien pensada.

Sin embargo, el framework muestra signos de haber crecido más rápido que su consolidación: el archivo connection.ts tiene 865 líneas y merece ser partido, engine-auth.ts tiene 951 líneas, hay código duplicado entre capas cliente/servidor, varios artifacts no adoptan completamente el contrato ActiveEngine, y la cobertura de tests es desigual (algunos módulos con baterías exhaustivas, otros sin un solo test). La documentación de diseño (DESIGN_CONN.md) referencia archivos que ya no existen.

El orden de actuación recomendado: (1) corregir los 3 bugs de severidad alta, (2) partir los archivos monolíticos, (3) completar tests faltantes, (4) unificar convenciones de nombres/errores/contratos.


Hallazgos críticos

HC-1: Http engine nunca se libera en ActiveApp.dispose() — fuga de recursos

  • Archivo: src/arts/aapp/active-app.svelte.ts:110,264
  • Severidad: Alta | Clasificación: bug confirmado | Esfuerzo: Bajo (1 línea)
  • Explicación: createEngineHttp() se construye en línea 110 pero Http.dispose() no aparece en el cascade de dispose() (líneas 264-286). Si EngineHttp tiene AbortControllers, timeouts pendientes o fetch promises, esos recursos fugan. El orden correcto es Cache → Timers → Http → Frontend → Dom → Formats → Storage → Lang → Logger.
  • Propuesta: Añadir Http.dispose() entre Timers.dispose() y teardownPersistence().

HC-2: connection.ts (865 líneas) — monolito que viola el diseño declarado

  • Archivo: src/arts/conn/connection.ts
  • Severidad: Alta | Clasificación: refactor | Esfuerzo: Alto (4-6h)
  • Explicación: El archivo contiene state machine, transport lifecycle, heartbeat, reconnect, auth, buffering, channels, session bridge y browser lifecycle. DESIGN_CONN.md:440-457 declara explícitamente archivos separados (reconnect.ts, heartbeat.ts, backpressure.ts, ack.ts) que no existen. El diseño original se consolidó en un solo archivo, dificultando el mantenimiento y testing aislado.
  • Propuesta: Extraer reconnect.ts (líneas 332-357, 670-710), heartbeat.ts (359-392), buffer.ts (431-456), auth.ts (504-541), request.ts (543-577). Mantener connection.ts como orquestador.

HC-3: writeBatch fallback loop puede multiplicar entradas de fallo en logger

  • Archivo: src/arts/logr/engine-logger.ts:377
  • Severidad: Alta | Clasificación: bug confirmado | Esfuerzo: Bajo (30min)
  • Explicación: Cuando writeBatch no está definido, flushTransport llama a writeOne por cada item en el buffer. Si el transport falla en cada writeOne, se crea una entrada sintética de fallo POR CADA ITEM. Sin failureThrottleMs, esto multiplica el volumen de logs catastróficamente.
  • Propuesta: Registrar fallo a nivel de flush — si writeOne falla durante un flush batch, detener iteración y emitir una sola entrada de fallo para el batch.

HC-4: isPromiseLike implementado 3 veces con lógica inconsistente

  • Archivos: src/arts/conn/connection.ts:128 vs src/arts/sium/core/internals.ts:23 vs src/libs/standard-schema.ts:72
  • Severidad: Alta | Clasificación: bug confirmado | Esfuerzo: Bajo (15min)
  • Explicación: La versión en connection.ts usa 'then' in value que retorna true para objetos como { then: 42 } que NO son thenables, causando que sendFrame haga await de un no-promise. Las otras dos versiones usan typeof value.then === 'function' que es correcto. Tres implementaciones con firmas diferentes.
  • Propuesta: Todas las implementaciones deben importar desde $libs/standard-schema. Eliminar las locales.

Hallazgos medios

HM-1: Can.svelte no re-evalúa cuando cambia el contexto de permisos

  • Archivo: src/arts/perm/Can.svelte:30-49
  • Severidad: Media | Clasificación: bug confirmado | Esfuerzo: Bajo
  • Explicación: El $effect depende de action, resource, context, optimistic — pero NO del permissions context. Si se llama setPermissionsContext() después de montar <Can/>, el componente no re-evalúa.
  • Propuesta: Leer permissions.currentSnapshot.version dentro del effect como dependencia reactiva.

HM-2: Active auth re-lanza error crudo burlando la normalización segura

  • Archivo: src/arts/auth/active-auth.svelte.ts:185-186
  • Severidad: Media | Clasificación: bug confirmado / seguridad | Esfuerzo: Bajo
  • Explicación: catch (error) { lastError = normalizeClientError(error); throw error; } — re-lanza el error original, que puede contener stack traces o datos internos. Si el caller captura directamente en vez de leer Auth.lastError, recibe el error inseguro.
  • Propuesta: Lanzar AuthInvalidResponseError o AuthRequestFailedError con el mensaje normalizado, no el error original.

HM-3: revokeDevice no termina sesiones asociadas con DB adapter

  • Archivo: src/svrs/auth/engine-auth.ts:513-532
  • Severidad: Media | Clasificación: bug confirmado / seguridad | Esfuerzo: Medio
  • Explicación: revokeDevice() en el adapter de memoria sí revoca session bindings, pero el DB adapter (db.ts:108-112) solo actualiza el registro del dispositivo — las sesiones bindings quedan activas. Un dispositivo revocado podría mantener sesiones válidas.
  • Propuesta: Mover la lógica de revocación de session bindings al engine (no al adapter). Llamar sess.end() para sesiones asociadas al dispositivo revocado.

HM-4: signOutGlobal no revoca refresh token families

  • Archivo: src/svrs/auth/engine-auth.ts:306-334
  • Severidad: Media | Clasificación: bug confirmado / seguridad | Esfuerzo: Bajo
  • Explicación: signOutGlobal revoca session bindings pero NO las refresh token families del actor. Un refresh token emitido antes del logout global podría potencialmente rotar a nuevas sesiones. refresh-rotation.ts tiene revokeRefreshFamily pero no se llama.
  • Propuesta: Añadir store.revokeRefreshFamily() durante signOutGlobal.

HM-5: mono-lang.svelte.ts register() retorna tipo falseado

  • Archivo: src/arts/lang/mono-lang.svelte.ts:139-142
  • Severidad: Media | Clasificación: bug confirmado | Esfuerzo: Bajo
  • Explicación: register() crea ActiveLang<LangNode> pero lo castea as unknown as ActiveLang<LangNode & { [K in NS]: M }>. La instancia retornada no tiene conocimiento real del namespace — lang.t('shop.product') devolvería el path literal, no una traducción.
  • Propuesta: Hacer que mono-lang's register realmente mergee módulos en un schema interno, o tipar el retorno como ActiveLang<LangNode> sin pretensión de type safety.

HM-6: ActiveAppOptions inconsistente: sess pero no conn para factories

  • Archivo: src/arts/aapp/active-app.svelte.ts:155-261
  • Severidad: Media | Clasificación: simplificación / coherencia | Esfuerzo: Medio
  • Explicación: El constructor acepta sess como opción con onSignedOut(), pero conn (Connections) no tiene opción equivalente para inyectar configuración inicial. Esto fuerza a llamar App.createActiveConnections() sin poder preconfigurar. La asimetría con sess/auth/perm rompe el patrón de factories.
  • Propuesta: Aceptar connections?: Omit<ConnectionsOptions, 'timers' | 'logger'> en ActiveAppOptions.

HM-7: ActiveDom creado internamente en ActiveFrontend nunca se libera

  • Archivo: src/arts/fend/active-frontend.svelte.ts:82,224-229
  • Severidad: Media | Clasificación: bug confirmado | Esfuerzo: Bajo
  • Explicación: Cuando applyDom === true (default) y no se pasa dom, se crea ActiveDom interno que adjunta un resize listener a window. ActiveFrontend.dispose() no llama a dom.dispose(), filtrando el listener hasta que se cierre la página.
  • Propuesta: Guardar referencia al dom creado internamente y llamar dom.dispose() en el método dispose().

HM-8: ActiveSession y ActiveConnections no implementan el contrato ActiveEngine

  • Archivos: src/libs/active.ts, src/arts/sess/, src/arts/conn/
  • Severidad: Media | Clasificación: refactor / coherencia | Esfuerzo: Bajo
  • Explicación: ActiveEngine<TSnapshot, TError> es implementado por ActiveAuth, ActivePermissions, ActiveCache pero NO por ActiveSession ni ActiveConnections — ambos tienen loading, lastError, snapshot(), dispose() y onChange(). El contrato está a medio adoptar.
  • Propuesta: Extender ActiveSession y ActiveConnections con ActiveEngine o eliminar el contrato parcial y documentar que es solo para "network-augmented" artifacts.

HM-9: buildNumeralMap recomputado en cada parse() call

  • Archivo: src/arts/fmts/nums/engine-numbers.ts:37-44,119-123
  • Severidad: Media | Clasificación: optimización | Esfuerzo: Bajo
  • Explicación: Cada llamada a parse() invoca buildNumeralMap(locale) que crea Intl.NumberFormat, formatea un número constante y construye un Map iterando caracteres. Este mapa es constante por locale.
  • Propuesta: Cachear el numeral map por locale, similar al formatCache.

HM-10: Duplicación masiva de boilerplate Active en los 4 sub-módulos de fmts

  • Archivos: fmts/curr/active-currency.svelte.ts, fmts/dates/active-dates.svelte.ts, fmts/nums/active-numbers.svelte.ts, fmts/unts/active-units.svelte.ts
  • Severidad: Media | Clasificación: refactor | Esfuerzo: Medio
  • Explicación: Cuatro archivos comparten ~80% de estructura idéntica: version = $state(0), SvelteSet para listeners, notifyPreferences(), syncLocale(), unsubscribeLocale, dispose(). ~100 líneas cada uno con ~60 líneas de boilerplate.
  • Propuesta: Crear helper genérico createReactiveSubEngine<E>(engine, subs) en fmts/helpers.ts. Cada wrapper bajaría a ~30 líneas.

HM-11: unref pattern duplicado 3 veces

  • Archivos: src/arts/http/retry.ts:68, src/arts/http/timeout.ts:52,68
  • Severidad: Media | Clasificación: refactor | Esfuerzo: Bajo
  • Explicación: (id as unknown as { unref?: () => void }).unref?.() aparece 3 veces.
  • Propuesta: Extraer a tryUnref(handle: unknown) en $libs/timers.

Hallazgos menores

HL-1: libs/times/index.ts — módulo vacío (dead code)

  • Archivo: src/libs/times/index.ts
  • Severidad: Baja | Clasificación: bug confirmado | Esfuerzo: Bajo
  • Propuesta: Poblar con utilidades de tiempo o eliminar el directorio y alias.

HL-2: resolveDir duplicado entre arts/fend/locale-defaults.ts y libs/dom/locale.ts

  • Archivos: src/arts/fend/locale-defaults.ts:8-10, src/libs/dom/locale.ts:1-5
  • Clasificación: refactor | Esfuerzo: Bajo
  • Propuesta: Mover resolveDir y RTL_LOCALES a libs/dom/locale.ts. Re-exportar desde fend.

HL-3: disposedXxxMessage() duplicado en 3 locations

  • Archivos: arts/perm/helpers.ts:3-5, svrs/perm/helpers.ts:3-5, svrs/cach/helpers.ts:3-5
  • Clasificación: refactor | Esfuerzo: Bajo
  • Propuesta: Extraer a $libs/active como disposedMessage(artifact, method).

HL-4: isLangBranch vive en helpers.ts pero pertenece a guards.ts

  • Archivo: src/arts/lang/helpers.ts:171-179
  • Clasificación: refactor | Esfuerzo: Bajo
  • Propuesta: Mover a guards.ts.

HL-5: toError helper duplicado

  • Archivos: src/arts/sess/engine-session.ts:792-794
  • Clasificación: refactor | Esfuerzo: Bajo
  • Propuesta: Mover a $libs/reactive/utils.

HL-6: NodeJS.Timeout type rompe en entornos browser

  • Archivo: src/libs/timers/debounce.ts:3
  • Clasificación: bug confirmado | Esfuerzo: Bajo
  • Propuesta: Usar ReturnType<typeof setTimeout>.

HL-7: SvelteSet y SvelteMap innecesarios donde Set/Map bastan

  • Archivos: src/arts/stor/active-storage.svelte.ts:46,115
  • Clasificación: optimización | Esfuerzo: Bajo
  • Explicación: listeners y userSubs solo se iteran imperativamente (.forEach, .values()), nunca en $derived o template. La reactividad de SvelteSet/Map no se aprovecha.
  • Propuesta: Reemplazar con Set y Map planos.

HL-8: namesBy hace O(N*G) filtering en cada getter reactivo

  • Archivo: src/arts/conn/active-connections.svelte.ts:36-76
  • Clasificación: optimización | Esfuerzo: Bajo
  • Propuesta: Precomputar arrays categorizados con un solo $derived.

HL-9: computeIdentity definida dos veces idénticamente

  • Archivos: src/arts/sess/engine-session.ts:188-191, src/arts/sess/active-session.svelte.ts:31-34
  • Clasificación: refactor | Esfuerzo: Bajo
  • Propuesta: El wrapper active debe delegar a engine.identity en vez de recomputar.

HL-10: Dead conditional en normalización de identificadores

  • Archivo: src/libs/auth/normalize.ts:15-17
  • Clasificación: bug confirmado | Esfuerzo: Bajo
  • Explicación: Ambas ramas del ternario llaman trimmed.toLocaleLowerCase(). El condicional está muerto.
  • Propuesta: Eliminar condicional o aplicar normalización diferente por rama.

HL-11: next/prev son redundantes con forward/backward en arrays

  • Archivo: src/libs/arrays/utilities.ts:89-169
  • Clasificación: refactor | Esfuerzo: Bajo
  • Propuesta: Reimplementar next/prev como wrappers de forward(array, index, 1, loop).

HL-12: libs/http/index.ts y todas las barrels de libs/* usan export *

  • Archivos: src/libs/*/index.ts
  • Severidad: Baja | Clasificación: simplificación / coherencia
  • Explicación: El arts/README.md afirma que "All barrels use named re-exports" — esto es falso para toda la capa libs/. Para libs de utilidades es aceptable, pero para libs/auth (400+ líneas de tipos), libs/perm, libs/cach penaliza el tree-shaking.
  • Propuesta: Actualizar README para reflejar la realidad: "libs barrels usan export *; arts barrels usan named re-exports." Opcional: convertir las libs grandes a named re-exports.

HL-13: libs/numbers/utilities.ts — parámetro confuso numerator

  • Archivo: src/libs/numbers/utilities.ts:24
  • Clasificación: refactor | Esfuerzo: Bajo
  • Propuesta: Renombrar a mod(value: number, modulus: number).

HL-14: Archivos de re-export type de 1 línea en svrs/auth/integrations/

  • Archivos: svrs/auth/integrations/{timr,sess,http,cach}.ts (1 línea cada uno)
  • Clasificación: simplificación | Esfuerzo: Bajo
  • Propuesta: Eliminar archivos intermedios. Re-exportar directamente desde svrs/auth/index.ts.

Refactorizaciones recomendadas

# Descripción Archivo(s) Esfuerzo
R1 Partir connection.ts (865 líneas) en módulos separados src/arts/conn/connection.ts Alto
R2 Partir engine-auth.ts (951 líneas) extrayendo password flow, session binding, OAuth src/svrs/auth/engine-auth.ts Alto
R3 Extraer boilerplate Active de fmts en createReactiveSubEngine() src/arts/fmts/*/active-*.svelte.ts Medio
R4 Extraer getLocale/setLocale duplicado en 4 engines fmts src/arts/fmts/*/engine-*.ts Medio
R5 Unificar resolveDir + Direction en libs/dom/locale.ts fend/locale-defaults.ts, libs/dom/locale.ts Bajo
R6 Extraer disposedXxxMessage() a helper compartido perm/helpers.ts, cach/helpers.ts Bajo
R7 Mover readField, toError, escapeId a libs/ sess/engine-session.ts, adom/roving-focus-group Bajo
R8 Convertir libs/auth/index.ts de export * a named re-exports src/libs/auth/index.ts Medio
R9 Actualizar DESIGN_CONN.md para reflejar la implementación real src/arts/conn/DESIGN_CONN.md Medio
R10 Alinear activeEngine contract: extender ActiveSession/ActiveConnections src/libs/active.ts Bajo

Simplificaciones recomendadas

# Descripción Archivo Esfuerzo
S1 Eliminar close() duplicado de engine-connections (alias de closeConnection) src/arts/conn/engine-connections.ts:163-165 Bajo
S2 Eliminar FormatsLocaleSource (type alias muerto de LocaleSource) src/arts/fmts/types.ts:10 Bajo
S3 Consolidar AUTO_VALUE/AUTO_CURRENCY/AUTO_UNIT_SYSTEM (todos son 'auto') fmts/consts.ts, curr/consts.ts, unts/consts.ts Bajo
S4 Reemplazar SvelteSet/SvelteMap innecesarios con Set/Map active-storage.svelte.ts:46,115 Bajo
S5 Eliminar archivos de 1 línea en svrs/auth/integrations/* svrs/auth/integrations/ Bajo
S6 Eliminar svrs/auth/context.ts (usado solo en test page) svrs/auth/context.ts Bajo
S7 noop() debería aceptar rest args para compatibilidad universal libs/funcs/noop.ts:4 Bajo
S8 Simplificar subscribe() delegando a addTransport en logger src/arts/logr/engine-logger.ts:543-551 Bajo

Optimizaciones recomendadas

# Descripción Archivo Esfuerzo
O1 Cachear buildNumeralMap por locale (recomputado en cada parse()) fmts/nums/engine-numbers.ts:119-123 Bajo
O2 Precomputar namesBy en un solo $derived en vez de 5 filtros O(N) conn/active-connections.svelte.ts:36-76 Bajo
O3 getLogs clona todas las entradas antes de filtrar — filtrar primero logr/engine-logger.ts:473-490 Bajo
O4 Object.keys(globalContext).length > 0 aloca array en hot path logr/engine-logger.ts:194-199 Bajo
O5 sameSnapshot usa JSON.stringify en cada cambio externo — shortcut con generation sess/engine-session.ts:786-789 Bajo
O6 refines.ts regex() clona RegExp innecesariamente sin flags g/y sium/core/refines.ts:229 Bajo
O7 cookieAdapter.get() re-parsea document.cookie en cada lectura stor/adapters/cookie.ts:94-96 Medio
O8 Timers sin cancelar en body-scroll-lock durante HMR reloads adom/body-scroll-lock.svelte.ts:150-169 Bajo
O9 $effect sin debounce en Can.svelte para cambios rápidos de props perm/Can.svelte:30-48 Bajo
O10 decisionKey() llamada incluso para cache hits — diferir tras cache miss perm/client.ts:252-254 Bajo

Incoherencias de arquitectura

  1. Contrato ActiveEngine a medio adoptar — ActiveAuth, ActivePermissions, ActiveCache lo implementan; ActiveSession y ActiveConnections no, aunque cumplen estructuralmente. O se adopta universalmente o se elimina.

  2. Clases vs factories en adom — BodyScrollLock, DOMContext, RovingFocusGroup son clases con new; el resto del ecosistema usa createEngine*/createActive*. Inconsistencia de API.

  3. Naming plural vs singular — createEngineTimers (plural) vs createEngineHttp (singular). Solo timr usa plural.

  4. Errores: clases vs string-templates — timr/http/conn/perm usan clases Error con type guards; fmts usa string factories. Inconsistente para catch programático.

  5. Patrón de errores disposed — CachDisposedError, PermDisposedError, AuthDisposedError vs STORAGE_ERRORS.DISPOSED (string). Sin patrón unificado.

  6. DESIGN_CONN.md referencia archivos inexistentes — reconnect.ts, heartbeat.ts, backpressure.ts, ack.ts, presence.ts, app-integration.ts no existen. El diseño se consolidó sin actualizar la documentación.

  7. libs/times/ — alias en config pero directorio vacío — el alias $libs/times resuelve a un index.ts de 0 bytes.

  8. AappAlreadyCreatedError no se usa para sesión — createActiveSession() lanza SessAlreadyCreatedError, no AappAlreadyCreatedError como los demás factories.

  9. Sin DESIGN_*.md para http, fmts, stor — solo timr y conn tienen documentos de diseño detallados.


Tests faltantes

Sin tests (crítico)

Módulo Archivos sin tests
libs/timers backoff.ts, debounce.ts (0 tests)
arts/conn active-connections.svelte.ts (sin archivo de test)
arts/conn websocket.ts (sin tests unitarios)

Escenarios faltantes (importante)

Módulo Escenario
svrs/auth CSRF: token con wrong signing key, wrong tenant, cookie tampering
svrs/auth Engine: duplicate sign-up, password policy, session binding verification, global sign-out binding revocation
arts/auth Cliente: sign-in/out integration, double-dispose, concurrent loadCurrent/signIn races
arts/conn Heartbeat interval, reconnect exhaustion, browser lifecycle, dispose cleanup verification
arts/conn openConnection/closeConnection/reconnectConnection per-connection methods
arts/sess visibilitychange handler en auto-refresh
arts/stor dynamicEntry con keyFn que lanza error
libs/dom isIOS detection con mock de navigator.userAgent
libs/arrays getNextMatch con edge cases (empty values, spaces, cycling)

Preguntas abiertas

  1. ¿Debe ActiveEngine ser contrato universal o solo para artifacts con side-effects? — Actualmente a medio adoptar. O se extiende a Session/Connections o se documenta como específico de "network-augmented" artifacts.

  2. ¿Mantener clases en adom o migrar a factories? — BodyScrollLock, DOMContext, RovingFocusGroup usan new; el resto usa create*(). La inconsistencia actual confunde.

  3. ¿Cuál es el plan para libs/times/? — Directorio vacío con alias en config. ¿Se puebla con duration math, delay(), sleep() o se elimina?

  4. ¿Nivel de madurez de OAuth y MFA? — El README dice "no deben documentarse como production-ready". verifyMfaChallenge siempre lanza error. OAuth tiene incompatibilidad con DB adapter. ¿Roadmap?

  5. ¿Estándar de idioma para documentación? — adom/README.md está en español, stor/README.md en inglés. Sin estándar definido.

  6. ¿Mover validación de sesión en SSR al engine? — readSessionFromCookies no valida schema; depende del caller pasar por adoptServer. ¿Debería el helper ser más defensivo?

  7. ¿Estrategia de barrels? — El README dice "named re-exports" para todos los barrels pero libs/* usa export *. ¿Actualizar README o convertir libs?


Veredicto

El ecosistema Active es un framework sólido, bien diseñado y con fundamentos arquitectónicos excelentes. La separación en capas, el patrón Engine/Active, la política de tree-shaking, el aislamiento de scope en caché y permisos, y la implementación de CSRF son de calidad profesional.

Lo que frena la calidad hoy:

  1. Deuda de consolidación — Archivos monolíticos (connection.ts 865 líneas, engine-auth.ts 951 líneas) que contradicen su propio diseño documentado. La duplicación de boilerplate entre sub-módulos de fmts y entre capas cliente/servidor indica que el framework creció sin pausas de refactorización.

  2. Cobertura de tests desigual — Algunos módulos tienen baterías exhaustivas (50 tests en engine-timers.test.ts, 622 líneas en engine-http.test.ts); otros tienen cero tests (libs/timers, active-connections, websocket). Las áreas sin tests son precisamente donde hay más bugs potenciales (conexiones, reconexión, heartbeats).

  3. Convenciones inconsistentes — Nombres plural/singular, clases vs factories, errores clase vs string, contrato ActiveEngine a medio adoptar. Esto crea fricción para nuevos contribuidores y hace que el código parezca menos cohesionado de lo que realmente es.

Orden de actuación recomendado:

  1. Semana 1-2 — Corrección de bugs: HC-1 (Http dispose), HC-3 (writeBatch loop), HC-4 (isPromiseLike), HM-1 (Can reactivity), HM-2 (auth error re-throw)
  2. Semana 3-4 — Refactors estructurales: Partir connection.ts, extraer boilerplate fmts, añadir Http.dispose()
  3. Semana 5-6 — Seguridad: HM-3 (revokeDevice DB), HM-4 (signOutGlobal refresh families), HM-6 (session fixation docs)
  4. Semana 7-8 — Tests: Añadir tests para libs/timers, active-connections, heartbeat, CSRF edge cases
  5. Mes 2-3 — Unificación: Adoptar ActiveEngine universalmente o eliminarlo, unificar naming (plural→singular en timr), estandarizar errores (clases everywhere), actualizar DESIGN_CONN.md

Lo que está excepcionalmente bien:

  • Separación cliente/servidor: cero imports cruzados $arts↔$svrs (fuera de tests de integración)
  • CSRF: double-submit cookie + HMAC-SHA256 + timing-safe comparison + __Host- prefix
  • Scope isolation en caché: SCOPE_ACTOR, SCOPE_TENANT, SCOPE_PERMISSION con hash en key
  • Generation guard en permisos: previene escritura de resultados stale en snapshots posteriores
  • Sistema de timers deterministas con fake clock injection para testing
  • Tree-shaking: separación .svelte.ts vs .ts, barrels con named re-exports en arts/
  • Documentación de diseño: DESIGN_TIMR.md (1530 líneas) y DESIGN_CONN.md (1770 líneas) son ejemplares

Powered by TurnKey Linux.