You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/theming/channels.md

237 lines
12 KiB

---
title: The channels as one system — synthesis
type: notes
audience: human + agent
authority: E3 — the capstone tying the per-channel RFCs to the book's thesis
status: current
source: migrated from src/uix/eidos/CHANNELS_SYNTHESIS.md (2026-07-02, docs-book F7.3)
---
# The channels as one system — synthesis ("Diseñando lo que ocurre")
> The **capstone** of the per-channel RFCs. The thesis of the book *Diseñando
> lo que ocurre*: an interaction **is not** color, or movement, or sound — it
> is **one perceptual occurrence spread across N channels**, under the
> **two-moment** model. This ties together [`rfc-color-engine`](../rfcs/rfc-color-engine.md) ·
> [`rfc-typography`](../rfcs/rfc-typography.md) · [`rfc-depth`](../rfcs/rfc-depth.md) ·
> [`rfc-shape`](../rfcs/rfc-shape.md) ·
> [`rfc-structure`](../rfcs/rfc-structure.md). Live demo: **`/temas/orquesta`** (the mixer).
## 1. Two moments, joined by a token: sema emits, eidos reads
An occurrence flows through **two moments**, connected by the **token**:
- **Sema's moment (emission)** — sema evaluates the occurrence and **emits**
it. Sound + haptics it **executes right there** (runtime channels); for the
visual, it **stamps it as tokens** `data-event-*` (family · intent ·
phase). Sema **knows no DOM/CSS**.
- **Eidos's moment (materialization)** — eidos **reads** those tokens
(+ `data-state`) and **materializes** them in CSS (the visual channel). It
is the **sole visual owner**.
The **token is the contract**: sema writes, eidos reads — which is why the
layers decouple (sema DOM-agnostic, eidos free of semantic logic).
Over that producer → consumer axis runs the **temporal** axis (motion F1) —
*which* token:
| Token | Nature | Who writes it | Eidos reads it as |
|---|---|---|---|
| `data-state` | persistent — what the element **is** | soma / morfo | `presets` |
| `data-event-*` | transient — what **occurs** (during the `hold`) | sema (emits) | `signatures` |
`sequence` (`pre` / `coincident` / `post`) orders the two.
## 2. Expression channels (the book) vs sema channels (runtime)
The book has **8 expression channels** (perceptual dimensions). The framework
docs(sema): la doctrina deja de mentir — deriva propia, copias podridas y dos leyes muertas Pasada de saneamiento documental tras los tres commits de sonido, con el informe de AUDIT-sema-2026-08-05 como mapa. Tres clases de defecto. 1) DERIVA QUE YO MISMO DEJE (lo mas urgente) El 05 renombre las claves silenciadoras a `commit.silent` / `emerge.silent` y el 06 las elimine del catalogo — dejando media docena de docs citando claves que ya no existen, y afirmando ademas comportamientos que ya no ocurren: - switch / toggle / toggle-group README: decian «silent-by-default (`commit.silent`)». Hoy es `commit.medium` (gain 0.1) y suenan. Corregidos con el nivel real y su razon (un tercio de una pulsacion de boton). - tooltip README + docblock del pack: decian que el tuning «resta el gain de la familia, asi que neutral emite a 0» y que «threat / fulfill siguen aflorando». Ambas cosas son falsas desde el 06: es `SILENT`, el resolver retira el canal y NADA aflora, porque no queda ganancia que subir. - toggle-group apuntaba a `LIBRO_VARIACIONES_Y_EXTENSIONES.md`, que es un stub movido; ahora apunta a book-deviations. - La nota del renombrado en D.5 decia «hoy es `commit.silent`»: una clave que vivio UN DIA. Marcada como tal, con la lista de las que siguen vivas. 2) COPIAS PODRIDAS — la ley del corpus es enlazar, no copiar, y estas dos entradas la incumplian - D.9 transcribia `SEMA_HOLDS_BY_INTENT` entera y se habia quedado atras: D.12 corrigio DOS valores el 2026-07-06 (`commit.fulfill` noticed→settled, `signal.loss` noticed→brief, ambos contra el texto del libro) y la copia siguio afirmando los viejos un mes. Sustituida por el puntero a holds.ts + el enumerado generado, con la leccion escrita en el sitio. - sema.md transcribia `SEMA_VERBS` y le faltaban DOS verbos vivos: `commit.unselect` y `handle.zoom`. Retirada; queda el puntero a verbs.ts y a vocabularies.md, que si se genera y tiene guard de frescura. 3) LEYES QUE LA PRACTICA YA HABIA DEROGADO, Y NADIE REGISTRO - El contrato `emit` publicaba `Promise<void>`; devuelve `Promise<string>` desde D.9. Corregido, y explicado que ese string es el id de la ocurrencia — el unico asidero para cerrar una senal persistente con `clear`. - channels.md fijaba «3 canales runtime» y D.8 cerraba la puerta a Announce («hoy no»). El `AnnounceChannel` existe desde el 2026-07-04: built-in, opt-in y exportado. channels.md pasa a 4 con la distincion que importa (visual/sound/haptic EXPRESAN; announce SUSTITUYE) y D.8 queda marcada PARCIALMENTE SUPERSEDED. - sema.md prescribia `{ announce: uix.announce }` — y eso NO COMPILA: las dos firmas no casan (bolsa de opciones vs posicional). Ahora ensena el adaptador de una linea que si compila, y declara que ninguna raiz cablea el canal hoy, asi que activarlo cae en el fallback que anade un SEGUNDO par de live regions. El arreglo de codigo queda sin tomar: es decision de diseno. - La cabecera de engine-sound.ts y la fila de arts/README seguian afirmando como MECANISMO que «`prefs.sound` mapea al bus ui», que la auditoria AU-4 ya habia corregido en el README del propio arte: lo garantizado por construccion es el NEGATIVO (ninguna politica de UI escribe el bus content). AMBIGUEDADES QUE NO RESUELVO PORQUE SON TUYAS, pero que dejan de estar escondidas: D.7 prohibe canonizar samples fuera de `signal` y `proof-of-human` los usa en dos eventos `commit`, aplastando la modulacion por intent que esa misma entrada existe para proteger —y el comentario del pack afirma literalmente lo contrario de lo que hace—; y D.8 declara que los packs deben respetar el `activeChannels` de la familia mientras sema.md prescribe lo contrario y el pack de dialog deja dos reglas inertes. Ambas quedan marcadas con su estado real y los dos caminos excluyentes, pendientes de tu firma. VERIFICADO: docs:check 0/616 · sonido + sema 244/244 · cero referencias vivas a las tres claves muertas (las que quedan en book-deviations son historia declarada como tal, y las de cronica no se reescriben) · prettier: los 6 docs con avisos ya estaban sucios en HEAD, no los toco. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
implements them with sema's **runtime channels**, which are **4** — and here
is the key that avoids the classic confusion:
| Sema channel (runtime) | Covers (the book's expression) | Who materializes it |
|---|---|---|
| **visual** (sema projects `data-event-*`) | movement · presence · depth · shape · color | **eidos** — CSS over `data-event-*` + `data-state` |
| **sound** | sound | sema (`chans/sound`) |
| **haptic** | haptics | sema (`chans/haptic`) |
docs(sema): la doctrina deja de mentir — deriva propia, copias podridas y dos leyes muertas Pasada de saneamiento documental tras los tres commits de sonido, con el informe de AUDIT-sema-2026-08-05 como mapa. Tres clases de defecto. 1) DERIVA QUE YO MISMO DEJE (lo mas urgente) El 05 renombre las claves silenciadoras a `commit.silent` / `emerge.silent` y el 06 las elimine del catalogo — dejando media docena de docs citando claves que ya no existen, y afirmando ademas comportamientos que ya no ocurren: - switch / toggle / toggle-group README: decian «silent-by-default (`commit.silent`)». Hoy es `commit.medium` (gain 0.1) y suenan. Corregidos con el nivel real y su razon (un tercio de una pulsacion de boton). - tooltip README + docblock del pack: decian que el tuning «resta el gain de la familia, asi que neutral emite a 0» y que «threat / fulfill siguen aflorando». Ambas cosas son falsas desde el 06: es `SILENT`, el resolver retira el canal y NADA aflora, porque no queda ganancia que subir. - toggle-group apuntaba a `LIBRO_VARIACIONES_Y_EXTENSIONES.md`, que es un stub movido; ahora apunta a book-deviations. - La nota del renombrado en D.5 decia «hoy es `commit.silent`»: una clave que vivio UN DIA. Marcada como tal, con la lista de las que siguen vivas. 2) COPIAS PODRIDAS — la ley del corpus es enlazar, no copiar, y estas dos entradas la incumplian - D.9 transcribia `SEMA_HOLDS_BY_INTENT` entera y se habia quedado atras: D.12 corrigio DOS valores el 2026-07-06 (`commit.fulfill` noticed→settled, `signal.loss` noticed→brief, ambos contra el texto del libro) y la copia siguio afirmando los viejos un mes. Sustituida por el puntero a holds.ts + el enumerado generado, con la leccion escrita en el sitio. - sema.md transcribia `SEMA_VERBS` y le faltaban DOS verbos vivos: `commit.unselect` y `handle.zoom`. Retirada; queda el puntero a verbs.ts y a vocabularies.md, que si se genera y tiene guard de frescura. 3) LEYES QUE LA PRACTICA YA HABIA DEROGADO, Y NADIE REGISTRO - El contrato `emit` publicaba `Promise<void>`; devuelve `Promise<string>` desde D.9. Corregido, y explicado que ese string es el id de la ocurrencia — el unico asidero para cerrar una senal persistente con `clear`. - channels.md fijaba «3 canales runtime» y D.8 cerraba la puerta a Announce («hoy no»). El `AnnounceChannel` existe desde el 2026-07-04: built-in, opt-in y exportado. channels.md pasa a 4 con la distincion que importa (visual/sound/haptic EXPRESAN; announce SUSTITUYE) y D.8 queda marcada PARCIALMENTE SUPERSEDED. - sema.md prescribia `{ announce: uix.announce }` — y eso NO COMPILA: las dos firmas no casan (bolsa de opciones vs posicional). Ahora ensena el adaptador de una linea que si compila, y declara que ninguna raiz cablea el canal hoy, asi que activarlo cae en el fallback que anade un SEGUNDO par de live regions. El arreglo de codigo queda sin tomar: es decision de diseno. - La cabecera de engine-sound.ts y la fila de arts/README seguian afirmando como MECANISMO que «`prefs.sound` mapea al bus ui», que la auditoria AU-4 ya habia corregido en el README del propio arte: lo garantizado por construccion es el NEGATIVO (ninguna politica de UI escribe el bus content). AMBIGUEDADES QUE NO RESUELVO PORQUE SON TUYAS, pero que dejan de estar escondidas: D.7 prohibe canonizar samples fuera de `signal` y `proof-of-human` los usa en dos eventos `commit`, aplastando la modulacion por intent que esa misma entrada existe para proteger —y el comentario del pack afirma literalmente lo contrario de lo que hace—; y D.8 declara que los packs deben respetar el `activeChannels` de la familia mientras sema.md prescribe lo contrario y el pack de dialog deja dos reglas inertes. Ambas quedan marcadas con su estado real y los dos caminos excluyentes, pendientes de tu firma. VERIFICADO: docs:check 0/616 · sonido + sema 244/244 · cero referencias vivas a las tres claves muertas (las que quedan en book-deviations son historia declarada como tal, y las de cronica no se reescriben) · prettier: los 6 docs con avisos ya estaban sucios en HEAD, no los toco. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
| **announce** | — (accessible substitute, not a book channel) | sema (`chans/announce`) |
`announce` joined on 2026-07-04 as a built-in opt-in channel: it is the
accessible counterpart the book demands when a modality is unavailable
(`BK-SIGNAL-A11Y`), not a ninth perceptual dimension. `visual`, `sound` and
`haptic` are the three that express; `announce` is the one that substitutes.
> **Don't get confused**: **motion · depth · shape · color are NOT "eidos
> channels"** — they are sema's **`visual` channel**, which sema projects and
> **eidos materializes**. Eidos is the visual channel's *materializer*, not
> the owner of channels of its own. The eidos RFCs (`COLOR/DEPTH/SHAPE_ENGINE`)
> describe *how* eidos materializes each visual facet — not independent
> channels.
Plus **space** ([`rfc-structure.md`](../rfcs/rfc-structure.md)) — **structural,
not expressive**: it doesn't "occur", it is the stage (state-only).
## 3. The layer chain (each with its role)
An occurrence **crosses the layers** — it is not "all sema", and eidos is not
a shim:
1. **morfo** — **declares** the event and its semantics (family · intent ·
verb). The contract / DNA; pure TypeScript, no runtime.
2. **soma** — **fires** it (`runtime.trigger`): the sequence `prewrite →
sema.emit → handler →` state attrs. It writes `data-state` (the
state-moment).
3. **sema** — **emits** it: dispatches the signal to its channels —
**executes** `sound` + `haptic`; **projects** the visual channel by
stamping `data-event-*`. It knows no DOM/CSS.
4. **eidos** — **materializes** it: reads `data-state` (`presets`) +
`data-event-*` (`signatures`) and renders them with its **token engines**
(color · motion · depth · shape · space — the RFCs). It is the **complete
visual system** and the sole owner of the visual.
> Eidos does **not "make sema visible"**: sema contributes the **what**
> (family / semantic intent), eidos contributes the **how** (the visual
> vocabulary and its materialization). Co-layers, not one subordinate.
(The full canonical narrative lives in `CLAUDE.md` → "Sema: open channel
registry".)
## 4. The composition — one event, N channels
The **signature** (`BUILTIN_SIGNATURES` + `BUILTIN_KEYFRAMES`) is
**cross-modal**: a single keyframe carries several modalities. A real example
(`press-squeeze`, family `contact`):
```
contact · press → scale 0.96 (motion)
+ box-shadow → flat (depth recede)
+ --shape-smoothing 2→3 (shape: the corner firms up)
+ tick (sema sound)
+ vibration (sema haptic)
```
One `engine.emit(...)` stamps `data-event-*` (the **visual** channel reacts —
eidos materializes it as color/motion/depth/shape) **and** fires sound +
haptic — from the **same** event. There are no systems coordinating by hand;
there is one occurrence expressing itself through sema's channels.
## 5. Runtime builders — the sextet (an open cage)
Every axis retunable at runtime, same pattern (seed → managed block), opt-in
over the authored scale:
fix(cleanroom): F2+F3+F4-C+SEM-4s1 — lote mecánico, censos con guard, corpus documental y el close polimórfico de los pickers VIVO F2 — lote mecánico (13 ítems): - DEP-2 ogl eliminado (0 imports) · DEP-1 clsx inlineado como toClassString propio + suite de contrato (props.test.ts; soma.md §12 cerrado). - THM-7: los 5 selectores manuales de sema.md reescritos con semaSelector (los ejemplos [data-toast-root] apuntaban a un part INEXISTENTE — la deriva que el builder previene, demostrada en el propio doc). - MOR-1 escape isomorfo + validación de attr-names en semaSelector + 9 tests (selectors.test.ts, matches() real con comillas/corchetes) · MOR-2 partMarkerAttr = única fuente compilador↔builder + test de paridad · MOR-3 _resetCompileCache borrado (0 usos). - SOM-2 keydown continue en match sin handler + keyboardFixtureMorfo · SOM-1 no-await de handlers (censo async = 0; contrato V1 cumplido) + pin. - SEM-2 trigger pre-attacha catch con logger (void trigger sin unhandled rejection; throw intacto para awaiters) + pin · SEM-3 fallback muerto de applyDominance → skip defensivo + timer tope de awaitExpression cancelado · SEC-1 adjudicado YA implementado (assertCssVariableValue desde 2026-05-11) + pin del path de VALOR. - accordion → outline (§32; su outline:none dejaba CERO anillo en HCM) — verificado en vivo · THM-6 radius-full 9999px · EID-4 recuentos 33. F3 — censos con guard: - SOM-3 cerrado: announcer + image-provider migrados a scheduler-preferred (consumidores cableados: date/time-field vía soma.uix.timers; avatar/image vía eidos.timers — verificado en vivo); guard de timers ENSANCHADO de soma/components a TODO soma y pasado a EVIDENCIA (setTimeout exige .schedule( en el fichero — layers/ y datetime/ escapaban del ámbito viejo). - THM-5: R-4.7 nueva (válvula same-line /* important: <razón> */, escaneo comment-blanked) + las 15 declaraciones anotadas con su razón + canon recipe-contract §3/§4. - SOM-4 adjudicado: el censo/guard YA existían (49 pins); knob/mask-field/ timeline pinneados (overrides documentados en call-site); media-player Batch-4 (35 hits, cero renderProps) = único batch restante, registrado. - THM-4 doctrinado en eidos.md §unused (comportamiento/composición = legítimo; deuda = eje visual sin consumidor; hotspots por lotes). F4-C — corpus documental (decisiones de usuario aplicadas): - DOC-3: los 15 enlaces muertos resueltos (repoint a la edición FINAL trackeada / des-link históricos) · docs:check I6-links WARN→ERROR. - DOC-1: tabla «Build contract» MIGRADA a component-guide con estados modernizados (A3–A5 → LIVE + guards de hoy); banners reapuntados; citas de CANON/sema.md historificadas; lápida-redirect en el §13 del fósil. - DOC-4: hold chain → holds.ts · FAQ event:* SUPERSEDED por signatures · gradient añadido a los DOS capstones (sextet real) · nota de paleta de demo-authoring corregida (universalPaletteDecls + decisión THM-2 = mecanismo universal como sucesor del tracker borrado). - DOC-5/6: recuentos anti-frágiles datados · §4.11 dup → §4.12 · Known gaps historificado · N-6/N-7 recuperadas de git (d68d2c45^) y canonizadas en eidos.md §pickers · authoring E2 → canon/tsc.md · air-old des-linkado · EID-3 (placement) en la fila RTL · AUX-2 disabledDom documentado. SEM-4 sesión 1 — el close polimórfico de los pickers, VIVO (D.11): - Reconciliación: los morfos ya no declaran close (delegated al Popover, de-dialoged 06-27); el agujero real era el cierre programático bypaseando dismissWith → save/cancel/select eran perceptualmente SILENCIOSOS. - Fix: PickerShellHandle.setPopoverDismiss + closeWith(cause) en los 5 providers (14 sitios; select/commit → 'save' = commit.save+fulfill, cancel → 'cancel' = emerge; fallback raw para headless) + UN inyector en el eidos PickerShell root (norma N-8). Picker genérico fuera a propósito (ya suena commit-set/cancel por diseño S9). - Verificado en vivo (date-picker): Done → close·commit·fulfill·active · Cancel → close·emerge · cierre real. Gates: matriz 141/141 (los 6 morfos nuevos de la pista de texto paralela también PASS) · contracts 38/38 · eidos 314 · sema 178 · morfo 94 · docs:check 0/0 con I6 en error · baseline propio 57. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
`applyColorScheme` · `applyTypeScale` · `applyDepth` · `applyShape` ·
`applySpacing` · `applyGradients`
And the **capstone composing them**: **`applyTheme(seed)`** — a single seed
fix(cleanroom): F2+F3+F4-C+SEM-4s1 — lote mecánico, censos con guard, corpus documental y el close polimórfico de los pickers VIVO F2 — lote mecánico (13 ítems): - DEP-2 ogl eliminado (0 imports) · DEP-1 clsx inlineado como toClassString propio + suite de contrato (props.test.ts; soma.md §12 cerrado). - THM-7: los 5 selectores manuales de sema.md reescritos con semaSelector (los ejemplos [data-toast-root] apuntaban a un part INEXISTENTE — la deriva que el builder previene, demostrada en el propio doc). - MOR-1 escape isomorfo + validación de attr-names en semaSelector + 9 tests (selectors.test.ts, matches() real con comillas/corchetes) · MOR-2 partMarkerAttr = única fuente compilador↔builder + test de paridad · MOR-3 _resetCompileCache borrado (0 usos). - SOM-2 keydown continue en match sin handler + keyboardFixtureMorfo · SOM-1 no-await de handlers (censo async = 0; contrato V1 cumplido) + pin. - SEM-2 trigger pre-attacha catch con logger (void trigger sin unhandled rejection; throw intacto para awaiters) + pin · SEM-3 fallback muerto de applyDominance → skip defensivo + timer tope de awaitExpression cancelado · SEC-1 adjudicado YA implementado (assertCssVariableValue desde 2026-05-11) + pin del path de VALOR. - accordion → outline (§32; su outline:none dejaba CERO anillo en HCM) — verificado en vivo · THM-6 radius-full 9999px · EID-4 recuentos 33. F3 — censos con guard: - SOM-3 cerrado: announcer + image-provider migrados a scheduler-preferred (consumidores cableados: date/time-field vía soma.uix.timers; avatar/image vía eidos.timers — verificado en vivo); guard de timers ENSANCHADO de soma/components a TODO soma y pasado a EVIDENCIA (setTimeout exige .schedule( en el fichero — layers/ y datetime/ escapaban del ámbito viejo). - THM-5: R-4.7 nueva (válvula same-line /* important: <razón> */, escaneo comment-blanked) + las 15 declaraciones anotadas con su razón + canon recipe-contract §3/§4. - SOM-4 adjudicado: el censo/guard YA existían (49 pins); knob/mask-field/ timeline pinneados (overrides documentados en call-site); media-player Batch-4 (35 hits, cero renderProps) = único batch restante, registrado. - THM-4 doctrinado en eidos.md §unused (comportamiento/composición = legítimo; deuda = eje visual sin consumidor; hotspots por lotes). F4-C — corpus documental (decisiones de usuario aplicadas): - DOC-3: los 15 enlaces muertos resueltos (repoint a la edición FINAL trackeada / des-link históricos) · docs:check I6-links WARN→ERROR. - DOC-1: tabla «Build contract» MIGRADA a component-guide con estados modernizados (A3–A5 → LIVE + guards de hoy); banners reapuntados; citas de CANON/sema.md historificadas; lápida-redirect en el §13 del fósil. - DOC-4: hold chain → holds.ts · FAQ event:* SUPERSEDED por signatures · gradient añadido a los DOS capstones (sextet real) · nota de paleta de demo-authoring corregida (universalPaletteDecls + decisión THM-2 = mecanismo universal como sucesor del tracker borrado). - DOC-5/6: recuentos anti-frágiles datados · §4.11 dup → §4.12 · Known gaps historificado · N-6/N-7 recuperadas de git (d68d2c45^) y canonizadas en eidos.md §pickers · authoring E2 → canon/tsc.md · air-old des-linkado · EID-3 (placement) en la fila RTL · AUX-2 disabledDom documentado. SEM-4 sesión 1 — el close polimórfico de los pickers, VIVO (D.11): - Reconciliación: los morfos ya no declaran close (delegated al Popover, de-dialoged 06-27); el agujero real era el cierre programático bypaseando dismissWith → save/cancel/select eran perceptualmente SILENCIOSOS. - Fix: PickerShellHandle.setPopoverDismiss + closeWith(cause) en los 5 providers (14 sitios; select/commit → 'save' = commit.save+fulfill, cancel → 'cancel' = emerge; fallback raw para headless) + UN inyector en el eidos PickerShell root (norma N-8). Picker genérico fuera a propósito (ya suena commit-set/cancel por diseño S9). - Verificado en vivo (date-picker): Done → close·commit·fulfill·active · Cancel → close·emerge · cierre real. Gates: matriz 141/141 (los 6 morfos nuevos de la pista de texto paralela también PASS) · contracts 38/38 · eidos 314 · sema 178 · morfo 94 · docs:check 0/0 con I6 en error · baseline propio 57. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
(`{ color?, type?, depth?, shape?, space?, gradient? }`) composes the six
axes in **one managed write** (vs six loose `apply*`), **atomically**: the
axes you pass are applied, the ones you omit revert to the authored
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.
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>
2 months ago
## 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.
feat(sema): el mapa perceptual se TEMATIZA, y con la misma forma que los ejes visuales El autor: «que yo no pueda definir a nivel de tema la personalizacion es un fallo». Lo era, y medirlo destapo algo peor que el hueco que se veia. NO ERA UN CAMPO QUE FALTABA EN EL SEED: eran dos formas distintas de personalizar. Eidos retunea en vivo, atomico y revertible (applyX/clearX); sema leia `overrides.runtime` UNA VEZ en el constructor y nunca mas, sin forma de revertir. Un `applyTheme` unico construido encima habria sido peor que dos puertas honestas — una llamada donde el color cambia ahora y el sonido no cambia nunca, porque ya arranco. Por eso la puerta unica va la ultima y aqui va el sustrato. 1. EL CATALOGO ENTRA EN EL MAPA (`families · intents · sounds`). Es dato, y el mapa es la estructura que un tema direcciona. Mientras vivio fuera como const de modulo —error mio de ayer— el VOCABULARIO era el unico eje perceptual que un producto no podia re-vocear a ninguna hora. El resolver pasa a resolver el nombre contra el mapa que recibe, no contra un const, que es lo que hace que un tema surta efecto. 2. `applyMap(seed)` · `applySounds({ soft: { gain } })` · `clearMap()` — en vivo y revertibles, reconstruyendo desde el mapa autorado, asi que aplicar dos veces es aplicar una. Misma forma que los seis applyX de eidos. El engine ya pasaba `this.map` por emision, asi que el retuneo surte efecto en la siguiente ocurrencia sin re-arranque. 3. UNA RUTA MALA LANZA (S-09). Antes CREABA la rama: `families.commmit.…` dejaba el valor real intacto y hacia crecer un fantasma al lado, en silencio; y descender a traves de una hoja primitiva la sustituia por un objeto. En una API de theming eso es intolerable — el sintoma es «el sonido no cambio» y no hay nada a lo que apuntar. Se valida tambien en construccion, no solo en el retuneo. LO QUE SIGUE CERRADO A PROPOSITO: el vocabulario. `SoundName` es `keyof SOUND_CATALOGUE`, asi que un tema cambia a que suena un nombre y no puede inventar uno que ningun componente pueda referenciar. La voz se abre; las palabras no. ⚠️ REGRESION PROPIA, cazada y corregida antes de commitear: tipar el mapa `as const satisfies SemaMap` daba las claves literales que `SoundName` necesita, pero estrechaba TODO el mapa y rompia a los consumidores que lo recorren en generico (el estudio de sema: +5 errores en check). El catalogo pasa a ser su propio const y `SEMA_MAP` conserva su anotacion ancha. ⚠️ Y un hallazgo del harness: los tres primeros tests median 0.85x de lo esperado. No era el codigo — era BK-FREQ-MEMORY atenuando la tercera emision del mismo evento. La memoria de frecuencia funciona. VERIFICADO: `theming.test.ts` 7/7 (re-vocea un NOMBRE en vivo y revierte, re-vocea una FAMILIA, idempotente, no muta el mapa canonico, rechaza las dos rutas malas de S-09) · sema+morfo+sound 444/444 · check 75 = baseline · docs:check 0/618. QUEDA, y es decision de producto no defecto: la puerta unica `uix.applyTheme(seed)` en la raiz de composicion. Mas dos menores: `masterGain` sigue descartandose en silencio (S-05/S-32) y la tabla haptica `kind → pattern` sigue en el canal en vez de en el mapa, asi que es el unico eje perceptual que un tema no alcanza. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**The gap, and its closing (2026-08-06).** The table above used to have a
second failure hiding behind the first: not only was there no `sound` axis in
the seed, the two halves had different SHAPES. Eidos retunes live, atomically,
revertibly; sema read `overrides.runtime` ONCE in the engine constructor and
never again. A single `applyTheme` built over that would have been worse than
two honest doors — one call where the colour changes now and the sound changes
never, because it had already booted.
So the substrate was made symmetric first:
- **The catalogue moved INTO `SEMA_MAP`** (`families · intents · sounds`). It
is data, and the map is the structure a theme addresses. While it sat outside
as a module const, the VOCABULARY was the one perceptual axis a product could
not re-voice at any time.
- **`applyMap(seed)` / `applySounds(seed)` / `clearMap()`** — live and
revertible, rebuilt from the authored map so applying twice is applying once.
Same shape as every `applyX` / `clearX` on the visual side.
- **A bad path now THROWS** (`SemaConfigError`). It used to create the branch:
`families.commmit.…` left the real value untouched and grew a phantom beside
it, in silence (audit S-09). A theming API that swallows a typo is worse than
no API — the symptom is «the sound did not change», with nothing to point at.
What a theme can reach today, measured in `theming.test.ts`:
| Target | Path |
| --- | --- |
| a family's base | `families.commit.base.sound.gain` |
| an intent's delta | `intents.fulfill.deltas.sound.gain` |
| **what a NAME sounds like** | `sounds.soft.gain`, or `applySounds({ soft: { gain: 0.08 } })` |
**What stays closed on purpose**: the vocabulary. `SoundName` is
`keyof SOUND_CATALOGUE`, so a theme changes what a name sounds like and cannot
invent one no component could reference. The voice is open; the words are not.
feat(eidos,sema): un tema es UNA cosa — siete ejes, y ninguno fuera del alcance Cierra el eje: el haptico entra al mapa, `masterGain` deja de descartarse, y `applyTheme` gana el septimo eje. ⚠️ CORRIGE MI PROPIA PROPUESTA. Dije que la puerta unica iba en la raiz de composicion. Al medirlo, la dependencia corre AL REVES: `ActiveEidos` tiene `#uix` y lo conduce (registra presets con `uix.motion`), mientras `ActiveUix` no tiene eidos. Un tema compuesto en la raiz habria invertido la direccion. Va donde ya vive el metodo que se llama a si mismo «the capstone composing them», y que hasta hoy componia seis de ocho. eidos.applyTheme({ color: '#3b5bdb', sound: { soft: { gain: 0.08 } } }); eidos.clearTheme(); // revierte los siete Un eje que pasas se aplica, uno que omites revierte — la misma lectura atomica que ya tenian los seis visuales, ahora sobre siete. Sin `uix` (render headless) el eje perceptual se salta y los visuales siguen: un tema que se aplica a medias es mejor que uno que revienta. EL HAPTICO, AL MAPA. Su tabla `kind → pattern` vivia como const dentro del canal, asi que era el unico eje perceptual que un tema no alcanzaba — exactamente la asimetria que el trabajo del sonido existia para quitar, dejada en pie un canal mas alla. Ahora es `SEMA_MAP.haptics`, con perfiles derivados (`floorMs` + `scale`/`gapMs`) o constantes (`pattern`): los tres kinds evaluativos son ritmos constantes porque lo que hace reconocible a un `error` es su cadencia, no su longitud. El canal lo lee por GETTER, no por copia, asi que un `applyMap()` en caliente le llega sin re-registrarlo. `masterGain` SE APLICA. Pasarlo con motor compartido se tiraba en silencio (S-05/S-32) pese a ser una perilla viva del motor y la forma natural de poner el volumen desde la raiz. Ahora llega a `master.setGain`, y de las opciones que de verdad no pueden aplicarse a un motor ajeno se AVISA en vez de desaparecer. Y `DeltaValue` pasa a exportarse: es la forma de toda semilla de override, sin ella un consumidor no puede tipar el tema que esta pasando. Su ausencia era un error VIVO en el estudio de sema, que lo importaba y no podia — por eso check BAJA a 73 desde el baseline de 75. VERIFICADO: theming 8/8 (incluye re-vocear el haptico y que la tabla autorada quede intacta) · sema+eidos+morfo 802/802 · check 73 (baseline 75, −2 por el export) · docs:check 0/618 · suite completa 7 fallos, los mismos ajenos y preexistentes de contracts.test. ⚠️ Nada de esto se ha OIDO ni TOCADO en un dispositivo real: todo esta medido con el resolver y las suites. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**One door, and it is `applyTheme`.** The seed gained a seventh axis:
```ts
eidos.applyTheme({ color: '#3b5bdb', sound: { soft: { gain: 0.08 } } });
eidos.clearTheme(); // reverts all seven, visual and perceptual
```
It lives on `ActiveEidos` and not on the composition root, which corrects the
first reading of this section: **the dependency runs eidos → uix**, not the
other way. `ActiveEidos` already holds `uix` and drives it (it registers motion
presets through `uix.motion`); `ActiveUix` does not hold eidos, so a composed
theme could not live there without inverting the direction. An axis you pass is
applied, an axis you omit reverts — the same atomic reading the six visual axes
already had, now over seven. Without a `uix` (a headless render) the sound axis
is skipped and the visual ones still apply: a theme that half-applies beats one
that throws.
**And the vocabulary is complete**: the haptic `kind → pattern` table moved into
the map too (`haptics.success.pattern`), so no perceptual axis is left that a
theme cannot reach. Leaving haptic behind would have swapped one asymmetry for
another.
`masterGain` is applied as well: passing it to a shared engine used to be
dropped in silence (audit S-05 / S-32); it now reaches `engine.master.setGain`,
and the options that genuinely cannot apply to a shared engine are warned about
instead of vanishing.
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>
2 months ago
## 6. Position vs the references
No reference system gathers the channels under **one semantic model**.
Material has shape morph + motion (ad-hoc, without integrated
sound/haptics); Apple, continuity + materials (platform-bound); everyone,
color. **UIX gathers them** — the visual channel (sema → eidos) + sound +
haptic (sema) — over the two-moment model, fired from one event, with
runtime builders and an open registry. That is the framework, not the sum of
its parts.
## 7. Demo
`/temas/orquesta` — the mixer: press a control and mute each facet to hear
its part. The **visual channel's** 4 facets materialize with eidos tokens
(toggleable so each can be muted one by one); **sound + haptic** are fired by
the real sema engine (`EngineSemantic.emit`, filtered channels). One event,
sema's channels.

Powered by TurnKey Linux.