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>
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):
| **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.
| `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
| **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
| `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 |
| `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