`PLAN-sound-engine` daba por cableada la propiedad del motor «en los dos modos
de arranque». En attach no lo estaba: `defineUixServices` registraba `motion` y
`scene` junto a `dom` pero NO `sound`, y `defineEngineSemantic` no reenviaba
`soundEngine`. Una app en attach con sonido activo tenía el motor privado del
canal MÁS `uix.sound` — dos motores, y dos contextos en cuanto el reproductor
primase el suyo. `attachActiveUix` DOCUMENTABA el footgun en un comentario en
vez de cerrarlo, mientras `contracts.ts` ya prometía `singleContextPerDocument`.
- `defineUixServices` declara `sound` junto a `motion` / `scene`, y
`defineEngineSemantic` lo toma como `serviceDependencies: ['dom', 'sound']` y
lo reenvía. Degrada igual que `dom`: sin el servicio declarado llega
`undefined` y el canal vuelve a crear el suyo (construcción directa y tests).
- La nota de `attachActiveUix` pasa de describir el footgun a describir el
cierre, y deja escrito el único caso que sigue vivo: un esquema de servicios
a mano que declare `events` sin `sound`.
- Fila `events` de `contracts.ts` con `serviceDependencies`, y el assert DERIVA
de la tabla en vez de hardcodearla — que es lo que su propia doctrina pide.
- El estudio de sema (`/temas/sema`) enchufa `uix.sound`. Era la única página
capaz de ejercitar la inyección y no lo hacía.
MEDIDO en Chrome, no deducido. Con el motor privado, cada edición del draft
reconstruye el `EngineSemantic`, el `dispose()` del canal cierra el contexto y
el siguiente disparo abre otro: 9 ediciones dejaron **10 contextos creados y 9
cerrados**, perdiendo además la caché de samples decodificados en cada una. Con
el motor compartido: **1 contexto, 0 cierres**, y 24 osciladores construidos en
6 ciclos — el earcon sigue vivo.
Dos guards, y los dos VISTOS FALLAR sin el cableado. Es la lección de la
retirada de F2, donde una medición vacua se presentó como prueba:
- `active-uix.svelte.test.ts`: el `events` de la app prima el motor de la app
(quitando el reenvío: `expected null not to be null`).
- `sound-e2e.test.ts`: un motor inyectado sobrevive a 3 reconstrucciones con UN
contexto y sin `close()`; sólo lo cierra quien lo creó.
Verificación: `client` 130/130 · 842/842 · `server` 262/264 · 3718/3722 — los 3
rojos son ajenos (`aura`, `menubar`, `radio-group`) y el cuarto fue un timeout
de `soma-attr-audit` por contención, verde aislado en 3,1 s · `check` en la
baseline exacta (73 errores, 0 propios).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>