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

264 lines
11 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
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho componentes y `emerge-open` en tres. No era estetica — un preset de movimiento engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna firma y simplemente no animaba, sin romper una sola prueba. Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos, 256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran 40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y `handle-drop` ya existian en 10 y 4 componentes. `validateMorfo` cierra la puerta: un `events[].name` que no empiece por su familia ahora lanza. Visto fallar antes con un nombre pelado inyectado. Lo que el renombrado destapo, y va aqui tambien: - La receta del splitter enganchaba `commit-resize`, muerto desde `bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el catalogo de morfos — el guard que lo habria cazado en su dia. - La familia `shift` era muda en el canal visual, contra su propia doctrina (c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora tiene firma direccional: sexto atributo del sello (`data-event-direction`, `forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por `:dir()`. Medido: LTR -30px/+30px, RTL los invierte. - El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues —media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze` moria sin pintar un fotograma. Una superficie, una ranura (A-36). - 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de decision, el componente vivo). Las docs desfasadas, corregidas; los nueve DEFECTOS de codigo obsoleto quedan abiertos y sin tocar. - `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y habia tres cosas distintas deletreadas «direction». check en su linea base con 0 errores nuevos por diferencia de conjuntos · docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador medidas con raton real y rAF vivo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
* must draw from (archetypes, sema families + holds + verbs, intents,
* directions, 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.
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
*
* 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';
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho componentes y `emerge-open` en tres. No era estetica — un preset de movimiento engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna firma y simplemente no animaba, sin romper una sola prueba. Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos, 256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran 40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y `handle-drop` ya existian en 10 y 4 componentes. `validateMorfo` cierra la puerta: un `events[].name` que no empiece por su familia ahora lanza. Visto fallar antes con un nombre pelado inyectado. Lo que el renombrado destapo, y va aqui tambien: - La receta del splitter enganchaba `commit-resize`, muerto desde `bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el catalogo de morfos — el guard que lo habria cazado en su dia. - La familia `shift` era muda en el canal visual, contra su propia doctrina (c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora tiene firma direccional: sexto atributo del sello (`data-event-direction`, `forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por `:dir()`. Medido: LTR -30px/+30px, RTL los invierte. - El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues —media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze` moria sin pintar un fotograma. Una superficie, una ranura (A-36). - 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de decision, el componente vivo). Las docs desfasadas, corregidas; los nueve DEFECTOS de codigo obsoleto quedan abiertos y sin tocar. - `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y habia tres cosas distintas deletreadas «direction». check en su linea base con 0 errores nuevos por diferencia de conjuntos · docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador medidas con raton real y rAF vivo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
import { SEMA_DIRECTIONS } from '../src/uix/sema/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 { 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('');
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho componentes y `emerge-open` en tres. No era estetica — un preset de movimiento engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna firma y simplemente no animaba, sin romper una sola prueba. Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos, 256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran 40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y `handle-drop` ya existian en 10 y 4 componentes. `validateMorfo` cierra la puerta: un `events[].name` que no empiece por su familia ahora lanza. Visto fallar antes con un nombre pelado inyectado. Lo que el renombrado destapo, y va aqui tambien: - La receta del splitter enganchaba `commit-resize`, muerto desde `bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el catalogo de morfos — el guard que lo habria cazado en su dia. - La familia `shift` era muda en el canal visual, contra su propia doctrina (c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora tiene firma direccional: sexto atributo del sello (`data-event-direction`, `forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por `:dir()`. Medido: LTR -30px/+30px, RTL los invierte. - El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues —media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze` moria sin pintar un fotograma. Una superficie, una ranura (A-36). - 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de decision, el componente vivo). Las docs desfasadas, corregidas; los nueve DEFECTOS de codigo obsoleto quedan abiertos y sin tocar. - `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y habia tres cosas distintas deletreadas «direction». check en su linea base con 0 errores nuevos por diferencia de conjuntos · docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador medidas con raton real y rAF vivo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
// Directions
L.push(`## Directions (${SEMA_DIRECTIONS.length})`);
L.push('');
L.push('The sense of a traversal (`SEMA_DIRECTIONS`, `src/uix/sema/types.ts`),');
L.push('projected as `data-event-direction`. Per emission like the intent, and');
L.push('unlike it undeclarable on the EVENT: one `shift-navigate` is the previous');
L.push('month and the next one is the following month, so only the caller knows');
L.push('(`TriggerOptions.direction`). Optional — most occurrences have no sense to');
L.push('declare, and an invented one is worse than none.');
L.push('');
L.push('A SENSE, never an axis: eidos maps `forward` onto the inline end and');
L.push('`backward` onto the inline start, so RTL flips through `:dir(rtl)` and');
L.push('nothing upstream knows about it. Distinct from `SoundContour`');
feat(soma,sema): el acarreo de chronos habla, los 3 resolvers se van, y un guard cierra la clase D10 · no era una decision abierta: era una firmada sin ejecutar --------------------------------------------------------------- Mi correccion de mi propia propuesta estaba sobre-corregida: lei la linea 95 de un plan y la 367 de otro, no los documentos. - `PLAN-sound-engine.md` esta CERRADO (2026-07-30) y su columna «sema conserva» miente en 4 de 5 celdas: `SOUND_LIBRARY` no existe, `SOUND_TUNINGS` se retiro el 2026-08-06 (con guard que lo mantiene retirado), el sonido salio de la cascada, y «que firma para que familia × intent» lo sustituyo la ley del nombre. Es un inventario del 30 de julio, no un mandato. - `PLAN-audio-player.md` esta ⛔ GATE RECHAZADO — SUSPENDIDO, y su cabecera dice «nada de lo de abajo se implementa». - La fuente que gobierna es `docs/decisions/book-deviations.md`, de DECISIONES, que el 2026-08-06 escribio en pasado: «un gesto continuo suena por REPETICION … y los tres resolvers se borraron con el». `git log -S` sobre `sounds.ts` devuelve UN SOLO COMMIT en toda su vida (mayo): nunca se borraron. El registro dio por ejecutado lo que no se ejecuto. Retirados los tres, `DragSoundParams`, el `SoundContour` de sema, sus exports, los dos tests que solo se probaban a si mismos y los helpers `clamp`/`lerp` que quedaron huerfanos: `sounds.ts` pasa de 160 a 79 lineas. Con ellos cae el homonimo `SoundContour` — el nombre queda para el art, sin renombrar nada. El plan cerrado no se reescribe, pero su §4 lleva ya una nota fechada porque me engaño a mi. D9 · el acarreo habla --------------------- `handle` pregunta «¿estoy manipulando directamente este objeto?», y una familia que no responde su propia pregunta ha fallado en lo unico para lo que existe (CANON §2). Chronos es el componente mas manipulable del catalogo y hablaba solo el suelte: el acarreo entero estaba mudo. El *handle invisible*. No se autora nada — `handle` ya declara `sounds: { default: 'step' }` y los dos canales, asi que el RITMO es el sonido. Estrangulado a ~14 Hz como slider y splitter, y ANCLADO: los destinos son partes repetidas, asi que es aplicacion directa de la ley de `e5811e422`. Donde el acarreo NO es de chronos: arrastrar un chip con puntero pasa por el `DragDrop` compuesto, que declara tres eventos y emite tres, sin acarreo continuo (CANON regla 6). Dos componentes narrando un gesto seria peor. Asi que `handle-drag` es el acarreo de TECLADO y `handle-resize` cubre las dos vias. Medido: el arrastre del tirador con puntero da 6 `handle-resize` en 6 pasos, todos sobre `event-resize-handle`. El guard, y dos hallazgos que salieron de medirlo -------------------------------------------------- `book-deviations.md` tipifica el defecto hermano como «mecanicamente comprobable y merece guard». Este es la misma forma un piso arriba, y es la clase que produjo D1, D3, D4 y D9. La prueba NO es `trigger('literal')` — chronos despacha por variable y eso me dio dos falsos negativos durante la auditoria; se comprueba que el nombre aparezca como literal donde el provider alcance: su directorio soma mas las librerias compartidas (sin `$libs/selection` en el ambito, combobox y select se leen como inertes y no lo son). D13 — quedan 7 eventos inertes en 4 componentes, enumerados en `INERT_EVENT_DEBT`, que no son exenciones sino la cola. El peor: `tooltip` 3 de 3, cero `trigger(` en todo su directorio, abriendo y cerrando sin firma alguna. D14 — midiendo D9 salio un defecto anterior: `restoreChipFocus` reenfoca en un `queueMicrotask` que corre ANTES de que Svelte reponga el nodo, asi que enfoca la nada y el foco cae al `<body>`. La segunda flecha ya no llega al manejador y el Escape tampoco: la interaccion que el propio componente anuncia («Grabbed. Arrow keys move, Enter drops, Escape cancels») muere al primer paso. No lo toco aqui — es de foco, no de firma, y merece su propia medicion en navegador real. Verificado: guard visto fallar deshaciendo D9 (caza los dos eventos); `check` 70 errores en linea base; 2250 tests del uix — los 6 fallos de `contracts.test.ts` siguen siendo de la otra sesion; `docs:check` 0/623 tras regenerar `vocabularies.md`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
L.push('(`src/arts/sound/types.ts`), which shapes a pitch over a note, and');
L.push('from `Morfo.direction`, which names the parts that carry the `dir` stamp');
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho componentes y `emerge-open` en tres. No era estetica — un preset de movimiento engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna firma y simplemente no animaba, sin romper una sola prueba. Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos, 256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran 40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y `handle-drop` ya existian en 10 y 4 componentes. `validateMorfo` cierra la puerta: un `events[].name` que no empiece por su familia ahora lanza. Visto fallar antes con un nombre pelado inyectado. Lo que el renombrado destapo, y va aqui tambien: - La receta del splitter enganchaba `commit-resize`, muerto desde `bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el catalogo de morfos — el guard que lo habria cazado en su dia. - La familia `shift` era muda en el canal visual, contra su propia doctrina (c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora tiene firma direccional: sexto atributo del sello (`data-event-direction`, `forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por `:dir()`. Medido: LTR -30px/+30px, RTL los invierte. - El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues —media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze` moria sin pintar un fotograma. Una superficie, una ranura (A-36). - 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de decision, el componente vivo). Las docs desfasadas, corregidas; los nueve DEFECTOS de codigo obsoleto quedan abiertos y sin tocar. - `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y habia tres cosas distintas deletreadas «direction». check en su linea base con 0 errores nuevos por diferencia de conjuntos · docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador medidas con raton real y rAF vivo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
L.push('(the RTL contract — see `docs/canon/direction-contract.md`).');
L.push('');
L.push(SEMA_DIRECTIONS.map((d) => `\`${d}\``).join(' · '));
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
// 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.