You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/scripts/docs-vocabularies.ts

243 lines
9.7 KiB

feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1) An agent building a component from the docs-book alone could not assign archetypes / holds / haptic kinds: the closed sets live only in code consts and the corpus (correctly) forbids copying them into prose, so they were invisible from the docs. STUMBLES #1, the top stumble. Fix: docs/canon/vocabularies.md is GENERATED from the consts by 'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled out (generated = no drift objection), which every other doc links to. It covers part archetypes (with a one-line role each), sema families with their default hold/persistence + per-intent overrides, verbs by family, intents, the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes, variant archetypes, and the shared common.* strings. - scripts/docs-vocabularies.ts imports the consts (robust — no fragile JSDoc/regex parsing) and formats them; the generation is an exported function so docs-check can compare. - Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the authoritative one-liners the appendix reads — descriptions become first-class data instead of inline union JSDoc a tool would have to parse. - docs-check gains I7: it regenerates in-memory and fails if the committed file drifts from the consts (negative-tested: corrupt -> error, regen -> green). The appendix's own counts pass I1, so I1 doubles as a second guard. - Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md (doctrine here, enumerated lists there), architecture/morfo.md (pick a part's archetype), and the component-audit minimum-package list (STUMBLES #8). docs:check 0 errors; npm run check unchanged in my files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
/**
* docs:vocabularies — generate the canonical-vocabulary appendix from the code
* consts, so agents building from the docs-book can SEE the closed sets they
* must draw from (archetypes, sema families + holds + verbs, intents, haptic
* kinds, palette scales, sizes, variants, shared strings) without any
* copy-the-list drift objection. Generated = the ONE sanctioned place these
* lists are spelled out; every other doc links here.
*
* Closes STUMBLES #1 (invisible canonical vocabularies — an agent could not
* assign archetypes / holds from the docs alone).
*
* Usage: npm run docs:vocabularies (writes the file)
* imported by scripts/docs-check.ts (freshness guard: the committed
* file must equal the generator)
*/
import { readFileSync, writeFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { ARCHETYPE_VOCABULARY, ARCHETYPE_DESCRIPTIONS } from '../src/uix/morfo/types';
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
import {
PALETTE_SCALES,
SIZES,
SIZE_PRIMITIVE_KEYS,
EIDOS_VARIANTS
} from '../src/uix/eidos/lib/types';
feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1) An agent building a component from the docs-book alone could not assign archetypes / holds / haptic kinds: the closed sets live only in code consts and the corpus (correctly) forbids copying them into prose, so they were invisible from the docs. STUMBLES #1, the top stumble. Fix: docs/canon/vocabularies.md is GENERATED from the consts by 'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled out (generated = no drift objection), which every other doc links to. It covers part archetypes (with a one-line role each), sema families with their default hold/persistence + per-intent overrides, verbs by family, intents, the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes, variant archetypes, and the shared common.* strings. - scripts/docs-vocabularies.ts imports the consts (robust — no fragile JSDoc/regex parsing) and formats them; the generation is an exported function so docs-check can compare. - Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the authoritative one-liners the appendix reads — descriptions become first-class data instead of inline union JSDoc a tool would have to parse. - docs-check gains I7: it regenerates in-memory and fails if the committed file drifts from the consts (negative-tested: corrupt -> error, regen -> green). The appendix's own counts pass I1, so I1 doubles as a second guard. - Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md (doctrine here, enumerated lists there), architecture/morfo.md (pick a part's archetype), and the component-audit minimum-package list (STUMBLES #8). docs:check 0 errors; npm run check unchanged in my files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
import { SEMA_HOLDS_BY_INTENT } from '../src/uix/sema/holds';
import { SEMA_VERBS } from '../src/uix/sema/verbs';
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
import { SOUNDS } from '../src/uix/sema/sound-names';
feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1) An agent building a component from the docs-book alone could not assign archetypes / holds / haptic kinds: the closed sets live only in code consts and the corpus (correctly) forbids copying them into prose, so they were invisible from the docs. STUMBLES #1, the top stumble. Fix: docs/canon/vocabularies.md is GENERATED from the consts by 'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled out (generated = no drift objection), which every other doc links to. It covers part archetypes (with a one-line role each), sema families with their default hold/persistence + per-intent overrides, verbs by family, intents, the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes, variant archetypes, and the shared common.* strings. - scripts/docs-vocabularies.ts imports the consts (robust — no fragile JSDoc/regex parsing) and formats them; the generation is an exported function so docs-check can compare. - Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the authoritative one-liners the appendix reads — descriptions become first-class data instead of inline union JSDoc a tool would have to parse. - docs-check gains I7: it regenerates in-memory and fails if the committed file drifts from the consts (negative-tested: corrupt -> error, regen -> green). The appendix's own counts pass I1, so I1 doubles as a second guard. - Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md (doctrine here, enumerated lists there), architecture/morfo.md (pick a part's archetype), and the component-audit minimum-package list (STUMBLES #8). docs:check 0 errors; npm run check unchanged in my files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
import { SEMA_DURATIONS } from '../src/uix/sema/durations';
import { INTENTS } from '../src/uix/intent';
import { commonLangs } from '../src/uix/langs/common';
const REPO = join(dirname(fileURLToPath(import.meta.url)), '..');
const OUT = join(REPO, 'docs/canon/vocabularies.md');
/** Parse the `kind: 'a' | 'b' | …` union off the HapticSignature. */
function hapticKinds(): string[] {
const src = readFileSync(join(REPO, 'src/uix/sema/channels.ts'), 'utf8');
const m = src.match(/kind:\s*((?:'[\w-]+'\s*\|\s*)*'[\w-]+')\s*;/);
if (!m) throw new Error('cannot find HapticSignature kind union in channels.ts');
return [...m[1].matchAll(/'([\w-]+)'/g)].map((x) => x[1]);
}
/** Collect the dotted leaf keys of commonLangs (e.g. `buttons.close`). */
function commonKeys(): string[] {
const out: string[] = [];
const walk = (node: Record<string, unknown>, prefix: string) => {
for (const [k, v] of Object.entries(node)) {
const path = prefix ? `${prefix}.${k}` : k;
if (v && typeof v === 'object' && !('es' in v) && !('en' in v)) {
walk(v as Record<string, unknown>, path);
} else {
out.push(path);
}
}
};
walk(commonLangs as Record<string, unknown>, '');
return out;
}
export function generateVocabulariesDoc(): string {
const L: string[] = [];
const desc = ARCHETYPE_DESCRIPTIONS as Record<string, string>;
const holdMs = (label: string) =>
label in SEMA_DURATIONS ? `${(SEMA_DURATIONS as Record<string, number>)[label]}ms` : label;
L.push('---');
L.push('title: Canonical vocabularies (generated)');
L.push('type: canon');
L.push('audience: human + agent');
L.push('authority: canonical — the closed sets, generated from the code consts');
L.push('status: current');
L.push('generated: npm run docs:vocabularies (do NOT edit by hand)');
L.push('---');
L.push('');
L.push('# Canonical vocabularies');
L.push('');
L.push('> **Generated from the code — do not edit.** Run `npm run docs:vocabularies`');
L.push('> to regenerate; `npm run docs:check` fails if this file drifts from the');
L.push('> consts. This is the ONE place the closed sets are spelled out (the reason');
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
L.push('> every other doc links here instead of copying a list that would go stale).');
feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1) An agent building a component from the docs-book alone could not assign archetypes / holds / haptic kinds: the closed sets live only in code consts and the corpus (correctly) forbids copying them into prose, so they were invisible from the docs. STUMBLES #1, the top stumble. Fix: docs/canon/vocabularies.md is GENERATED from the consts by 'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled out (generated = no drift objection), which every other doc links to. It covers part archetypes (with a one-line role each), sema families with their default hold/persistence + per-intent overrides, verbs by family, intents, the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes, variant archetypes, and the shared common.* strings. - scripts/docs-vocabularies.ts imports the consts (robust — no fragile JSDoc/regex parsing) and formats them; the generation is an exported function so docs-check can compare. - Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the authoritative one-liners the appendix reads — descriptions become first-class data instead of inline union JSDoc a tool would have to parse. - docs-check gains I7: it regenerates in-memory and fails if the committed file drifts from the consts (negative-tested: corrupt -> error, regen -> green). The appendix's own counts pass I1, so I1 doubles as a second guard. - Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md (doctrine here, enumerated lists there), architecture/morfo.md (pick a part's archetype), and the component-audit minimum-package list (STUMBLES #8). docs:check 0 errors; npm run check unchanged in my files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
L.push('> When building a component you draw part archetypes, event families/verbs,');
L.push('> intents and holds from exactly these sets (STUMBLES #1).');
L.push('');
// Archetypes
L.push(`## Part archetypes (${ARCHETYPE_VOCABULARY.length})`);
L.push('');
L.push('The cross-component classification a `part` may declare');
L.push('(`ARCHETYPE_VOCABULARY`, `src/uix/morfo/types.ts`). Omit it for a plain');
L.push('display part that pulls no shared styling.');
L.push('');
L.push('| Archetype | Role |');
L.push('| --- | --- |');
for (const a of ARCHETYPE_VOCABULARY) L.push(`| \`${a}\` | ${desc[a] || '—'} |`);
L.push('');
// Sema families + holds
const families = Object.keys(SEMA_HOLDS_BY_INTENT);
L.push(`## Sema families (${families.length}) — default hold + persistence`);
L.push('');
L.push('Each event declares a `semantic.family`; the default perceptual hold and');
L.push('persistence come from `SEMA_HOLDS_BY_INTENT` (`src/uix/sema/holds.ts`),');
L.push('overridable per event. Named holds map to `SEMA_DURATIONS`.');
L.push('');
L.push('| Family | Default hold | Persistence | Per-intent overrides |');
L.push('| --- | --- | --- | --- |');
for (const [family, intents] of Object.entries(SEMA_HOLDS_BY_INTENT)) {
const table = intents as Record<string, { hold: string; persistence: string }>;
const def = table._default;
const overrides = Object.entries(table)
.filter(([k]) => k !== '_default')
.map(([k, v]) => `${k}: ${holdMs(v.hold)}/${v.persistence}`)
.join('; ');
L.push(
`| \`${family}\` | ${holdMs(def.hold)} (\`${def.hold}\`) | ${def.persistence} | ${overrides || '—'} |`
);
}
L.push('');
// Verbs per family
L.push('## Sema verbs, by family');
L.push('');
L.push('The canonical verb set an event may use for each family (`SEMA_VERBS`,');
L.push('`src/uix/sema/verbs.ts`). A `family.verb` pairing outside this is drift.');
L.push('');
L.push('| Family | Verbs |');
L.push('| --- | --- |');
for (const [family, verbs] of Object.entries(SEMA_VERBS)) {
L.push(`| \`${family}\` | ${(verbs as string[]).map((v) => `\`${v}\``).join(' · ')} |`);
}
L.push('');
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
// Sound catalogue
const soundNames = Object.keys(SOUNDS);
L.push(`## Sounds (${soundNames.length})`);
fix(sema): los toggles recuperan voz, y el silencio deja de sintetizarse — muere el prefijo form. Tres cosas que el mismo hilo destapo, de la mas visible a la mas de fondo. 1) LOS TOGGLES SUENAN OTRA VEZ (directiva de autor 2026-08-05) switch, toggle y toggle-group llevaban SILENCIADOS desde el 2026-05-26: su pack aplicaba un tuning que restaba exactamente el gain base de la familia commit (0.3 - 0.3 = 0). Medido en navegador: pico 0.0001, el suelo de la rampa. Un silencio por defecto no se percibe como sobriedad sino como componente roto. Pasan a `commit.medium` (gain 0.1): audible, y un tercio de una pulsacion de boton (contact base 0.25) para que un flip no pese mas que una activacion. Los deltas de intent siguen montando encima (threat +0.1, fulfill +0.05). El haptico ligero no cambia. Medido despues: switch pico 0.1. Conservan el silencio, con su justificacion intacta, solo los dos donde el silencio ES la firma: tooltip (revela al pasar el raton, sonaria en cada cruce) y media-player (su propia salida ES audio — D-AP2.7, decision del hilo de audio). 2) EMITIR SILENCIO YA NO CUESTA `SoundChannel.handle` cortaba DESPUES de resolver la firma, asi que un tuning silenciador levantaba dos osciladores, un filtro y una envolvente programada para no sonar, en cada interaccion. Ahora corta antes de sintetizar cuando la ganancia resuelta es <= 0. El corte mira la ganancia RESUELTA, no el tuning, asi que un intent que la devuelve por encima de 0 (threat, fulfill) suena igual. Fijado con test propio, verificado en ROJO quitando el corto. 3) EL AFINADO SE NOMBRA POR LA FAMILIA QUE MODIFICA El catalogo mezclaba tres ejes en el primer segmento —familia (emerge.*, contact.silent), componente (tooltip.*, tabs.*) y la categoria huerfana form.*— sin regla declarada y sin guard que mirara. `form.commit.soft/subtle` entro el 2026-05-19 (09e261878, el commit que CREA sounds.ts) nombrando con verdad a siete controles de formulario; el 2026-05-26, 2d562f378 lo extendio a menus y arboles en la misma pasada que escribia la doctrina: 7 -> 29 consumidores en un commit, 50 hoy, de los que exactamente UNO era el Form. El D.6 de ese commit llego a poner el nombre correcto —commit.select— en la linea que usaba la clave incorrecta. form.commit.soft -> commit.soft form.commit.subtle -> commit.subtle form.toggle.silent -> commit.silent tooltip.silent -> emerge.silent tabs.select.soft -> commit.select.soft 131 ocurrencias en 68 ficheros, dirigido por `git ls-files` — nunca por recorrido de directorio: hay 3 worktrees vivos y build/ con 449 apariciones mas que un sed recursivo habria corrompido en silencio. La cola queda LIBRE a proposito: media-player aplica el silenciador al commit-set de un slider, no a un toggle — la cola describe la FIRMA, no el evento, asi que cerrarla contra el vocabulario de verbos fabricaria otra mentira. Por eso emerge.* no se toca pese a que `exit` no sea verbo canonico. Quedan tres silenciadores, uno por familia con base audible, con el mismo nombre: contact.silent (-0.25), commit.silent (-0.3), emerge.silent (-0.2). Eso convierte la coherencia en INVARIANTE: - `sounds-grammar.test.ts` (guard nuevo): cabeza ∈ SEMA_FAMILIES; forma {family}.{tail}; y todo {family}.silent resta EXACTAMENTE el gain base de su familia, leido de sema-map.ts —nunca copiado—. Verificado en ROJO: el guard calculo solo el -0.4 esperado de signal. - Ley escrita en sema.md § Tuning naming shapes, junto a la seccion hermana que ya legislaba los nombres de EVENTO. - vocabularies.md —"the ONE place the closed sets are spelled out"— pasa a enumerar los 14 afinados por familia; el generador los extrae del const y docs:check vigila su frescura. - D.5 gana las dos notas: el renombrado y la reversion del silencio. VERIFICACION: check 75 (linea base exacta) · sema 198/198 · docs:check 0/615 · navegador antes/despues (switch 0.0001 -> 0.1; button 0.25 sin tocar) · los 25 avisos de prettier entre lo tocado son deriva YA en HEAD, ninguno mio. Cronica NO reescrita (4 ficheros): AUDIT-blocks-2026-08-01, PLAN-audio-player, handoffs-claude-md y audit/components/date-field registran lo que ERA cierto. DESVIACION DECLARADA: sema/components/chronos.ts entra con 6 sustituciones mecanicas —chronos es de escritura excluida, pero dejarlo fuera rompia el type-check del repo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
L.push('');
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
L.push('THE catalogue (`SOUNDS`, `src/uix/sema/sound-names.ts`). A component names');
L.push('one of these and writes nothing else: the type of a pack rule accepts a name');
L.push('or `SILENT`, and nothing more. Whether a name resolves to a synthesised');
L.push('recipe or to a `.wav` is decided here, never at the component.');
fix(sema): el silencio deja de ser una resta y pasa a ser un valor — SILENT Directiva del autor: «el silencio no admite verbos ni intents, es universal, por lo tanto es un valor canonico que cuando se le envia al canal semantico este simplemente lo ignora y no envia nada al soundengine». LO QUE ERA. `commit.silent`, `emerge.silent` y `contact.silent` no eran silencio: eran una RESTA del gain base de su familia (-0.3, -0.2, -0.25). Por eso hacian falta tres —cada una tenia que saber que base cancelaba— y por eso el silencio tenia familia, que es el absurdo que el autor senalo. Y un cero es un NUMERO, y las capas posteriores mueven numeros. LA MEDICION QUE LO CIERRA. Los deltas de intent suman RUGOSIDAD (`risk` +0.2, `threat` +0.4), lo que cruza el umbral del AM del motor; y el modulador se conecta al AudioParam de la envolvente —y conectar a un AudioParam SUMA, no multiplica—, asi que con la envolvente en cero el tremolo pasaba a ser la senal entera. Renderizado offline con el grafo real del motor: toggle "silenciado" neutral -77.0 dBFS (mudo de verdad) toggle "silenciado" risk -13.9 dBFS <- con gain declarado 0 toggle "silenciado" threat -6.7 dBFS boton normal, referencia -9.3 dBFS El "silencio" era mas fuerte que un boton, y lo que sonaba no era el earcon: era un zumbido sin ataque ni caida, cortado en seco. La medicion que yo mismo di por buena esta manana (0.0001) solo cubria el caso neutral. LO QUE ES AHORA. `SILENT` — un valor canonico exportado por `$uix/sema`. Se declara en el slice del canal y el RESOLVER lo honra retirando ese canal de la firma. Un solo mecanismo para todos los canales, y nada aguas abajo ve jamas un string donde espera una firma. Es la forma que el propio motor ya prescribia y se estaba saltando: «muting DROPS the channel rather than scaling to zero, because a `0` still buzzes» (engine.ts). - universal por construccion: ninguna familia, ningun verbo, ningun intent puede resucitarlo, porque no queda ningun numero que mover; - liftable solo por REEMPLAZO: declarar sonido es una decision, no un delta; - `SoundOverride` lo EXCLUYE por tipo — un afinado es un delta, nunca una ausencia. Migrado: tooltip (1) y media-player (6). Los tres tunings mueren del catalogo. GUARDS. `resolver.test.ts` fija las cuatro mitades del contrato (retira el canal · universal sobre 4 familias x 4 intents · vale igual para haptic · liftable por reemplazo), verificado en ROJO. `sounds-grammar.test.ts` cambia su invariante muerta por las dos que ahora importan: ninguna clave `*.silent`, y ningun afinado que deje su familia en `gain <= 0` — silencio disfrazado. Doctrina al dia: sema.md gana la seccion «Silence is a value, not a tuning» con la medicion; D.5 marca DEROGADA su solucion de mayo y registra el porque; vocabularies.md deja de enumerar silenciadores y explica donde vive el silencio. VERIFICADO: sema 211/211 · check 76 = la base 75 + 1 error del fichero huerfano `__adv-verify-haptic-dead.test.ts` que dejo un agente de la auditoria (sin trackear, no se borra sin instruccion) · docs:check 0/616 · navegador: el transporte del player EMITE (`contact-activate`, `commit-toggle-play`) y levanta CERO osciladores · prettier limpio (el unico aviso mio, resolver.ts, formateado: +17 lineas, sin ruido). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
L.push('');
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
L.push('A name is applied over the family base and BEFORE the intent deltas, so the');
L.push('evaluative profile always survives it — which is what makes the old D.7 / S-07');
L.push('class of defect impossible rather than merely forbidden.');
fix(sema): los toggles recuperan voz, y el silencio deja de sintetizarse — muere el prefijo form. Tres cosas que el mismo hilo destapo, de la mas visible a la mas de fondo. 1) LOS TOGGLES SUENAN OTRA VEZ (directiva de autor 2026-08-05) switch, toggle y toggle-group llevaban SILENCIADOS desde el 2026-05-26: su pack aplicaba un tuning que restaba exactamente el gain base de la familia commit (0.3 - 0.3 = 0). Medido en navegador: pico 0.0001, el suelo de la rampa. Un silencio por defecto no se percibe como sobriedad sino como componente roto. Pasan a `commit.medium` (gain 0.1): audible, y un tercio de una pulsacion de boton (contact base 0.25) para que un flip no pese mas que una activacion. Los deltas de intent siguen montando encima (threat +0.1, fulfill +0.05). El haptico ligero no cambia. Medido despues: switch pico 0.1. Conservan el silencio, con su justificacion intacta, solo los dos donde el silencio ES la firma: tooltip (revela al pasar el raton, sonaria en cada cruce) y media-player (su propia salida ES audio — D-AP2.7, decision del hilo de audio). 2) EMITIR SILENCIO YA NO CUESTA `SoundChannel.handle` cortaba DESPUES de resolver la firma, asi que un tuning silenciador levantaba dos osciladores, un filtro y una envolvente programada para no sonar, en cada interaccion. Ahora corta antes de sintetizar cuando la ganancia resuelta es <= 0. El corte mira la ganancia RESUELTA, no el tuning, asi que un intent que la devuelve por encima de 0 (threat, fulfill) suena igual. Fijado con test propio, verificado en ROJO quitando el corto. 3) EL AFINADO SE NOMBRA POR LA FAMILIA QUE MODIFICA El catalogo mezclaba tres ejes en el primer segmento —familia (emerge.*, contact.silent), componente (tooltip.*, tabs.*) y la categoria huerfana form.*— sin regla declarada y sin guard que mirara. `form.commit.soft/subtle` entro el 2026-05-19 (09e261878, el commit que CREA sounds.ts) nombrando con verdad a siete controles de formulario; el 2026-05-26, 2d562f378 lo extendio a menus y arboles en la misma pasada que escribia la doctrina: 7 -> 29 consumidores en un commit, 50 hoy, de los que exactamente UNO era el Form. El D.6 de ese commit llego a poner el nombre correcto —commit.select— en la linea que usaba la clave incorrecta. form.commit.soft -> commit.soft form.commit.subtle -> commit.subtle form.toggle.silent -> commit.silent tooltip.silent -> emerge.silent tabs.select.soft -> commit.select.soft 131 ocurrencias en 68 ficheros, dirigido por `git ls-files` — nunca por recorrido de directorio: hay 3 worktrees vivos y build/ con 449 apariciones mas que un sed recursivo habria corrompido en silencio. La cola queda LIBRE a proposito: media-player aplica el silenciador al commit-set de un slider, no a un toggle — la cola describe la FIRMA, no el evento, asi que cerrarla contra el vocabulario de verbos fabricaria otra mentira. Por eso emerge.* no se toca pese a que `exit` no sea verbo canonico. Quedan tres silenciadores, uno por familia con base audible, con el mismo nombre: contact.silent (-0.25), commit.silent (-0.3), emerge.silent (-0.2). Eso convierte la coherencia en INVARIANTE: - `sounds-grammar.test.ts` (guard nuevo): cabeza ∈ SEMA_FAMILIES; forma {family}.{tail}; y todo {family}.silent resta EXACTAMENTE el gain base de su familia, leido de sema-map.ts —nunca copiado—. Verificado en ROJO: el guard calculo solo el -0.4 esperado de signal. - Ley escrita en sema.md § Tuning naming shapes, junto a la seccion hermana que ya legislaba los nombres de EVENTO. - vocabularies.md —"the ONE place the closed sets are spelled out"— pasa a enumerar los 14 afinados por familia; el generador los extrae del const y docs:check vigila su frescura. - D.5 gana las dos notas: el renombrado y la reversion del silencio. VERIFICACION: check 75 (linea base exacta) · sema 198/198 · docs:check 0/615 · navegador antes/despues (switch 0.0001 -> 0.1; button 0.25 sin tocar) · los 25 avisos de prettier entre lo tocado son deriva YA en HEAD, ninguno mio. Cronica NO reescrita (4 ficheros): AUDIT-blocks-2026-08-01, PLAN-audio-player, handoffs-claude-md y audit/components/date-field registran lo que ERA cierto. DESVIACION DECLARADA: sema/components/chronos.ts entra con 6 sustituciones mecanicas —chronos es de escritura excluida, pero dejarlo fuera rompia el type-check del repo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
L.push('');
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
L.push('Silence is not a name: it is `SILENT`, one canonical value for the whole');
L.push('system (`$uix/sema`). Declared on a channel slice, the resolver honours it by');
L.push('dropping that channel.');
L.push('');
L.push(soundNames.map((n) => `\`${n}\``).join(' · '));
fix(sema): los toggles recuperan voz, y el silencio deja de sintetizarse — muere el prefijo form. Tres cosas que el mismo hilo destapo, de la mas visible a la mas de fondo. 1) LOS TOGGLES SUENAN OTRA VEZ (directiva de autor 2026-08-05) switch, toggle y toggle-group llevaban SILENCIADOS desde el 2026-05-26: su pack aplicaba un tuning que restaba exactamente el gain base de la familia commit (0.3 - 0.3 = 0). Medido en navegador: pico 0.0001, el suelo de la rampa. Un silencio por defecto no se percibe como sobriedad sino como componente roto. Pasan a `commit.medium` (gain 0.1): audible, y un tercio de una pulsacion de boton (contact base 0.25) para que un flip no pese mas que una activacion. Los deltas de intent siguen montando encima (threat +0.1, fulfill +0.05). El haptico ligero no cambia. Medido despues: switch pico 0.1. Conservan el silencio, con su justificacion intacta, solo los dos donde el silencio ES la firma: tooltip (revela al pasar el raton, sonaria en cada cruce) y media-player (su propia salida ES audio — D-AP2.7, decision del hilo de audio). 2) EMITIR SILENCIO YA NO CUESTA `SoundChannel.handle` cortaba DESPUES de resolver la firma, asi que un tuning silenciador levantaba dos osciladores, un filtro y una envolvente programada para no sonar, en cada interaccion. Ahora corta antes de sintetizar cuando la ganancia resuelta es <= 0. El corte mira la ganancia RESUELTA, no el tuning, asi que un intent que la devuelve por encima de 0 (threat, fulfill) suena igual. Fijado con test propio, verificado en ROJO quitando el corto. 3) EL AFINADO SE NOMBRA POR LA FAMILIA QUE MODIFICA El catalogo mezclaba tres ejes en el primer segmento —familia (emerge.*, contact.silent), componente (tooltip.*, tabs.*) y la categoria huerfana form.*— sin regla declarada y sin guard que mirara. `form.commit.soft/subtle` entro el 2026-05-19 (09e261878, el commit que CREA sounds.ts) nombrando con verdad a siete controles de formulario; el 2026-05-26, 2d562f378 lo extendio a menus y arboles en la misma pasada que escribia la doctrina: 7 -> 29 consumidores en un commit, 50 hoy, de los que exactamente UNO era el Form. El D.6 de ese commit llego a poner el nombre correcto —commit.select— en la linea que usaba la clave incorrecta. form.commit.soft -> commit.soft form.commit.subtle -> commit.subtle form.toggle.silent -> commit.silent tooltip.silent -> emerge.silent tabs.select.soft -> commit.select.soft 131 ocurrencias en 68 ficheros, dirigido por `git ls-files` — nunca por recorrido de directorio: hay 3 worktrees vivos y build/ con 449 apariciones mas que un sed recursivo habria corrompido en silencio. La cola queda LIBRE a proposito: media-player aplica el silenciador al commit-set de un slider, no a un toggle — la cola describe la FIRMA, no el evento, asi que cerrarla contra el vocabulario de verbos fabricaria otra mentira. Por eso emerge.* no se toca pese a que `exit` no sea verbo canonico. Quedan tres silenciadores, uno por familia con base audible, con el mismo nombre: contact.silent (-0.25), commit.silent (-0.3), emerge.silent (-0.2). Eso convierte la coherencia en INVARIANTE: - `sounds-grammar.test.ts` (guard nuevo): cabeza ∈ SEMA_FAMILIES; forma {family}.{tail}; y todo {family}.silent resta EXACTAMENTE el gain base de su familia, leido de sema-map.ts —nunca copiado—. Verificado en ROJO: el guard calculo solo el -0.4 esperado de signal. - Ley escrita en sema.md § Tuning naming shapes, junto a la seccion hermana que ya legislaba los nombres de EVENTO. - vocabularies.md —"the ONE place the closed sets are spelled out"— pasa a enumerar los 14 afinados por familia; el generador los extrae del const y docs:check vigila su frescura. - D.5 gana las dos notas: el renombrado y la reversion del silencio. VERIFICACION: check 75 (linea base exacta) · sema 198/198 · docs:check 0/615 · navegador antes/despues (switch 0.0001 -> 0.1; button 0.25 sin tocar) · los 25 avisos de prettier entre lo tocado son deriva YA en HEAD, ninguno mio. Cronica NO reescrita (4 ficheros): AUDIT-blocks-2026-08-01, PLAN-audio-player, handoffs-claude-md y audit/components/date-field registran lo que ERA cierto. DESVIACION DECLARADA: sema/components/chronos.ts entra con 6 sustituciones mecanicas —chronos es de escritura excluida, pero dejarlo fuera rompia el type-check del repo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
L.push('');
feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1) An agent building a component from the docs-book alone could not assign archetypes / holds / haptic kinds: the closed sets live only in code consts and the corpus (correctly) forbids copying them into prose, so they were invisible from the docs. STUMBLES #1, the top stumble. Fix: docs/canon/vocabularies.md is GENERATED from the consts by 'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled out (generated = no drift objection), which every other doc links to. It covers part archetypes (with a one-line role each), sema families with their default hold/persistence + per-intent overrides, verbs by family, intents, the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes, variant archetypes, and the shared common.* strings. - scripts/docs-vocabularies.ts imports the consts (robust — no fragile JSDoc/regex parsing) and formats them; the generation is an exported function so docs-check can compare. - Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the authoritative one-liners the appendix reads — descriptions become first-class data instead of inline union JSDoc a tool would have to parse. - docs-check gains I7: it regenerates in-memory and fails if the committed file drifts from the consts (negative-tested: corrupt -> error, regen -> green). The appendix's own counts pass I1, so I1 doubles as a second guard. - Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md (doctrine here, enumerated lists there), architecture/morfo.md (pick a part's archetype), and the component-audit minimum-package list (STUMBLES #8). docs:check 0 errors; npm run check unchanged in my files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// Intents
L.push(`## Intents (${INTENTS.length})`);
L.push('');
L.push('The evaluative axis (`INTENTS`, `src/uix/intent.ts`). `commit` and `signal`');
L.push('require one; the intent policy per family is `SEMA_FAMILY_POLICY`.');
L.push('');
L.push(INTENTS.map((i) => `\`${i}\``).join(' · '));
L.push('');
// Hold durations
L.push('## Hold / perceptual durations');
L.push('');
L.push('The named perceptual scale (`SEMA_DURATIONS`, `src/uix/sema/durations.ts`);');
L.push('an event `hold` is a label from here or a raw ms number.');
L.push('');
L.push('| Label | ms |');
L.push('| --- | --- |');
for (const [label, ms] of Object.entries(SEMA_DURATIONS)) L.push(`| \`${label}\` | ${ms} |`);
L.push('');
// Haptic kinds
const kinds = hapticKinds();
L.push(`## Haptic kinds (${kinds.length})`);
L.push('');
L.push('The `kind` a `haptic` channel signature may use (`HapticSignature`,');
L.push('`src/uix/sema/channels.ts`). Haptic is opt-in (`new EngineSemantic({ haptic: true })`).');
L.push('');
L.push(kinds.map((k) => `\`${k}\``).join(' · '));
L.push('');
// Palette scales
L.push(`## Palette scales (${PALETTE_SCALES.length})`);
L.push('');
L.push('The donor scales a component `color` prop accepts under `intent="neutral"`');
L.push('(`PALETTE_SCALES`, `src/uix/eidos/lib/types.ts`) — the per-instance override');
L.push('(`<X color="teal">`). NEVER hand-count this list.');
L.push('');
L.push(PALETTE_SCALES.map((s) => `\`${s}\``).join(' · '));
L.push('');
// Sizes
L.push(`## Sizes (${SIZES.length})`);
L.push('');
L.push('The size scale (`SIZES`; the physical primitives are `SIZE_PRIMITIVE_KEYS`,');
L.push('`src/uix/eidos/lib/types.ts`). `full` is a layout semantic, not a physical');
L.push('tier. Each component exposes the subset its recipe supports.');
L.push('');
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
L.push(`Physical: ${SIZE_PRIMITIVE_KEYS.map((s) => `\`${s}\``).join(' · ')} · layout: \`full\``);
feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1) An agent building a component from the docs-book alone could not assign archetypes / holds / haptic kinds: the closed sets live only in code consts and the corpus (correctly) forbids copying them into prose, so they were invisible from the docs. STUMBLES #1, the top stumble. Fix: docs/canon/vocabularies.md is GENERATED from the consts by 'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled out (generated = no drift objection), which every other doc links to. It covers part archetypes (with a one-line role each), sema families with their default hold/persistence + per-intent overrides, verbs by family, intents, the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes, variant archetypes, and the shared common.* strings. - scripts/docs-vocabularies.ts imports the consts (robust — no fragile JSDoc/regex parsing) and formats them; the generation is an exported function so docs-check can compare. - Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the authoritative one-liners the appendix reads — descriptions become first-class data instead of inline union JSDoc a tool would have to parse. - docs-check gains I7: it regenerates in-memory and fails if the committed file drifts from the consts (negative-tested: corrupt -> error, regen -> green). The appendix's own counts pass I1, so I1 doubles as a second guard. - Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md (doctrine here, enumerated lists there), architecture/morfo.md (pick a part's archetype), and the component-audit minimum-package list (STUMBLES #8). docs:check 0 errors; npm run check unchanged in my files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
L.push('');
// Variants
L.push('## Variant archetypes');
L.push('');
L.push('The canonical variant sets per archetype (`EIDOS_VARIANTS`,');
L.push('`src/uix/eidos/lib/types.ts`). A component narrows to one set; component-only');
L.push('values live in its own `types.ts`.');
L.push('');
L.push('| Archetype | Variants |');
L.push('| --- | --- |');
for (const [archetype, values] of Object.entries(EIDOS_VARIANTS)) {
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
L.push(
`| \`${archetype}\` | ${(values as readonly string[]).map((v) => `\`${v}\``).join(' · ')} |`
);
feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1) An agent building a component from the docs-book alone could not assign archetypes / holds / haptic kinds: the closed sets live only in code consts and the corpus (correctly) forbids copying them into prose, so they were invisible from the docs. STUMBLES #1, the top stumble. Fix: docs/canon/vocabularies.md is GENERATED from the consts by 'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled out (generated = no drift objection), which every other doc links to. It covers part archetypes (with a one-line role each), sema families with their default hold/persistence + per-intent overrides, verbs by family, intents, the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes, variant archetypes, and the shared common.* strings. - scripts/docs-vocabularies.ts imports the consts (robust — no fragile JSDoc/regex parsing) and formats them; the generation is an exported function so docs-check can compare. - Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the authoritative one-liners the appendix reads — descriptions become first-class data instead of inline union JSDoc a tool would have to parse. - docs-check gains I7: it regenerates in-memory and fails if the committed file drifts from the consts (negative-tested: corrupt -> error, regen -> green). The appendix's own counts pass I1, so I1 doubles as a second guard. - Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md (doctrine here, enumerated lists there), architecture/morfo.md (pick a part's archetype), and the component-audit minimum-package list (STUMBLES #8). docs:check 0 errors; npm run check unchanged in my files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
}
L.push('');
// Common strings
const keys = commonKeys();
L.push(`## Shared strings (\`common.*\`, ${keys.length})`);
L.push('');
L.push('The shared idlangref leaves (`commonLangs`, `src/uix/langs/common.ts`),');
L.push('reachable via `v.commonRef(...)` or `#?common.{path}|Fallback` — reuse these');
L.push('instead of re-declaring a close/cancel/clear label per component.');
L.push('');
L.push(keys.map((k) => `\`${k}\``).join(' · '));
L.push('');
return L.join('\n') + '\n';
}
// Direct run → write the file.
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente F1-F5 del PLAN-sound-names, por orden directa del autor. El componente dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un .wav no es asunto suyo. sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle' centroid: {op:'add',value:2800}, decay: {op:'add',value:20} } EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones, cuatro caracteres, dos samples. Los nombres NO llevan familia delante y esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia 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. EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en modo replace, asi que la eleccion del componente borraba el perfil evaluativo del intent — medido: un dialog con `threat` sonaba igual que uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de los deltas de intent: base de familia -> EL NOMBRE -> intent -> evento -> app Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan: se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no para los 3 packs que un guard anterior muestreaba. LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT. Al estrecharlo, el compilador enumero la migracion entera — 173 errores en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se queda abierto a proposito: `overrides.cascade` es la puerta de personalizacion de la app, y una app puede legitimamente autorar un sonido que el catalogo no lleva. El framework se aplica a si mismo la regla estricta. MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92 (43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz — mas brillante que `signal`, la familia mas brillante del mapa— porque era el residuo aritmetico de un sample borrado (4600-1800). Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia audible; cada una se revierte editando UNA linea. La unica grande es deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto de todo el sistema —mas que una alerta—, residuo de una de las diez cabezas de tuning que mentian (S-39). Ahora es `soft.rise`. GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes, con los comentarios fuera — la prosa que menciona la forma vieja no es la forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante 3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre existe». Bug propio cazado por su test: `namedSound` devolvia la entrada del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo que un nombre significa para toda la sesion. VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto · docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis cambios (los 6 de contracts.test son ajenos y preexistentes). QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain, luego sobre un .wav el intent solo mueve el volumen). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
if (
import.meta.url === `file://${process.argv[1]}` ||
process.argv[1]?.endsWith('docs-vocabularies.ts')
) {
feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1) An agent building a component from the docs-book alone could not assign archetypes / holds / haptic kinds: the closed sets live only in code consts and the corpus (correctly) forbids copying them into prose, so they were invisible from the docs. STUMBLES #1, the top stumble. Fix: docs/canon/vocabularies.md is GENERATED from the consts by 'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled out (generated = no drift objection), which every other doc links to. It covers part archetypes (with a one-line role each), sema families with their default hold/persistence + per-intent overrides, verbs by family, intents, the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes, variant archetypes, and the shared common.* strings. - scripts/docs-vocabularies.ts imports the consts (robust — no fragile JSDoc/regex parsing) and formats them; the generation is an exported function so docs-check can compare. - Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the authoritative one-liners the appendix reads — descriptions become first-class data instead of inline union JSDoc a tool would have to parse. - docs-check gains I7: it regenerates in-memory and fails if the committed file drifts from the consts (negative-tested: corrupt -> error, regen -> green). The appendix's own counts pass I1, so I1 doubles as a second guard. - Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md (doctrine here, enumerated lists there), architecture/morfo.md (pick a part's archetype), and the component-audit minimum-package list (STUMBLES #8). docs:check 0 errors; npm run check unchanged in my files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
writeFileSync(OUT, generateVocabulariesDoc(), 'utf8');
console.log(`docs:vocabularies → ${OUT}`);
}

Powered by TurnKey Linux.