La lectura completa del corpus (6 lectores Opus, 6/6 censos, ~4,5M tokens)
destapo lo que la tanda 1 no vio, y esta tanda lo cierra:
- El modelo «Effects sync attrs» — repetido VERBATIM en cuatro capitulos
(morfo, soma-architecture, overview, active-architecture), en sus tablas
de piezas, en los cuatro pasos de trigger y en las dos frases-resumen —
muere: la pieza es la bolsa de render; ADom aplica solo el prewrite.
- morfo.md: partProps re-descrito (bolsa completa), la cadena causal de
commits, y las filas de la tabla de ejecutores (data/aria/role → bolsa).
- coincident: los DOS docstrings que ensenaban «in-flight» (types.ts y
morfo.md) se alinean con la equivalencia FIRMADA de sema.md §slider
(«emit-then-handler, like pre; declared indivisible») — el hallazgo del
informe queda refutado como defecto de runtime y reducido a esto.
- glossary: el kind fantasma `internal` (la clase exacta que docs-check:422
mata y su regex no ve en tablas markdown) → `public|private|virtual` real.
- El gate entra en la doctrina: check:gate/gate en el loop de verificacion
(testing-and-tooling y getting-started), eidos:lint como script npm en su
fila, y la tabla de Commands de morfo.md.
- Mi propia sobreafirmacion de ayer, calificada: el test SSR es un CANARIO
de dos representantes sobre mecanismo compartido, no un censo — el censo
por provider queda encolado (P1). morfo:check en getting-started declara
su alcance real (data-*; role/aria sin validador DOM).
- Supervivientes de C2c: el assert-vacio de cropper (la clase «guard sobre
VACIO pasa») retirado con acta; sticky/README deja de citar syncAttrs.
- gradient-builder/README: fila data-kind del Track + acotacion mesh v1.
- Handoff: DOS fe de erratas — «nunca los vio nadie» era falso (vistos y
reducidos 12→3 en cb554ae2e; lo que faltaba era la puerta) y la ley del
escritor imperativo gana su excepcion abierta (textarea autosize, con su
cierre correcto encolado).
Verificacion: docs:check 0/817 · cropper 19/19 · morfo schema+compile 133/133.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| `parts[].aria` | Effect of attrs — except NAMING attrs (`aria-label`), which ship in the part's render bag so the consumer's attr can win (see Step 4) | Reactive `aria-*` |
| `parts[].role` | Effect of attrs | Stable role |
@ -20,11 +20,11 @@ npm run dev # vite dev — routes resolve from web/routes/
Open the dev server. There are three route trees:
| Route | What it is |
| --- | --- |
| **`/uix`** | The UIX component system. `/uix/components/{name}` is an interactive testbed per component — try `/uix/components/toggle` and `/uix/components/dialog`. Each demo has **Live · API · Morfo · Sema · Recipe · A11y** tabs. |
| **`/active`** | The runtime artifacts (`arts`): `/active/docs/{name}` per artifact (auth, cache, http, format, …), plus `/active/get-started/*` and `/active/security`. |
| **`/temas`** | Themes (`/temas/grafito`) and motion (`/temas/animations`). |
| **`/uix`** | The UIX component system. `/uix/components/{name}` is an interactive testbed per component — try `/uix/components/toggle` and `/uix/components/dialog`. Each demo has **Live · API · Morfo · Sema · Recipe · A11y** tabs. |
| **`/active`** | The runtime artifacts (`arts`): `/active/docs/{name}` per artifact (auth, cache, http, format, …), plus `/active/get-started/*` and `/active/security`. |
| **`/temas`** | Themes (`/temas/grafito`) and motion (`/temas/animations`). |
## 2. The mental model (5 minutes)
@ -69,16 +69,19 @@ sema stamps the event, eidos paints — no layer reaches into another's job.
## 4. The verification loop
```bash
npm run check # types — svelte-check, expect 0 errors
npm run check:gate # types WITH policy — src/ owes zero; web/ vs the ledger
npm run check # raw svelte-check (web/ carries frozen ledger errors)
npm run test # vitest suite (two projects: browser client + node server)
npx vitest run <file> # one file
npm run gate # what the pre-push hook runs (docs/testing-and-tooling §The gate)
npm run lint # prettier --check (npm run format to fix)
```
For a component specifically:
```bash
npm run morfo:check # the real DOM vs the morfo contract
npm run morfo:check # the real DOM vs the morfo contract (data-* attrs;
# role/aria-* have no DOM validator yet — informe P1)
npm run smoke # runtime/hydration errors (needs `npm run dev` running)
npm run component:audit # acceptance matrix (see guides/completion-checklist.md)
npm run perm:check # re-validate morfo across state transitions
@ -17,80 +17,80 @@ New here? Start at [`docs/README.md`](./README.md).
## The layers
| Term | Meaning |
| --- | --- |
| **morfo** | The declarative contract (a component's "DNA"): its public DOM surface — parts, `data-*`/ARIA, keyboard, events — declared once in a typed object. Every other layer reads it. → [`architecture/morfo`](./architecture/morfo.md) |
| **soma** | The headless behavior layer: keyboard, focus, ARIA wiring, state machines, composition. No visuals. → [`architecture/soma`](./architecture/soma.md) |
| **sema** | The perceptual engine: turns a declared event into sound / haptic (runtime) and a `data-event-*` projection (for eidos), via a cascade. → [`architecture/sema`](./architecture/sema.md) |
| **eidos** | The visual layer: CSS recipes, tokens, themes, sizes, variants — reacts to the DOM attrs morfo promises. → [`architecture/eidos`](./architecture/eidos.md) |
| **active-uix** | The composition root that wires the layers — `createActiveUix` (standalone) or `attachActiveUix` (attach to an app). → [`architecture/active-uix`](./architecture/active-uix.md) |
| **ActiveDom / `$adom`** | The single reactive DOM service: the only sanctioned surface for managed DOM writes, listeners, queries, focus and scroll. |
| **pack** | An encapsulated opt-in collection above the layers (decorative leaves: no morfo, outside the acceptance matrix, one-way dependency). Admission rule + P contract → [`architecture/packs`](./architecture/packs.md) |
| **scene (`EngineScene` / `$scene`)** | The ambient-scene runtime art: mounts a WebGL/canvas-2D **effect** on a host with the citizenship done once (frame loop, off-view pause, DPR cap, mandatory reduced-motion, context loss/restore, budget, teardown). Effects are shared resources (`$scene/effects`) — the same one a pack mounts decoratively, `Aura` will mount semantically. → [`arts/scene/README`](../src/arts/scene/README.md) |
| **Ambient** | The first pack (`$packs/ambient`) — animated backgrounds. `<Ambient effect="…">` mounts a registered scene effect; colors accept theme tokens (P-4). → [`src/packs/ambient/README`](../src/packs/ambient/README.md) |
| **Aura** | The canonical **agent-presence** component: it materializes the `delegate` + `sustain` families by consuming `$scene/effects` semantically (intent → speed/amplitude/hue). The promotion path it walked is in [`architecture/packs`](./architecture/packs.md). → [`eidos/components/aura/README`](../src/uix/eidos/components/aura/README.md) |
| **Background** | The canonical **host of a surface's background layers** — image, video, pattern, gradient, scrim, or a scene the app mounts itself. A CHILD, never a wrapper: it renders inside the surface it dresses and pins itself behind that surface's content, so any element can host it and layout never moves. The parent is ADOPTED by a foundation rule rather than configured. The animated effect stays in the pack tier; the host is canon because it owns a contract (`aria-hidden` layers, the WCAG 2.2.2 pause control, a token surface). → [`eidos/components/background/README`](../src/uix/eidos/components/background/README.md) |
| **text effects** | A canon family of eidos components that treat REAL text content (`TextGradient`, `TextCircular`, `TextBlur`, `TextFocus`, `TextScramble`) plus the service `CountUp` — the animated siblings of the typographic primitives. Content stays the accessibility surface; the animation is presentation. Each self-documents (README + demo). |
| **morfo**| The declarative contract (a component's "DNA"): its public DOM surface — parts, `data-*`/ARIA, keyboard, events — declared once in a typed object. Every other layer reads it. → [`architecture/morfo`](./architecture/morfo.md) |
| **soma**| The headless behavior layer: keyboard, focus, ARIA wiring, state machines, composition. No visuals. → [`architecture/soma`](./architecture/soma.md) |
| **sema**| The perceptual engine: turns a declared event into sound / haptic (runtime) and a `data-event-*` projection (for eidos), via a cascade. → [`architecture/sema`](./architecture/sema.md) |
| **eidos**| The visual layer: CSS recipes, tokens, themes, sizes, variants — reacts to the DOM attrs morfo promises. → [`architecture/eidos`](./architecture/eidos.md) |
| **arts**| Runtime artifacts: the `Engine*` / `Active*` services (auth, cache, http, format, langs, dom, motion, …). → [`arts/README`](../src/arts/README.md) |
| **active-uix**| The composition root that wires the layers — `createActiveUix` (standalone) or `attachActiveUix` (attach to an app). → [`architecture/active-uix`](./architecture/active-uix.md) |
| **ActiveDom / `$adom`**| The single reactive DOM service: the only sanctioned surface for managed DOM writes, listeners, queries, focus and scroll. |
| **pack**| An encapsulated opt-in collection above the layers (decorative leaves: no morfo, outside the acceptance matrix, one-way dependency). Admission rule + P contract → [`architecture/packs`](./architecture/packs.md) |
| **scene (`EngineScene` / `$scene`)** | The ambient-scene runtime art: mounts a WebGL/canvas-2D **effect** on a host with the citizenship done once (frame loop, off-view pause, DPR cap, mandatory reduced-motion, context loss/restore, budget, teardown). Effects are shared resources (`$scene/effects`) — the same one a pack mounts decoratively, `Aura` will mount semantically. → [`arts/scene/README`](../src/arts/scene/README.md) |
| **Ambient**| The first pack (`$packs/ambient`) — animated backgrounds. `<Ambient effect="…">` mounts a registered scene effect; colors accept theme tokens (P-4). → [`src/packs/ambient/README`](../src/packs/ambient/README.md) |
| **Aura**| The canonical **agent-presence** component: it materializes the `delegate` + `sustain` families by consuming `$scene/effects` semantically (intent → speed/amplitude/hue). The promotion path it walked is in [`architecture/packs`](./architecture/packs.md). → [`eidos/components/aura/README`](../src/uix/eidos/components/aura/README.md) |
| **Background**| The canonical **host of a surface's background layers** — image, video, pattern, gradient, scrim, or a scene the app mounts itself. A CHILD, never a wrapper: it renders inside the surface it dresses and pins itself behind that surface's content, so any element can host it and layout never moves. The parent is ADOPTED by a foundation rule rather than configured. The animated effect stays in the pack tier; the host is canon because it owns a contract (`aria-hidden` layers, the WCAG 2.2.2 pause control, a token surface). → [`eidos/components/background/README`](../src/uix/eidos/components/background/README.md) |
| **text effects**| A canon family of eidos components that treat REAL text content (`TextGradient`, `TextCircular`, `TextBlur`, `TextFocus`, `TextScramble`) plus the service `CountUp` — the animated siblings of the typographic primitives. Content stays the accessibility surface; the animation is presentation. Each self-documents (README + demo). |
## Morfo vocabulary
| Term | Meaning |
| --- | --- |
| **part** | A named sub-element of a component (`provider`, `trigger`, `content`, …). |
| **archetype** | Cross-component classification of a part (`trigger`, `item`, `option`, …) — used for transversal eidos selectors and sema verbs. |
| **data-attr contract** | The stable markers a part emits: `data-{component}` (provider) and `data-{component}-{part}`. Never `data-soma-*`. The eidos/sema frontier. |
| **value sources (`v.*`)** | Typed origins for an ARIA/data value in morfo: `v.literal`, `v.stateRef`, `v.partRef`, `v.propRef`, `v.translationRef`. |
| **scope** | Which layers implement the component: `['soma']`, `['soma', 'eidos']`, … |
| **2-of-3 rule** | A morfo field is justified only if at least 2 of soma / sema / eidos consume it. |
| **compileMorfo** | Turns a morfo into a `CompiledMorfo` (resolved attr/keyboard/action plans + the closed set of CSS selectors), cached by morfo identity. |
| **expression** | How a morfo materializes its perceptual signature: `'pack'` \| `'family-default'` \| `'delegated'` \| `'none'`. |
| **data-attr contract**| The stable markers a part emits: `data-{component}` (provider) and `data-{component}-{part}`. Never `data-soma-*`. The eidos/sema frontier. |
| **value sources (`v.*`)** | Typed origins for an ARIA/data value in morfo: `v.literal`, `v.stateRef`, `v.partRef`, `v.propRef`, `v.translationRef`. |
| **scope**| Which layers implement the component: `['soma']`, `['soma', 'eidos']`, … |
| **2-of-3 rule**| A morfo field is justified only if at least 2 of soma / sema / eidos consume it. |
| **compileMorfo**| Turns a morfo into a `CompiledMorfo` (resolved attr/keyboard/action plans + the closed set of CSS selectors), cached by morfo identity. |
| **expression**| How a morfo materializes its perceptual signature: `'pack'` \| `'family-default'` \| `'delegated'` \| `'none'`. |
## Soma vocabulary
| Term | Meaning |
| --- | --- |
| **provider** | The concrete state class for a component or part. The root registers context; sub-parts read it. Exported as `Xxx.Provider`. |
| **SomaRuntime** | The morfo interpreter in soma. `runtime.part()` registers a part; `runtime.trigger(event)` sequences prewrite → emit → handler → effect-driven attrs. |
| **layer (soma)** | A shared behavior class consumed by providers: `Presence`, `FocusScope`, `Dismissal`, `ScrollLock`, `Gesture`, `SafePolygon`. → [`SOMA_ARCHITECTURE`](./architecture/soma-architecture.md) §6 |
| **Presence** | Animation-aware mount/unmount (waits for exit animations before removing). |
| **Active\<T\> / State\<T\>** | Reactive containers (readonly / mutable, exposing `.current`) that let runes be passed by reference between classes. |
| **context convention** | The `X.create()` / `X.get()` / `X.require()` static methods every context-using class follows. |
| **roving vs virtual focus** | Two keyboard strategies: real DOM focus with one `tabindex=0` (roving) vs focus stays on the input and items are `data-highlighted` via `aria-activedescendant` (virtual). |
| **polymorphic close** | One `emerge-close` event with `allowedFamilies`; the provider chooses the family at dismiss time (used by Dialog/Drawer/Popover). |
| **prewrite / commit**| DOM written imperatively *before* the semantic emit (`prewrite`, e.g. `data-last-action`) vs the structural state written *after* (`commit`). |
| **activeDir** | The direction resolver a wrapper runs for a component; returns `Active<Direction \| undefined>`, where `undefined` means nobody asserted a direction. → [`canon/direction-contract`](./canon/direction-contract.md) |
| **resolvedDir** | A provider's concrete direction — `activeDir`'s value with the fallback applied, once, for the component's own maths. |
| **provider**| The concrete state class for a component or part. The root registers context; sub-parts read it. Exported as `Xxx.Provider`. |
| **SomaRuntime**| The morfo interpreter in soma. `runtime.part()` registers a part; `runtime.trigger(event)` sequences prewrite → emit → handler → effect-driven attrs. |
| **layer (soma)**| A shared behavior class consumed by providers: `Presence`, `FocusScope`, `Dismissal`, `ScrollLock`, `Gesture`, `SafePolygon`. → [`SOMA_ARCHITECTURE`](./architecture/soma-architecture.md) §6 |
| **Presence**| Animation-aware mount/unmount (waits for exit animations before removing). |
| **Active\<T\> / State\<T\>** | Reactive containers (readonly / mutable, exposing `.current`) that let runes be passed by reference between classes. |
| **context convention**| The `X.create()` / `X.get()` / `X.require()` static methods every context-using class follows. |
| **roving vs virtual focus**| Two keyboard strategies: real DOM focus with one `tabindex=0` (roving) vs focus stays on the input and items are `data-highlighted` via `aria-activedescendant` (virtual). |
| **polymorphic close**| One `emerge-close` event with `allowedFamilies`; the provider chooses the family at dismiss time (used by Dialog/Drawer/Popover). |
| **prewrite / commit** | DOM written imperatively _before_ the semantic emit (`prewrite`, e.g. `data-last-action`) vs the structural state written _after_ (`commit`). |
| **activeDir**| The direction resolver a wrapper runs for a component; returns `Active<Direction \| undefined>`, where `undefined` means nobody asserted a direction. → [`canon/direction-contract`](./canon/direction-contract.md) |
| **resolvedDir**| A provider's concrete direction — `activeDir`'s value with the fallback applied, once, for the component's own maths. |
## Sema vocabulary
The values live in [`CANON.md`](./CANON.md); these are the term shapes.
| Term | Meaning |
| --- | --- |
| **family** | One of the **8** perceptual event families (contact · commit · signal · handle · emerge · shift · sustain · delegate). → CANON |
| **intent** | The evaluative load of an occurrence (neutral · affirm · fulfill · risk · threat · loss) — only on valenced families. → CANON |
| **verb** | The specific act within a family (`tap`, `select`, `close`, …). → CANON |
| **direction** | The sense of a traversal (`forward` \| `backward`, `SemaDirection`), stamped as `data-event-direction`. Two values, because the event NAME already separates `shift-enter-mode` from `shift-exit-mode`; what a name cannot carry is which way THIS occurrence went. Decided per emit and optional, like intent. A SENSE, not an axis — eidos maps it onto the inline axis so `:dir(rtl)` flips it. → CANON |
| **channel** | An expression modality. Sema runs two at runtime (sound, haptic) and projects `visual`; eidos owns the rest (motion/presence/depth/shape/color). → CANON |
| **hold** | The minimum perceptible duration a signal stays projected (`data-event-*` stamped during it). |
| **cascade** | The layered resolution of a perceptual signature (1 family base → 2 intent deltas → 3 per-event → 4 globals → 5a packs / 5b app rules). → [`architecture/sema`](./architecture/sema.md) |
| **persistence** | A signal's lifecycle, distinct from hold: `transient` \| `untilAction` \| `untilFix` \| `stateBound`. |
| **family**| One of the **8** perceptual event families (contact · commit · signal · handle · emerge · shift · sustain · delegate). → CANON |
| **intent**| The evaluative load of an occurrence (neutral · affirm · fulfill · risk · threat · loss) — only on valenced families. → CANON |
| **verb**| The specific act within a family (`tap`, `select`, `close`, …). → CANON |
| **direction**| The sense of a traversal (`forward` \| `backward`, `SemaDirection`), stamped as `data-event-direction`. Two values, because the event NAME already separates `shift-enter-mode` from `shift-exit-mode`; what a name cannot carry is which way THIS occurrence went. Decided per emit and optional, like intent. A SENSE, not an axis — eidos maps it onto the inline axis so `:dir(rtl)` flips it. → CANON |
| **channel**| An expression modality. Sema runs two at runtime (sound, haptic) and projects `visual`; eidos owns the rest (motion/presence/depth/shape/color). → CANON |
| **hold**| The minimum perceptible duration a signal stays projected (`data-event-*` stamped during it). |
| **cascade**| The layered resolution of a perceptual signature (1 family base → 2 intent deltas → 3 per-event → 4 globals → 5a packs / 5b app rules). → [`architecture/sema`](./architecture/sema.md) |
| **persistence** | A signal's lifecycle, distinct from hold: `transient` \| `untilAction` \| `untilFix` \| `stateBound`. |
## Eidos vocabulary
| Term | Meaning |
| --- | --- |
| **recipe** | A component's token + CSS definition (in `EidosConfig.recipes` / `{name}.css`). |
| **token** | A CSS custom property. Public: `--{component}-*`; private recipe-internal: `--_{component}-*`. Never `--eidos-*` / `--soma-*`. |
| **TSC (Token Scope Contract)** | Where each token is allowed to be emitted (`:root` / `[data-{c}]` / by color / by event) + transitivity validation. → [`canon/tsc`](./canon/tsc.md) |
| **variant** | A fixed visual archetype (`solid`, `outline`, `ghost`, …). Canon of eidos — a theme cannot invent or redefine one. |
| **role** | One of the **9** canonical color roles (`primary`, `secondary`, `tertiary`, `neutral`, `affirm`, `fulfill`, `risk`, `threat`, `loss`). |
| **scaling / density** | Orthogonal structural axes: global zoom (90–110) vs spacing (compact/comfortable/spacious). |
| **theme** | A retint of the perceptually-fixed: it changes *which hex* is `affirm`, never *what*`outline` means. → [`THEMING`](./theming/reference.md) |
| **`data-dir`** | A component's own direction attribute, carrying the resolved value — opt-in, for a recipe that needs a hook which always matches; native `dir` carries the raw value and is absent when nobody asserted one. |
| **RTL-1** | The eidos lint rule (`npm run rtl:check`) that flags a logical inline anchor paired with a physical inline translate in the same CSS block. |
| **`rtl-physical:`** | The sanctioned comment that exempts a block from RTL-1 when its geometry genuinely is physical: `/* rtl-physical: <reason> */`. |
| **recipe**| A component's token + CSS definition (in `EidosConfig.recipes` / `{name}.css`). |
| **token**| A CSS custom property. Public: `--{component}-*`; private recipe-internal: `--_{component}-*`. Never `--eidos-*` / `--soma-*`. |
| **TSC (Token Scope Contract)** | Where each token is allowed to be emitted (`:root` / `[data-{c}]` / by color / by event) + transitivity validation. → [`canon/tsc`](./canon/tsc.md) |
| **variant**| A fixed visual archetype (`solid`, `outline`, `ghost`, …). Canon of eidos — a theme cannot invent or redefine one. |
| **role**| One of the **9** canonical color roles (`primary`, `secondary`, `tertiary`, `neutral`, `affirm`, `fulfill`, `risk`, `threat`, `loss`). |
| **scaling / density**| Orthogonal structural axes: global zoom (90–110) vs spacing (compact/comfortable/spacious). |
| **theme**| A retint of the perceptually-fixed: it changes _which hex_ is `affirm`, never _what_`outline` means. → [`THEMING`](./theming/reference.md) |
| **`data-dir`** | A component's own direction attribute, carrying the resolved value — opt-in, for a recipe that needs a hook which always matches; native `dir` carries the raw value and is absent when nobody asserted one. |
| **RTL-1**| The eidos lint rule (`npm run rtl:check`) that flags a logical inline anchor paired with a physical inline translate in the same CSS block. |
| **`rtl-physical:`** | The sanctioned comment that exempts a block from RTL-1 when its geometry genuinely is physical: `/* rtl-physical: <reason> */`. |
npm run lint # prettier --check · npm run format to fix
npm run check:gate # types WITH the policy: src/ owes ZERO; web/ measured
# against the shrinking ledger scripts/check-debt.ts
npm run check # raw svelte-check (src/ expect 0; web/ carries the
# frozen ledger errors, which may only shrink)
npm run test # the vitest suite (one run)
npm run gate # everything the pre-push hook runs (see §The gate)
npm run lint # prettier --check · npm run format to fix
# (NOT in `gate` yet — pre-existing repo-wide format debt;
# the one-shot is `npm run format` on a quiet tree)
```
For a component you also run the contract validators (below). A change is not
@ -61,7 +67,7 @@ These catch classes of bug that `check` (types) and an HTTP 200 (SSR) miss.
| `npm run layer:check` | a **shared visual layer losing a cascade fight** — for every element carrying a layer hook (`data-viewport-placement`), asserts the computed `position`, that the stacking token resolved, and that no override slot declared inline computes to nothing (the signature of a custom-property CYCLE). Needs `npm run dev`. Its consumer list is DERIVED from who imports the layer, so a component joins the day it migrates. **Deliberate hole, stated in the script**: geometry. `getComputedStyle` reports the USED value, so an `inset: auto` reads back as pixels (measured: `-1976.7px`) — there is no property-level way to tell a dead `calc()` from an intended value. |
| `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). |
| `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.) |
| `npm run eidos:lint` | classifies every `[data-*]` selector in eidos CSS as **morfo-backed / eidos-only / invalid** — drift between the morfo contract and the CSS. A `gate` member since P0 fase B (audit 2026-08-26); the architectural defense is still the typed `semaSelector` builder — see the "Eidos drift defense" rule in CLAUDE.md. |
| `src/uix/eidos/shared-layer-contract.test.ts` (vitest) | the **text half** a browser cannot cover for a shared layer: every zone has a rule, `stretch` stays on the axes it was scoped to, no axis is read without its override slot, no geometry rule keys on the component identity, and each consumer imports the layer / stamps the hook / mints no parallel token / re-asserts `position` when the primitive it composes declares one. It exists for ONE thing the computed check is blind to: `getComputedStyle` of a safe-area slot returns `"0px"` on desktop, so a `:dir(rtl)` remap with one half flipped reads identical to a correct one on every CI machine and only surfaces on a notched phone, sideways, in RTL. |
| `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. |
@ -106,10 +112,12 @@ and headless tests working:
`npm run smoke` exists to catch.
- **The morfo contract server-renders** — since the render bag became the
single attr pipeline (P0 fase C, audit 2026-08-26), a part's `role` /
`aria-*` / `data-state` / literals ship in the server HTML, not only after
hydration. `src/uix/soma/ssr-contract.test.ts` (server project, node — no
window) pins it with a real-composition harness; it was born red against the
old client-only effect and is the canary for the whole pipeline.
`aria-*` / `data-state` / literals resolve at render time, server included.
The mechanism is SHARED (`partPropsForRegistration`) and pinned by
`src/uix/soma/ssr-contract.test.ts` (server project, node — no window) with
two representatives, Toggle and RadioGroup — a CANARY, not a per-provider
census; the per-component SSR snapshot census is still owed (informe P1).
It was born red against the old client-only effect.