docs(sema)!: retirar D.7 y S-07 — el orden hace imposible lo que prohibian

F6 del PLAN-sound-names, y el motivo importa mas que el borrado: no se
derogan por cambio de opinion, se DISUELVEN. La doctrina del autor que
las origino sigue siendo cierta; lo que cambia es quien la sostiene.

D.7 prohibia canonizar un sample sobre un evento cuya familia tuviera
base.sound, porque sound() devolvia una firma COMPLETA y la cascada la
aplicaba en replace DESPUES del intent: un fulfill perdia su +300 Hz
ascendente. S-07 era el mismo defecto una capa mas abajo — el gain
desnudo de los 14 afinados de escalera — y seguia ABIERTO por decision
del autor.

Con el nombre aplicado ANTES de los deltas de intent no queda ninguna
capa posterior que pueda pisarlos. Aplastar el perfil evaluativo ya no
esta prohibido: es INEXPRESABLE. Y S-07 pierde su premisa entera, porque
SOUND_TUNINGS ya no existe.

LO QUE SE CONSERVA, con su sitio nuevo escrito: el silencio como firma
valida (SILENT), el criterio de cuando crear un pack, y los samples como
recurso de producto (el catalogo los admite con nombre; la app autora los
suyos por overrides.cascade, que sigue abierto a proposito).

LO UNICO DE D.7 QUE HAY QUE SEGUIR SABIENDO, y por eso queda escrito en
tres sitios: playSample lee EXACTAMENTE dos campos, sampleUrl y gain. No
hay playbackRate ni detune. Sobre un .wav el intent solo mueve el
volumen — medido, risk es indistinguible de neutral porque solo aporta
roughness y la ruta de sample lo ignora. Un sample con carga evaluativa
necesita UN FICHERO POR INTENT, que es lo que hace signal con ping frente
a error y la verdadera razon de que fuese la excepcion. Es fisica del
audio, no politica.

Tocado: book-deviations §D.7 + §S-07 · sema.md §the sound catalogue
(reescrita entera, con el orden y el limite del WAV) · CLAUDE.md §sema
doctrine · los dos README del art · CONTINUE-sema-audit §3.0/§3.1/§3.6,
que es el handoff vivo y estaba describiendo una cola que ya no existe.

⚠️ Prettier reformatea entero book-deviations.md y sema.md porque NO
estaban formateados en HEAD; reaplicado a mano para que el diff sea solo
lo mio. Mismo motivo por el que CLAUDE.md y arts/README.md van con 1-2
lineas en vez de 30.

VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline · docs:check
0/618 · prettier limpio en los ficheros que toque.

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

@ -290,13 +290,22 @@ The rules you must actively obey when touching sema:
visual channel (motion / presence / depth / shape / color) in CSS. The
engine knows no DOM/CSS. Apps extend `SemaChannelSignatures` via
declaration merging. (Detail: `docs/theming/channels.md` + `docs/CANON.md`.)
- **Cascade rules add character, never the intent's evaluative profile.**
`intent.deltas` (layer 2) owns `pitch` / `gain` / `contour` (and per-intent
haptic `intensity`). A pack (layers 5a/5b) MUST NOT replace those
primitives — to shift one, compose with `{ op: 'add', value: … }`.
- **Per-component packs** live in `src/uix/sema/components/{name}.ts`;
packs compose `SOUND_TUNINGS`, never samples (exception: `signal` family
— [`book-deviations.md`](docs/decisions/book-deviations.md) D.7).
- **A pack NAMES a sound; it never writes a parameter.** `sound: 'soft'` — a
key of `SOUNDS` (`src/uix/sema/sound-names.ts`) or `SILENT`, and the type
accepts nothing else. Whether a name is a synth recipe or a `.wav` is the
catalogue's business, not the component's. **Adding a sound = adding a NAME
to the catalogue**, never widening a rule.
- **Order carries the design**: `family base → THE NAME → intent deltas →
event overrides → app cascade`. The family gives identity, the name gives
level / direction / character, and the intent lands LAST so its evaluative
profile is always audible. This is why D.7 and the S-07 finding were
RETIRED (2026-08-06): with the name underneath, flattening the intent is not
forbidden — it is inexpressible.
- **Over a `.wav` the intent can only move the LEVEL.** `playSample` reads only
`sampleUrl` + `gain`; `risk` is measurably indistinguishable from `neutral`.
A sample carrying evaluative weight needs ONE FILE PER INTENT (what `signal`
does with `ping` vs `error`). Physics, not policy.
- **Per-component packs** live in `src/uix/sema/components/{name}.ts`.
- Sound and haptic default **off** (audible/tactile side effects); haptic
opts in via `new EngineSemantic({ haptic: true })`.

@ -366,49 +366,69 @@ Coherence is guarded: `morfo-vocabulary-check` fails when a pack file exists
and `expression` declares anything other than `'pack'`, and warns when a pack
exists with no `expression` at all (verdict S11d).
### The sound repository — `sounds.ts`
### The sound catalogue — `sound-names.ts`
Components must not declare full sound signatures in every pack. Sema has a
nominal repository:
**A component names a sound. It never writes a parameter.** The whole authoring
surface of the sound channel is one word:
```ts
import { sample, sound, soundTuning } from '$uix/sema';
sound('handle.pickup.air');
soundTuning('emerge.exit.deep');
{ selector: onProvider({ eventName: 'commit-save' }), sound: 'settle' }
```
An entry can be synthetic or point to an external `.wav` file with a
synthetic fallback:
`Sema['cascade'].sound` accepts a `SoundName` or `SILENT`, **and nothing else** —
the law is a type, not a convention. Sixteen names cover the framework: five
levels (`subtle` `soft` `medium` `strong` `loud`), five directions
(`subtle.rise` `soft.rise` `subtle.fall` `soft.fall` `deep.fall`), four
characters (`air` `air.strong` `settle` `snap`) and two samples (`ping`
`error`). The generated list lives in
[`canon/vocabularies.md`](../canon/vocabularies.md).
```ts
sample('/sounds/uix/dialog-fail.wav', sound('handle.release.soft'), { preload: true });
```
Whether a name resolves to a synthesised recipe or to a `.wav` **is not the
component's business** — it is the catalogue's. A sample entry carries its URL
plus a synthetic fallback, and `SoundChannel` plays the WAV or falls back to the
recipe if fetch/decode fails. The consumer cannot tell, and that is the point.
`SoundChannel` understands `sampleUrl`: it tries to play the WAV and, if
fetch/decode fails, falls back to the synthetic signature. The rule is that
components reference names; URLs and parameters live in one place.
Names carry **no family prefix**, and the old `{family}.{tail}` grammar was
retired with its reason: a tuning was a delta _over_ a family, so the family was
the only honest head. A name is a BASE, and the same `soft` dresses a commit and
an emerge alike — because the family still supplies its own identity underneath.
Heading names with a family would now be the lie.
#### Tuning naming shapes
A `SOUND_TUNINGS` key takes one canonical shape:
#### Where a name applies — the ordering that carries the design
```
{family}.{tail}
family base → THE NAME → intent deltas → event overrides → app cascade
```
- **`{family}`** — the sema family whose base signature the tuning modifies.
Closed set (`SEMA_FAMILIES`), and the only axis that is both true and
mechanically checkable: a component name is neither. Never a component,
never a UI category.
- **`{tail}`** — free prose describing the resulting SIGNATURE, one or more
lowercase segments (`soft`, `subtle`, `medium`, `exit.deep`, `select.soft`).
Deliberately NOT the triggering event: `commit.select.soft` dresses a menu
item and a tree node alike, because it names the signature, not the gesture.
This is the one place where the tuning vocabulary and the *event* vocabulary
([Naming shapes](#naming-shapes)) deliberately diverge.
The family supplies **identity** (its centroid, its timbre). The name supplies
**level, direction and character**. The intent lands LAST, so the evaluative
profile is always audible.
That ordering is not a detail — it is what makes a whole class of defect
impossible. Until 2026-08-06 packs ran last and in `replace` mode, so a
component's chosen sound erased the intent that had just been applied: a dialog
with `threat` sounded exactly like a neutral one. Three separate mechanisms
existed to police that inversion (D.7, the S-07 finding, and a guard). All three
are gone, because with the name UNDERNEATH there is no way to express the
inversion. `sound-names.test.ts` certifies it for every name in the catalogue.
**Adding a sound means adding a NAME to the catalogue** — never widening a rule.
If a component needs something the catalogue does not carry, that is a catalogue
decision: made once, in one file, where the next component can reuse it.
#### Samples: what a `.wav` can and cannot carry
`playSample` reads **exactly two** fields of the resolved signature: `sampleUrl`
and `gain`. There is no `playbackRate` and no `detune`. So over a sample the
intent can only move the LEVEL — measured, `risk` is indistinguishable from
`neutral`, because `risk` contributes `roughness` and the sample path ignores it.
Consequence, and it is physics rather than policy: **a named sample that must
carry evaluative weight needs one file per intent.** That is exactly what the
`signal` family does with `ping` versus `error`, and the real reason `signal` was
the sanctioned exception in the retired D.7.
#### Silence is a value, not a tuning
#### Silence is a value, not a name
`SILENT` is THE canonical silence — one value for the whole system, exported
from `$uix/sema`. Set a channel slice to it and **the channel that owns the
@ -685,7 +705,7 @@ What the channel keeps — all of it doctrine:
- and the division of labour: **the channel resolves the LEVEL, the engine
applies the gain**.
Everything else — `SOUND_LIBRARY`, `SOUND_TUNINGS`, the gesture resolvers, the
Everything else — `SOUNDS` (the named catalogue), the gesture resolvers, the
cascade — is unchanged and still sema's.
**How it reaches the engine.** `EngineSemantic` takes a `soundEngine` option and

@ -392,69 +392,71 @@ type SemaExpressionMode = 'pack' | 'family-default' | 'delegated' | 'none';
semanas y una auditoría clean-room (2026-07-10) lo citó como evidencia de dormancia —
lección: el registro se actualiza EN EL MISMO PASE que el cableado.
### D.7 Sonido canónico y samples
- **Status**: **PROJECT_CANON** (regla doctrinal sin necesidad de pasar al libro)
- ⚠️ **Tuvo un infractor, y se corrigió en vez de ampliar la excepción**
(2026-08-06, directiva del autor: «D.7 debe ser canónico, y como está
reflejado, no quiero un puto verso suelto»). `proof-of-human` aplicaba
`sound('notification.ping')` y `sound('alert.error')` a `commit-confirm` /
`commit-fail` — familia `commit`, no `signal`. Como `sound()` devuelve una
firma COMPLETA y la cascada la aplica en modo `replace`, el sample aplastaba
la modulación por intent que esta entrada existe para proteger (un `fulfill`
perdía su +300 Hz / ascending / +0.05), y el fallback sintético sonaba ciego
al intent también; la cabecera del propio pack afirmaba lo contrario de lo
que hacía. Ahora compone tunings con `{ op: 'add' }`. Un producto que quiera
el ping lo registra desde la cascada de app (capa 5b), donde un sample SÍ es
recurso legítimo. Los otros tres usos de `sound()` en packs (`chat-log`,
`form` ×2) caen sobre eventos `signal-*`, la excepción canónica.
- **Doctrina (verbatim del autor)**:
> "El sonido canónico de la gramática debe ser modulable por familia, intent, frecuencia e intensidad.
>
> Los samples no deben sustituir la firma semántica base cuando esa sustitución impide la modulación por intent.
>
> Los samples pueden existir como recursos de producto, tema o branding, pero no forman parte del canon semántico por defecto.
>
> En eventos frecuentes, la prioridad es evitar fatiga. El silencio es una firma válida.
>
> Los packs solo deben crearse cuando corrigen una diferencia perceptiva real: frecuencia, fatiga, incongruencia, accesibilidad, patrón recurrente o necesidad de diferenciación."
- **Regla práctica**: si un pack solo selecciona un tuning existente y no evita un problema real, no se crea.
- **Decisión arquitectural inmediata**:
- `SOUND_LIBRARY` (samples + synth concretos) = **recursos**. NO se usa en packs canónicos.
- `SOUND_TUNINGS` (deltas paramétricas sobre family base) = **canon semántico**. Esta es la única capa que se usa en packs por defecto.
- Los packs componen tunings, nunca samples directos. Esto preserva `intent.deltas` (capa 2) que es lo que da diferenciación perceptual al sistema.
- **Excepción aceptable**: family `signal` (alarm / notify / announce) admite samples como replacement porque (a) tienen marca cultural prescriptiva (error wav, ping, ding), (b) la intent-variability es efectivamente nula en ese family. Si emerge un caso, se documenta explícitamente.
- **Lo que NO se hace**:
- No hay `sampleOverlay` (sample como capa adicional sobre synth). Sobreingeniería: añade mixing en WebAudio, layer de resolver, knobs extra al diseñador, y los casos donde aportaría son raros. Descartado permanentemente, no como pendiente.
- No se canonizan samples en packs de `commit`, `emerge`, `contact`, `shift`. Los packs viven de tunings.
- **ENMIENDA 2026-08-06 — `handle` sale de la lista, y con razón medida.** La
prohibición existe para preservar `intent.deltas`; en una familia **sin
`base.sound`** no hay firma que aplastar ni deltas que borrar, así que la
razón no aplica. `handle` declara sólo `haptic` en su base (`sema-map.ts`),
y sus 14 usos de `sound()` en 12 packs —`handle.pickup.air`,
`handle.release.soft`, `handle.snap.chime`— son la ÚNICA fuente sonora del
gesto: aplicar la regla al pie de la letra dejaría mudo todo el arrastre del
framework. Igual para `sustain` y `delegate`, que tampoco declaran
`base.sound`. **La regla, enunciada por su motivo y no por su lista:** un
pack no canoniza un sample sobre un evento cuya familia tenga `base.sound`,
porque `sound()` devuelve una firma COMPLETA y la cascada la aplica en
`replace`. Donde no hay base, no hay nada que aplastar.
- **Y el mismo defecto vive una capa más abajo.** Un afinado de escalera trae
su `gain` como número DESNUDO (`commit.soft` = `{ gain: 0.05 }`), y un número
desnudo también se aplica en modo `replace`: sustituir un sample por
`soundTuning('commit.soft')` deja el intent igual de aplastado. Por eso las
reglas corregidas **componen** (`gain: { op: 'add', value: -0.25 }` sobre la
base 0.3) en vez de fijar. Es el hallazgo S-07 de
[`AUDIT-sema-2026-08-05`](../process/AUDIT-sema-2026-08-05.md), que alcanza a
las 14 claves del catálogo y sigue ABIERTO fuera de estas reglas.
- **Infractores corregidos el 2026-08-06** (todos canonizaban un sample de
`handle` sobre un evento `commit`, que sí tiene base y deltas):
`color-picker` · `gradient-builder` ×2 · `gradient-picker` ×3 · `splitter`,
más `proof-of-human` ×2. El carácter que el sample aportaba —el brillo del
encaje, la cola del asentamiento— viaja ahora como deltas de `centroid` y
`decay`, ejes que el intent no posee. Guard: `d7-intent-survives.test.ts`.
### D.7 Sonido canónico y samples — **RETIRADA el 2026-08-06, por disolución**
- **Status**: **RETIRADA**. No derogada por cambio de opinión: **disuelta por un
cambio de arquitectura que hace imposible el defecto que prohibía.** La
doctrina del autor que la originó sigue siendo cierta, pero ahora la sostiene
el orden de resolución en vez de la disciplina de quien escribe un pack.
- **Qué prohibía y por qué.** «Los samples no deben sustituir la firma semántica
base cuando esa sustitución impide la modulación por intent.» El mecanismo era
real: `sound()` devolvía una firma COMPLETA y la cascada la aplicaba en modo
`replace` DESPUÉS de los deltas de intent, así que canonizar un sample sobre un
evento `commit` borraba el perfil evaluativo — un `fulfill` perdía su +300 Hz
ascendente. La regla se enunció como lista de familias prohibidas, se le
encontró un infractor (`proof-of-human`), se enmendó para sacar a `handle` /
`sustain` / `delegate` (familias sin `base.sound`: donde no hay base no hay
nada que aplastar) y se le añadió un guard.
- **Por qué ya no hace falta.** Desde
[`PLAN-sound-names.md`](../process/PLAN-sound-names.md) un pack no autora una
firma: **nombra** una entrada del catálogo (`src/uix/sema/sound-names.ts`), y
el nombre se aplica sobre la base de familia **ANTES** de los deltas de intent:
```
base de familia → EL NOMBRE → intent → evento → app
```
Con el nombre debajo, el intent siempre sobrevive: no queda ninguna capa
posterior que pueda pisarlo. No es que esté prohibido aplastar el perfil
evaluativo — es que **no hay forma de expresarlo**. El tipo de una regla
(`Sema['cascade'].sound`) acepta un nombre o `SILENT` y nada más.
- **Lo que se conserva de la doctrina, y dónde vive ahora:**
- «El silencio es una firma válida» → `SILENT`, valor canónico honrado por el
resolver retirando el canal (D.5 + `sounds-grammar.test.ts`).
- «Los packs sólo se crean cuando corrigen una diferencia perceptiva real» →
sigue vigente como criterio de autor, y hoy es barato cumplirlo: un pack que
no tiene nada que decir no tiene nada que escribir.
- «Los samples son recursos de producto, no canon por defecto» → el catálogo
los admite con nombre (`ping`, `error`) y una app puede autorar los suyos por
`overrides.cascade`, que sigue abierto a propósito.
- **⚠️ EL LÍMITE FÍSICO, medido — lo único de esta entrada que hay que seguir
sabiendo.** `playSample` (`src/arts/sound/engine-sound.ts:694-698`) lee
**exactamente dos campos** de la firma: `sampleUrl` y `gain`. No hay
`playbackRate` ni `detune`. Luego **sobre un `.wav` el intent sólo puede mover
el volumen**: medido, `risk` es indistinguible de `neutral` (mismo gain; `risk`
sólo aporta `roughness`, que la ruta de sample ignora). Un nombre-WAV que deba
llevar carga evaluativa **necesita un fichero por intent** — que es exactamente
lo que hace la familia `signal` con `ping` frente a `error`, y la verdadera
razón de que fuese la excepción de esta entrada. Es física del audio, no
política: no se diseña alrededor de ella.
- **Lo que sigue descartado permanentemente**: `sampleOverlay` (sample como capa
adicional sobre el synth). Sobreingeniería — añade mixing en WebAudio, capa de
resolver y mandos extra al diseñador, para casos raros.
### S-07 — **DISUELTA junto con D.7**
El hallazgo S-07 de [`AUDIT-sema-2026-08-05`](../process/AUDIT-sema-2026-08-05.md)
decía que las 14 claves de escalera de `SOUND_TUNINGS` fijaban `gain` como número
DESNUDO, y un número desnudo se aplica en `replace` igual que un sample: cambiar
un sample por `soundTuning('commit.soft')` dejaba el intent igual de aplastado.
Era el mismo defecto que D.7 una capa más abajo, y quedó ABIERTO por decisión del
autor («se trata más detenidamente»).
Ya no existe: **`SOUND_TUNINGS` fue retirado el 2026-08-06**. Los niveles viven en
el catálogo de nombres y se aplican antes del intent, así que un `gain` fijo ya no
puede borrar nada. Los ~150 usos que el hallazgo contaba son hoy nombres.
### D.3 Family policy: separar requirement de guidance

@ -98,27 +98,46 @@ capas.** Las 9 reglas corregidas **componen** (`gain: { op: 'add', value: -0.25
## 3. LA COLA — por dónde seguir, en orden
El usuario aprobó todo lo de abajo **menos S-07**, que se trata «más
detenidamente» y por tanto NO se toca sin reabrirlo con él.
### 3.1 Las 17 reglas de 212 que no disparan o disparan mal ← EMPEZAR AQUÍ
Todas con evidencia en `AUDIT-sema-2026-08-05.md` (S-08, S-12, S-13, S-14,
S-15, S-30, S-37, S-38, S-39):
| Componente | Defecto | Efecto medido |
| ------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| **textarea** | Las 2 reglas apuntan al `provider`; el estampado cae en el `input` | `commit-submit` suena **6×** más fuerte; el aviso de desbordamiento **13×** |
| **navigation-menu** | `fallbackTarget` GANA sobre el part del morfo → 2 reglas `emerge` muertas | abrir/cerrar suena con la base de familia (0.2) en vez de 0.08 / 0.05 |
| **gradient-picker** | Regla para `commit-reset`, evento que el proveedor nunca emite | regla muerta + doc que miente |
| **chronos** | 2 reglas para `handle-drag` / `handle-resize`, que nadie emite | ⚠️ chronos es de **escritura excluida** — auditar, no editar |
| **tree-view** | `select(value)` sin elemento no dispara: el teclado no emite NADA | asimetría teclado/ratón; 2 reglas sólo alcanzables con puntero |
| 5 reglas | Escriben en un canal que la familia no activa (las inertes de D.8) | letra muerta — ahora infracción explícita |
| 10 reglas | Aplican un tuning cuya cabeza de familia MIENTE sobre la base que modifican | la ley de nombrado ya escrita las señala |
⚠️ **`fallbackTarget` gana sobre el `target` declarado en el morfo pese a su
nombre** (`runtime.svelte.ts:757`). Es la causa raíz de dos de las filas.
Decidir si se renombra o se documenta.
> **ACTUALIZADO 2026-08-06 tarde.** §3.1 está CERRADA y §3.6 quedó absorbida por
> un eje nuevo que abrió el autor: **el sonido se NOMBRA**
> ([`PLAN-sound-names.md`](./PLAN-sound-names.md), F1–F6 entregadas). Ver §3.0
> antes que nada; lo que sigue vivo es §3.2, §3.3, §3.4, §3.5 y §3.7.
### 3.0 Lo cerrado el 2026-08-06 por la tarde
| Commit | Qué |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `f7a7564b3` | **`allowedTargets`** — el morfo declara a qué partes puede viajar el estampado. Simétrico de `allowedFamilies`, con invariante en `schema.ts`. Resolvió las 12 divergencias que el censo destapó (calendar ×5, pagination ×5, rating-group, toolbar) |
| `0c1d945ba` | **S-12 textarea** (medido 0.30→0.05 y 0.40+risk→0.03) y **S-13 tree-view** (el teclado ya emite) |
| `2f63525da` | **`pack-census.test.ts`** — 4 invariantes mecánicos por regla; nació en rojo, reprodujo la auditoría y destapó dos clases nuevas |
| `7154fb77e` · `769e426c5` | **El sonido se NOMBRA**: catálogo de 16 nombres, tipo estrechado a `SoundName \| SILENT`, reorden del resolver (el nombre ANTES del intent), 71 packs migrados |
**Disuelto, no arreglado**: `SOUND_TUNINGS` retirado ⇒ **S-07 deja de existir**
(era su premisa) y **las 10 cabezas de tuning que mentían son hoy nombres**. D.7
retirada por el mismo motivo — con el nombre debajo del intent, aplastar el
perfil evaluativo es inexpresable. Razonado en
[`book-deviations.md`](../decisions/book-deviations.md) §D.7.
⚠️ **Renombrar `fallbackTarget` → `targetOverride`** sigue PENDIENTE: 193
apariciones en 63 ficheros. El contrato ya es honesto (`allowedTargets` lo
declara); falta que el nombre lo sea. Es un commit mecánico y aparte.
### 3.1 Las 17 reglas — CERRADA
Cerrada salvo tres decisiones que el autor no ha tomado y que viven como
**excepciones FIRMADAS** en `pack-census.test.ts` (el waiver registra, no
bendice; borrarlo es lo que cierra cada una):
| Pendiente | Estado |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **5 reglas hápticas inertes** (S-30/S-38) | D.8 exige `channels` con justificación escrita, o borrar. ⚠️ Ampliar **destapa S-31**: `shift`/`emerge` no tienen `base.haptic`, así que las firmas parciales (`{kind:'tick'}`) resuelven a `vibrate(NaN)` — hay que completarlas en el mismo commit |
| **gradient-picker `commit-reset`** (S-14) | Verbo delegado: el `Picker` compuesto ya emite el suyo. Es 1 de **6 casos idénticos** (color/date/date-range/gradient/time/time-range) |
| **tooltip `present`/`dismiss`/`dismiss-escape`** | HALLAZGO NUEVO, no está entre los 44: **no existe una sola llamada `trigger(` en `soma/components/tooltip`**. Su regla casa por familia, así que nada dispara y el silencio de hoy es accidental, no el declarado |
Censo de eventos huérfanos (medido): **14 de 249** en 104 morfos con soma. Tres
clases — verbo delegado (6), punto de extensión documentado en prosa
(virtual-list / virtual-grid `handle-scroll*`, que merecerían un `emission: 'app'`
declarado) y contrato muerto (tooltip). Más chronos ×2, de escritura excluida.
### 3.2 Los silenciosos
@ -159,11 +178,16 @@ algo que el usuario percibe rompe el test hasta que la instantánea se actualice
**en el mismo commit**, así que el cambio aparece en el diff en vez de esconderse
dentro de un pack. Habría cazado de golpe toda la clase 3.1.
### 3.6 Recalibrar los gains tras el fix del AM
### 3.6 Recalibrar los gains — ABSORBIDA por el catálogo de nombres
Los earcons con rugosidad bajaron 1.6–2.7 dB porque el trémolo ya no suma nivel.
Es corrección, no pérdida, pero cambia el volumen percibido de `signal` y de todo
`risk`/`threat`. Decisión de catálogo.
`risk`/`threat`.
Sigue siendo decisión de catálogo — y **ahora el catálogo existe**: los niveles
viven en 16 nombres dentro de `src/uix/sema/sound-names.ts`, así que recalibrar
es editar ese fichero y ver el diff en `sound-names.test.ts`, que instantanea qué
suena cada nombre bajo cada intent. Antes había que tocar 71 packs a ciegas.
### 3.7 Ejes, no tareas
@ -197,11 +221,15 @@ del catálogo sigue igual y es deliberado**.
para «¿se oye?».
- **Mutear antes de reproducir y parar al terminar** — el Chrome que suena es el
del usuario.
- Guards: `sounds-grammar.test.ts` · `cascade-scope.test.ts` ·
`d7-intent-survives.test.ts` · `chans/sound.test.ts` · `engine-sound.test.ts`.
**Baseline actual**: `check` **75** · `docs:check` **0/616** · sema+morfo+sound
**369/369**.
- Guards: `sound-names.test.ts` (la instantánea del catálogo) ·
`pack-census.test.ts` (4 invariantes por regla) · `sounds-grammar.test.ts`
(ningún pack AUTORA) · `cascade-scope.test.ts` · `chans/sound.test.ts` ·
`engine-sound.test.ts`.
**Baseline actual** (2026-08-06 tarde, HEAD `769e426c5`): `check` **75** ·
`docs:check` **0/618** · sema+morfo+sound **437/437**. La suite completa deja 6
fallos en `contracts.test.ts` que son AJENOS y preexistentes (medido con y sin
los cambios de este hilo).
---

@ -231,7 +231,7 @@ adom ──────────────────\ | / | connect
the `content` bus (delivery 2), and anything needing its own graph via
`uix.sound.context` / `bus(...).node`. Same shape as `motion` / `ethereal`:
the art owns the RUNTIME, the layer keeps its DATA and doctrine
(`SOUND_LIBRARY`, `SOUND_TUNINGS`, `SEMA_SOUND_VOICE`, the cascade). It
(`SOUNDS` — the named catalogue —, `SEMA_SOUND_VOICE`, the cascade). It
imports no other art — `SoundDom` and `SoundTimers` arrive injected. **One
context per document is its whole reason to exist**, so it warns when a
second one goes live.

@ -125,9 +125,9 @@ split now holds at the knowledge boundary too.
> **This art imports nothing from `$uix/sema`.** If it ever needs to, the cut
> was drawn wrong.
| Here (machinery + mix) | sema (doctrine) |
| ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AudioContext` lifecycle · unlock on gesture · bus tree + policy · synthesis + voice execution · sample cache · contour | `SOUND_LIBRARY` · `SOUND_TUNINGS` · **`SEMA_SOUND_VOICE`** (its calibration, as data) · the gesture resolvers · the cascade · which signature for which family × intent · the reduction policy |
| Here (machinery + mix) | sema (doctrine) |
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AudioContext` lifecycle · unlock on gesture · bus tree + policy · synthesis + voice execution · sample cache · contour | `SOUNDS` (the named catalogue) · **`SEMA_SOUND_VOICE`** (its calibration, as data) · the gesture resolvers · the cascade · which signature for which family × intent · the reduction policy |
A `SoundSignature` here is nine numeric knobs plus a contour — no family, no
intent, no evaluative loading. Sema's identically shaped type is structurally

Loading…
Cancel
Save

Powered by TurnKey Linux.