docs(corpus): add docs/testing-and-tooling.md — testing, validation, codegen, SSR

Consolidates the cross-cutting verification story that was scattered across
package.json, COMPONENT_GUIDE and SOMA_ARCHITECTURE into one reference:

- The two-project vitest suite (browser client / node server) and how to run one.
- Each validator and the class of bug it catches: morfo:check (DOM vs contract),
  morfo:vocabulary (verb drift), component:audit (acceptance), perm:check
  (state transitions), smoke (hydration), translations:check, eidos-lint.
- Codegen vs authored: generate:eidos-css, generate:contracts-docs, and the
  compileMorfo primitive.
- The SSR posture: dom:false -> disabledDom, ActiveDom owner-document resolution,
  ornamental sema, and why smoke (not HTTP 200) is what catches hydration bugs.

All grounded in the real package.json scripts. Wired into docs/README.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
active-uix
dev 4 months ago
parent 333d791e02
commit e737f0f78d

@ -118,6 +118,7 @@ and the server-authoritative engines in `src/svrs/`.
| Understand why a decision was made | `docs/decisions.md` → the relevant RFC / `LIBRO_VARIACIONES` |
| Use a runtime artifact (auth, cache, http, …) | `arts/README.md` + `src/arts/{name}/README.md` |
| **Write or edit documentation** | [`docs/authoring.md`](./authoring.md) — the authoring rules |
| Test or validate a change | [`docs/testing-and-tooling.md`](./testing-and-tooling.md) — tests, validators, codegen, SSR |
## Authoritative sources & rules

@ -39,6 +39,7 @@ permanente/efímero, drift, dos idiomas.
- **Fase 4 (3/3) — THEMING split (stubs)**: `THEMING.md` 2571→1930 L, partido renumber-safe en 3 docs-estrato: `eidos/TSC.md` (E2, §7+§18), `eidos/THEMING_GUIDE.md` (E4, §8+§9), `eidos/THEMING_NOTES.md` (E3, §15+§17). THEMING conserva stubs-puntero numerados → las 34 secciones y todas las citas `§N` del corpus/código sobreviven; §16 + `## Referencias` + §20-34 quedan in-place. Además §14 motion saneado (contradicción con eidos-motion.md). **Fase 4 cerrada.**
- **Fase 5 (1/n) — entrada E0**: nuevo `docs/README.md` — puerta única del corpus (lo pidió el usuario: "necesario para que los agentes empiecen y para cada sesión nueva"). Contiene: orientación de 60 seg, los 7 estratos, orden de lectura, mapa de docs por estrato (E1-E5 + process) y atajos task-oriented ("I want to…"). **Cableado desde `CLAUDE.md`** ("Start here", arriba de Reference Documents) para que toda sesión/agente lo lea primero. Todos los enlaces verificados. Pendiente del E0: solo adelgazar `CLAUDE.md` (ver Diferido).
- **Fase 5 (2/n) — glosario**: nuevo `docs/glossary.md` — el vocabulario inventado (las capas, morfo, soma, sema, eidos) definido en una línea cada término + puntero a la fuente autoritativa; el subset semántico (family/intent/verb/channel) **apunta a CANON, no se recopia** (sin drift). Enganchado en `docs/README.md` (estrato E0 + orden de lectura).
- **Fase 5 (5/n) — testing/tooling/codegen**: nuevo `docs/testing-and-tooling.md` — consolida la historia transversal de verificación (estaba dispersa en package.json / COMPONENT_GUIDE / SOMA_ARCHITECTURE): suite vitest de **2 proyectos** (client browser / server node), **validadores** (`morfo:check` · `morfo:vocabulary` · `component:audit` · `perm:check` · `smoke` · `translations:check` · eidos-lint — qué caza cada uno), **codegen** (`generate:eidos-css` · `generate:contracts-docs` + `compileMorfo`: generado vs autorado), y **postura SSR** (`dom:false`→disabledDom, ActiveDom owner-doc/iframe-safe, sema ornamental, hidratación = lo que `smoke` caza). Anclado en scripts reales de package.json. Enganchado en `docs/README.md` ("I want to… test").
- **Fase 5 (4/n) — getting-started**: nuevo `docs/getting-started.md` — el camino narrativo de cero a primer cambio: run it → modelo mental con la cadena de transcripción → **ver las 4 capas en un solo elemento del DOM** (toggle: `data-toggle`/`data-state`/`data-event-*` + el CSS de eidos) → loop de verificación con scripts reales → 3 caminos de primer cambio. Anclado en rutas reales (`/uix/components/*`, `/active`, `/temas`) y scripts reales (`check`/`test`/`morfo:check`/`smoke`/`component:audit`/`perm:check`); apunta a COMPONENT_GUIDE/THEMING_GUIDE sin duplicar. Enganchado en `docs/README.md` (callout arriba + fila "I want to"). Hay además un get-started de la app en `/active/get-started/*` (incl. ai-agents) — este es el doc-side.
- **Fase 5 (3/n) — reglas de autoría de doc**: nuevo `docs/authoring.md` — cómo se crea/edita documentación en el corpus, codificando lo que aprendió la migración: (1) linkear el canon, nunca copiarlo (la ley anti-drift); (2) cada doc tiene un estrato + dónde va; (3) docs de referencia atemporales (hand-offs/fechas/catálogos hardcoded → process o puntero); (4) una fuente por concern (roles distintos si hay 2 docs del mismo tema); (5) frontmatter; (6) ediciones renumber-safe (stub pattern para `§N` citadas, verificar links); (7) idioma EN; (8) naming `rfc-*`/`design-*`; + checklist pre-commit. Cada regla cita el fallo real que arregla. Enganchado en `docs/README.md` ("I want to… write a doc" + nota tras los estratos).
@ -54,7 +55,7 @@ permanente/efímero, drift, dos idiomas.
- **NOTA harness**: escribir un archivo EXISTENTE con PowerShell `Set-Content` o con regex (`\d+`, ` / ` sueltos) en el comando lo bloquea un analizador (lo lee como `Remove-Item path`). Funciona: `[System.IO.File]::WriteAllLines(abs, $arr, utf8NoBom)` + límites por `.StartsWith()` (sin regex). Crear archivos NUEVOS con `Set-Content` sí va. Las tools Edit/Write también.
### Fase 5 — huecos de libro (escribir nuevo)
~~Entrada E0~~ (hecho) · ~~Glosario~~ (hecho) · ~~reglas de autoría~~ (hecho) · ~~arco *getting-started*~~ (hecho) · decision-log consolidado (semilla: `src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md`) · walkthrough "construye tu propia capa" · comparativa honesta vs Radix/Ark/Mantine (hoy dispersa en THEMING_NOTES §15, eidos-motion §16) · historia transversal SSR/testing/codegen.
~~Entrada E0~~ (hecho) · ~~Glosario~~ (hecho) · ~~reglas de autoría~~ (hecho) · ~~arco *getting-started*~~ (hecho) · decision-log consolidado (semilla: `src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md`) · walkthrough "construye tu propia capa" · comparativa honesta vs Radix/Ark/Mantine (hoy dispersa en THEMING_NOTES §15, eidos-motion §16) · ~~historia transversal SSR/testing/codegen~~ (hecho).
### Diferido (decisión del usuario)
- **Rename físico de RFCs/design** (`*_ENGINE_RFC`/`*_RFC` → `rfc-*`; `DESIGN_CONN`/`DESIGN`/`DESIGN_TIMR` → `design-*`): los nombres actuales están citados como provenance en ~30 archivos de código (`eidos/lib/*.ts` ×~20, `arts/timer/*`, `arts/color/*`, `arts/session/types.ts`, tests) + ~10 docs + CLAUDE.md. `docs/decisions.md` ya da el naming consistente a nivel índice sin tocar nada. El rename físico solo vale la pena si se barren TODAS las citas en el mismo pass (si no, drift) — decisión del usuario por el coste/beneficio (cosmético vs ~40 archivos + `check`). De paso: ruta mala `soma/layers/GESTURES.md` (real: `layers/gesture/GESTURES.md`) en SOMA_ARCHITECTURE/old-README.

@ -0,0 +1,104 @@
---
title: Testing, Validation & Codegen
type: reference
audience: human + agent
status: current
---
# Testing, Validation & Codegen
The cross-cutting story of how the framework stays correct: the test suite, the
contract validators, what is generated vs authored, and the SSR posture. The
commands are the `scripts` in `package.json`; this groups them by what they are
*for*.
## The verification loop (the short version)
```bash
npm run check # types — svelte-kit sync && svelte-check (expect 0 errors)
npm run test # the vitest suite (one run)
npm run lint # prettier --check · npm run format to fix
```
For a component you also run the contract validators (below). A change is not
done until `check` is clean and the relevant validators pass.
## Tests — two projects
`vite.config.ts` defines **two vitest projects** (see CLAUDE.md → "Vitest
Two-Project Structure"):
- **client** — browser tests via Playwright, for `*.svelte.{test,spec}.{js,ts}`.
This is where component providers are exercised with real runes + DOM.
- **server** — Node environment for `*.{test,spec}.{js,ts}` (excludes the svelte
tests). Pure engines, libs, and server logic.
```bash
npm run test:unit # watch mode
npm run test # one run
npx vitest run src/uix/morfo/compile.test.ts # a single file
npx vitest run -t "describe name" # by test name
```
Component providers carry their own `{name}-provider.svelte.test.ts` under the
client project; the reusable engines they consume live in `$libs/datagrid`,
`$libs/forms`, `$libs/strings` and are tested there, not inside soma. See
[`SOMA_ARCHITECTURE.md`](../src/uix/soma/SOMA_ARCHITECTURE.md) §6 for the
per-component coverage map.
## Validation — what each script catches
These catch classes of bug that `check` (types) and an HTTP 200 (SSR) miss.
| Command | Catches |
| --- | --- |
| `npm run morfo:check` | the **real DOM vs the morfo contract** — navigates each demo and validates emitted `data-*` against the declaration. |
| `npm run morfo:vocabulary` | **canonical-verb / vocabulary drift** in morfo events (e.g. `open\|closed`, declared verbs not in `SEMA_VERBS`). Hard-fails CI on declared-verb drift. |
| `npm run component:audit` | the **acceptance matrix** of [`COMPONENT_COMPLETION_CHECKLIST.md`](../src/uix/COMPONENT_COMPLETION_CHECKLIST.md) — per-rule severity/applicability across all four layers + recipe + demo. |
| `npm run perm:check` | **state-transition** bugs — cycles a component through its declared states (via `data-perm-step` annotations) and re-validates morfo after each. Catches reactivity loops and transition-time drift `morfo:check` can't. |
| `npm run smoke` | **runtime / hydration** errors — walks every `+page.svelte` with Playwright and surfaces `pageerror`, `console.error`, missing translation keys, `Context "X" not found`, and `__uix_lang_missing__` markers. Needs `npm run dev` running. HTTP 200 is SSR only; smoke exercises client hydration. |
| `npm run translations:check` | missing / malformed translation keys. |
| `scripts/eidos-lint.ts` · `eidos-lint-all.ts` | classifies every `[data-*]` selector in eidos CSS as **morfo-backed / eidos-only / invalid** — drift between the morfo contract and the CSS. (Opt-in safety net; the architectural defense is the typed `semaSelector` builder — see the "Eidos drift defense" rule in CLAUDE.md.) |
## Codegen — what is generated vs authored
Some surfaces are produced from a source of truth, not hand-maintained. Don't
edit the output; edit the source and regenerate.
| Command | Generates from |
| --- | --- |
| `npm run generate:eidos-css` | the eidos foundation/recipe CSS, from `EidosConfig.recipes`. (`generated/base.css` is output — regenerate, don't hand-edit.) |
| `npm run generate:contracts-docs` | the contract documentation, from the morfos. |
The **morfo compiler** itself is the central codegen primitive: `compileMorfo(morfo)`
turns a declaration into a `CompiledMorfo` (resolved attr / keyboard / action
plans + the closed set of CSS selectors eidos may use), cached by morfo identity
(WeakMap). Every consumer reads the compiled morfo, never the raw declaration.
```bash
npm run eidos:purge # production: drop unreached foundation CSS (−46 to −55%)
```
## SSR posture
Components are **headless-functional without a DOM**. The architecture keeps SSR
and headless tests working:
- **`dom:false`** injects a shared `disabledDom` no-op consumed by Soma / Sema /
Eidos — there is no silent fallback to direct DOM writes (see
[`arts/adom/README`](../src/arts/adom/README.md) and `src/uix/contracts.ts`).
- **`ActiveDom`** resolves the *owner* `document` / `window` (`getDocument(node)`
/ `getWindow(node)`), so it is correct under iframes, popups and happy-dom —
not bound to the global `document`.
- **Sema is ornamental** — `ActiveUix.events` (the perceptual engine) is optional;
with no engine, `SomaRuntime.trigger()` skips the emit and the component stays
functional. So sound/haptic-free, audio-disabled and server environments work.
- **Hydration is what HTTP 200 misses** — server render succeeds long before a
hydration-time `Context not found` or effect loop would; that is exactly what
`npm run smoke` exists to catch.
## See also
- [`docs/getting-started.md`](./getting-started.md) — the short verification loop in context.
- [`soma/COMPONENT_GUIDE.md`](../src/uix/soma/COMPONENT_GUIDE.md) — the build checklist that calls these.
- [`COMPONENT_COMPLETION_CHECKLIST.md`](../src/uix/COMPONENT_COMPLETION_CHECKLIST.md) — what `component:audit` enforces.
Loading…
Cancel
Save

Powered by TurnKey Linux.