21 KiB
PLAN — El sonido se NOMBRA: un nombre y punto
⛔ SUPERADO EL MISMO DÍA (2026-08-06). No ejecutar nada de aquí.
Este plan entregó un modelo en el que un NOMBRE modificaba la base de familia y el intent modulaba después. Se ejecutó entero… y al oírlo, el autor lo rechazó: los dieciséis nombres sonaban prácticamente igual, porque cinco eran la misma nota a cinco volúmenes y las variantes se separaban 20 Hz. La directiva que lo sustituye fue literal: «evento.intent = sonido, y punto y nada de mierdas de que si el intent modifica nada».
El modelo vigente —el intent SELECCIONA un sonido entero, dos búsquedas, nada modula nada— vive en
architecture/sema.md§the sound pack y en las páginas/uix/docs/sound. Este documento se conserva como crónica de por qué NO se hizo así: la lección es que un sistema de modulación produce variantes que difieren sobre el papel y son idénticas al oído, y que sólo se detecta escuchando.
Tipo: plan de diseño + ejecución por fases (process — efímero, NO fuente de verdad). Fecha: 2026-08-06 · Estado: EJECUTADO — F1…F5 ENTREGADAS el mismo día, por orden directa del autor («aplícalo»). Queda F6: la doctrina (D.7 / S-07 retiradas con su motivo,
sema.md§tunings reescrito, el límite del WAV de §3.2 escrito). El vocabulario de §5 se aplicó con las colapsaciones anotadas en §5.1 — reversibles editando UN fichero. Kickoff para sesión nueva: «Leedocs/process/PLAN-sound-names.mdy cierra F6.» Antecedentes obligatorios:AUDIT-sema-2026-08-05.md(44 hallazgos) ·CONTINUE-sema-audit.md(la cola) ·architecture/sema.md·CANON.md.
1. El mandato (2026-08-06, palabras del autor)
«Yo sólo quiero en la semántica en sonido darle el nombre de un sonido y punto, y me da igual que sea generado o sea un wav. Todo lo demás es una puta mierda sobreingeniería para algo que es anecdótico y que convierte al sistema en supercomplejo.»
Y antes, sobre los literales:
«Hace ya que se hizo que los sonidos tenían que estar canonizados y se hizo un repositorio con los sonidos, y ahora veo que se meten valores y mierdas a nivel de componente.»
El mandato no es una simplificación de conveniencia: restaura la ley que ya
estaba escrita en sema.md:390 —«components reference
names; URLs and parameters live in one place»— y que nada vigilaba.
2. La medida — por qué el mandato es correcto
Censo en runtime sobre los 71 packs (2026-08-06, HEAD 2f63525da):
214 reglas de cascada. 33 firmas distintas. Las 8 primeras cubren el 76%.
| firma autorada | reglas |
|---|---|
{ gain: 0.05 } |
35 |
{ gain: 0.03 } |
35 |
| (sin sonido — sólo háptico/channels) | 34 |
{ gain: 0.08 } |
17 |
{ contour:'descending', gain:0.05, pitch:−120 } |
13 |
{ contour:'descending', gain:0.03, pitch:−80 } |
11 |
SILENT |
10 |
{ contour:'ascending', gain:0.18, pitch:−90 } |
7 |
| … 25 firmas más, casi todas ×1 | 42 |
Anatomía de lo que dicen esas reglas:
- 92 de 214 (43%) no dicen más que un volumen. Su contenido íntegro es
gain. - 2 de 214 nombran un WAV. De los 5
.wavembarcados, 3 no los usa nadie (whoosh,pop,ding— S-26, verificado). - Ejes tocados:
gain165 ·contour69 ·pitch67 ·centroid30 ·decay29 ·roughness23 ·duration23 ·attack22.
pitch, contour y roughness son ejes del intent. contour es un enum:
no admite {op:'add'}, sólo reemplazo — así que 69 reglas borran el contorno
que el intent acababa de poner, y no pueden hacer otra cosa. El guard nacido el
2026-08-06 para proteger el intent (d7-intent-survives.test.ts) comprueba
pitch, gain y roughness: está ciego justo en el único eje incomponible.
Y centroid (30 usos) es un eje de la FAMILIA, no del intent: los 6 intents
lo tocan cero veces; las familias lo fijan (contact 2000 · commit 1800 ·
signal 2400 · emerge 1500 · shift 1400 Hz). Es la huella tímbrica que distingue
una familia de otra — y 5 componentes le suman +2800 por copia-pega, dejando su
commit en 4600 Hz, casi el doble de brillante que la familia más brillante del
mapa.
Conclusión medida: la capa de packs autora 33 sonidos usando una superficie
de 8 ejes continuos, invade los ejes del intent 159 veces y dedica el 43% de sus
reglas a decir «más bajito». El coste de entrada para «meter un sonido» es
conocer 5 capas de cascada, replace vs add, D.7, D.8, S-07 y la ley de
nombrado. La complejidad no está pagando nada.
3. El diseño
// HOY — color-picker, commit-set
sound: {
gain: { op: 'add', value: -0.25 },
centroid: { op: 'add', value: 2800 },
decay: { op: 'add', value: 20 }
}
// OBJETIVO
sound: 'settle.bright'
Tres piezas, ninguna nueva — las dos primeras ya existen en el repo:
-
UN catálogo de sonidos con nombre. Se fusionan
SOUND_LIBRARY(que ya resuelve nombre → WAV con reserva sintética) ySOUND_TUNINGS(que ya es un catálogo, sólo que de aritmética). Cada nombre es una definición completa: receta sintética o sample con reserva. Al consumidor le da igual cuál sea — que es literalmente el mandato. Los parámetros viven ahí y en ningún otro sitio. -
El componente escribe un nombre, o
SILENT. El tipo de la regla pasa de bolsa-de-8-ejes asound?: SoundName | typeof SILENT. La ley deja de ser prosa: la impone el compilador. Se acabaronop:'add', el número desnudo y elcentroidcopiado cinco veces.Precedente en el propio repo: el canal háptico YA funciona así.
haptic: { kind: 'tick' }es un nombre categórico y el canal resuelve qué significa; nadie escribe milisegundos por componente. -
El nombre entra como BASE de la ocurrencia; el intent modula DESPUÉS, en el motor, invisible para el componente (§4, D-SN.1).
3.1 El reorden — la pieza que disuelve toda una clase de defectos
Orden de hoy (resolver.ts:135-180):
1 base de familia → 2 deltas de intent → 3 overrides de evento → 5a packs
El pack llega el último y se aplica en replace: por eso aplasta el intent.
D.7, S-07 y d7-intent-survives.test.ts existen los tres para vigilar esa
inversión.
Orden propuesto:
1 base = el NOMBRE que la cascada seleccione (o la base de familia si no hay)
2 deltas de intent
3 overrides de evento
5b cascada de app
La cascada sigue casando por selector igual que hoy (el target ya está en la
mano del resolver); lo único que cambia es qué produce: en vez de un delta
que pisa, elige la base sobre la que el intent trabaja.
Con el nombre DEBAJO, D.7 y S-07 dejan de poder existir. No se arreglan: se
disuelven. Un commit-save con fulfill sonará más brillante y más arriba que
con neutral sin que ningún componente sepa que eso ocurre.
3.2 El límite físico, ya medido
playSample (engine-sound.ts:694-698)
sólo lee dos campos: 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 (ambos gain 0.05; risk sólo aporta roughness, que
la ruta de sample ignora).
Consecuencia para el vocabulario: un nombre-WAV que deba llevar carga
evaluativa necesita un fichero por intent — que es exactamente lo que ya hace
la familia signal (notification.ping vs alert.error) y la razón de que D.7
la declare excepción. No es un defecto del plan: es física del audio, y hay que
escribirlo en la doctrina para que nadie lo redescubra.
4. Decisiones FIRMADAS (2026-08-06)
- D-SN.1 — El intent SIGUE modulando encima, en el motor. El nombre es la base de la ocurrencia; la carga evaluativa se aplica después y es invisible al componente. Es la tesis del libro con la complejidad en el piso que le toca: cero en el componente. Sobre nombres-WAV, la modulación se reduce a volumen (§3.2).
- D-SN.2 — Plan firmado antes de ejecutar. Este documento. Sin firma del vocabulario (§5) no se toca código.
- D-SN.3 — NO se congela el estado actual. Es un framework en desarrollo: no hay consumidor, no hay contrato de compatibilidad y la salida de hoy está medida como defectuosa. No se hace A/B contra ella; la referencia es el catálogo nuevo. Razonado en §7.1.
5. El vocabulario — PROPUESTA, PENDIENTE DE FIRMA
⚠️ Esto es diseño del autor, no mío. Lo que sigue es la colapsación mecánica de las 33 firmas medidas; los NOMBRES son una propuesta a enmendar.
Las 33 firmas colapsan a ~12–15 nombres. Ejes reales que las separan: nivel (3 escalones cubren 87 reglas), dirección (sube / baja / plana), y carácter (un puñado de timbres con identidad propia).
| firma medida | reglas | nombre propuesto |
|---|---|---|
gain 0.03 |
35 | subtle |
gain 0.05 |
35 | soft |
gain 0.08 |
17 | medium |
gain 0.10 / 0.15 |
2 | strong |
| descending, −80, 0.03 | 11 | subtle.fall |
| descending, −120, 0.05 | 13 | soft.fall |
| descending, −150 | 4 | deep.fall |
| ascending, +60…+120 | 3 | soft.rise |
| pitch 1080 · centroid 5200 (aire) | 12 | air (3 niveles) |
| pitch 920 · centroid 4600 | 4 | settle |
| pitch 1040 · centroid 5400 · arc | 4 | snap |
| centroid +2800 (residuo de ayer) | 5 | → settle |
| centroid +3600 (residuo de ayer) | 2 | → snap |
| sample ping | 1 | ping |
| sample error | 1 | error |
SILENT |
10 | SILENT (ya existe) |
5.1 Lo que se entregó, y las colapsaciones que lleva dentro
Catálogo final: 16 nombres + SILENT, en src/uix/sema/sound-names.ts.
Niveles subtle · soft · medium · strong · loud; direcciones
subtle.rise · soft.rise · subtle.fall · soft.fall · deep.fall;
carácter air · air.strong · settle · snap; samples ping · error.
Los nombres NO llevan familia por delante (pregunta 1, resuelta por su motivo):
la ley {family}.{tail} existía porque un tuning era un delta SOBRE una familia.
Un nombre es una base, y el mismo soft sirve a un commit y a un emerge porque
la familia sigue poniendo su identidad debajo. Encabezarlos con familia sería
hoy la mentira, no la verdad.
Las 32 firmas medidas colapsan a esos 16 nombres. Estas son todas las colapsaciones, con su consecuencia audible — cada una se revierte editando UNA línea del catálogo:
| firma vieja | nombre | consecuencia |
|---|---|---|
gain 0.04 (tabs) |
subtle |
−0.01 |
gain 0.06 (popover dismiss) |
soft |
−0.01 |
| desc −100 / 0.03 (×3) | subtle.fall |
pitch −80 en vez de −100 |
| desc −120 / 0.06 (×2) | soft.fall |
−0.01 |
| desc sin pitch / 0.05 (×2) | soft.fall |
gana la caída de −120 |
| asc +80 / 0.03 (editable) | subtle.rise |
+60 en vez de +80 |
| asc +120 / 0.05 (stepper) | soft.rise |
+80 en vez de +120 |
air @ 0.10 (×3) |
air.strong |
+0.02 |
delta centroid +2800 (×5) |
settle |
ninguna — era esa firma, en aritmética |
delta centroid +3600 (×2) |
snap |
ninguna — ídem |
| proof-of-human confirm | strong |
pierde su +600 de centroide |
| proof-of-human fail | error |
pasa a ser el WAV de error |
| timeline reveal | strong |
−0.02 |
| calendar/chronos navegar (×7) | soft.rise |
0.18 → 0.05 |
⚠️ La última es la única con consecuencia grande, y es deliberada: gain 0.18
era el valor más alto de todo el sistema —más que una alerta— para navegar un
calendario. Ninguna doctrina lo sostenía; era el residuo de
soundTuning('commit.subtle', { gain: 0.18 }), uno de los diez cabezas de
tuning que mentían (S-39). Si quieres que navegar suene fuerte, es una línea.
Pregunta 3 (los 3 WAV muertos) — resuelta por omisión: whoosh, pop y
ding NO entran al catálogo. Ningún pack los usaba y el catálogo es ahora el
conjunto cerrado de lo que un componente puede nombrar; meterlos sería
declararlos vivos sin que nadie los pida. Los ficheros siguen en
static/sounds/ — retirarlos es decisión aparte.
Las preguntas que quedaban, y cómo se cerraron:
- ¿Los nombres llevan familia por delante? Hoy la ley de
SOUND_TUNINGSexige{family}.{tail}porque un tuning es un delta SOBRE una familia. Con nombres que son BASES completas, esa razón desaparece:softsirve a un commit y a un emerge por igual. Mi lectura: nombres sin familia y la ley de nombrado se retira con su motivo. Decide. - ¿
subtle/soft/medium/stronges la escala, o prefieres otra? Es la que ya usa el catálogo de hoy. - ¿Los 3 WAV muertos (
whoosh,pop,ding) entran al catálogo con nombre, o se retiran?
6. Qué muere y qué sobrevive
Muere
SOUND_TUNINGS,soundTuning(),op:'add'y{ gain: número }en packs.- Las 9 bolsas de literales inline de
4fccf1662(color-picker, gradient-builder, gradient-picker ×3, proof-of-human ×2, splitter) y las viejas de knob/timeline. - S-07 entero (14 afinados en
replace, ~150 usos) — disuelto por el reorden. - Q3 de la cola (las 10 cabezas de tuning que mienten) — moot: serán nombres.
- La ley de nombrado
{family}.{tail}y mediasounds-grammar.test.ts. - El invariante 3 de
pack-census.test.ts→ se reduce a «¿es un nombre vivo?».
Sobrevive intacto
- Morfo, soma, eidos, la proyección
data-event-*, los holds, el háptico. - El corazón semántico: familia + intent deciden la percepción — pero desde el motor, no desde 71 ficheros.
SILENTy su honra por el resolver.- El censo de packs (invariantes 1, 2 y 4) y
d7-intent-survives.test.ts, que pasa de vigilar una inversión a certificar que el reorden funciona.
7. Fases — PENDIENTE DE FIRMA
| # | Fase | Verificación |
|---|---|---|
| F1 | Catálogo unificado + SoundName. Sin tocar packs |
check 75 · suites verdes |
| F2 | Instantánea del CATÁLOGO: ~15 nombres × 6 intents, la firma resuelta de cada uno | La tabla existe; editar el catálogo la rompe |
| F3 | Reorden del resolver (nombre = base, intent después) | La instantánea F2 certifica que el intent sobrevive a cada nombre |
| F4 | Estrechar el tipo de regla a SoundName | SILENT |
Rojo en los 71 packs — la enumeración es del compilador |
| F5 | Migración pack a pack, leyendo cada regla y su comentario | check a cero errores nuevos · censo verde |
| F6 | Doctrina: D.7/S-07 retiradas con su motivo · sema.md §tunings reescrito · el límite del WAV (§3.2) escrito |
docs:check 0 |
7.1 Por qué NO hay instantánea del estado actual (corregido 2026-08-06)
La primera redacción de este plan abría con una instantánea perceptual del sistema de HOY, declarada innegociable, para poder hacer A/B durante la migración. Era un error, y el autor lo señaló: un A/B necesita una referencia que merezca conservarse, y la salida de hoy no lo es.
- 92 de 214 reglas son un recorte de volumen accidental; 69 aplastan el
contourdel intent; 5 packs llevan uncentroidcopiado que deja sucommiten 4600 Hz. TextArea sonaba 6×/13× por encima de su firma escrita hasta ayer, los toggles llevaban mudos desde mayo y todo lo que tieneroughnessse movió 1,6–2,7 dB anteayer. Congelar eso es congelar el bug. - La instantánea de §3.5 fue diseñada para un sistema que se quedaba como estaba («cualquier cambio en algo que el usuario percibe rompe el test»). Esa premisa muere en cuanto lo que se sustituye es el modelo. Se importó la conclusión sin volver a derivarla.
- git YA es la instantánea. Cada valor viejo es recuperable para siempre
(
git show 2f63525da). Una tabla commiteada de números que estamos a punto de borrar no añade nada. - El compilador es mejor guard que una instantánea. F4 convierte cada regla no conforme en error de tipos: enumeración del 100% por construcción. Una instantánea sólo comprueba lo que alguien se acordó de meter en ella.
Comprobado además que no hay materia que preservar: de las 17 firmas que
aparecen ≤2 veces, 5 ya son claves del catálogo (emerge.medium,
emerge.exit, emerge.dismiss.passive, emerge.strong, commit.select.soft),
3 son el residuo de 4fccf1662 (los centroid +2800/+3600/+600), 2 son samples
que ya tienen nombre (ping en chat-log, error en form) y las 7 restantes
son empujones de ±60…±120 de pitch sobre una clave del catálogo. Lo único
deliberado —dirección (sube/baja), tres niveles y dos samples—
sobrevive al nombrado trivialmente.
La instantánea sí se construye (F2), pero sobre el catálogo, no sobre los packs: ~15 nombres × 6 intents ≈ 90 filas. Ese es el guard permanente que §3.5 quería de verdad — fija qué suena cada NOMBRE, así que tocar el catálogo sale en el diff. Es más pequeño, es estable, y no ancla en basura.
8. Riesgos
- Pérdida de matices legítimos: MEDIDA Y DESCARTADA (§7.1). Las 17 firmas raras se reparten en claves del catálogo, residuo de ayer, samples ya nombrados y empujones de pitch. Aun así F5 migra leyendo cada regla y su comentario, que es donde vive el razonamiento — no en los números.
- Rama compartida. Otra sesión trabaja en
blocks. F4/F5 tocan 71 ficheros: clasificar «míos vs ajenos» por contenido del diff, siempre. - Sin flecos (directiva del autor, 2026-08-06). El plan se ejecuta entero: no se queda un tier a medio migrar ni conviven dos vocabularios. F4 es la puerta que lo garantiza — mientras quede una regla sin nombre, el árbol no compila.
- Se cruza con la cola abierta. Q2 (5 reglas hápticas inertes) y Q4 (14 eventos huérfanos) son independientes — no los bloquea este plan. Q3 sí queda absorbido. S-07 queda absorbido. chronos: la exclusión de escritura se levantó el 2026-08-10 (directiva del autor) — ya es accionable.
preloady el fallback mudo (S-04/S-24/S-25) siguen rotos. Si el catálogo va a poblarse de WAVs, S-24 debe arreglarse antes o un 404 será invisible para siempre.
9. Lo que este plan NO hace
No toca el eje del verbo. La medida sugiere que falta un piso de verbo en
SEMA_MAP (hoy sólo hay families e intents), y que por eso «un cierre baja»
se re-escribe en 16 packs. Con nombres, esas 16 pasan a decir soft.fall — el
síntoma se cura. La causa —que la dirección de un cierre es un hecho del verbo,
no del componente— queda anotada aquí y sin abrir. Es el siguiente eje, y es
otra conversación.