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
---
title: Canonical vocabularies (generated)
type: canon
audience: human + agent
authority: canonical — the closed sets, generated from the code consts
status: current
generated: npm run docs:vocabularies (do NOT edit by hand)
---
# Canonical vocabularies
> **Generated from the code — do not edit.** Run `npm run docs:vocabularies`
> to regenerate; `npm run docs:check` fails if this file drifts from the
> consts. This is the ONE place the closed sets are spelled out (the reason
> every other doc links here instead of copying a list that would go stale).
> When building a component you draw part archetypes, event families/verbs,
> intents and holds from exactly these sets (STUMBLES #1).
## Part archetypes (26)
The cross-component classification a `part` may declare
(`ARCHETYPE_VOCABULARY`, `src/uix/morfo/types.ts` ). Omit it for a plain
display part that pulls no shared styling.
| Archetype | Role |
| --- | --- |
| `provider` | root context provider |
| `trigger` | activates an action or opens an overlay (button-like) |
| `field-trigger` | a field/input/select/combo trigger — NOT a button; owns the flush field-control treatment, never the generic button/popover chrome |
| `content` | primary content panel of an overlay or section |
| `overlay` | modal/dim backdrop behind content |
| `viewport` | scrollable / focusable container |
| `item` | list / tree / menu item |
| `option` | selectable option in a select-like list |
| `indicator` | visual progress / decorative state |
| `thumb` | draggable handle (slider, scroll, switch) |
| `track` | background of slider / scroll / progress |
| `label` | text label associated with a control |
| `title` | overlay or section title |
| `description` | secondary descriptive text |
| `close` | dismiss / close button |
| `action` | call-to-action button |
| `header` | section header (often above content) |
| `footer` | section footer (often below content) |
| `image` | `<img>` -based content |
| `fallback` | shown when primary content unavailable |
| `arrow` | pointer arrow (tooltip / popover) |
| `separator` | visual divider |
| `group` | grouping container for related items |
| `input` | form input element |
| `segment` | discrete sub-input (pin-input segment, OTP digit) |
| `preview` | file / link / data preview |
## Sema families (8) — default hold + persistence
Each event declares a `semantic.family` ; the default perceptual hold and
persistence come from `SEMA_HOLDS_BY_INTENT` (`src/uix/sema/holds.ts`),
overridable per event. Named holds map to `SEMA_DURATIONS` .
| Family | Default hold | Persistence | Per-intent overrides |
| --- | --- | --- | --- |
| `contact` | 120ms (`glimpse`) | transient | — |
| `emerge` | 240ms (`brief`) | transient | — |
| `shift` | 600ms (`noticed`) | transient | — |
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| `commit` | 240ms (`brief`) | transient | fulfill: 400ms/transient |
| `signal` | 240ms (`brief`) | transient | risk: 240ms/untilFix; threat: 240ms/untilAction; loss: 240ms/transient |
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
| `handle` | 240ms (`brief`) | transient | — |
| `sustain` | 600ms (`noticed`) | stateBound | — |
| `delegate` | 600ms (`noticed`) | transient | — |
## Sema verbs, by family
The canonical verb set an event may use for each family (`SEMA_VERBS`,
`src/uix/sema/verbs.ts` ). A `family.verb` pairing outside this is drift.
| Family | Verbs |
| --- | --- |
| `contact` | `press` · `tap` · `activate` · `focus` · `trigger` · `release` |
| `commit` | `select` · `unselect` · `toggle` · `save` · `submit` · `confirm` · `complete` · `fail` · `cancel` · `reset` · `discard` · `delete` · `restore` · `expire` · `acknowledge` · `apply` · `partial` · `block` · `move` · `set` · `remove` · `reorder` · `upload` |
| `signal` | `announce` · `notify` · `warn` · `alert` · `inform` · `emphasize` · `remind` |
| `handle` | `pick` · `carry` · `drop` · `drag` · `resize` · `reorder` · `rotate` · `scroll` · `zoom` |
| `emerge` | `present` · `dismiss` · `open` · `close` · `expand` · `collapse` · `reveal` · `hide` |
| `shift` | `enter-mode` · `exit-mode` · `navigate` · `route` · `step` · `return` · `context` |
| `sustain` | `start` · `progress` · `loading` · `waiting` · `syncing` · `processing` · `streaming` · `pending` · `retrying` · `upload` · `end` |
| `delegate` | `offer` · `plan` · `authorize` · `act` · `review` · `escalate` · `return` |
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
## Sounds (16)
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
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
THE catalogue (`SOUNDS`, `src/uix/sema/sound-names.ts` ). A component names
one of these and writes nothing else: the type of a pack rule accepts a name
or `SILENT` , and nothing more. Whether a name resolves to a synthesised
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
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
A name is applied over the family base and BEFORE the intent deltas, so the
evaluative profile always survives it — which is what makes the old D.7 / S-07
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
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
Silence is not a name: it is `SILENT` , one canonical value for the whole
system (`$uix/sema`). Declared on a channel slice, the resolver honours it by
dropping that channel.
refactor(sema)!: el sonido se ELIGE, no se modula — evento.intent = sonido
Directiva del autor, sin matices: «todo el sistema de sonido es una puta
mierda... la unica solucion es evento sonido, evento.intent = sonido, y
punto y nada de mierdas de que si el intent modifica nada».
Tenia razon, y lo que lo destapo fue oirlo: los dieciseis nombres del
catalogo anterior sonaban practicamente igual. Medido despues: cinco de
ellos eran LA MISMA nota de 700 Hz a cinco volumenes, y las variantes de
direccion se separaban 20 Hz. El modelo de modulacion producia teoria
bonita y mush audible.
LA REGLA ENTERA, y no hay mas:
nombre = per-emit ?? cascada ?? pack ?? morfo ?? familia[verbo] ?? familia.default
sonido = pack[`${nombre}.${intent}`] ?? pack[nombre] ?? nada
Dos busquedas. El intent SELECCIONA un sonido entero; si no existe la
variante se desprecia y suena la base; si no hay base, silencio. Un
`threat` no es un `tick` con mas aspereza: es otro sonido —mas grave, mas
rasposo, descendente— y no se pueden confundir.
EL TIER DEL VERBO es lo que vacia los packs. La tabla de familia se
indexa por verbo (`emerge.close → 'close'`), asi que ni un solo overlay
escribe su cierre. De ~165 reglas de sonido quedan 30, en 16 de los 71
packs — y las 135 borradas no decian mas que el defecto. Para que el
verbo llegue, ahora VIAJA en la señal: se calculaba y se tiraba con un
`void` (hallazgo S-40), el tipo prometia que se emitia y era falso.
CATALOGO NUEVO: 10 bases + 6 variantes, cada una un sonido diseñado
entero. Medido en el grafo de audio real: de 240 Hz (`tick.loss`) a
1900 Hz (`step`), casi tres octavas, y las variantes evaluativas traen su
propio modulador de aspereza — texturas distintas, no un tono movido.
100% sintesis a proposito: el pack por defecto no lleva binarios, funciona
sin red y no puede dar 404. Los .wav quedan como pack de EJEMPLO.
EL GUARD QUE FALTABA: `distinguishability` exige que dos entradas se
separen en al menos DOS ejes perceptuales. Nacio en rojo contra mi propio
catalogo —ocho pares casi gemelos— y dirigio el diseño hasta separarlos.
Una instantanea no lo habria cazado: cada valor era exactamente el que la
tabla decia; lo que faltaba era una RELACION entre entradas.
GESTOS POR REPETICION (idea del autor). Un arrastre ya emite a su ritmo,
asi que toca `step` (18 ms) por emision: la velocidad del gesto ES la del
trinquete, como una rueda fisica. Mueren los tres resolvers que
sintetizaban tono desde posicion y velocidad —la ultima aritmetica del
sistema— y `handle` queda EXENTA de la memoria de frecuencia, que si no
estrangularia el trinquete al primer arrastre. Perdida firmada: el ritmo
lleva la velocidad, pero la posicion ya no mapea a altura.
NADIE ESCRIBE PARAMETROS, ni la app. `SemaSignatureOverride.sound` y
`SemaCascadeRule.sound` aceptan un nombre o SILENT. La rendija de autoria
inline «para la personalizacion» sobraba: un producto REGISTRA su sonido
(nombre + definicion, una vez) y lo nombra.
⚠️ BUG PROPIO durante la cirugia: al reemplazar el bloque del sonido borre
tambien la aplicacion de deltas de intent, y el HAPTICO dejo de recibirlos.
Lo cazaron sus tests. Restaurado — el haptico sigue modulando porque su
vocabulario es categorico y sus deltas cambian el KIND, que tambien es
seleccion.
MUERE: los deltas de sonido del intent, las bases de sonido de familia, la
capa 1.5, `op:'add'` para sonido, `mergeSoundOverride`, y
`d7-intent-survives.test.ts` — vigilaba una inversion que ya no puede
existir.
VERIFICADO: sema+morfo+soma+sound 1108/1108 · docs:check 0/618 · la pagina
`/uix/docs/sound` reescrita y medida en navegador (commit+threat →
tick.threat; commit+affirm → tick, el intent se desprecia; emerge+close →
close sin una linea de pack; sustain → silencio). Los 7 fallos de la suite
completa son los ajenos y preexistentes de contracts.test.
⚠️ NO SE HA OIDO. Todo esta medido en el grafo de audio, no con un oido.
Si `open` suena a lo que debe sonar un panel abriendose es tu decision, y
se afina en un fichero.
⚠️ El estudio `/temas/sema` compila pero edita firmas crudas: su modelo es
el viejo. Queda por reconvertir.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
`touch` · `step` · `tick` · `open` · `close` · `slide` · `alert` · `air` · `settle` · `snap` · `tick.fulfill` · `tick.risk` · `tick.threat` · `tick.loss` · `alert.risk` · `alert.threat`
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
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 (6)
The evaluative axis (`INTENTS`, `src/uix/intent.ts` ). `commit` and `signal`
require one; the intent policy per family is `SEMA_FAMILY_POLICY` .
`neutral` · `affirm` · `fulfill` · `risk` · `threat` · `loss`
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 (2)
The sense of a traversal (`SEMA_DIRECTIONS`, `src/uix/sema/types.ts` ),
projected as `data-event-direction` . Per emission like the intent, and
unlike it undeclarable on the EVENT: one `shift-navigate` is the previous
month and the next one is the following month, so only the caller knows
(`TriggerOptions.direction`). Optional — most occurrences have no sense to
declare, and an invented one is worse than none.
A SENSE, never an axis: eidos maps `forward` onto the inline end and
`backward` onto the inline start, so RTL flips through `:dir(rtl)` and
nothing upstream knows about it. Distinct from `SoundContour`
(`ascending` / `descending` , `src/uix/sema/sounds.ts` ), which shapes a pitch,
and from `Morfo.direction` , which names the parts that carry the `dir` stamp
(the RTL contract — see `docs/canon/direction-contract.md` ).
`forward` · `backward`
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 / perceptual durations
The named perceptual scale (`SEMA_DURATIONS`, `src/uix/sema/durations.ts` );
an event `hold` is a label from here or a raw ms number.
| Label | ms |
| --- | --- |
| `subliminal` | 50 |
| `glimpse` | 120 |
| `brief` | 240 |
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| `settled` | 400 |
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
| `noticed` | 600 |
| `insistent` | 1200 |
| `persistent` | 3000 |
## Haptic kinds (7)
The `kind` a `haptic` channel signature may use (`HapticSignature`,
`src/uix/sema/channels.ts` ). Haptic is opt-in (`new EngineSemantic({ haptic: true })`).
`tick` · `tap` · `pulse` · `thud` · `success` · `warning` · `error`
## Palette scales (33)
The donor scales a component `color` prop accepts under `intent="neutral"`
(`PALETTE_SCALES`, `src/uix/eidos/lib/types.ts` ) — the per-instance override
(`< X color = "teal" > `). NEVER hand-count this list.
`gray` · `slate` · `blue` · `cyan` · `teal` · `green` · `yellow` · `amber` · `orange` · `red` · `pink` · `purple` · `mauve` · `sage` · `olive` · `sand` · `tomato` · `ruby` · `crimson` · `plum` · `fuchsia` · `violet` · `iris` · `indigo` · `jade` · `grass` · `brown` · `sky` · `mint` · `lime` · `gold` · `bronze` · `steel`
## Sizes (8)
The size scale (`SIZES`; the physical primitives are `SIZE_PRIMITIVE_KEYS` ,
`src/uix/eidos/lib/types.ts` ). `full` is a layout semantic, not a physical
tier. Each component exposes the subset its recipe supports.
Physical: `xxs` · `xs` · `sm` · `md` · `lg` · `xl` · `xxl` · layout: `full`
## Variant archetypes
The canonical variant sets per archetype (`EIDOS_VARIANTS`,
`src/uix/eidos/lib/types.ts` ). A component narrows to one set; component-only
values live in its own `types.ts` .
| Archetype | Variants |
| --- | --- |
| `control` | `surface` · `outline` · `ghost` |
| `selection` | `solid` · `outline` · `ghost` |
| `chip` | `soft` · `solid` · `outline` · `ghost` |
| `marker` | `solid` · `soft` · `outline` |
| `tabs` | `line` · `surface` · `pills` · `segmented` |
feat(morfo,soma,eidos): chronos deja de tener partes que el morfo no conoce
C3 cierra con `more-link` y C4 con los seis marcadores que eidos estilizaba
sin declaracion. Los tres botones sin fuentes (`day-add`, `edit-action`,
`search-button`) comparten una clase; `undo`/`redo` se cablean sobre la del
nav; `timegrid` pasa a `time-view` porque estaba a un guion de la parte
`time-grid` que vive dentro; `peek-panel` y `day-peek` quedan como partes de
display puro con marcador inline. Cada una renderiza un `<button>` real
cuando el morfo lo declara: una `Region` que cae a `<div>` pondria el `type`
declarado sobre un no-boton.
La lupa deja de ser un adorno sin `onclick`: abre una paleta `Command.Dialog`
sobre TODOS los eventos, no un filtro de la vista, con `goToDate` nuevo en el
provider — la generalizacion de la que `goToday` era un caso particular. El
texto buscable viaja en `keywords` porque el scorer de Command lee value +
keywords y NUNCA los hijos renderizados, asi que se busca por titulo, por tipo
y por fecha.
Y tres etiquetas que mentian en silencio: `translationRef` / `commonRef`
normalizan la clave LITERAL y no consultan ningun mapa, asi que
`prevMonth` / `nextMonth` / `moreEvents` y `action.undo` / `action.redo` caian
al fallback ingles en una pagina `es`. Corregidas contra el catalogo, con
`common.buttons.undo` / `redo` anadidas.
Verificado: check 74 = base (delta cero) · 458 vitest · docs:check 0/621 ·
rtl:check 0/177 · eidos-lint chronos morfo-backed 32 -> 44, invalid 0 ·
navegador limpio en las tres vistas, con el A/B del undo medido
(18 jun -> drag -> 25 jun -> undo -> 18 jun -> redo -> 25 jun) y el salto de
la paleta (noviembre -> elegir Retro -> junio).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## Shared strings (`common.*`, 40)
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
The shared idlangref leaves (`commonLangs`, `src/uix/langs/common.ts` ),
reachable via `v.commonRef(...)` or `#?common.{path}|Fallback` — reuse these
instead of re-declaring a close/cancel/clear label per component.
feat(morfo,soma,eidos): chronos deja de tener partes que el morfo no conoce
C3 cierra con `more-link` y C4 con los seis marcadores que eidos estilizaba
sin declaracion. Los tres botones sin fuentes (`day-add`, `edit-action`,
`search-button`) comparten una clase; `undo`/`redo` se cablean sobre la del
nav; `timegrid` pasa a `time-view` porque estaba a un guion de la parte
`time-grid` que vive dentro; `peek-panel` y `day-peek` quedan como partes de
display puro con marcador inline. Cada una renderiza un `<button>` real
cuando el morfo lo declara: una `Region` que cae a `<div>` pondria el `type`
declarado sobre un no-boton.
La lupa deja de ser un adorno sin `onclick`: abre una paleta `Command.Dialog`
sobre TODOS los eventos, no un filtro de la vista, con `goToDate` nuevo en el
provider — la generalizacion de la que `goToday` era un caso particular. El
texto buscable viaja en `keywords` porque el scorer de Command lee value +
keywords y NUNCA los hijos renderizados, asi que se busca por titulo, por tipo
y por fecha.
Y tres etiquetas que mentian en silencio: `translationRef` / `commonRef`
normalizan la clave LITERAL y no consultan ningun mapa, asi que
`prevMonth` / `nextMonth` / `moreEvents` y `action.undo` / `action.redo` caian
al fallback ingles en una pagina `es`. Corregidas contra el catalogo, con
`common.buttons.undo` / `redo` anadidas.
Verificado: check 74 = base (delta cero) · 458 vitest · docs:check 0/621 ·
rtl:check 0/177 · eidos-lint chronos morfo-backed 32 -> 44, invalid 0 ·
navegador limpio en las tres vistas, con el A/B del undo medido
(18 jun -> drag -> 25 jun -> undo -> 18 jun -> redo -> 25 jun) y el salto de
la paleta (noviembre -> elegir Retro -> junio).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
`buttons.cancel` · `buttons.clear` · `buttons.close` · `buttons.decrement` · `buttons.dismiss` · `buttons.edit` · `buttons.increment` · `buttons.next` · `buttons.prev` · `buttons.redo` · `buttons.remove` · `buttons.reset` · `buttons.save` · `buttons.submit` · `buttons.undo` · `calendar.prev-month` · `calendar.next-month` · `calendar.month-select` · `calendar.year-select` · `month-grid.prev-year` · `month-grid.next-year` · `year-grid.prev-page` · `year-grid.next-page` · `date.year` · `date.month` · `date.day` · `time.hour` · `time.minute` · `time.second` · `time.day-period` · `time.time-zone` · `time.am` · `time.pm` · `time.period.dawn` · `time.period.morning` · `time.period.afternoon` · `time.period.dusk` · `time.period.night` · `time.period.late-night` · `field.empty`
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