# CONTINUE — auditoría del sistema semántico y sus arreglos > **Kickoff para la sesión siguiente**: _«Lee `docs/process/CONTINUE-sema-audit.md` > y sigue por la cola de §3, empezando por las 17 reglas.»_ > **Fecha**: 2026-08-06 · Rama `alpha-0.1-dir-prefs` (compartida — otra sesión > trabaja en `blocks`; `git reset -q` + add sólo lo propio, siempre). > **Fuente de verdad de los hallazgos**: [`AUDIT-sema-2026-08-05.md`](./AUDIT-sema-2026-08-05.md) > — 44 confirmados tras refutación adversarial, con fichero, línea y evidencia. > Este documento sólo dice **qué se cerró, qué queda y cómo medirlo**. --- ## 1. Cómo empezó, para que nadie repita el camino El usuario oyó que «no se oyen muchos componentes: Switch, Toggle, algunos botones de la toolbar». El diagnóstico inicial fue **incorrecto por medir a medias** y la corrección la dio él: _«el silent emitía sonido, ese era el problema»_. Tenía razón, y de ahí salió todo lo demás. Lecciones de método que costaron tiempo real y que valen para cualquier hilo de este repo: - **Medir la envolvente NO es medir la salida.** Se midió la automatización del `AudioParam` (0.0001) y se declaró silencio; la salida real de esa misma firma con intent `risk` era **−13.9 dBFS**, más fuerte que un botón. Para verificar audio: **render offline replicando el grafo** (`OfflineAudioContext`) y medir el pico de muestra. Contar osciladores miente. - **«Lee toda la documentación» es precondición, no consejo.** Se le preguntó al usuario por D.8 sin haber leído `sema.md` §componentes continuos, que contenía la respuesta. La lectura completa del corpus de sema + motor son ~24 000 líneas en 42 ficheros; el núcleo indispensable está en §5. - **Un guard que no has visto fallar no vale.** Los cuatro guards nuevos de esta sesión nacieron en rojo. Dos de ellos **corrigieron el arreglo** que estaban vigilando (ver §2.4). - **El árbol está compartido.** Clasificar siempre «míos vs ajenos» por contenido del diff, nunca por confianza. --- ## 2. Lo cerrado en esta sesión | # | Qué | Commit | | --- | ---------------------------------------------------------------------------- | ----------- | | 2.1 | El silencio deja de ser aritmética: **`SILENT`**, valor canónico | `24d30ee1a` | | 2.2 | La rugosidad pasa a **trémolo EN SERIE** (era aditivo sobre el `AudioParam`) | `316452263` | | 2.3 | Saneamiento documental: deriva propia, copias podridas, dos leyes muertas | `432106f1f` | | 2.4 | **`contact-press`** en los toggles + D.7 canónica + **D.8 reescrita** | `b9d73c19b` | | 2.5 | D.7 sin versos sueltos (7 infractores) + guard del intent | `4fccf1662` | | 2.6 | Una regla de cascada casa con SU objetivo, **nunca con un ancestro** (S-01) | `ccfea0f10` | ### 2.1 `SILENT` — el silencio es un VALOR, no una resta Había tres claves silenciadoras (`commit.silent`, `emerge.silent`, `contact.silent`), cada una restando el gain base de SU familia. No eran silencio: eran un número, y las capas posteriores mueven números. Medido: un toggle «silenciado» con intent `risk` salía a **−13.9 dBFS** y con `threat` a **−6.7**, frente a **−9.3** de un botón normal — porque los deltas de intent suman **rugosidad** (`risk` +0.2, `threat` +0.4), eso cruzaba el umbral del AM, y el modulador estaba conectado al `AudioParam` de la envolvente. Hoy: `SILENT` (exportado por `$uix/sema`) se declara en el slice del canal y el **resolver lo honra retirando ese canal**. Absorbente: ningún delta lo resucita. Se levanta sólo REEMPLAZANDO el slice con una firma completa. ### 2.2 El trémolo, en serie `modulatorGain.connect(envelope.gain)` — conectar a un `AudioParam` **suma**. Ahora hay una etapa de ganancia en serie a `1 - depth` con el modulador sobre su propio `.gain`. Medido viejo→nuevo: el clic de cierre de cada earcon de `signal` **0.0666 → 0.0002**; la cola de `commit+risk` 0.263 → 0.103; y `commit.subtle`+`threat` de RMS **0.143 → 0.018** (era 7× su nivel declarado). **Contrapartida declarada**: los earcons con rugosidad bajan 1.6–2.7 dB. ### 2.4 Decisiones FIRMADAS — no re-litigar - **`contact-press`** (directiva del autor). Los tres toggles declaran los dos momentos del libro: el CONTACTO habla (`contact.soft`, gain 0.08 — el «click breve opcional» del cap. 22) y el COMMIT calla (`SILENT`, cap. 23: «sonido: cierre opcional»). ⚠️ Deroga el anexo Tabla 1, que decía «un único evento; el contacto es implícito»; la reversión está escrita en `compile.test.ts`. - **D.7 canónica, con enmienda por su motivo**: no se canoniza un sample sobre un evento cuya familia **tenga `base.sound`**. `handle`/`sustain`/`delegate` salen de la lista — sin base no hay nada que aplastar, y los 14 usos de `sound()` sobre `handle` son la única fuente sonora del gesto. - **D.8 reescrita**: `activeChannels` es el DEFAULT de la familia, no una prohibición. `channels` ajusta en las dos direcciones; **ampliar exige justificación perceptual escrita**. Lo prohibido es declarar una firma de canal que la activación no incluye (regla inerte). ### 2.5 El hallazgo que conecta dos capas Al arreglar D.7 con `soundTuning('commit.soft', …)`, **el guard lo tumbó**: un afinado de escalera trae su `gain` como **número desnudo**, y eso se aplica en modo `replace` igual que el sample. **D.7 y S-07 son el mismo defecto en dos capas.** Las 9 reglas corregidas **componen** (`gain: { op: 'add', value: -0.25 }`). --- ## 3. LA COLA — por dónde seguir, en orden > **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 | ### 3.0-bis El sonido se REHIZO otra vez, y esta es la versión buena ⚠️ Lo de §3.0 sobre el catálogo de nombres **quedó superado el mismo día**. Al oírlo, el autor rechazó el modelo de modulación entero: _«evento.intent = sonido, y punto»_. Modelo vigente, en dos líneas: ``` nombre = per-emit ?? cascada ?? pack ?? morfo ?? familia[verbo] ?? familia.default sonido = pack[`${nombre}.${intent}`] ?? pack[nombre] ?? nada ``` | Commit | Qué | | --- | --- | | `86fa9a1b7` | El sonido se ELIGE: tabla por verbo en el mapa, catálogo de 10 bases + 6 variantes diseñadas, resolver de dos búsquedas, guard de **distinguibilidad**, gestos por repetición (mueren los 3 resolvers), 135 reglas de pack borradas (de ~165 a 30) | | `11b54025d` | Pack de ejemplo `ui-mp3` con los ficheros del autor, recortados y normalizados (arrancaban hasta a 347 ms) + `applySoundPack()` | | `4f1b9d423` · `05d422ef4` | Las cuatro páginas de `/uix/docs/sound` | **Nada de esto se ha oído en producción todavía** — está medido en el grafo de audio, no con un oído sobre un componente real. Y el catálogo es una PROPUESTA: que `open` suene a lo que debe sonar un panel abriéndose es decisión del autor, y se afina en un fichero. **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`: HECHO el 2026-08-10** (202 apariciones, 64 ficheros; crónicas conservan el nombre viejo por diseño). Y dejó de ser cosmético: es la PRECONDICIÓN del censo de emisión. Mientras la opción se leía como un fallback, un provider que apuntaba a otra PARTE era indistinguible de uno que apuntaba a la instancia correcta de la parte declarada. Con el nombre honesto, el runtime puede afirmar la diferencia (`assertTargetOverride`) y el guard censarla. ### 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 (la exclusión de escritura se levantó el 2026-08-10 — ya accionables). ### 3.2 Los silenciosos - **`preload()` se cuelga en `resume()`** fuera de un gesto → no precarga nada Y abre un `AudioContext` en el arranque de la página (`engine-sound.ts:248`). - **Las opciones de motor se descartan en silencio**: `createActiveUix({ events: { sound: { masterGain: 0.5 } } })` compila y **no hace nada** — `EngineSemantic` filtra las claves antes de construir el canal (`engine.ts:299-319`). El aviso que existe en `SoundChannel` no llega a dispararse. - **El fallback de sample es mudo**: 4 salidas de fallo sin un diagnóstico, y el catálogo `SOUND_DIAGNOSTIC_EVENTS` no tiene evento para ello. Un 404, un CORS o un códec no soportado quedan enmascarados para siempre. - **3 de los 5 WAV son código muerto**: `whoosh`, `pop`, `ding` no los referencia ningún pack. - **Tipos**: la rama `Record` de `SemaSignatureOverride` desactiva toda comprobación de clave y valor — un `gain` de tipo _string_ llega hasta el audio. - **`applyMapOverrides` se traga las erratas de ruta** y crea ramas fantasma. ### 3.3 El interruptor Sound del topbar de las demos no silencia nada — **ARREGLADO** `web/routes/uix/+layout@.svelte` no pasa `preferences` al canal, que se queda en `'full'`. El framework sí lo soporta. Medido y sin tocar (viene del hilo del audio, `CONTINUE-sound-engine.md`). **Cableado el 2026-08-06 (`d161cedcc`) y medido en navegador el 2026-08-12**: `SemaPreferences` vivo en el layout, toggle → `'full' | 'off'`; A/B con parche de prototipo — Sound ON +2 osciladores por click, OFF +0. Detalle en `CONTINUE-sound-engine.md`. ### 3.4 El `announce` no compila `AnnounceFn` toma la prioridad en una bolsa de opciones y `uix.announce` la toma posicional; y **ninguna raíz cablea el canal**, así que activarlo cae en el fallback que añade un SEGUNDO par de live regions. `sema.md` ya enseña el adaptador de una línea; falta la decisión de código. ### 3.5 La instantánea perceptual ← la que impide que todo esto vuelva Guard propuesto y aprobado: una **tabla commiteada con la firma resuelta de cada (componente, evento, intent neutral)** — gain, hold, háptico. Cualquier cambio en 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 — 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`. 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 - **`contact` está ausente en 84 de los 91 morfos con eventos.** Hoy lo expresan 7 (los tres toggles de esta sesión + button, file-upload, navigation-menu, palabras). El libro le dedica el capítulo 22 entero y CANON §8 lo pone como regla 1: «contact precede al resultado cuando hay acción directa». Es el eje que explica por qué media interfaz no responde al «¿me has sentido?». - **`Toolbar.Button` reimplementa su `