|
|
# before_0_1
|
|
|
|
|
|
> Lo que hay que **cerrar, incluir o verificar** antes de etiquetar
|
|
|
> `0.1.0`. Construido sobre las evidencias del repo a fecha de hoy y los
|
|
|
> hallazgos del audit. Un par de bloqueantes son políticas / hygiene que
|
|
|
> pesan más que cualquier hallazgo técnico individual.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 1. Qué significa `0.1` aquí (definición operativa)
|
|
|
|
|
|
`0.1` no es _production-ready_. Es la versión que **un equipo externo
|
|
|
puede probar sin sorpresas grandes**, sabiendo que la API pública no
|
|
|
romperá silenciosamente durante la línea `0.1.x`. Esto fija el listón:
|
|
|
|
|
|
- API pública de los 9 roots always-present **congelada** durante `0.1.x`.
|
|
|
- Las 5 factorías scoped (`Sium`, `Session`, `Connections`, `Auth`,
|
|
|
`Permissions`) pueden iterar pero deben respetar deprecation policy.
|
|
|
- Sin bugs de seguridad conocidos en flujos hot.
|
|
|
- Repo aceptable para un PR externo: licencia, README, contributing, CI.
|
|
|
- Docs suficientes para enviar una feature sin leer el código fuente.
|
|
|
- Build estático funciona end-to-end (no solo el typecheck).
|
|
|
|
|
|
Lo que **no** entra en `0.1`: MFA en producción, OAuth con todos los
|
|
|
proveedores, todos los módulos con docs profundas, métricas / telemetría,
|
|
|
ejemplos reales más allá de las páginas `/test`.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2. Estado real verificado hoy
|
|
|
|
|
|
Antes de listar gaps, lo que ya está cerrado (corroborado leyendo el
|
|
|
repo, los tests y los gates locales):
|
|
|
|
|
|
- **Tests reales**: 108 archivos / 1208 tests verdes.
|
|
|
- **Typecheck**: `npm run check` 0/0/0.
|
|
|
- **Build estático**: `npm run build` verde.
|
|
|
- **Gate completo**: `npm run test:all` verde (`check` + `test` +
|
|
|
`build` + `test:static` + `test:bundle`).
|
|
|
- **Bundle smoke**: `createActiveApp({})` está medido con Vite/OXC en
|
|
|
`scripts/bundle-smoke.mjs`; baseline actual ~65 KB gzip, presupuesto
|
|
|
`0.1` en 70 KB gzip configurable con `ACTIVE_BUNDLE_GZIP_LIMIT_KB`.
|
|
|
- **Refactor wave** documentada en `NEXT_STEPS.md` ha resuelto
|
|
|
parcialmente varios de mis hallazgos del audit:
|
|
|
- **C2/C3** (Auth → Permissions/Cache invalidation) — cableado a través
|
|
|
de `arts/aapp/integrations/auth-cache.ts`. _A verificar_ con tests
|
|
|
de race cross-actor que el flujo real cierra el gap.
|
|
|
- **C1** (perm race cross-actor) — el cliente perm se ha partido en
|
|
|
`client-cache.ts` / `client-keys.ts` / `client-snapshot.ts`. _A
|
|
|
verificar_ que `pending` se invalida en `hydrate` / cambio de scope.
|
|
|
- Cobertura: `auth + sess + perm + cach` ya tienen test de
|
|
|
integración cruzada.
|
|
|
- **Brand brief** (`BRAND.md`) escrito y assets estáticos añadidos.
|
|
|
- **Documentación interna** (`/active`): shell + landing + Get Started,
|
|
|
páginas para todos los módulos bajo `docs/<module>`, sección de capa
|
|
|
servidor (`$svrs`), seguridad, versionado y guía para AI agents. La
|
|
|
profundidad por módulo sigue siendo desigual y es trabajo pendiente.
|
|
|
|
|
|
Lo que **sigue abierto** sale en §3.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 3. Bloqueantes duros para `0.1`
|
|
|
|
|
|
Cada bloqueante lleva una _Acceptance criteria_ verificable.
|
|
|
|
|
|
### 3.1 Repo hygiene (sin esto el repo no está listo para PR externos)
|
|
|
|
|
|
| Falta | Acceptance |
|
|
|
| ------------------ | ----------------------------------------------------------------------------------------- |
|
|
|
| `LICENSE` | Archivo en raíz; campo `license` en `package.json` coincide. |
|
|
|
| `README.md` (raíz) | Tagline, install, quick start, link a `/active`, link a `BRAND.md`, badges típicos. |
|
|
|
| `SECURITY.md` | Política de reporte (correo / GitHub Security Advisories), modelo de amenazas resumido. |
|
|
|
| `CONTRIBUTING.md` | Cómo correr tests, layout `libs/svrs/arts`, regla "docs y comentarios en inglés". |
|
|
|
| `CHANGELOG.md` | Iniciado con `0.1.0` siguiendo Keep a Changelog + SemVer. |
|
|
|
| `.github/` | Issue templates (bug, feature), PR template, workflow CI mínimo (lint+check+test+build). |
|
|
|
| `.gitignore` | Añadir `tmp-active-docs-*.log`, `AUDIT_*.md` no, `BRAND.md` no — solo logs/temporales. |
|
|
|
| `package.json` | Campos `license`, `engines.node` (`>=22`), `repository`, `bugs`, `homepage`. |
|
|
|
|
|
|
> **Nota**: hay 4 ficheros `tmp-active-docs-*.log` en la raíz hoy. O se
|
|
|
> commitean (no recomendado) o se ignoran. Decidir antes del tag.
|
|
|
|
|
|
### 3.2 Seguridad
|
|
|
|
|
|
| ID | Bloqueante | Acceptance |
|
|
|
| --- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
|
| S1 | `AuthRateLimitPort` definido pero no cableado en flows críticos (audit C7). | `signInPassword`, `signUpPassword`, `requestPasswordReset`, `requestEmailVerification`, `startOAuth` consultan el puerto antes de procesar. Test que verifica que un puerto que devuelve `denied` corta el flujo. |
|
|
|
| S2 | PKCE no validado server-side en `completeOAuth` (audit C6). | `provider.mapProfile` recibe `verifier` y un test cubre intento de callback con `code_verifier` distinto al `code_challenge` original; debe fallar. |
|
|
|
| S3 | `verifyMfaChallenge` lanza `AuthConfigError` (audit C5, sigue stub). | O bien implementación funcional, o bien **excluido del barrel y de `handlers`** + nota en `SECURITY.md`. Coherente con la promesa: lo que se exporta funciona. |
|
|
|
| S4 | Memory adapters (`svrs/auth`, `svrs/perm`, `svrs/cach`) no marcados como dev-only. | Bandera `process.env.NODE_ENV === 'production'` que loguea warning explícito al instanciar; nota en README de cada adapter; test que verifica el warning. |
|
|
|
| S5 | CSRF cookie default `SameSite=Lax` (audit M2). | Default `Strict` en `AUTH_COOKIE_POLICY.SAME_SITE`. Tests de regresión. |
|
|
|
| S6 | _Verificar_ que C1/C2/C3 están realmente cerrados tras el refactor (NEXT_STEPS). | Test de integración: actor A en vuelo → `hydrate(B)` antes de la respuesta → la decisión cacheada NO queda bajo scope B. Hoy el test no existe. |
|
|
|
| S7 | `SECURITY.md` con threat model. | Documenta: cookie scopes, CSRF flow, refresh rotation, OAuth state binding, MFA estado actual, modelos de actor en perm. |
|
|
|
|
|
|
### 3.3 Correctitud / API
|
|
|
|
|
|
| ID | Bloqueante | Acceptance |
|
|
|
| --- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
|
| A1 | `ensureLive` con nombre de método incorrecto (audit C4). | Constantes `AUTH_METHOD_ON_CHANGE`, `PERMISSION_METHOD_CLEAR_ERROR`, etc.; test que valida el message del error contiene el método real. |
|
|
|
| A2 | API pública de los 9 roots **comprometida** durante `0.1.x`. | Snapshot tests del shape de cada `Engine*`/`Active*` always-present; cualquier cambio en `0.1.x` debe ser aditivo o pasar por `__EXPERIMENTAL_*`. |
|
|
|
| A3 | Política de deprecación documentada. | `CONTRIBUTING.md` describe: `@deprecated` JSDoc + warning runtime + `__EXPERIMENTAL_*` namespace + minimum una minor antes de remove. |
|
|
|
| A4 | `AuthRateLimitPort` exportado **solo si** se cablea (S1). Si no, sacar del barrel. | Si no se implementa para `0.1`, no aparece en `index.ts` y no aparece en docs; añadir TODO de `0.2`. |
|
|
|
| A5 | `mono-lang` cast `as unknown as ActiveLang<S>` (audit M8). | Decidir: o se restringe el tipo de `App.Lang` cuando no hay schema, o se documenta explícitamente la ruptura de tipos. README de `lang` lo refleja. |
|
|
|
|
|
|
### 3.4 Documentación mínima
|
|
|
|
|
|
`/active` ya tiene rutas para todos los módulos, pero la profundidad sigue
|
|
|
siendo desigual. Para `0.1`:
|
|
|
|
|
|
| Bloqueante | Acceptance |
|
|
|
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
|
| Los **9 roots always-present** con docs reales (no stub). | `App, Lang, Logger, Formats, Frontend, Dom, Storage, Http, Timers, Cache` tienen sección Overview / Quick start / API / Limits / Testing. |
|
|
|
| Las 5 factorías scoped pueden ser stub si llevan una nota "shape final en `0.2`". | El stub indica explícitamente qué partes de su API no están comprometidas. |
|
|
|
| `SECURITY.md` (cubre S7) **enlazado** desde sidebar de `/active`. | Item en la sección `Get Started` con ruta `/active/security`. |
|
|
|
| Migration / versioning policy publicada. | Página `/active/get-started/versioning` o sección dentro de Composition. |
|
|
|
|
|
|
### 3.5 Build y tooling
|
|
|
|
|
|
| Bloqueante | Acceptance |
|
|
|
| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
|
| Build estático real verificado. | `npm run build` produce `build/` sin errores; `npm run preview` sirve `/active` y al menos `/test/ecosystem`.|
|
|
|
| CI en GitHub Actions. | Workflow corre `test:typecheck`, `test`, `build`, `test:static` y `test:bundle` en push y PR. Branch protection en `master`/`main`. |
|
|
|
| Scripts npm completos. | `test:typecheck`, `test:static`, `test:bundle` y `test:all` existen. `test:all` ejecuta `check` + `test` + `build` + `test:static` + `test:bundle`. |
|
|
|
| Reproducibilidad. | `package-lock.json` commiteado (ya está). Node version pinneada en `engines` y `.nvmrc`. |
|
|
|
| Bundle size sanity. | Smoke test que importa `createActiveApp({})` y verifica gzip bajo `ACTIVE_BUNDLE_GZIP_LIMIT_KB` (default 70 KB). |
|
|
|
|
|
|
> `npm run lint` existe, pero sigue siendo deuda global pre-`0.1`; CI no
|
|
|
> lo ejecuta hasta que el árbol completo quede limpio. La regla mientras
|
|
|
> tanto es no añadir deuda nueva en archivos tocados.
|
|
|
|
|
|
### 3.6 Marca y assets
|
|
|
|
|
|
`BRAND.md` describe lo que debe hacerse; `static/` solo tiene `robots.txt`.
|
|
|
|
|
|
| Bloqueante | Acceptance |
|
|
|
| -------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
|
| `static/favicon.svg` | Concept A o B del brief, monocromo, vector limpio. |
|
|
|
| `static/favicon-16/32/48.png` | Pixel-snapped (no scaling automático), sRGB. |
|
|
|
| `static/apple-touch-icon.png` | 180×180 con padding 10%. |
|
|
|
| `static/icon-192/512.png` + `icon-maskable-512.png` | PWA-ready aunque la PWA no esté declarada todavía. |
|
|
|
| `static/og-image.png` | 1200×630 según prompt §10 de `BRAND.md`. |
|
|
|
| `app.html` | `<link rel="icon">`, `<link rel="apple-touch-icon">`, `<meta name="theme-color">`, OG tags por defecto. |
|
|
|
| `manifest.webmanifest` | Opcional para `0.1` pero recomendado, listado de iconos PWA. |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 4. Bloqueantes blandos (pueden caer en `0.1.1`–`0.1.x`)
|
|
|
|
|
|
Son hallazgos del audit con impacto real pero acotables a un parche:
|
|
|
|
|
|
- M4 (refactor `validateOptionalField` en sess) — limpieza; no rompe nada.
|
|
|
- M5 (acoplamiento `aapp` ↔ `stor` en mensajes de log) — se puede pulir
|
|
|
más tarde sin tocar la superficie pública.
|
|
|
- M9 (body-scroll-lock con scheduling propio en vez de delegar a `timr`) —
|
|
|
optimización; no afecta la API.
|
|
|
- M10 (HTTP recompone headers en cada retry) — perf, no semántica.
|
|
|
- M14 (sess `BroadcastChannel` ignora el `event` recibido y vuelve a
|
|
|
leer storage) — documentar como diseño y seguir.
|
|
|
- m1–m15 (menores: docs faltantes, magic strings residuales, refactors
|
|
|
cosméticos).
|
|
|
|
|
|
Estos pueden entrar como issues etiquetados `0.1.x` y resolverse de
|
|
|
manera incremental.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 5. Fuera de alcance para `0.1` (explícito)
|
|
|
|
|
|
Para evitar que el sprint se infle, dejar fuera y comunicarlo:
|
|
|
|
|
|
- MFA real (TOTP, WebAuthn, recovery codes).
|
|
|
- OAuth provider catalog (GitHub, Google, etc.) — solo el motor + un
|
|
|
proveedor de ejemplo.
|
|
|
- Métricas / telemetría / OpenTelemetry export.
|
|
|
- Server-side rendering completo del docs site (es estático).
|
|
|
- Componentes UI prefabricados (eso vive _encima_ de los artefactos).
|
|
|
- Internacionalización del propio docs site (English-only en `0.1`).
|
|
|
- Distribución por `npm publish` — `0.1` puede vivir solo como repo
|
|
|
template / submódulo / vendoring. Decidir si publicar es objetivo.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 6. Verificación pre-tag (un solo comando)
|
|
|
|
|
|
Antes de etiquetar `0.1.0`, todo esto debe pasar en CI y local:
|
|
|
|
|
|
```sh
|
|
|
# 1. Gate completo automatizado
|
|
|
npm run test:all # → check + tests + build + static smoke + bundle smoke
|
|
|
|
|
|
# 2. Tipos, si se quiere aislar el paso
|
|
|
npm run check # → 0 errors / 0 warnings
|
|
|
|
|
|
# 3. Suite, si se quiere aislar el paso
|
|
|
npm test # → 0 failed; assert >= 1208 tests
|
|
|
|
|
|
# 4. Build estático, si se quiere aislar el paso
|
|
|
npm run build # → produce build/ sin errores
|
|
|
|
|
|
# 5. Bundle smoke, si se quiere aislar el paso
|
|
|
npm run test:bundle # → createActiveApp({}) <= 70 KB gzip por defecto
|
|
|
|
|
|
# 6. Static smoke, si se quiere aislar el paso tras build
|
|
|
npm run test:static # → rutas/docs/assets críticos existen en build/
|
|
|
```
|
|
|
|
|
|
Y, cuando se quiera verificar el sitio servido:
|
|
|
|
|
|
```sh
|
|
|
npm run preview
|
|
|
curl -fsS http://localhost:4173/active | grep -q "active"
|
|
|
curl -fsS http://localhost:4173/test/ecosystem | grep -q "ecosystem"
|
|
|
```
|
|
|
|
|
|
`npm run lint` debe ejecutarse antes del tag, pero hoy todavía representa
|
|
|
una limpieza global separada. No debe bloquear el gate automático hasta
|
|
|
que esa deuda esté cerrada.
|
|
|
|
|
|
Y manual:
|
|
|
|
|
|
- [ ] Abrir `/active` y `/test/ecosystem` en Chromium, Firefox, Safari
|
|
|
desktop. **No console errors. No layout shifts grandes.**
|
|
|
- [ ] `App.dispose()` se llama dos veces seguidas → no throw, no warn.
|
|
|
- [ ] Cambio de locale a `ar` aplica `dir="rtl"` y propaga a Formats.
|
|
|
- [ ] Sign-in → sign-out limpia perm cache y session bridge.
|
|
|
- [ ] Refresh durante revoke → no resucita la sesión.
|
|
|
- [ ] 404 en una ruta `/active/docs/<inexistente>` → muestra fallback.
|
|
|
- [ ] Tema oscuro: contraste suficiente en sidebar activo, code blocks,
|
|
|
callouts.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 7. Orden sugerido
|
|
|
|
|
|
Dos sprints serios bastan si nadie se desvía.
|
|
|
|
|
|
**Sprint A — Seguridad y correctitud (1 semana)**
|
|
|
|
|
|
1. S1 — cablear rate-limit (engine + handlers + tests).
|
|
|
2. S2 — pasar verifier a OAuth provider y validar.
|
|
|
3. S3 — decisión MFA: implementar mínimo o sacar de barrel.
|
|
|
4. S5 — flip default CSRF a Strict.
|
|
|
5. S6 — test de integración perm race cross-actor.
|
|
|
6. A1 — corregir constants en `ensureLive`.
|
|
|
7. S4 — warning runtime en memory adapters.
|
|
|
|
|
|
**Sprint B — Hygiene, docs y release (1 semana)**
|
|
|
|
|
|
1. Repo hygiene: LICENSE, README, CONTRIBUTING, SECURITY, CHANGELOG,
|
|
|
`.github/` con CI básica.
|
|
|
2. A2/A3 — snapshot tests de superficie + política de deprecación
|
|
|
escrita. **Cerrado para la superficie actual**; mantenerlo actualizado
|
|
|
con cada miembro público nuevo.
|
|
|
3. Completar los 9 roots always-present en `/active/docs`.
|
|
|
4. Marca: ejecutar al menos `favicon.svg` + `favicon-32/16` +
|
|
|
`og-image.png` + meta tags en `app.html`.
|
|
|
5. Build estático, static smoke y bundle smoke verificados en CI.
|
|
|
6. Tag `0.1.0` con changelog real.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 8. Decisiones abiertas (necesitan criterio humano antes de avanzar)
|
|
|
|
|
|
1. **¿Se publica en npm o se distribuye como repo / template?** Cambia
|
|
|
la forma de empaquetar (`exports`, `files`, `sideEffects` granular).
|
|
|
2. **¿`AuthRateLimitPort` se cablea ahora o se posterga?** Si se
|
|
|
posterga, sacarlo del barrel hasta `0.2`.
|
|
|
3. **¿MFA en `0.1` o explicitamente en `0.2`?** Hoy está stubbed; un
|
|
|
stub que lanza no debe estar en la superficie pública.
|
|
|
4. **¿`mono-lang` es público o interno?** Si público, se documenta y
|
|
|
se asume el cast laxo; si interno, se quita del barrel.
|
|
|
5. **¿La doc de los 5 factorías scoped (`Sess`, `Auth`, `Perm`, `Conn`,
|
|
|
`Sium`) llega a `0.1` profunda o stub?** Mi recomendación: `Auth`,
|
|
|
`Sess` y `Perm` profundas (son las que un consumidor pisa en
|
|
|
onboarding); `Conn` y `Sium` pueden quedar como stubs marcados.
|
|
|
6. **¿Adapter Static es el target final?** Si hay plan de SSR (auth
|
|
|
server-side real), conviene cambiar a `adapter-node` antes de
|
|
|
`0.1` para no romper consumidores en `0.1.x`.
|
|
|
7. **¿La API de `App.Cache.invalidate({tags})` queda como contrato
|
|
|
estable?** Se merece ser parte del primer snapshot test de
|
|
|
superficie porque cae en el camino crítico de `Auth`.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 9. Métricas de "listo"
|
|
|
|
|
|
Cuando todo lo anterior pase, el repo debería poder responder _sí_ a:
|
|
|
|
|
|
- "¿Un dev externo clona, sigue el README y arranca `/active` en
|
|
|
< 5 min?"
|
|
|
- "¿Existe un punto de contacto claro para reportar un fallo de
|
|
|
seguridad?"
|
|
|
- "¿Hay manera de saber qué cambia entre `0.1.0` y `0.1.1` sin leer
|
|
|
commits?"
|
|
|
- "¿La superficie de los 9 roots always-present está documentada con
|
|
|
una garantía explícita de no-break en la línea `0.1.x`?"
|
|
|
- "¿Si elimino los assets de marca, queda algo identificable en el
|
|
|
navegador?" (Hoy: no, `static/` solo tiene `robots.txt`).
|
|
|
|
|
|
Si las cinco respuestas son sí, etiqueta `0.1.0`.
|