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>
4 months ago
---
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
docs(book): F7.2 (7/7) — soma-architecture chapter translated; architecture/ batch COMPLETE
src/uix/soma/SOMA_ARCHITECTURE.md (1058 L, Spanish) translated to English
as docs/architecture/soma-architecture.md, same s1-s17 numbering: layer
architecture, design principles, the closed six-piece model + SomaRuntime,
component model (picker composition), runtime parts, the layers inventory,
the Soma class + date/time domain + statics convention, the reactive
system, internal helpers, data-* contracts, IDs, barrels, boundaries,
directory structure, anti-patterns, current shape, stability rule,
checklist. The frozen per-provider test list (dated 2026-05-15, ~140 L)
became the timeless fact: the NO_MISSING_PROVIDER_TESTS guard + the test
tree ARE the coverage inventory (testing-and-tooling aligned). Stub with
the full sN map at the old path; corpus links swept.
F7.2 is complete: docs/architecture/ now holds the whole E1 stratum in
English (7 chapters, ~5.4k lines), with thin stubs + sN maps next to the
code. docs:check 0 errors (251 docs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
client project (a guard fails when an active provider ships without one — the
test tree IS the coverage inventory); the reusable engines they consume live
in `$libs/datagrid` , `$libs/forms` , `$libs/strings` and are tested there, not
inside soma. See
[`soma-architecture.md` ](./architecture/soma-architecture.md ) §6.
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>
4 months ago
## 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. |
docs(book): F7.5 (1/2) — checklist + audit guide + demo guide moved to docs/guides/
Three English guides moved verbatim (copy + link touch-ups, no
translation): COMPONENT_COMPLETION_CHECKLIST -> guides/
completion-checklist.md, COMPONENT_AUDIT_GUIDE -> guides/
component-audit.md, DEMO_AUTHORING_GUIDE -> guides/demo-authoring.md.
Tooling moved with its doc in the same pass: docs-check I5 now parses
the checklist at the new path (verified: 0 errors, rule tables found)
and component-audit.ts's two path strings updated. One reconciliation
in the audit guide's s5, applied as link-don't-copy: the copied v1
6-tab template list (stale vs the v2 9-tab guide it links, drawer
canary vs button) is replaced by a pointer to demo-authoring.md; the
domain canaries in s7 stay. demo-authoring's harness links now reach
into web/routes/uix/lib/ (the modules stay with the code); the deleted
pendiente_color_demos.md reference is marked TODO(reconcile). Corpus
swept: building-a-component (frontmatter related: + phases 6/8),
authoring, architecture/soma, theming/reference s5-parity,
testing-and-tooling, getting-started, README (E4 rows + component-audit
row added + two stale F7.3 paths in 'I want to' fixed). Remaining
warns = links to component-guide.md, which lands in F7.5 (2/2).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| `npm run component:audit` | the **acceptance matrix** of [`completion-checklist.md` ](./guides/completion-checklist.md ) — per-rule severity/applicability across all four layers + recipe + demo. |
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>
4 months ago
| `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. |
| `npm run docs:check` | **doc-corpus drift** — copied vocabulary counts vs the source consts, phantom fields (the legacy morfo text field; rejected API shapes), dependency claims vs `package.json` , the variant-vocab mirror in `component-audit.ts` , checklist↔audit rule-ID sync, and relative links (warn severity). Guards the "link the canon, never copy it" law of [`docs/authoring.md` ](./authoring.md ). |
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>
4 months ago
| `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.) |
fix(eidos): THEME-SYS-1 — consolidate overlay z-index into a named scale (+ guard + docs)
The ~12 overlay recipes hardcoded an ad-hoc parallel z-index scale (raw integers
60–99/1200) that duplicated nothing reusable and had drifted out of order
(tooltip 76 < dropdown 80 — a tooltip painted BEHIND a dropdown). They are now a
named scale.
- `STATIC_Z_INDEX_OVERLAY` (static.ts) → emitted as `--z-index-overlay-{inline,
backdrop,content,floating,tooltip,detached,toast}`. A SEPARATE scale from the
global `--z-index-*` ladder (which orders the depth planes) — overlays portal
to <body> as siblings of modals, so they share one flat low band where each
rung sits just above the modal scrim. Mapping them to the 300–900 ladder would
hide a dropdown/select/popover opened INSIDE a dialog (dropdown 300 < modal
700); the combobox recipe already warned about this. `tooltip` now sits above
`floating` (fixes the inversion); `toast` stays above the soma FloatPanel band.
- Every overlay recipe token (`content-z`/`overlay-z`/`inline-z`/`toaster-z`/
`preview-z`) now references `var(--z-index-overlay-*)` — zero raw integers.
dialog/drawer gain an explicit `content-z` rung (drops the `calc(... + 1)`).
- Guard (contracts.test.ts, "overlay z-index against raw integers"): a recipe
`*-z` token must reference the scale, never a bare integer. Proven to catch
drift (a raw `'76'` makes it fail). Local `z-index: 0..5` (avatar/tabs/sticky)
is intra-component relative stacking — out of scope, stays.
- Docs: THEMING.md §35 rewritten to describe the consolidated scale + the
flat-band rationale + the guard; token table gains `--z-index-overlay-*`;
testing-and-tooling.md documents the catalogue guards (VG-8/SYS-1/A31/A30/
THEME-SYS-1).
Stacking order verified from the resolved CSS (deterministic z compare: content
70 < floating 80 < tooltip 90 < toast 1200; dropdown-in-dialog preserved). A
live browser check was blocked by a port conflict with another session's server.
check: 0 new type errors; the 5 guards green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
| `src/uix/contracts.test.ts` (vitest, via `npm run test` ) | **catalogue invariants** that keep declarations / recipes coherent as the framework grows. Each fails on drift naming the offender, and excludes the active-dev-track set so it stays green for the maintained catalogue: **VG-8** every morfo is `as const satisfies Morfo` , never `: Morfo` (a `: Morfo` annotation widens the literal so the schema can't check it); **SYS-1 scope-drift** a component shipping an `eidos/components/{c}/` recipe declares `'eidos'` in `scope` ; **A31** no per-item membership predicate (`isSelected` / `isItemPressed` / …) doing `.current.includes` (O(N²) — lift a `Set` , use `.has()` ); **A30** `inputId` registered with the parent Field in the constructor, not wrapped in a `$effect` ; **THEME-SYS-1** overlay z-index references the named `--z-index-overlay-*` scale, never a raw integer. |
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>
4 months ago
## 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.) |
The `data-*` contract is **not** a generated doc — it is the morfo itself, validated
live by `npm run morfo:check` . (A legacy `generate:contracts-docs` script and its
output `DATA_ATTRS.md` , both fossils of the removed `terra` layer, were deleted.)
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>
4 months ago
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.
docs(book): F7.5 (1/2) — checklist + audit guide + demo guide moved to docs/guides/
Three English guides moved verbatim (copy + link touch-ups, no
translation): COMPONENT_COMPLETION_CHECKLIST -> guides/
completion-checklist.md, COMPONENT_AUDIT_GUIDE -> guides/
component-audit.md, DEMO_AUTHORING_GUIDE -> guides/demo-authoring.md.
Tooling moved with its doc in the same pass: docs-check I5 now parses
the checklist at the new path (verified: 0 errors, rule tables found)
and component-audit.ts's two path strings updated. One reconciliation
in the audit guide's s5, applied as link-don't-copy: the copied v1
6-tab template list (stale vs the v2 9-tab guide it links, drawer
canary vs button) is replaced by a pointer to demo-authoring.md; the
domain canaries in s7 stay. demo-authoring's harness links now reach
into web/routes/uix/lib/ (the modules stay with the code); the deleted
pendiente_color_demos.md reference is marked TODO(reconcile). Corpus
swept: building-a-component (frontmatter related: + phases 6/8),
authoring, architecture/soma, theming/reference s5-parity,
testing-and-tooling, getting-started, README (E4 rows + component-audit
row added + two stale F7.3 paths in 'I want to' fixed). Remaining
warns = links to component-guide.md, which lands in F7.5 (2/2).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
- [`component-guide.md` ](./guides/component-guide.md ) — the build checklist that calls these.
- [`completion-checklist.md` ](./guides/completion-checklist.md ) — what `component:audit` enforces.