diff --git a/CLAUDE.md b/CLAUDE.md index 09fa8095c..12a130b41 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 })`. diff --git a/docs/architecture/sema.md b/docs/architecture/sema.md index 7e03d5980..9228fc53a 100644 --- a/docs/architecture/sema.md +++ b/docs/architecture/sema.md @@ -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 diff --git a/docs/decisions/book-deviations.md b/docs/decisions/book-deviations.md index 405f38453..77b5c2454 100644 --- a/docs/decisions/book-deviations.md +++ b/docs/decisions/book-deviations.md @@ -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 diff --git a/docs/process/CONTINUE-sema-audit.md b/docs/process/CONTINUE-sema-audit.md index daa6d12b1..cc940db0d 100644 --- a/docs/process/CONTINUE-sema-audit.md +++ b/docs/process/CONTINUE-sema-audit.md @@ -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). --- diff --git a/src/arts/README.md b/src/arts/README.md index c18f525d3..23713912e 100644 --- a/src/arts/README.md +++ b/src/arts/README.md @@ -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. diff --git a/src/arts/sound/README.md b/src/arts/sound/README.md index 6cfc809cf..2d575f33b 100644 --- a/src/arts/sound/README.md +++ b/src/arts/sound/README.md @@ -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