docs(sema,theming): el orden real, como se autora cada canal, y el hueco del theming

Tres cosas que el cambio del sonido dejo sin plasmar.

1. EL ORDEN CANONICO MENTIA. La numeracion 1·2·3·4·5a·5b esta citada como
   canonica en sema.md, resolver.ts y engine.ts, y despues del reorden era
   falsa para `sound`: un pack ya no autora en 5a, lo hace en 1.5. Corregido
   en los tres sitios a la vez, con el porque — todo lo que AUTORA un sonido
   corre ahi conservando su precedencia relativa; solo cambio su posicion
   respecto al intent. El resto de la regla (channels/haptic/hold) mantiene
   el «gana el ultimo» porque no son ejes evaluativos.

2. COMO SE AUTORA CADA CANAL (CANON §7). El reparto por dueño estaba
   escrito; lo que un componente ESCRIBE, no. Tabla nueva: el canal visual
   no se escribe (se declara el evento y eidos reacciona), `sound` es UN
   NOMBRE, `haptic` es un `kind`. La regla es la misma en las tres filas y
   ese es el punto: un componente dice QUE ocurre, nunca cuan fuerte, cuan
   brillante ni cuanto dura. El sonido era la excepcion hasta ayer.

3. EL ANALISIS DEL THEMING que el autor pidio (theming/channels.md §5b).

   Lo que el cambio NO toco: nada de eidos. El canal visual se proyecta
   estampando data-event-*, y esa ruta quedo intacta — ni una receta, ni un
   token, ni un selector. Eidos no sabe que existe el sonido y no le hizo
   falta. Lo que cambio fue DONDE se autora (un catalogo en vez de 71
   ficheros) y CUANDO se aplica (antes del intent), ambos dentro de sema.

   Lo que si cambio, y es lo util: `applyTheme(seed)` retunea SEIS ejes
   visuales (color·type·depth·shape·space·gradient) y el sonido tiene CERO.
   Antes esa asimetria se justificaba sola — con 214 reglas autorando 33
   firmas a mano no habia objeto que retunear. Hoy hay exactamente uno:
   16 nombres en un const. Un eje `sound` en ThemeSeed seria la misma forma
   que los otros seis.

   Y las tres puertas de personalizacion, medidas, ninguna llega al
   catalogo: `overrides.runtime` recorre rutas de SEMA_MAP y el catalogo no
   esta en el mapa (ademas se traga las erratas, S-09); `overrides.cascade`
   es por selector, no un tema; `masterGain` se descarta en SILENCIO
   (S-05/S-32). Un producto puede silenciar y puede pisar una ocurrencia,
   pero NO puede re-voceear el sistema. Queda escrito en vez de ser
   folclore.

⚠️ chronos: `src/uix/sema/components/chronos.ts` entro en la migracion de
`769e426c5` pese a ser de escritura excluida. Era forzoso — con el tipo
estrechado, dejarlo sin migrar rompe la compilacion del arbol entero — y
el cambio es mecanico (soundTuning(...) -> nombre), sin decision de diseño.
Su SPEC.md NO se ha tocado y sigue citando `soundTuning('commit.soft')`.

VERIFICADO: sema+morfo+sound 437/437 · check 75 · docs:check 0/618.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-dir-prefs
dev 2 months ago
parent fb7759b849
commit 1302f28a5d

@ -220,6 +220,29 @@ The framework **splits these channels by owner** (decision recorded in
- **eidos** materializes the visual channels (motion / presence / depth / shape /
color) by reacting in CSS to `data-event-*` + the morfo's state attrs.
**How each channel is AUTHORED** — the owner split says who materializes a
channel; this says what a component actually WRITES, which is the part authors
get wrong:
| Channel | The component writes | Where the values live |
| --- | --- | --- |
| visual (motion / color / presence / depth / shape) | nothing — it declares the EVENT; eidos reacts in CSS to `data-event-*` | eidos recipes + the token engines |
| `sound` | **one NAME**: `sound: 'soft'` — a key of `SOUNDS`, or `SILENT` | [`sound-names.ts`](../src/uix/sema/sound-names.ts), the single catalogue |
| `haptic` | a categorical `kind` (`tick` / `tap` / `pulse` / `success` / …) | `HapticChannel` maps kinds to patterns |
The rule is the same in all three rows and it is the point: **a component says
WHAT occurs, never how loud, how bright or how long.** The sound row was the
exception until 2026-08-06 — packs carried an open bag of eight continuous
axes, 43 % of the rules said nothing but a volume, and 69 of them erased the
contour the intent had just set. It is now a word, enforced by the type.
Adding a sound means **adding a NAME to the catalogue**, never widening a rule:
made once, in one file, where the next component reuses it. Detail and the
resolution order in [architecture/sema.md](./architecture/sema.md); what a
`.wav` can and cannot carry (the intent only moves its LEVEL — physics, not
policy) in [decisions/book-deviations.md](./decisions/book-deviations.md) D.7,
retired but kept for that measurement.
So the book's 8 channels are conceptual; at runtime they are owned by different
layers. `hold` (the registration FLOOR — a minimum, never a ceiling: the
expression completes before unstamping, capped only by the channel's

@ -176,19 +176,37 @@ On every `emit`, the engine:
```
1. FAMILY base — SEMA_MAP.families[signal.family].base
sound / haptic + activeChannels + hold
1.5 THE SOUND — the catalogue NAME the morfo event and the cascade
select (sound-names.ts), in that relative order.
HOISTED above the intent — see below.
2. INTENT deltas — SEMA_MAP.intents[signal.intent] when present;
numbers ADD by default — they are modifiers
3. MORFO overrides — signal.overrides + signal.channels
3. MORFO overrides — signal.overrides + signal.channels, minus its `sound`
(numbers REPLACE by default — they are set values)
4. RUNTIME overrides — engineOpts.overrides.runtime (path-based globals;
baked into the map in the constructor; numbers REPLACE)
5a. PACK cascade — engineOpts.components (per-component packs)
5a. PACK cascade — engineOpts.components (per-component packs), minus
their `sound`: only channels / haptic / hold land here
5b. APP cascade — engineOpts.overrides.cascade (appended after the
packs; wins specificity ties by declaration order).
CSS-like selectors against signal.target with the
data-event-* already stamped; numbers REPLACE.
```
**Why 1.5 exists, and why it is not a wart.** Everything that AUTHORS a sound
runs there, keeping its old relative precedence (morfo event first, then the
cascade, which still wins). Only its POSITION with respect to the intent
changed. Before 2026-08-06 the sound was authored at 5a/5b — after the intent
and in replace mode — so a component's chosen sound erased the evaluative
profile that had just been applied: a dialog with `threat` sounded exactly like
a neutral one. Three mechanisms existed to police that inversion (D.7, the S-07
finding and a dedicated guard); all three are gone, because with the sound
underneath there is no way to express the inversion.
Everything else keeps the CSS-like «last match wins» reading, because
`channels` / `haptic` / `hold` are not evaluative axes: the intent has nothing
to say about them that a later layer would be overruling.
3. Dispatches to each channel with the resolved signature.
4. Awaits the VisualChannel (which contributes the hold).
5. Runs the cleanup of the handles returned by `prepare`.
@ -343,10 +361,12 @@ canonized at the component-audit checkpoint (verdicts S3a/S11 — historical
record in [`docs/audit/components/_veredictos.md`](../audit/components/_veredictos.md)):
four legitimate stances, each with shipped exemplars —
1. **`'pack'`** — a cascade in `sema/components/{kebab}.ts` tunes the
signature per event. Justified when the pattern ADDS meaning beyond
family + intent deltas (date-field's composed tunings; menubar's in-place
note: "subtle commit + tap haptic for high-frequency top-level triggers").
1. **`'pack'`** — a cascade in `sema/components/{kebab}.ts` NAMES the sound
per event. Justified when the pattern ADDS meaning beyond family + intent
deltas (date-field naming a fall for its clear; menubar's in-place note:
"subtle commit + tap haptic for high-frequency top-level triggers"). A pack
that only restates the family default is not a pack — it is noise, and the
type makes that cheap to see: one word, or nothing.
2. **`'family-default'`** — family base + intent deltas suffice. Legitimate
when (a) the gesture is generic contact (button), (b) the book itself
counsels restraint (command, citing ch. 22 §8: celebrating at the click is

@ -130,6 +130,48 @@ foundation. `clearTheme()` reverts everything. For surgical per-axis tweaks,
the individual `apply*` remain. No reference system gathers the six
perceptual axes under a single runtime theme builder.
## 5b. How each channel is AUTHORED — and the asymmetry that is left
The sextet above retunes the **visual** channel at runtime. Sound and haptic
have no equivalent, and after the 2026-08-06 rework that gap is worth naming
precisely, because the reason it existed is gone.
| Channel | Where a component AUTHORS it | Where a THEME retunes it |
| --- | --- | --- |
| visual — color · motion · depth · shape · space | eidos recipes, reacting in CSS to `data-event-*` | `applyTheme(seed)` + the six `apply*` (runtime, atomic) |
| **sound** | **names one entry of `SOUNDS`** (`src/uix/sema/sound-names.ts`) — a word, never a parameter | **nothing** — see below |
| **haptic** | a categorical `kind` (`tick` / `tap` / `pulse` / …) | **nothing** |
**What the sound rework did NOT change.** Nothing in eidos. The visual channel
is projected by stamping `data-event-*`, and that path was untouched: no
recipe, no token, no selector moved. Eidos does not know sound exists and did
not need to. The rework changed WHERE the sound is authored (a catalogue
instead of 71 pack files) and WHEN it is applied (before the intent instead of
after it) — both entirely inside sema.
**What it did change, and it is the useful part.** Until then, "theming the
sound" was not a coherent idea: 214 cascade rules authored 33 hand-written
signatures across 71 files, and there was no object to retune. Today there
is exactly one — sixteen names in one const — so a `sound` axis in `ThemeSeed`
would be the same shape as the other six, and a brand could ship its own
`soft` the way it ships its own colour ramp.
**The measured surface today, for anyone who tries.** Three doors, none of
which reaches the catalogue:
- `overrides.runtime` — path-based mutations of `SEMA_MAP`, so it reaches
family bases and intent deltas but **not** the catalogue, which is a module
const outside the map. It also swallows path typos in silence (audit S-09).
- `overrides.cascade` — app-level rules; the type stays open on purpose so an
app CAN author a raw signature. But it is per-selector, not a theme: you
restyle one occurrence, not the word `soft` everywhere.
- `masterGain` via `createActiveUix` — **discarded in silence** (audit S-05 /
S-32): the engine filters the key before the channel is built.
So: **a product can silence sound, and can override one occurrence, but cannot
re-voice the system.** That is a real gap, it is now cheap to close, and it is
not closed — recorded here rather than left as folklore.
## 6. Position vs the references
No reference system gathers the channels under **one semantic model**.

@ -12,6 +12,10 @@
*
* Override layers (ver `resolver.ts`):
* 1. family base — SEMA_MAP.families
* 1.5 the SOUND — the catalogue name selected by the morfo
* event and the cascade (sound-names.ts),
* applied BEFORE the intent so the
* evaluative profile always survives it
* 2. intent deltas — SEMA_MAP.intents (any family with intent)
* 3. morfo per-event — signal.overrides + signal.channels
* 4. runtime path overrides — engineOpts.overrides.runtime

@ -4,10 +4,19 @@
*
* Resolution cascade (each layer overrides the previous):
*
* 1. FAMILY base — `SEMA_MAP.families[signal.family].base`
* 2. INTENT deltas — `SEMA_MAP.intents[signal.intent]` when present
* 3. MORFO overrides — `signal.overrides` / `signal.channels`
* (from the morfo event, copied by SomaRuntime)
* 1. FAMILY base — `SEMA_MAP.families[signal.family].base`
* 1.5 THE SOUND — the NAME the morfo event and the cascade select,
* resolved through `sound-names.ts`. Hoisted ABOVE the
* intent on 2026-08-06: everything that AUTHORS a
* sound runs here, keeping its old relative precedence
* (morfo event first, then cascade), so the intent is
* always the last word on the evaluative axes. That is
* what made D.7 / S-07 inexpressible rather than
* merely forbidden.
* 2. INTENT deltas — `SEMA_MAP.intents[signal.intent]` when present
* 3. MORFO overrides — `signal.overrides` / `signal.channels`
* (from the morfo event, copied by SomaRuntime;
* its `sound` already ran at 1.5)
* 4. RUNTIME overrides — `options.runtimeMap` already has
* `engineOpts.overrides.runtime` baked in by the
* engine via `applyMapOverrides()` at construction
@ -19,7 +28,9 @@
* against `signal.target` AFTER the engine has
* stamped `data-event-*` on it (see `stamp.ts`).
* Rules are flat: `{ selector, priority?,
* channels?, sound?, haptic? }`. The selector is
* channels?, sound?, haptic? }`, and their `sound` is
* a catalogue NAME applied at 1.5 — only `channels` /
* `haptic` / `hold` land here. The selector is
* matched natively via `target.matches()` /
* `target.closest()`.
*

Loading…
Cancel
Save

Powered by TurnKey Linux.