astra
alpha-0.1-background
alpha-0.1-dir-prefs
alpha-0.1-sec-dom
menubar-v4-safe
active-uix
morfo-runtime
morfo-driven-soma
semantuix
glm-5
main
sium-v1.0
${ noResults }
77 Commits (dd188f1255ce566c9823219e30c8d09842e2c468)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
bd916ef3fb |
refactor(eidos): el catálogo habla un idioma — 269 claves al vocabulario firmado
D-TH.6, ejecutada. El slot de tinta pasa a `fg` y el modificador interactivo
se pone delante, que es lo que theming §6.7 r7 documentaba sin guard desde
que se escribió. Value-preserving: renombra la clave en `recipes/base.ts` y
sus 793 referencias en 101 ficheros; no toca un solo valor.
claves renombradas 269 en 66 componentes
referencias 793 en 101 ficheros
censo antes/después 162 recetas · 5.203 knobs · 1.841 públicos (37 %) · 56
sin contrato — IDÉNTICO, como debe ser un rename
--names antes/después 269 desviadas → 0
Qué NO entra, y por qué:
47 `{rol}-{slot-de-rol}` canónicas: COLOR_ROLE_SLOTS pone el modificador
detrás POR CONSTRUCCIÓN (`primary-solid-hover`)
13 exentas firmadas `--focus-ring-*` es familia del sistema; el color
de `aura` es un sustantivo; `stop-color` ES una
parte de gradient-builder
47 hovers neutros por VALOR: migran a la capa de estado
(§38 + R-4.3), no se renombran — firma 3
6 ocurrencias en historia changelog, errores-toxico, PLAN-affix/background:
reescribir un registro fechado lo vuelve mentira
El clasificador vive en el censo (`--names`), no en un script suelto, para que
el guard R-5.3 consuma la MISMA gramática que el codemod. Su muta-prueba
(`__names-mutatest.ts`, 27 casos) es lo que hizo el trabajo: cazó que yo
promovía al frente CUALQUIER valor declarado por el morfo, y así
`--sidebar-width-icon` (la anchura del raíl colapsado) se convertía en
`--sidebar-icon-width` (la anchura de un icono), que es otra cosa. La firma
dice «delante lo interactivo, detrás lo dimensional y contextual»: ahora sólo
promociona el vocabulario interactivo cerrado, y lo contextual —`below`,
`loaded`, `vertical`, `icon`— se queda donde estaba. Los cinco casos de
regresión están en la muta-prueba.
También cazó que `dropdown-menu.item-bg-hover` lee `var(--color-primary-element)`:
es un hover CON VALENCIA (el palette swap de recipe-contract §2), no el hover
bespoke que §38 deprecó. Clasificar por el nombre lo habría metido en una
migración que no le toca; se clasifica por el VALOR.
Verificación (§7.4, artefacto por paso):
diff de generated/ 269 renombres 1:1 · 0 cambios de valor · 22 privados
reapuntados a los nombres nuevos (`__names-verify-diff`)
computed tag-group · field · tabs: 6.467 valores en 23 estados,
0 diffs — y comprobado en el navegador que sirve el CSS
nuevo, para que ese 0 no sea el de una copia cacheada
huérfanos 0 en código
suite eidos 434 pasan · 1 rojo, el conocido (`skin-media-player`).
`recipe-css-contract` verde: es el guard que caza el
`var()` sin fallback a un nombre que ya no se emite,
o sea el fallo exacto de un rename a medias
component:audit 163 PASS · 3 NEEDS-WORK, los tres SIN TOCAR por esto
check 0 errores en ficheros tocados (los 73 globales son de
otras sesiones de la rama; se atribuye por fichero)
rtl:check 0 · docs:check 0
formato el renombrado no alarga ninguna línea: ningún fichero
tiene más líneas de 100 chars que antes, así que no se
pasa prettier — hacerlo reformateaba 300 ficheros de
deriva ajena
Entra aquí la corrección del repaso de los 7 ya hechos:
`gradient-builder.checker-color` → `checker-fg` (el damero de transparencia;
ahí `color` era slot). Sus `stop-color-*` no se tocan.
Queda para el paso siguiente: R-5.3 no puede graduar a `error` directo
mientras los 47 hovers neutros sigan hablando el idioma viejo — o migran
antes (firma 3, ya firmada), o el guard necesita una exención greppable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
cae88ca15b |
uix(navigation-menu): un <ul> no es una región, y por eso el landmark salía sin nombre
El morfo declaraba el nombre por defecto de la navegación en la parte que pinta
el <ul>, con la razón escrita al lado: «the root <nav> delegates its name to the
list it wraps». La delegación no existe. El gesto que un lector de pantalla
ofrece para saltar entre las áreas de una página llega al <nav>, y llegaba a un
<nav> ANÓNIMO — con el nombre del consumidor puesto una capa más adentro, en un
elemento que ninguna navegación por landmarks visita.
Sobrevivió tres semanas de tier porque con UNA navegación en la página un
landmark anónimo es admisible, y todas las demos del componente y todos los
blocks de sitio montan una. Apareció en cuanto el app-shell montó dos ámbitos,
que es lo que un shell hace por definición (A-111). La APG es explícita: si una
página incluye más de un landmark de navegación, cada uno lleva su nombre.
El arreglo NO es el que la ficha proponía. Su paso 1 quería que la lista
heredase el nombre por aria-labelledby; el barrido de los morfos hermanos con
defaultElement 'nav' dijo otra cosa: breadcrumb (<ol>) y nav-tree (<ul>)
declaran aria: [] en su lista y nombran sólo el <nav>. navigation-menu era el
único fuera de esa forma, así que la pareja de nombre —el aria-label con su
suppression prop-falsy ariaLabelledby, y el aria-labelledby por propRef— sube
entera a la parte Provider y la lista se queda con su aria-orientation.
En soma, la fuente ariaLabelledby sube al nivel del runtime (la forma de
breadcrumb) y el wrapper DEJA de sacar aria-label de restProps: viaja con el
resto y gana el merge por política de naming, que es lo que A-85 dejó escrito y
lo que este componente no aplicaba. Se van con ello el opt ariaLabel y el
reenvío condicional que la List hacía a mano.
Y como la regla no estaba escrita en ninguna doctrina —grep de «landmark» en
morfo.md, canon/ y guides/ daba cero—, se escribe: architecture/morfo.md §Step 4
gana «A landmark is named on ITS OWN element», y nace el censo
morfo/landmark-census.test.ts, que falla si una parte con defaultElement 'nav'
no declara un attr de nombre EN SU PARTE. Probado en rojo por mutación tres
veces: deshaciendo el arreglo (navigation-menu.provider), retirando la excepción
(palabras.breadcrumb) y vaciando el catálogo, que es la mutación que impide que
un censo sobre nada pase en verde. El ámbito es 'nav' a propósito: main, header
y footer son únicos por página, y section y form sólo son landmark cuando ya
tienen nombre.
El guard destapó de paso un segundo caso de la misma clase: la parte Breadcrumb
de palabras declara un <nav> con aria: [] y nadie la nombra, ni en soma ni en
eidos. Queda como A-116 con EXCEPCIÓN FIRMADA por el autor: palabras es el eje
de otra sesión y su rama es compartida. La tercera fila del censo obliga a
retirar la excepción el día que esa parte declare un nombre.
Verificado con el nombre COMPUTADO desde el árbol AX de Chrome por CDP, nunca
leyendo el atributo (un aria-label en el DOM prueba el atributo, no el nombre),
con servidor recién arrancado y esperando la puerta de hidratación honesta:
/blocks/app-shell/preview navigation: ['Navegación principal',
'Menú de la aplicación',
'Migas de pan'] (antes: el 2.º null)
/uix/components/navigation-menu (sin label del consumidor)
el <nav> computa 'Principal' — el default del morfo,
traducido, sobre el landmark; el <ul>, sin nombre
Gates: 420/420 en morfo+sema+soma+eidos tocados · component:audit --only
navigation-menu PASS · morfo:check PASS · svelte-check 72/62 antes y después ·
docs:check 0/0. Los avisos de prettier de los ficheros tocados ya fallaban en
HEAD (comprobado con git show HEAD:… | prettier --check); el fichero nuevo va
formateado.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
fd02780002 |
uix(box): ocultar por CSS descarga el vídeo igual, y evitarlo pedía no tener SSR
`visibleFrom` / `hiddenFrom` no ocultan: no renderizan. El subárbol no está en
el DOM, sus efectos no corren y su media no se pide — la mitad que
`display={{ base: 'none' }}` no puede dar. Los once contenedores que componen
Box (Section, Container, Flex, Grid, Stack, Group, Wrap, AutoGrid, Surface,
AspectRatio, Float) la heredan sin tocar sus ficheros. La puerta es un
`$derived` sobre `eidos.dom.isAtLeast(bp)` y un `{#if}`: sin atributos nuevos,
sin generador, sobre los breakpoints que configura el app.
Ninguna referencia lo hace así. Mantine (`hiddenFrom`/`visibleFrom`), Chakra y
Panda (`hideFrom`/`hideBelow`), Framer y Webflow ocultan por CSS y dejan el DOM
montado — Webflow lo documenta como coste de ancho de banda y Framer llega a
duplicar secciones enteras por breakpoint. Eligieron CSS porque con SSR una
puerta JS pinta la variante equivocada en el HTML (MUI devuelve un `matches` por
defecto en el primer montaje; Vuetify #17252 reporta los saltos de layout), y el
precio de evitarlo es adivinar el ancho por User-Agent o Client Hints.
Aquí ese precio no existe: el proyecto se despliega como SPA (`adapter-static` +
`fallback`, ninguna ruta con `prerender = true`), así que el primer render
ocurre en el navegador con el `innerWidth` real — el build es un `200.html` de
1,8 KB, shell puro. El que SÍ se paga está escrito en el tipo: cruzar un
breakpoint destruye el subárbol y su estado. Cuando el estado deba sobrevivir,
`display`. Sólo el dev server renderiza en servidor (ancho 0) y empieza a
descargar la media de la variante `base` antes de que la hidratación la pode:
queda como caveat de desarrollo en el README.
`base` sale del tipo en ambas props: `visibleFrom="base"` sería «siempre» y
`hiddenFrom="base"` «nunca renderiza», y ninguna de las dos es una cosa que una
prop deba poder decir.
Card, Banner, ScrollArea y Sticky rinden su propia raíz y NO heredan la puerta:
queda como gap diferido con la regla del `as` (≥2 casos reales). El puente de
SSR queda escrito con su disparador, no construido — hoy sería un atributo que
nadie lee.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
f3bf6a0299 |
docs(blocks): la ficha del shell cabía en cinco líneas porque no miraba el ecosistema
Fase 0 de F3.1 `app-shell`, firmada. La ficha original se escribió antes de que existieran el canon `Sidebar` y la capa `affix`, y el autor preguntó lo que había que preguntar: si su alcance tenía delante `active-app` y `active-uix` enteros. No los tenía. Las cuatro decisiones que se firman, y lo que las obligó: - **Q1 — el block NO cablea servicios.** La integración del ecosistema se demuestra en una app de referencia en app-land, no dentro del block. Un shell que leyera `App.session` para dibujar el menú de usuario sería una raíz de composición disfrazada: funcionaría, y metería el arranque del ecosistema dentro de una pieza que ha de poder caer en cualquier app. - **Q2 — dos modelos de scroll por prop.** `main` (rejilla 100dvh, sin fixed, sin números) es lo que cierra A-95 por construcción; `body` es lo que `docs-shell` necesitará para anclas y TOC pegada. - **Q3 — cabecera dentro del inset.** El provider del `Sidebar` ES la fila flex, así que su `Trigger` sólo vive dentro. Una cabecera a todo lo ancho exigiría partir ese provider: queda como gap de canon para v2. - **Q4 — canon `skip-link` + uno por región montada** (técnica G124). De paso, tres cosas que estaban mal escritas y ahora lo dicen: - El dossier afirmaba que **ninguna referencia trae skip-link** y que era superación nuestra. Es falso: Polaris lo trae en `Frame` y Atlassian genera un menú entero. La superación real es generarlos del mismo contrato que estampa el landmark. Y el icon-rail no es de Mantine (su `collapsed` es booleano); es de shadcn, AntD, Toolpad y Atlassian. El dossier tampoco miró nunca el `navigation-system` actual de Atlassian, y el `page-layout` que sí miró está deprecado. - **D-BLK.6 ha derivado**: la celda dice «ni prefs ni langs ni eidos» y la doctrina vigente (`blocks.md` §Services) sancionó dos servicios en julio y agosto. Queda enmendada con lo que sigue siendo cierto: ningún block CREA ni cablea servicios. - **E-5 está desfasada**: `Command.Dialog` ya trae el atajo (`shortcut`, `mod+k` por defecto). El listener a mano de F4.1 no hay que escribirlo. - **F3.2 `auth`**: la ficha dice cuatro vistas y el dominio tiene seis. Falta `update-password`, que no es un lujo — es donde aterriza el enlace de recuperación, y sin ella el camino de `recover` no termina. `blocks.md` gana un párrafo «Shells» bajo §Conventions: qué poseen (geometría, landmarks, alturas) y dónde está la línea (la raíz de composición es de la app). Verificado en código antes de escribirlo: `Sidebar.Inset` acepta `child`, `Box` expone minHeight/position/overflow y `Grid` templateRows —la rejilla del shell no necesita CSS propio—, y `Sticky` expone `root`. docs:check 0 errores, 0 avisos (635 docs). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
4caf1100a1 |
refactor(blocks)!: el eje de tamano dice de QUIEN es — size deja de significar dos cosas
LA AMBIGUEDAD, medida. En el canon `size` no es ambiguo: es «el eje de tamano de
ESTE componente», y cada uno lo materializa a su manera — padding de bloque en
`Section`, densidad de la tira en `Banner`, escala tipografica en `Text`. La
ambiguedad la creaba el tier: un block no es ninguno de esos componentes, y al
reenviar `size` estaba reenviando el eje de una pieza INTERNA. Desde fuera:
<FeatureGrid size="xl"> cambia el AIRE de la seccion
<SiteBanner size="lg"> cambia la ALTURA de la tira
Misma prop, dos cosas, y una con un valor menos en la escala (`BannerSize` no
tiene `xl`). Un consumidor que aprende el eje en trece blocks se equivoca en el
catorceavo.
EL RENOMBRADO:
- `size` -> `sectionSize` en los 13 que envuelven `Section`
- `container` -> `containerSize` en los 14 que envuelven `Container`
- `size` queda LIBRE y solo lo usa `banner`, donde si es el tamano del block
- `minColumnWidth` -> `minChildWidth` en `site-footer`: el mismo concepto tenia
dos nombres en el tier, y ninguno era el del canon
El patron no se invento: el tier YA nombraba un eje por su pieza (`container`).
`containerSize` + `sectionSize` lo hace explicito y simetrico.
LA REGLA, en `architecture/blocks.md` §Conventions, para que no reaparezca: un
block que reenvia el eje de tamano de una pieza interna lo nombra `{pieza}Size`,
y `size` se reserva para el tamano del block. Corolario: un eje que el canon ya
nombra se reenvia CON SU NOMBRE.
METODO — se renombraron los TIPOS primero y se dejo que `svelte-check` senalara
cada consumidor, en vez de buscar a mano. Cazo los cinco call sites que quedaban
(`CtaSite`, `HeroSite`, `SiteHeaderSite`, `TeamSite` y un uso suelto en
`cta.svelte`). ⚠️ Y cazo tambien un error propio: el primer patron era demasiado
ancho y renombro `size` en SIETE sub-partes donde ese `size` es el del `Card` o
el `Text` que envuelven (`testimonials-item`, `contact-reason`,
`team-member-role`…). Revertidas antes de seguir.
DOCUMENTACION revisada entera: corregidas las tres filas de README que describian
props del block con el nombre viejo (`hero` x2, `site-header`); conservadas las
seis que hablan del `size` de un componente del canon (`Banner`, `Accordion`,
`Heading`), que no cambia. `PLAN-blocks.md` se deja intacto: es bitacora, y
reescribir el registro historico para que cuadre con el presente lo falsearia.
LEDGER — el contraste doctrinal llega a 15/15, y cinco filas nuevas:
- A-95 `banner`: con `affix="top"` el aviso tapa la cabecera pegada (medido:
`elementFromPoint` sobre el header devuelve la tira). NO se arregla aqui: la
pieza que falta es la que POSEE las alturas de pagina — Mantine lo resuelve en
`AppShell`, que declara `header={{ height }}` y desplaza el resto; nuestro
equivalente es el block `app-shell`, sin construir.
- A-96 `banner`: `affixOffset` es prop publica sin control vivo en la demo.
- A-97 `feature-grid`: RETIRADA el mismo dia. Se midio el texto `muted` con el
4,5:1 generico de WCAG y el proyecto tiene OTRA vara — «floors APCA >= 60 ∧
WCAG >= 3», con guard propio, y la tinta secundaria es «marginal sub-4.5:1 by
design». Re-medido con `src/arts/color/apca.ts`: Lc 63,7 y 3,70:1, cumple las
dos. (El primer intento con `apcaLc` devolvio 102666 porque le pase 0..255
donde pide 0..1: un valor fuera de rango no es un hallazgo, es un formato.)
- A-98 `hero` + A-99 `Group`: las acciones no apilan ni envuelven. La causa NO es
del tier — `Group` no aplica el `wrap` que su README promete en TRES sitios:
`group.svelte` nunca se lo pasa a `Flex`, el recipe no lo declara, y
`Omit<FlexProps,'wrap'>` impide compensarlo desde fuera. El `align: center` si
se cumple, lo que descarta que el recipe no cargue.
Y A-25 cae (la demo ya no promete elevacion) y A-47 corrige su disposicion: el
anillo de foco es «a config axis» por doctrina — endurecerlo es una decision de
VALOR en `color.focus.ring`, «never a per-component CSS change», que es
exactamente lo que hacia el intento revertido en `e468e764b`.
Gates: blocks:check verde (15 blocks / 115 ficheros) · svelte-check 72 errores /
59 avisos = linea base exacta, ninguno en blocks · docs:check 0/0 sobre 629 docs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
948f7cf7e1 |
feat(blocks): el guard vigila la frontera, y siete filas del ledger caen medidas
FASE 5 DEL SANEAMIENTO — `blocks:check` deja de vigilar solo la forma del tier
y pasa a vigilar su frontera, su documentacion y su catalogo. Tres reglas, cada
una con su fixture negativo en el selfTest():
1. Lista blanca de importaciones (B-4). La frontera dura 1 era prosa; ahora es
codigo: $uix, los arts publicos, $libs/forms, svelte y los relativos del
propio block. Los arts se DERIVAN de src/arts/*, no se listan a mano — una
lista escrita se queda atras el dia que aterriza un art. El tier ya cumplia:
cero violaciones al encenderla.
2. README completo (B-9) + declaracion de landmark (B-8). Salieron 7 de 15 sin
ella; escritas leyendo la fuente de cada block, no la plantilla.
3. Ficha en el catalogo de demos (B-9), en los DOS sentidos: un block que el
catalogo no publica es invisible para el rail, y un slug publicado sin block
detras es un enlace muerto.
De paso el escaner deja de leer los comentarios como codigo: los index.ts
documentan su uso con un `// import { Cta } from '$blocks/cta'`, y una lista
blanca que lee prosa habria empezado a acusar a los ejemplos.
Verificado EN ROJO sobre el arbol real, no solo contra los fixtures: un zod y un
$libs/days metidos en hero/types.ts salen con su linea exacta mientras el mismo
import dentro de un comentario NO salta; el catalogo falla en las dos
direcciones; y al romper isAllowedSpec a proposito el self-test aborta el guard
en vez de dar verde.
ARREGLOS DEL CONTRASTE DOCTRINAL — siete filas, cada una con medicion o gate:
- A-88 team: `height="100%"` en el Stack del Member. El div que pinta Motion es
un bloque pelado, asi que el Stack se quedaba a altura de contenido y el
`margin-top:auto` de los enlaces repartia CERO. Medido: la desviacion entre
filas de iconos pasa de 20px a 0, y el margen reparte 20,297px.
- A-94/A-28 (feature-grid, team, testimonials): los tres declaraban su tipo como
`= AutoGridProps` y ponian {...rest} ANTES de las props que fijan, asi que un
consumidor podia escribir align="start" y no obtener nada. Cerrados con
Pick<AutoGridProps, ...>, que conserva los tipos exactos del canon.
- A-84 feature-grid: el eje `align` llega a las partes por contexto, como en
team, en vez de que el typedoc instruya al app a repetirlo. Medido: con
align=center el align-items computado es center en cabecera Y celdas.
- A-19 faq: `marginX="auto"` en el Header — Box no centra solo. Medido: el Box
de 768px resuelve margin-inline 248px/248px donde antes daba 0.
- A-27 testimonials y A-86 pricing: el typedoc decia `soft` donde el codigo hace
`outline`, y el ejemplo de la puerta de entrada no compilaba.
- A-91 banner (fila nueva): el block hereda el eje intent/color que el canon
declara fuera del sistema abierto en su propio typedoc, sin registrarlo.
LEDGER — dos lecciones de instrumento escritas dentro, porque ambas produjeron
hallazgos falsos publicados:
1. El dev server sirve codigo ANTERIOR a HEAD si reusa cache de Vite. Tres filas
(A-36, A-69, A-85) se dieron por vivas estando arregladas. El delator fue
aritmetico: las duraciones medidas eran EXACTAMENTE las que el comentario del
arreglo cita como estado anterior.
2. Contar nodos WebAudio no es medir sonido: la sintesis crea dos osciladores
por earcon, un sound pack de muestras cambia la aritmetica entera, y crear
nodos no es sonar. El contador responde "hubo actividad" y nada mas.
Y el error de metodo del que ambas son sintoma, tambien registrado: se fue a
re-medir A-67 sin leer su ficha, que ya traia los gains del catalogo, el empate
de applyDominance y la regla de CANON.md incumplida — mejor razonado que la nota
que lo sustituia.
Gates: blocks:check verde (15 blocks / 115 ficheros) · svelte-check 69 errores /
57 avisos = linea base exacta, ninguno en lo tocado · docs:check 0/0 · prettier
limpio en todo lo del commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
f1b672da79 |
docs: la doctrina alcanza a los cambios de la jornada — cinco backlogs y una llave de mas
Auditoria de deriva doc<->codigo sobre los 33 commits del 2026-08-13/14: cinco sitios seguian describiendo lo que el codigo dejo de hacer ayer. Se corrigen COMO BITACORA, al final de cada fichero (el cuerpo es el acta de lo que se firmo, no el estado del codigo; reescribirlo falsearia la firma). El formato es el que ya existia en `arts/adom` y `soma/textarea`: `## Backlog`. - `decisions/book-deviations.md` — dos entradas. D.7 nombraba `IntentExpectedFamily`, tipo que M6 ( |
2 months ago |
|
|
23b20fff5c |
feat(active-uix,sema)!: S-19(ii) — announce encendido por defecto: un sumidero, materializado por la raiz
¿Debia encenderse? El analisis dijo que era LA RAIZ O NADIE: la opcion
`announce` se pasa al construir el engine dentro de `createActiveUix(options)`,
y en ese momento `uix.announce` no existe todavia — huevo y gallina. El unico
cableado que un app podia escribir (`announce: { dom }`) caia en las regiones
propias del canal: un SEGUNDO par de regiones vivas, exactamente lo que la
doctrina «ONE sink» (AUX-1) prohibe. La unica puerta alcanzable violaba la
doctrina; la raiz es el unico sitio que sostiene las dos puntas.
El impl registra el canal POST-construccion con cierre tardio —
`events.register(new AnnounceChannel({ announce: (m, p) => this.announce(m, p) }))`
— la misma forma para standalone y attach. Default ON, y la asimetria con
sound/haptic (apagados) es doctrina, no inconsistencia: esos son ORNAMENTO
(opt-in); announce es SUSTITUCION (sema.md §channels — «the one that
substitutes»), la categoria que el framework ya enciende solo (las regiones de
uix.announce se crean solas, el camino de soma esta siempre activo). La norma
del campo hace lo mismo: Angular CDK LiveAnnouncer es singleton por defecto,
React Aria usa una region ambiental de modulo. Un framework de referencia no
hace opt-in la accesibilidad.
Opt-outs estandar: `events: { announce: false }` — el engine gana el flag
legible `announceOptedOut` (las opciones de construccion no eran observables) —
y un `announce` explicito del app gana: la raiz jamas pisa un canal existente.
Cero doble anuncio, por diseño ya verificado: el runtime de soma no mete
`message` en la señal perceptual (su a11y viaja por sources.announce), asi que
el canal no-opea para todos los componentes de soma.
Visto en ROJO via stash del impl (canal sin registrar) y en verde con el:
una señal con `message` por `uix.events` aterriza en la region COMPARTIDA
(`uix-announce-assertive`, prioridad derivada del intent threat) y el
documento tiene UN solo [role='alert'] — el par fantasma nunca nace. El
opt-out respetado. `sema.md` §announce reescrito: el ⚠️ de «ninguna raiz lo
cablea» pasa a documentar el default y sus salidas.
Verificado: sema+morfo+eidos+contracts 947 ✓ / 6 ajenos · soma navegador
1278✓/1 (el timeout ajeno — TODOS los tests de navegador arrancan la raiz
tocada) · check 69 = base aislada, diff VACIO · docs 0/624 · prettier:
engine.ts limpio en HEAD queda limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
1c4303b10f |
feat(morfo,sema,contracts): M1(i) — el contrato dice QUIEN dispara cada evento (D.2)
El morfo no podia decir quien emite un evento declarado: todo se presumia del runtime, y la verdad de las excepciones vivia fuera del contrato — en la lista de deuda del guard D9 y en las excepciones del censo de packs. Las tablas de eventos de los README prometian percepcion que no ocurre (la ficha M1, P1). Ejecuta la decision firmada D.2 (IMPLEMENTATION_CONTRACT): - `emission: 'runtime' | 'host' | 'external' | 'declared-only'` en MorfoEvent + literal en el schema sium. Ausente = 'runtime': las ~250 declaraciones existentes no se tocan y la presuncion sigue siendo la norma. - El guard D9 exime por DECLARACION en vez de por lista: solo los 'runtime' exigen emisor. Y si un evento eximido sigue en INERT_EVENT_DEBT, el guard FALLA con «FIXED — remove it», para que la deuda no sobreviva a su resolucion. - El pack-census gana el chequeo simetrico: una regla cuyo alcance son SOLO eventos 'declared-only' afina una percepcion que jamas estampara — muerta por definicion, sin lista de excepciones. 'host' NO cuenta como muerto: el anfitrion dispara por el runtime y la regla casa normal. Fixture con los dos casos (positivo y negativo con hermano runtime al alcance). - `morfo.md` §Step 5.5 lleva el apendice tecnico en los terminos que D.2 exige: la tabla de los cuatro valores × sus guards, y la nota doctrinal de por que NO es doctrina del libro — marcar un evento inerte como declared-only para callar al guard es ensanchar la deuda con otro nombre. La sonda temporal (flag sobre tooltip.emerge-present → correr → revertir) destapo un agujero real y lo cerro: el flag entre `name` y `semantic` hacia INVISIBLE el evento al regex del guard — ni censado ni exento. El regex tolera ahora la linea opcional, y la sonda termino dando la conducta disenada exacta: exencion + exigencia de limpiar la deuda. NADIE estrena el flag: los 7 de INERT_EVENT_DEBT quedan intactos y son la parte (ii) — siete decisiones perceptuales del autor, una a una (la de tooltip: ¿el componente mas ubicuo merece firma, o silencio declarado?). La instancia original de la ficha (los 5 pickers con close inerte) esta muerta desde la normalizacion de nombres. Verificado: suites 945 ✓ / 6 ajenos · docs 0/624 · check 69 = base aislada, diff VACIO · prettier: mis dos limpios siguen limpios, pack-census formateado (regresion mia), contracts y morfo.md ya fallaban en HEAD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
4e7d0be670 |
fix(sema,docs): S-19 — el puerto de anuncios encaja con su sumidero sin adaptador
`AnnounceFn` pedia la prioridad en una bolsa de opciones y `ActiveUix.announce`
la toma POSICIONAL, asi que el cableado que la doctrina prescribe —una sola
region viva compartida, este canal como uno de sus dos emisores— no se podia
escribir: `{ announce: uix.announce }` no compilaba. Y forzarlo era peor que no
tenerlo: el sumidero recibia un OBJETO donde lee una prioridad,
`liveRegionIds[obj]` es `undefined`, y TODO aterrizaba en la region polite — un
`threat` dejaba de interrumpir, que es justo lo que un aviso critico no puede
hacer.
La firma pasa a posicional, como el sumidero y como la fuente equivalente de
soma. Tres puertos, una sola forma. Cambio de conducta: CERO — el canal es
opt-in y ninguna raiz lo enciende hoy.
Visto en rojo antes de tocar: un test con la forma EXACTA de `uix.announce`
(`(message, priority?, timeout?)`) recibia `{priority:'assertive'}` en el hueco
de la prioridad. Tres aserciones existentes migran de bolsa a posicional — su
contrato cambia, y era el contrato defectuoso.
⚠️ Mi analisis inicial estaba equivocado y el autor lo mando revisar. Habia
concluido que el canal estaba muerto porque el runtime de soma no mete `message`
en la señal. No lo mete, cierto, pero es DISEÑO: son dos emisores para dos
publicos —soma cubre sus componentes, el canal cubre a quien usa
`EngineSemantic` sin soma y escribe su propia señal— y precisamente por eso
nada se anuncia dos veces. La propuesta de fusionarlos habria roto ese diseño;
retirada antes de escribir una linea.
`sema.md` §announce reescrito: fuera el aviso del adaptador (ya no hace falta),
dentro la razon de los dos publicos, y el aviso que SI queda — ninguna raiz
enchufa el canal, y encenderlo sin inyectar el anunciador añadiria un segundo
par de regiones vivas junto al compartido. Eso es decision aparte, sin firmar.
Verificado: announce 6 ✓ · sema+morfo+contracts 541 ✓ / 6 ajenos · check 69 =
base aislada, diff VACIO · docs 0/624 · prettier limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
b8aa333fd1 |
feat(soma,morfo)!: targetOverride BORRADO — el estado ilegal del eje es inexpresable
El endgame que §3.0 firmo al abrir F3: con el censo a 0, la opcion de elemento anonimo sale de TriggerOptions, y con ella assertTargetOverride (existia solo para vigilarla; queda assertAnchor, el guard de la puerta que la sustituye). Las dos resoluciones `opts.targetOverride ?? resolveEmitTarget(...)` (emit y foco a11y) quedan en la resolucion unica, y los Omit<..., 'targetOverride'> de los triggers anclados se vuelven vacuos y desaparecen. El tipo lleva LAPIDA doctrinal: la opcion nacio fallbackTarget, se renombro el 2026-08-10 como precondicion del censo, y compensaba un registro sin identidad de instancia — es lo que dejo a tres auditorias (fable S1, sema S-17, blocks A-36/A-65) encontrar la misma deriva sin poder cerrarla. No volver a añadir una opcion de elemento. Y la tesis se probo sola al ejecutarla: el compilador, ya como censo, cazo 9 usos en TESTS que el grep del scratch nunca escaneo. De ellos, 3 eran redundantes (la parte registraba el mismo elemento), 2 del test de metrics migran al registro + trigger plano, y 4 fijaban la conducta borrada — el describe «targetOverride contract» entero y el test del override preferente, retirados con lapida; el guard A-36 de los overlays (morfo sin redireccion + post) se conserva, des-anidado. El scratch del censo, retirado: el censo es `npm run check`. Docs vivos adjudicados: morfo.md §allowedTargets describe la puerta anclada (EventNameTargeting + assertAnchor) y §repeated-part nombra partInstance donde decia targetOverride; los docblocks de las 3 factorias de vistas de eidos dicen la forma nueva; errors.ts deja de ofrecer la opcion muerta como remedio. Las menciones historicas (A-36, «the retired...») se conservan como historia. Verificado: soma navegador 1278✓/1 (el timeout ajeno preexistente de soma-attr-audit, documentado en la base) · runtime+metrics 53/53 · check 69 = base, 0 propios · docs:check 0/623 · prettier limpio en lo que estaba limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
4a1a0c3907 |
refactor(sema): los 3 resolvers de gesto se retiran — esta vez desde el eje, y el registro se hace verdad
El rediseno del 2026-08-06 (gestos por REPETICION, sound:'step' por emision)
los dejo sin llamador, y book-deviations D.7 escribio en pasado un borrado que
nunca ocurrio. Hoy ocurre, firmado y desde una sesion del eje que empezo por
su handoff — la condicion que la retirada revertida de
|
2 months ago |
|
|
e5811e422c |
feat(morfo,soma,eidos): el cruce de mes lo sella la vista paginada, no un mes suelto
Con `numberOfMonths=2` el calendario tenia un *shift invisible* a medias: al pulsar «siguiente», junio cambiaba sus fechas quieto y julio cruzaba. Medido — un solo sello, sobre el segundo grid. La causa no es el runtime ni el llamador. `grid` es una parte REPETIDA, y un emit sin ancla resuelve a la instancia viva mas reciente (`resolveEmitTarget`). `shift-navigate` apuntaba a `grid` desde el 2026-08-11, cuando se le saco del boton para que dejara de pisar su `contact-activate` (A-36). Era correcto para un mes y falso para dos, y un destino que solo vale para un valor del parametro no es un destino. La ley que este mismo eje escribio ya traia la respuesta (`morfo.md` §Where the stamp lands): *if it isn't the subject, the morfo is wrong*. El sujeto es la VISTA PAGINADA — lo que cruza como unidad bajo cualquier numero de meses. No existia como parte, asi que se declara: `months`, arquetipo `viewport`, con `targetFallback: [grid]` para que una composicion headless sin ella degrade a lo de antes en vez de callarse. Se descartaron tres alternativas, y por que ------------------------------------------- - Emitir N veces ancladas: acuna N ocurrencias para UN gesto, N ids en el arbitro de dominancia y una repeticion en la memoria de frecuencia (C-2 solo exime a `handle`), y luego hay que silenciar N-1 desde el componente. - Un emit con N sellos: `resolveEmitTarget` es UNA resolucion para TRES lectores «so they can never disagree» — y el movimiento de foco a11y necesita uno. Ademas declararia que cruzaron dos cosas; el cap. 27 dice que cruzo una. - Volver al provider con CSS descendiente a mano: el provider no es el sujeto (la cabecera no se mueve), deshace una correccion medida y duplica el shorthand de animacion en cuatro recetas. De paso, dos partes clandestinas legalizadas -------------------------------------------- `data-calendar-month-panel` lo fabricaban a mano los cuatro demos multi-mes y `calendar.css` lo estilizaba igualmente, sin que ningun morfo lo declarara; el contenedor lo montaban con un `style` en linea que rederivaba el numero de meses en cada pagina, o con clases locales (`.range-months`, `.month-stack`) que apuntaban a un `--calendar-month-gap` que NO EXISTE — el fallback `36px`/`32px` hacia todo el trabajo. Ahora son partes, la disposicion vive en la receta (`grid-auto-flow: column`, sin contar meses) y el token es `--calendar-months-gap`. Correccion de lo que dije al proponerlo: `eidos-lint` NO cazaba esa deriva — clasificaba el selector como `eidos-only`, no como invalido. Lo que gana el cambio es mover 2 selectores de eidos-only a morfo-backed. Quien si lo caza es `morfo:check`, contra el DOM real. Medido ------ Navegador, `numberOfMonths=2`: el sello cae en `[data-calendar-months]` con `data-event-direction=forward`, corre `shift-cross-forward 0.32s`, y los DOS grids se desplazan 10 px a los 40 ms del cruce (antes: uno). En RTL, `--motion-shift-sign` pasa a -1 y los dos van a -10 px. El boton conserva su `contact-activate` en su propia ranura. Consola limpia en los cuatro demos; el date-range-picker, que muestra dos meses por defecto, es donde mas mordia. `morfo:check` contra el DOM: calendar y range-calendar PASS. `eidos-lint`: invalid 0 en ambos, morfo-backed 27→29 y 39→41. `docs:check` 0/623. Verificado sobre el ARBOL INDEXADO en un worktree aparte, no sobre el mio: `check` 57 errores, identico a HEAD, cero nuevos; 2154/2154 en morfo + soma + eidos + sema. Los 6 fallos de `contracts.test.ts` que se ven en mi arbol de trabajo son de la otra sesion (waveform, media-player, audio-player, menubar, aura, radio-group, tabs): en el arbol firmado pasan. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
25e59e18f9 |
feat(morfo,soma): el attr de nombrado es un DEFAULT del contrato, no una imposicion
A-85 estaba confirmado desde el ledger de blocks: la demo de site-header
escribia `aria-label` en el trigger del cajon y el canon lo pisaba. El censo
dio la magnitud real — 132 declaraciones en 59 morfos — y la disposicion
escrita (replicar el `prop-truthy` de button en las 132) resulto ser la
direccion inversa a la buena: obligaba a 264 entradas de boilerplate y hacia
que el texto del consumidor diese una vuelta entera por el grafo reactivo para
aterrizar donde ya estaba.
Las 132 declaraciones eran CORRECTAS. El defecto era la precedencia. Asi que
el contrato gana dos clases, por VOCABULARIO y no por declaracion:
contract (role, data-*, aria-expanded, aria-controls, roledescription...)
el runtime SIEMPRE gana; un consumidor que los pisara haria mentir
al componente, y esa garantia es algo que las librerias
user-props-last no tienen.
naming (`ARIA_NAMING_ATTRS`, hoy `aria-label`) el morfo da un DEFAULT y
el attr explicito del consumidor gana — espejo de la propia cadena
de nombre accesible de la plataforma.
Tres puntos, uno por capa, y ninguno de los 132 morfos tocado: `compileMorfo`
clasifica el plan (`consumerWins`) y deja de hoistear esos literales a
staticAttrs; el runtime los saca del efecto `syncAttrs` —el `dom.apply`
post-render ERA el pisado— y los embarca en el bag de la parte, el mismo
vehiculo que `dir`; y `mergeProps` los resuelve consumer-first (`a ?? b`, para
que un `aria-label=""` explicito sobreviva).
Efecto medido que no se buscaba: los labels salen ahora en el HTML de SSR. Antes
los escribia un efecto, que en servidor no corre — el propio ledger lo tenia
medido y nadie habia atado los dos cabos.
`aria-labelledby` se queda en contract A PROPOSITO: es cableado `partRef`, y la
plataforma ya hace que un labelledby del consumidor gane a cualquier label sin
que tengamos que arbitrarlo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
6f42eebfe8 |
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 |
|
|
8864ba93ca |
docs(morfo,chronos): la doctrina alcanza a lo que se construyo hoy
`morfo.md` gana la trampa que se llevo cinco etiquetas: **`texts` es una
DECLARACION, nunca una tabla de consulta**. `normalizeTranslationRef` toma la
clave VERBATIM y no traduce un alias a una ruta, asi que `prevMonth` frente al
`prev-month` del catalogo resuelve a una ruta que no existe y **envia el
fallback ingles sin error ni aviso**. Igual con `commonRef('action.undo')`
cuando `common.action` no existe. Y el aviso que faltaba: el guard que deberia
cazarlo, `npm run translations:check`, **estaba cascando** antes de comprobar
nada — arreglarlo y censar los 166 morfos antes de volver a fiarse.
README de soma/chronos: `SearchButton` faltaba en la tabla de partes, y ahora
documenta los dos permisos de manipulacion directa (`canMove` / `canResize` —
separados a proposito aunque hoy coincidan, porque el handler de teclado
preguntaba el equivocado) y el estado `searchOpen`.
README de eidos/chronos: seccion **Composicion** nueva (la ranura `children`,
que las partes publicas las decide `kind` en el morfo, que son ergonomicas y no
ganchos vacios, y que los overlays los pone la RAIZ). La coletilla «sin API
compound publica» queda marcada como ANULADA en su gap. Busqueda y undo/redo
pasan de hueco a hecho; la recurrencia anota que la manipulacion directa se
bloquea de forma visible y que falta PINTARLO.
`CONTINUE.md` de chronos se marca como NO vigente y apunta al handoff del eje.
El handoff gana una seccion §0 «donde estas» con los seis commits y el orden de
lo siguiente, y §4 recoge las dos decisiones de autor medidas y listas.
docs:check 0/621 · rtl:check 0/177. Los .md tocados que prettier marca ya
estaban asi en HEAD salvo el README de eidos, cuyo contenido SI pasa (el aviso
es solo CRLF del working tree, que git normaliza).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
56a96114f8 |
feat(morfo,soma): la emisión deja de aceptar un elemento anónimo
El defecto que tres auditorías encontraron y ninguna cerró no era que N eventos redirigieran sin declararlo: era que la API de emisión aceptaba un HTMLElement suelto, compensando que el registro de partes perdía la identidad de instancia. Censar y arreglar a mano reproduce el modo de fallo. Esto lo hace inexpresable. F1 — SomaRuntime<M> genérico, SIN default. Nombres de evento, kebabs de parte y claves de events/actions tipados contra la declaración (EventNameOf / PartKebabOf / ActionNameOf, los extractores que semaSelector ya usaba del otro lado). 104 anotaciones migradas + 3 wrappers que devolvían tipado pero tomaban sources ANCHO. SomaRuntime<Morfo> queda sólo en la tabla de contratos. F2 — registro por instancia: Map<part, PartRegistration[]>, pertenencia mantenida por el stream del attachment (poda al desmontar, re-entrada al remontar), resolución liveReg = la instancia VIVA más nueva. Mata de paso la clase "la más nueva muerta ensombrece a una viva". F3 — tres formas legales de destino: el target declarado; el anclaje por instancia (SomaRuntimePart.trigger, tipado por EventNameTargeting, + runtime.partInstance por identidad de referencia); y targetFallback, la cadena por estado de montaje declarada en el morfo y resuelta por resolveEmitTarget — LA resolución única para validación, emit y foco a11y, que ya no pueden discrepar. ~30 componentes migrados; censo de targetOverride 148 -> 89. F4 — A-36 tenía un superviviente. Medido en dropdown-menu: bajo `pre` el `open` no colisionaba con el contact-activate del Button compuesto, se PERDÍA entero porque su content no había montado. Aplicada la receta de float-panel a dropdown-menu Y context-menu. Ahora: contact-activate en trigger, open en content — dos superficies, ambas expresando. Submenús mudos, tres componentes. No era cableado: el morfo no declaraba el evento. onion-menu recibe emerge-expand/emerge-collapse (la forma de TreeView: un nivel revela sus hijos en el sitio); dropdown-menu y context-menu reciben sub-open/sub-close sobre sub-content, distintos del open raíz a propósito porque el sub es su propia superficie flotante. Nacen con la receta A-36 para no repetirla en código nuevo. chronos — declaraba 26 partes y registraba UNA; 17 role= a mano y el ARIA declarado no llegaba nunca al DOM. Se anula la mitad sobre-extendida de la decisión de 29ffe902a: la reutilización sigue viajando por el motor puro, pero la ESTRUCTURA se compone como en Table y Calendar. C1 (las 6 partes que emiten, chronos con cero targetOverride) + C2 (8 singletons) + C3 8/9. Registrar una parte DESTAPA declaraciones falsas, tres veces en un pase: el chip declaraba defaultElement button y ponía type= sobre un div (anida el asa de resize); grid-row declaraba archetype item y pintó la semana entera como fila de lista; y un aria por propRef desaparece en SILENCIO si el provider no publica la fuente. En los tres el arreglo fue la DECLARACIÓN, no el DOM. Verificado: check 74 = línea base medida con stash, DELTA CERO sostenido en cada lote · 458 pasan (chronos+morfo+sema) · navegador limpio en las tres vistas de chronos, con el contrato ARIA vivo por primera vez (aria-labelledby resolviendo a «junio de 2026», aria-readonly/disabled que no se habían emitido nunca). Handoff en docs/process/CONTINUE-perceptual-surface.md. Aparte, y decidido por el autor: docs/process/PLAN-event-name-normalization.md — prefijo de familia siempre; 36 de 252 sin prefijo, cero ambiguos. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b1b48fb3aa |
fix(sema,morfo,soma): una superficie no admite dos ocurrencias, y el nombre ya no miente
`data-event-*` es UNA RANURA por elemento. Tres auditorias independientes encontraron el mismo defecto y ninguna lo cerro: fable S1 (2026-07-01, con el README diciendo «findings executed»), sema S-17 (2026-08-05, midio el estampado del knob muriendo a los 24ms de un hold de 240) y blocks A-36/A-65 (reproducido en navegador, dos estampados a 1,1ms). Lo que faltaba no era diagnostico: nadie podia distinguir una redireccion legitima de una deriva, porque la opcion que redirigia se llamaba `fallbackTarget` y hacia lo contrario de lo que decia. EL NOMBRE PRIMERO. `fallbackTarget` -> `targetOverride` (202 apariciones, 64 ficheros, dirigido con `git ls-files`). Siempre GANO sobre el ref registrado; el nombre honesto es la precondicion para auditarlo. Las cronicas conservan el viejo por diseño. LA PROPIEDAD. `unstampEventAttrs` recibia el signal y lo DESCARTABA, asi que la ocurrencia que terminaba antes borraba a la que tenia la ranura. Ahora comprueba `data-event-id`. Con eso, `replace` deja de ser el comportamiento roto y pasa a ser el suelo correcto. A-36, SIN `regime`. Los cuatro overlays cedian el estampado del `open` al trigger por un workaround de la era `sequence: 'pre'`; con `post` el content ya esta montado. Retirado el override (float-panel migrado `pre`->`post`, ultimo fuera de la doctrina que sema.md escribe). Medido en /blocks/site-header/preview a 375px: contact-activate en el TRIGGER +18,7ms y open en el CONTENT +20,6ms, con press-squeeze y slide-from-right-full corriendo. Antes: los dos en el trigger, press-squeeze jamas. `regime` DEFINIDO — y son dos valores, no cuatro. `replace` (default) y `queue`, este solo para las 4 parejas irreducibles: los tres toggles (el provider ES el boton) y el knob. `collapse` retirado (su caso murio con la propiedad: medido, 0 huecos en 5 emisiones a 72ms) y `lock` retirado (significaria «un cierre que no se anuncia»), con sus 4 declaraciones muertas. El guard de `queue` PASO EN VERDE SIENDO INCORRECTO: con timers falsos no hay animaciones, asi que awaitExpression volvia al instante. El navegador midio el commit encolado llegando 1,6s tarde. `queue` espera el HOLD, no la expresion. Medir la envolvente no es medir la salida. `renderAttrs` — los attrs que no son partes. Vaciar ACTIVE_DEV_TRACK (palabras y chronos ya no estan excluidos) destapo 23 `data-palabras-*` que ningun morfo declaraba y que palabras.css SI estiliza: aterrizan en el arbol que el usuario escribio, asi que no pueden ser partes. Campo nuevo con la linea afilada —si el consumidor puede componerlo, es una PARTE— y 3 consumidores (palabras, waveform, aura). Declarado en types.ts Y en el esquema sium: la leccion de MorfoElement. Ademas: 5 selectores muertos en el predicado del focus-scope de palabras (barrido de todo el repo: aparecian solo ahi) · RTL-1 real en palabras-chrome (inset logico + translate fisico = doble volteo; rtl:check 1->0) · el union inline al `Direction` canonico, que destapo que el censo de direccion grepea el NOMBRE del tipo · deuda de chronos (scope, barrel, README). Y de la cola: S-36 (barrel 68->71 + guard), S-31 (vibrate(NaN)), S5 (la preferencia de reduced-motion del usuario ya gana a los channels del morfo), SO2 (el warn que el JSDoc prometia), SO4 (validar el target antes del prewrite). Todos los guards nuevos vistos fallar antes de arreglar nada. Base: check 74 (= base) · sema+morfo+contracts 454 verdes / 6 ajenos preexistentes · soma navegador 1258/1259 (el rojo es un timeout ajeno, reproducido con los cambios en stash) · docs:check 0/618 · rtl:check 0/177. Handoff: docs/process/CONTINUE-perceptual-surface.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
49362d89e9 |
feat(sema,sound): un pack se entrega como quieras — y un `data:` no se pide, se descodifica
La entrega deja de ser parte del contrato. Un pack de ficheros puede llegar
como quince URLs o como UN JSON de base64 (`packFromBase64`): mismo tipo, mismo
intercambio en caliente, misma reserva sintética por entrada, y un nombre que
no existe sigue sin compilar. Medido sobre `static/sounds/ui-inline.json`: una
petición en vez de quince, y 69.082 B con brotli frente a 79.332 B de los
ficheros sueltos — el +33 % del base64 lo deshace la compresión, y el JSON gana
además porque nadie comprime `audio/mpeg` y todos comprimen `application/json`.
## `step` no se oía
Era a la vez el MÁS CORTO (18 ms) y el MÁS FLOJO (gain 0.025), y esas dos cosas
se multiplican: el oído integra energía durante ~100-200 ms, así que un tono
corto necesita MÁS amplitud, no menos. Medido, rendía a −29,8 dBFS con la
novena parte de la energía de `touch`. Ahora 0.06 a los mismos 18 ms.
Lo que no había era el guard: la distinguibilidad es una RELACIÓN, y dos
sonidos pueden ser perfectamente distintos entre sí y ser los dos inaudibles.
`sound-names.test.ts` gana un suelo absoluto sobre `gain² × duración`.
## El precalentamiento no precalentaba
- `preload` pedía el contexto con `getOrCreateContext()`, que hace
`await resume()` — y antes del primer gesto esa promesa se queda PENDIENTE en
Chrome, no se rechaza. No descargaba nada hasta que el usuario ya había
pulsado, que es justo la latencia que el preload existe para quitar.
`decodeAudioData` funciona en un contexto suspendido.
- Aplicar un pack en caliente no calentaba la caché: cada nombre pagaba su
viaje la primera vez que sonaba. Ahora `rebuildMap()` llama a `warmSamples()`.
- Con `preferences.sound === 'off'` no se descarga NADA — un pack que no se
puede oír son bytes gastados en silencio. Pero la petición se RECUERDA en vez
de tirarse: el nivel se lee en el dispatch, así que la primera reproducción
audible vacía la cola y encender el sonido no devuelve una caché fría.
## Un `data:` se descodifica en el sitio
`connect-src 'self'` BLOQUEA `fetch('data:…')` — la directiva casa por ESQUEMA
y `'self'` no cubre `data:` — mientras deja pasar una ruta del mismo origen. Un
pack inline enrutado por `fetch` caía a síntesis EN SILENCIO justo en el
entorno que llevó a alguien a hacerlo inline. Y es ~6x más lento. `bytesFor()`
lo resuelve con `atob`.
## De la revisión adversarial
- Las claves del JSON se validan: una que no sea `SoundName` LANZA, igual que
una ruta mala en `assertMapPaths` y por la misma razón — tragarse la errata
da el síntoma «parte de mi pack no se aplicó» sin nada a lo que apuntar.
- `isSoundName` preguntaba `name in SEMA_MAP.sounds`, y `in` recorre el
prototipo: `isSoundName('toString')` devolvía `true`. Inofensivo mientras
todos los llamantes tecleaban el nombre; nada inofensivo al validar red.
- `warmSamples` sólo tenía una de las dos ramas que sí tiene el constructor, así
que una app con canal de sonido propio se calentaba al arrancar y nunca más;
y esa rama leía la preferencia UNA vez, en construcción. Las dos se caían por
lo mismo: ahora comparten `warmUrls()` en vez de duplicarse.
- El demo del catálogo cancela su fetch: elegir `inline` y saltar a `synth`
antes de que llegara reinstalaba el pack sobre un `clearMap()` ya hecho.
- `fallbackFor(name)` borra los dos casts que copiaba todo el que autorase un
pack de ficheros.
## Documentación
`architecture/sema.md` gana la sección de las dos formas de entrega y el
párrafo de `preloadSamples` reescrito — describía un precalentamiento que ya no
es el que ocurre. El README del arte conoce la ruta `data:`. La página de packs
gana su sección equivalente y deja de señalar unos `.wav` sueltos como «el
ejemplo».
Y un fantasma: `soft` no existe — es un nombre del catálogo viejo que quedaba
como ejemplo en ocho sitios, incluido el docstring de `applySounds`, cuyo
ejemplo copiado literalmente LANZABA.
`static/sounds/ui-inline.json` se genera desde `packs/ui-mp3.ts`; el comando
está documentado ahí y reproduce el fichero byte a byte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
e6a445c453 |
docs: el corpus entero deja de describir un sistema que ya no existe
Tras rehacer el sonido dos veces en un dia, la doctrina escrita seguia explicando la cascada de cinco capas, los tunings y la modulacion por intent — exactamente la clase de mentira que esta sesion entera se dedico a perseguir en el codigo. 23 ficheros al dia. FUENTES DE DOCTRINA - `architecture/sema.md` — la seccion de resolucion ya no numera seis capas para el sonido: enuncia las dos busquedas y dice que el sonido NO participa de la cascada, que es el diseño. La convencion de deltas se acota al haptico y a `hold`, que son los unicos que aun tienen numeros. La seccion del catalogo se reescribe como «el sound pack»: bases y variantes, el pack por defecto sin binarios, el guard de distinguibilidad con la razon de por que existe (se detecto por OIDO, no por test), y por que una biblioteca nombrada por componente hay que TRADUCIRLA. - `CANON.md` §7 — la fila del sonido dice ahora «un nombre, y solo cuando difiere de su familia», y se añade que el intent SELECCIONA, con el motivo perceptual: reconocemos sonidos, no desplazamientos de parametro. - `CLAUDE.md` — la doctrina operativa pasa de tres reglas a seis, y todas enunciadas por su mecanismo: las dos busquedas, que NADIE escribe un parametro, el tier del verbo que vacia los packs, el guard de distinguibilidad, el limite fisico de las grabaciones y los gestos por repeticion. - `book-deviations` D.7/S-07 — dejan de explicarse por «el nombre va debajo del intent» (modelo intermedio, tambien muerto) y se explican por la ausencia de aritmetica. Y D.8 corrige la tabla de canales: `handle` suena, y los tres resolvers de gesto que la entrada citaba como justificacion ya no existen. PROCESO - `PLAN-sound-names.md` se marca SUPERADO EL MISMO DIA, con la leccion escrita arriba del todo: un sistema de modulacion produce variantes que difieren sobre el papel y son identicas al oido, y eso solo se detecta escuchando. Se conserva como cronica de por que NO se hizo asi. - `CONTINUE-sema-audit.md` gana §3.0-bis con el modelo vigente, los tres commits y dos avisos que la siguiente sesion necesita: que nada se ha oido sobre un componente real, y que el catalogo es una PROPUESTA. COMPONENTES — 16 READMEs citaban nombres de tuning que ya no existen (`commit.subtle`, `emerge.exit.soft`, `handle.pickup.air`…). Migrados a los nombres vivos. ⚠️ Distingui los que eran pares FAMILIA.VERBO (`commit.set`, `emerge.open`) y esos no se tocan: siguen siendo correctos. ⚠️ `chronos/SPEC.md` lo migre y lo REVERTI al darme cuenta: es de escritura excluida, asi que sigue citando nombres muertos — decision tuya. VERIFICADO: docs:check 0/618 · check sin errores nuevos · las unicas referencias a `SOUND_TUNINGS` que quedan son las de S-07, deliberadas: citan lo que ya no existe para explicar por que dejo de existir. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
1302f28a5d |
docs(sema,theming): el orden real, como se autora cada canal, y el hueco del theming
Tres cosas que el cambio del sonido dejo sin plasmar.
1. EL ORDEN CANONICO MENTIA. La numeracion 1·2·3·4·5a·5b esta citada como
canonica en sema.md, resolver.ts y engine.ts, y despues del reorden era
falsa para `sound`: un pack ya no autora en 5a, lo hace en 1.5. Corregido
en los tres sitios a la vez, con el porque — todo lo que AUTORA un sonido
corre ahi conservando su precedencia relativa; solo cambio su posicion
respecto al intent. El resto de la regla (channels/haptic/hold) mantiene
el «gana el ultimo» porque no son ejes evaluativos.
2. COMO SE AUTORA CADA CANAL (CANON §7). El reparto por dueño estaba
escrito; lo que un componente ESCRIBE, no. Tabla nueva: el canal visual
no se escribe (se declara el evento y eidos reacciona), `sound` es UN
NOMBRE, `haptic` es un `kind`. La regla es la misma en las tres filas y
ese es el punto: un componente dice QUE ocurre, nunca cuan fuerte, cuan
brillante ni cuanto dura. El sonido era la excepcion hasta ayer.
3. EL ANALISIS DEL THEMING que el autor pidio (theming/channels.md §5b).
Lo que el cambio NO toco: nada de eidos. El canal visual se proyecta
estampando data-event-*, y esa ruta quedo intacta — ni una receta, ni un
token, ni un selector. Eidos no sabe que existe el sonido y no le hizo
falta. Lo que cambio fue DONDE se autora (un catalogo en vez de 71
ficheros) y CUANDO se aplica (antes del intent), ambos dentro de sema.
Lo que si cambio, y es lo util: `applyTheme(seed)` retunea SEIS ejes
visuales (color·type·depth·shape·space·gradient) y el sonido tiene CERO.
Antes esa asimetria se justificaba sola — con 214 reglas autorando 33
firmas a mano no habia objeto que retunear. Hoy hay exactamente uno:
16 nombres en un const. Un eje `sound` en ThemeSeed seria la misma forma
que los otros seis.
Y las tres puertas de personalizacion, medidas, ninguna llega al
catalogo: `overrides.runtime` recorre rutas de SEMA_MAP y el catalogo no
esta en el mapa (ademas se traga las erratas, S-09); `overrides.cascade`
es por selector, no un tema; `masterGain` se descarta en SILENCIO
(S-05/S-32). Un producto puede silenciar y puede pisar una ocurrencia,
pero NO puede re-voceear el sistema. Queda escrito en vez de ser
folclore.
⚠️ chronos: `src/uix/sema/components/chronos.ts` entro en la migracion de
`769e426c5` pese a ser de escritura excluida. Era forzoso — con el tipo
estrechado, dejarlo sin migrar rompe la compilacion del arbol entero — y
el cambio es mecanico (soundTuning(...) -> nombre), sin decision de diseño.
Su SPEC.md NO se ha tocado y sigue citando `soundTuning('commit.soft')`.
VERIFICADO: sema+morfo+sound 437/437 · check 75 · docs:check 0/618.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
fb7759b849 |
docs(sema)!: retirar D.7 y S-07 — el orden hace imposible lo que prohibian
F6 del PLAN-sound-names, y el motivo importa mas que el borrado: no se
derogan por cambio de opinion, se DISUELVEN. La doctrina del autor que
las origino sigue siendo cierta; lo que cambia es quien la sostiene.
D.7 prohibia canonizar un sample sobre un evento cuya familia tuviera
base.sound, porque sound() devolvia una firma COMPLETA y la cascada la
aplicaba en replace DESPUES del intent: un fulfill perdia su +300 Hz
ascendente. S-07 era el mismo defecto una capa mas abajo — el gain
desnudo de los 14 afinados de escalera — y seguia ABIERTO por decision
del autor.
Con el nombre aplicado ANTES de los deltas de intent no queda ninguna
capa posterior que pueda pisarlos. Aplastar el perfil evaluativo ya no
esta prohibido: es INEXPRESABLE. Y S-07 pierde su premisa entera, porque
SOUND_TUNINGS ya no existe.
LO QUE SE CONSERVA, con su sitio nuevo escrito: el silencio como firma
valida (SILENT), el criterio de cuando crear un pack, y los samples como
recurso de producto (el catalogo los admite con nombre; la app autora los
suyos por overrides.cascade, que sigue abierto a proposito).
LO UNICO DE D.7 QUE HAY QUE SEGUIR SABIENDO, y por eso queda escrito en
tres sitios: playSample lee EXACTAMENTE dos campos, sampleUrl y gain. No
hay playbackRate ni detune. Sobre un .wav el intent solo mueve el
volumen — medido, risk es indistinguible de neutral porque solo aporta
roughness y la ruta de sample lo ignora. Un sample con carga evaluativa
necesita UN FICHERO POR INTENT, que es lo que hace signal con ping frente
a error y la verdadera razon de que fuese la excepcion. Es fisica del
audio, no politica.
Tocado: book-deviations §D.7 + §S-07 · sema.md §the sound catalogue
(reescrita entera, con el orden y el limite del WAV) · CLAUDE.md §sema
doctrine · los dos README del art · CONTINUE-sema-audit §3.0/§3.1/§3.6,
que es el handoff vivo y estaba describiendo una cola que ya no existe.
⚠️ Prettier reformatea entero book-deviations.md y sema.md porque NO
estaban formateados en HEAD; reaplicado a mano para que el diff sea solo
lo mio. Mismo motivo por el que CLAUDE.md y arts/README.md van con 1-2
lineas en vez de 30.
VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline · docs:check
0/618 · prettier limpio en los ficheros que toque.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
f7a7564b39 |
feat(morfo): allowedTargets — el morfo declara a que partes puede viajar el estampado
El runtime deja que el proveedor redirija el estampado por emision (fallbackTarget GANA sobre el ref del target declarado). Es comportamiento correcto para partes repetidas — el dia pulsado del Calendar, la pagina clicada de Pagination — pero el morfo no decia nada: la declaracion leia target: provider mientras cada emision real caia en day / item / prev-button. Un pack escrito desde el morfo apuntaba a un nodo que nunca lleva el estampado, y nada podia distinguir ese error de esta redireccion legitima. La auditoria 2026-08-05 midio el error (S-12: TextArea 6x/13x sobre su firma escrita); el censo nuevo encontro 12 reglas identicas en forma que estaban BIEN. Solo el morfo puede separarlas, asi que el morfo lo dice. allowedTargets es el simetrico exacto de allowedFamilies en el otro eje: el target canonico sigue siendo el default que el runtime resuelve; la lista declara las demas superficies donde una emision puede caer. Invariantes en schema.ts: parte inexistente o auto-listado = error de compilacion del morfo. Declarado en calendar (5 superficies), pagination (5), rating-group, toolbar y tree-view (branch) — cada entrada verificada contra sus emisores reales, no contra el pack. Pendiente declarado: renombrar fallbackTarget -> targetOverride (nombre honesto para una opcion que gana) va en commit aparte — 193 apariciones en 63 ficheros esconderian este contrato dentro de un diff de renombrado. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
866089a407 |
fix(blocks,canon): el ledger dice la verdad y el arco perceptivo vuelve a sonar
La auditoria del tier tenia los veredictos desemparejados de sus hallazgos: un join por POSICION, y el journal del workflow era de sesion. Reproducido en vivo al re-verificar — el journal devuelve los resultados en otro orden que la entrada. Ledger nuevo con 90 ids estables (AUDIT-blocks-ledger.md), union SIEMPRE por id: 41 arreglados, 34 confirmados, 12 refutados con motivo escrito, 3 dato. La tasa real de refutacion es del 13%, no del 28%. De las 12 ALTA de percepcion, 8 tenian la causa raiz en el CANON. Los blocks componen bien; lo que estaba roto era el arco perceptivo. CANON - Link: la prop color era inerte en subtle/plain (inherit a 0-2-0 ganaba a la paleta a 0-1-0), el hover clavaba primary y el active quedaba tapado por el hover de variante. Medido: el enlace del footer pasa de la tinta del padre a la suya. - Card: prometia BoxProps y no los aplicaba — height="100%" era un atributo inerte. El tipo dice la verdad y el eje de tamano se aplica con un helper compartido (buildSizeStyle). Tarjetas al fin de igual alto. - Form.Submit / Form.Reset: componen el Button del canon, asi que la accion principal de un formulario recupera el contact-activate en el gesto. Y el aria-label generico deja de pisar el texto propio (WCAG 2.5.3 para todo consumidor), verificado con el AX tree de Chrome. - Field: dejaba de duplicar commit-submit dentro de un Form — un Enter emitia dos commits con intents contradictorios y dos earcons. - NavigationMenu: commit-select se mueve al Link (navegar es el acto evaluable), el despliegue habla como emerge-open/close en vez de fingir una seleccion por hover, y el pack casa por fin con la parte que recibe el estampado — antes sonaba a la ganancia base, 10x lo disenado. - Badge: el boton de quitar compone IconButton; la altura del chip pasa a ser la del control, asi que md significa lo mismo en todo el sistema. - Fundacion: [data-on] arrastra la propiedad color, no solo las variables. BLOCKS - hero: la CTA secundaria pasa de 1.61:1 a 6.61:1. - contact: separa incomplete de invalid — el camino de error por campo era inalcanzable por construccion — y su frase llega a la AT. - newsletter: coordina (fase 4 del plan). Maquina de cinco estados, palabras propias, Submit y Reason como partes que leen el contexto. - site-header ya no congela la pagina al cruzar el breakpoint; site-footer emite su commit; pricing no se vacia; cta llega a sangre de verdad. DOCTRINA - La frontera dura 1 nombra $libs/forms como puerta sancionada. - El contrato B admite un segundo servicio: el anunciador. Un block que posee las palabras de sus estados tiene que poder decirlas. Gates: blocks:check 15/0 · vitest 20/20 en blocks y 402/402 en eidos+blocks · svelte-check 75/54 (linea base) · docs:check 0/0 · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
432106f1f6 |
docs(sema): la doctrina deja de mentir — deriva propia, copias podridas y dos leyes muertas
Pasada de saneamiento documental tras los tres commits de sonido, con el
informe de AUDIT-sema-2026-08-05 como mapa. Tres clases de defecto.
1) DERIVA QUE YO MISMO DEJE (lo mas urgente)
El 05 renombre las claves silenciadoras a `commit.silent` / `emerge.silent` y
el 06 las elimine del catalogo — dejando media docena de docs citando claves
que ya no existen, y afirmando ademas comportamientos que ya no ocurren:
- switch / toggle / toggle-group README: decian «silent-by-default
(`commit.silent`)». Hoy es `commit.medium` (gain 0.1) y suenan. Corregidos
con el nivel real y su razon (un tercio de una pulsacion de boton).
- tooltip README + docblock del pack: decian que el tuning «resta el gain de
la familia, asi que neutral emite a 0» y que «threat / fulfill siguen
aflorando». Ambas cosas son falsas desde el 06: es `SILENT`, el resolver
retira el canal y NADA aflora, porque no queda ganancia que subir.
- toggle-group apuntaba a `LIBRO_VARIACIONES_Y_EXTENSIONES.md`, que es un stub
movido; ahora apunta a book-deviations.
- La nota del renombrado en D.5 decia «hoy es `commit.silent`»: una clave que
vivio UN DIA. Marcada como tal, con la lista de las que siguen vivas.
2) COPIAS PODRIDAS — la ley del corpus es enlazar, no copiar, y estas dos
entradas la incumplian
- D.9 transcribia `SEMA_HOLDS_BY_INTENT` entera y se habia quedado atras: D.12
corrigio DOS valores el 2026-07-06 (`commit.fulfill` noticed→settled,
`signal.loss` noticed→brief, ambos contra el texto del libro) y la copia
siguio afirmando los viejos un mes. Sustituida por el puntero a holds.ts +
el enumerado generado, con la leccion escrita en el sitio.
- sema.md transcribia `SEMA_VERBS` y le faltaban DOS verbos vivos:
`commit.unselect` y `handle.zoom`. Retirada; queda el puntero a verbs.ts y a
vocabularies.md, que si se genera y tiene guard de frescura.
3) LEYES QUE LA PRACTICA YA HABIA DEROGADO, Y NADIE REGISTRO
- El contrato `emit` publicaba `Promise<void>`; devuelve `Promise<string>`
desde D.9. Corregido, y explicado que ese string es el id de la ocurrencia —
el unico asidero para cerrar una senal persistente con `clear`.
- channels.md fijaba «3 canales runtime» y D.8 cerraba la puerta a Announce
(«hoy no»). El `AnnounceChannel` existe desde el 2026-07-04: built-in,
opt-in y exportado. channels.md pasa a 4 con la distincion que importa
(visual/sound/haptic EXPRESAN; announce SUSTITUYE) y D.8 queda marcada
PARCIALMENTE SUPERSEDED.
- sema.md prescribia `{ announce: uix.announce }` — y eso NO COMPILA: las dos
firmas no casan (bolsa de opciones vs posicional). Ahora ensena el adaptador
de una linea que si compila, y declara que ninguna raiz cablea el canal hoy,
asi que activarlo cae en el fallback que anade un SEGUNDO par de live
regions. El arreglo de codigo queda sin tomar: es decision de diseno.
- La cabecera de engine-sound.ts y la fila de arts/README seguian afirmando
como MECANISMO que «`prefs.sound` mapea al bus ui», que la auditoria AU-4 ya
habia corregido en el README del propio arte: lo garantizado por
construccion es el NEGATIVO (ninguna politica de UI escribe el bus content).
AMBIGUEDADES QUE NO RESUELVO PORQUE SON TUYAS, pero que dejan de estar
escondidas: D.7 prohibe canonizar samples fuera de `signal` y
`proof-of-human` los usa en dos eventos `commit`, aplastando la modulacion por
intent que esa misma entrada existe para proteger —y el comentario del pack
afirma literalmente lo contrario de lo que hace—; y D.8 declara que los packs
deben respetar el `activeChannels` de la familia mientras sema.md prescribe lo
contrario y el pack de dialog deja dos reglas inertes. Ambas quedan marcadas
con su estado real y los dos caminos excluyentes, pendientes de tu firma.
VERIFICADO: docs:check 0/616 · sonido + sema 244/244 · cero referencias vivas a
las tres claves muertas (las que quedan en book-deviations son historia
declarada como tal, y las de cronica no se reescriben) · prettier: los 6 docs
con avisos ya estaban sucios en HEAD, no los toco.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
2 months ago |
|
|
316452263f |
fix(sound): la rugosidad pasa a ser un tremolo EN SERIE — deja de sumarse a la envolvente
Conectar un nodo a un AudioParam SUMA a su automatizacion; no la escala. El modulador de AM colgaba de `envelope.gain`, asi que su profundidad se mantenia CONSTANTE mientras la envolvente se movia: el tremolo ni crecia con el ataque ni moria con la caida, y alli donde la automatizacion bajaba de la profundidad el parametro CRUZABA CERO — modulacion en anillo, no tremolo. MEDIDO con render offline del grafo real, viejo contra nuevo: caso cuerpo RMS cola PICO corte A commit neutral (control) 0.242 -> 0.242 0.149 -> 0.149 0.0002 (igual) B signal neutral 0.332 -> 0.275 0.145 -> 0.088 0.0666 -> 0.0002 C commit + risk 0.243 -> 0.200 0.263 -> 0.103 ~0 D commit.subtle + threat 0.143 -> 0.018 0.333 -> 0.009 ~0 - B: el clic del cierre de cada aviso/error desaparece (x333 menos amplitud en el corte). Era el tick audible al final de TODOS los earcons de `signal`. - C: la cola dejaba +76% de pico sobre el control; ahora muere por debajo, que es lo que una cola debe hacer. El sustain no se toca (ahi el tremolo ES el percepto de rugosidad buscado). - D: el caso grave. `commit.subtle` (gain 0.03) cargado con `threat` (roughness 0.5) salia a RMS 0.143 — SIETE VECES su nivel declarado, con inversiones de polaridad. Ahora sale a 0.018, su nivel de diseño. Escenario real: un item de menu destructivo con intent="threat". - A: el control sin AM no se mueve ni un digito. El fix no toca lo que no debe. LA FORMA. Etapa de ganancia en serie entre el filtro y la envolvente, con valor base `1 - depth` y el modulador sobre su propio `.gain`: el factor es `(1 - depth) + depth*sin`, que hace pico en 1 —el tremolo nunca ANADE nivel— y multiplica la envolvente, asi que silencio por cualquier cosa es silencio. `depth` clampeada a 0.5 para que `1 - 2*depth >= 0`: invertir polaridad es otro efecto. Y deja de escalarse por `gainScale`: siendo un ratio ya es relativa por construccion, que es justo lo que el comentario viejo perseguia a mano («scaling both keeps the timbre and only moves the volume»). CONTRAPARTIDA DECLARADA: los earcons con rugosidad bajan 1.6-2.7 dB de nivel medio (B: -1.6 dB de cuerpo, -2.7 dB de pico), porque el AM ya no suma nivel. Es correccion, no perdida — pero es un cambio perceptual real en la familia `signal` y en todo `risk`/`threat`. Si el autor quiere recalibrar los gains base, es una decision de catalogo aparte. GUARD. `engine-sound.test.ts` sustituye el test que fijaba la propiedad vieja (la profundidad escalada por gainScale) por el que fija la invariante que de verdad importa: NADA puede conectarse al AudioParam de la envolvente, y la etapa en serie vale `1 - depth`. Verificado en ROJO (al reconectar el modulador a `envelope.gain` falla con su mensaje). El fallo colateral que aparecio al hacerlo —el test de la voz desconocida avisando dos veces— era cascada: al fallar el assert no corre `dispose()`, el AudioContext queda vivo y el siguiente test recibe el aviso de «segundo contexto». Doctrina: `sema.md` (sintesis) y `arts/sound/README.md` (voces) describen ahora la etapa en serie y por que nunca se parchea un modulador sobre `envelope.gain`. VERIFICADO: sonido + sema 244/244 · check 76 = base 75 + 1 del fichero huerfano `__adv-verify-haptic-dead.test.ts` (sin trackear, sin borrar sin instruccion) · docs:check 0/616 · el dev server sirve el fix (comprobado por fetch del modulo) · prettier: los dos ficheros eran limpios en HEAD y quedan limpios. Cierra el hallazgo S-06 de AUDIT-sema-2026-08-05. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
2 months ago |
|
|
24d30ee1a8 |
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 |
|
|
d05ecc1aa8 |
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 ( |
2 months ago |
|
|
2870874cbb |
docs(opts): la auditoria exhaustiva del corpus — el canon deja de mentir
Barrido mecanico sobre los 673 .md del repo (1485 bloques de codigo en 319
docs): cada import contra la tabla de alias real, cada *Provider/*Opts contra
11481 simbolos del codigo, cada tabla de props de los 284 READMEs contra su
types.ts.
Causado por el eje de opts:
- active-architecture §8 (canon) daba OptsFromProps SIN el 4o parametro —el que
salva el `undefined`— y bindProps en forma v1; partOpts no aparecia pese a
ser el mecanismo de 288 wrappers. Tabla reescrita + las dos notas que la
tabla no puede llevar.
- architecture/soma.md tenia el UNICO bloque con bolsa manual que quedaba en
los 673, y sus DOS imports eran rutas fantasma ('../../reactive',
'../../props'). Ejemplo reescrito al canon.
- CONTINUE-direction afirmaba que switch no podia usar OptsFromProps y que
bindProps no acepta Active: falso por ambas mitades desde el eje. Marcado.
- CONTINUE-opts-canonicas se leia como plan VIVO con seis decisiones sin tomar
—una sesion futura habria rehecho 353 ficheros—: banner de cierre con lo que
cada decision acabo siendo.
- El invariante de catalogo sube al estrato arquitectura y gana filas de
aceptacion E-3.7 / E-3.8. drawer documenta que el snap SOBREVIVE al cierre;
knob y cropper precisan que su callback cubre reset y clamp.
Preexistentes que solo aparecen con el metodo exhaustivo:
- carousel documentaba `value`/`onValueChange`; son `index`/`onIndexChange` —y
el mismo README ya decia `index` doce lineas mas abajo.
- navigation-menu seguia con `skipDelayDuration`, renombrado a `groupSkipDelay`
en la pasada N6 (el propio types.ts registra el rename).
- textarea documentaba `submitShortcut` con **el default invertido**: el prop
es `submitOn` y por defecto es `false`, no 'mod+enter' —un consumidor
esperaba que Ctrl+Enter enviara de fabrica—, y `minLength` no existe.
- search-field ensenaba a envolver el callback en un debounce a mano (con el
alias `$lib`, retirado del proyecto) teniendo `debounceMs` propio: era
debounce sobre debounce.
- GESTURES nombraba SplitterTriggerProvider; la clase es
SplitterResizeTriggerProvider.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
b80ccb33cb |
docs(direction): el contrato pasa a ser canon, y el corpus deja de contradecirlo
El eje estaba cerrado en CODIGO y no en DOCUMENTACION. El contrato vivia solo en
`CONTINUE-direction.md`, un handoff que `docs/README.md` declara «never a source
of truth». Un capitulo E2 lo fija ahora: `docs/canon/direction-contract.md`.
Va a canon y no a arquitectura por la misma razon que `recipe-contract.md`: es
normativo y tiene guard ejecutable. Cubre la cadena y donde corre cada eslabon;
por que `undefined` no es `'ltr'`; las DOS atributos —`dir` crudo (nativo, lo que
`:dir()` mira) y `data-dir` resuelto (opt-in, para recetas que necesitan un hook
incondicional)—; cuando el estampado es OBLIGATORIO; la doctrina de selector; las
trampas que el guard no ve; que espeja y que no; y la mitad global de prefs.
EL CORPUS SE CONTRADECIA en cuatro sitios, y dos de ellos ENSENABAN mal:
- `component-guide.md:372` daba la cadena como `prefs → 'ltr'`, DOS eslabones,
mandando al wrapper a leer prefs directamente. Eso excluye la prop.
- `soma-architecture.md` §3.4 presentaba el resolutor a pelo (`soma.prefs.getDir()`
dentro del provider) como LA forma de obtener la direccion — justo lo que el eje
retiro del catalogo.
- `html.ts` ensenaba en su JSDoc `dir?: 'ltr' | 'rtl'`, el union a mano que la
regla E-2.5 ahora prohibe. El mecanismo que ensena (extender para estrechar una
clave HTML) es correcto y sobrevive; solo cambia el ejemplo.
- `active-architecture.md:416` no listaba `lang` en la proyeccion, contra
`contracts.ts` y contra su propio test de frontera. Igual `prefs/README.md`.
Y no lo mencionaba en absoluto: el glosario (`activeDir`, `resolvedDir`,
`data-dir`, RTL-1, el escape `rtl-physical:`), `eidos.md` (RTL-1 es el TERCER
guard de deriva y faltaba junto al builder tipado y eidos-lint), `soma.md`,
`building-a-component.md` (§Known traps es exactamente donde va «la prop mueve la
matematica y deja la pintura atras»), y la tabla de atributos de la arquitectura,
que afirma «todo lo que viaja entre capas viaja por atributos» y omitia `dir`.
El checklist gana cuatro reglas y el guard que faltaba (`rtl:check` no estaba en
la matriz de aceptacion pese a que la tabla canon lo nombra), mas un recorte en
E-3.5: «todas las props visuales mapean a `data-{prop}`» leia como mandato de
estampar `data-dir`, que `:dir()` no puede ver.
DIVERGENCIA DECLARADA (§7): la familia `chart` no tiene prop ni provider y
resuelve leyendo `getComputedStyle(node).direction`. Es el unico sitio del
catalogo que hace lo que §1 prohibe. Se registra en vez de esconderse.
Tres verificadores adversariales sobre el barrido; sus hallazgos, corregidos:
RTL-1 estaba anunciado como guard de TODO el capitulo (solo cubre §4) · «nunca
lee al padre» era absoluto y borraba la composicion sancionada en el punto de
llamada · el estampado se afirmaba incondicional en un sitio y condicional en
otro · las cuatro filas nuevas usaban una aplicabilidad que ni la leyenda ni
`component-audit.ts` conocen.
`docs:check` 0 errores sobre 564 docs. `check` 77 = la linea base.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
2f872ef864 |
feat(sound): $sound orquestador (buses+voces+media) y player v2 F1-F3 sobre el servicio
Rediseno clean-room del sonido (gate D-SR.1-12 firmado; la doctrina "la voz
es del art" de PLAN-sound-engine s16.1 queda revocada):
- Mezcla: master -> buses ui/content (gain/mute/duck refcount, gana la mas
fuerte; politica pre-contexto). prefs de UI jamas tocan la obra.
- Voces registradas: la calibracion es DATO del consumidor (sema registra
'sema'; el default conserva la calibracion historica - cero cambio audible,
A/B del usuario: "suena igual").
- Ciudadania de media: sound.media(el) PROVEE el transporte (la forma exacta
del puerto MediaProvider) + foco mixed/exclusive/duck PERSISTENTE (estado
derivado; el ducker mas reciente retiene la palabra) + duckUiWhileContent +
MediaSession de la fuente activa (play/pause/seekto + posicion, liberacion
y promocion) + visibilidad content-aware + attach() opt-in (irreversible,
CORS declarado). Re-registro idempotente por elemento.
- diagnostics.ts con catalogo tipado (contrato de arts) y guards post-dispose
(ningun camino abre un contexto huerfano). Auditoria AU-1..9 resuelta;
bundle medido: 16,2 KB min / 5,6 KB gz.
Player v2 (gate D-AP2.1-13 firmado) F1-F3:
- F1 gaps de framework: aria-valuetext en Slider (valueText por thumb) +
parte SecondaryRange con token --slider-secondary-bg (buffer/clip/capitulos)
+ formatDuration en $libs/days + fix del selector del pack (H-1: la regla
de play/pause construia sobre provider y el stamp aterriza en play-button).
- F2 morfo: 6 partes audio-only (artwork/artist/identity/transport/
rate-button/live-indicator), commit-set-rate, commit-set-time RETIRADO
(D-AP2.13: el scrubber delega en el commit-set del Slider compuesto),
sustain-loading cableado (H-9), teclado j/l, </> y 0-9 (con guard de
modificadores para no pisar atajos del navegador).
- F3 la migracion: el transporte default es uix.sound.media(el, {metadata})
(pineado con el motor real); nativeMediaProvider RETIRADO (media-provider.ts
queda como contrato puro del puerto, sin shim); G-5: los commits de
play/mute/rate cabalgan el evento de RESULTADO (play/pause saltando el
pause de ended, volumechange con transicion de muted, ratechange);
formatTime -> formatDuration; sub-providers + wrappers + exports de las 6
partes; prop metadata publica (captura al registrar, documentado).
Verificacion: 45 suites / 458 tests verdes en el alcance (sound, sema,
active-uix, active-app, morfo, slider, media-player, days) - check 74 =
baseline de la rama, 0 propios - docs:check 0 - invariante de UN AudioContext
intacta (e2e sin tocar). Planes de proceso y handoff al dia
(PLAN-sound-redesign, PLAN-audio-player-v2, CONTINUE-sound-engine).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
2 months ago |
|
|
b1b568e32f |
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B
El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con `validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la verificacion con los tres campos vacios, justo el hueco que la doctrina existe para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM: sincrono y sin escribir errores (`form.validate()` habria encendido los tres campos en rojo en el primer frame). `state.test.ts` fija la maquina con 9 casos — primer test unitario del tier, porque `state.ts` es su primera logica pura: el orden de prioridad, que solo `ready` deja enviar, y que todo estado bloqueado tiene frase. Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas que posee un block que coordina (estado, forma de sus datos, palabras), B-5 admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados como idlangref con fallback ingles) y la convencion de servicios acotada: el traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los blocks de layout no coordinan nada. i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas de arranque (galeria y previews, que duplican el boot) y la demo se lee en castellano en vez de caer al fallback ingles. Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico — `verification` con reto / sin reto, porque omitir el prop no es lo mismo que pasar un estado que nunca verifica. Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status` el mismo y el veredicto del app no se sostiene (hilo de canon abierto). Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 · svelte-check sin errores propios · prettier limpio en los ficheros propios. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
d9c79bf584 |
docs: corpus al día — blocks tier indexado, factorías nuevas, cifras atemporales
blocks.md entra en el índice y next-features gana el registro §8 del tier; active-app documenta las factorías que faltaban en la tabla (agent, motion, scene…); docs-check aprende a censar catálogos sin tests ni barrels; book-deviations y component-guide dejan de citar recuentos congelados. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
2 months ago |
|
|
59aa41a132 |
docs(barcode): la trampa de `MorfoElement`, y el registro de diferidos al día
Dos huecos que dejó el trabajo del componente. - **`morfo.md` no decía que el vocabulario `MorfoElement` vive DUPLICADO**: la unión de TypeScript en `types.ts` y la lista de literales del validador sium en `schema.ts`. Añadir solo la unión compila limpio y revienta en runtime, y `npm run check` no lo ve — lo caza `morfo:check`, o simplemente montar el componente. Entra como pitfall con el error literal que escupe y el incidente que lo destapó (`'text'`, para la interpretación legible del Barcode). - **El §10 del registro de diferidos se escribió ANTES del perfil ISBN** y ya no decía la verdad: listaba como pendiente un trabajo que se envió el mismo día. Ahora separa lo enviado (el perfil de entrada sobre EAN-13, con su razón de no ser una simbología) de lo que sigue diferido, y deja escrita junto al add-on EAN-5 la restricción dura de la hyphenación: no se calcula desde el número, necesita las tablas de rangos de la International ISBN Agency — dato versionado con fecha de caducidad que una librería de cero dependencias no puede tragarse. El string con guiones lo trae el consumidor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
b27f2345fe |
docs(process): el motor cierra sus fases y abre 5 hallazgos; el player, al gate
`PLAN-sound-engine` pasa a CERRADO (F0…F5) y `PLAN-audio-player` a DESBLOQUEADO. Pero cerrar las fases no es dejar el diseño incuestionado, así que el plan gana una auditoría propia que lo contradice donde toca. - **§15 — la invariante, cerrada y medida.** Incluye lo que la medición DESMINTIÓ: el handoff anterior predecía un aviso por consola en `/temas/sema` que no podía aparecer, porque el aviso cuenta contextos VIVOS y allí sólo había uno. Y **E-8**, el agujero de attach que E-1 dio por cableado sin estarlo. - **§16 — 5 hallazgos ABIERTOS sobre el art**, tres con decisión pendiente: `analyze.ts` sigue abriendo su contexto (medido 1 → 2 al importar un `.wav`); el aviso de segundo contexto es ciego a los contextos crudos; **S-1 NO está cerrado** y §7 afirmaba lo contrario —queda tachado en su sitio—; el canal tira en silencio `masterGain` y compañía cuando le inyectan el motor; y `gainScale`, que es de UNA reproducción, se escribe en el master compartido. - **§16.1 — la pregunta de fondo**: el corte separa módulos, **no conocimiento**. La quinta a 1.5×, el ADSR recortado a 15 %/40 %, el contour de ±400 cents y el mapeo de `roughness` son diseño sonoro calibrado contra el vocabulario de sema, viviendo dentro de lo que el plan llama «maquinaria». El precedente de `$motion` —datos en la capa, mecanismos en el art— no se replica tan de cerca como §3 afirma. Y la superficie no sirve a dos de los tres consumidores que la justificaban: `Waveform` y el analizador necesitan los `AudioBuffer`, y `preload(urls)` los esconde en una caché privada. - **`sema.md` corregido**: «sema no importa el art» era cierto para la INSTANCIA y falso para el grafo de módulos. - **`PLAN-audio-player` con las 5 correcciones de su cabecera incorporadas al cuerpo**, que era la condición para presentar nada: segmentos en la matriz §2 y en el contrato §7.5; **G-2 sube a bloqueante** (buffer, ventana de clip, capítulos); las dos invariantes de §7.6 (`prefs.sound` NUNCA toca el volumen del contenido; el silencio de UI debe alcanzar al `Slider` compuesto). **D-AP.11 escrita contra el art** —su premisa, «no hay motor», era falsa— y **D-AP.12** (segmentos `clip`) añadida; las dos existían como referencia sin fila en §4. **D-AP.7 reescrita** como ducking. - El handoff añade dos reglas que costaron caro: **un guard que no has visto fallar no vale**, y acoplarse al dev server vivo del usuario en vez de levantar otro. D-AP.1…D-AP.12 siguen SIN FIRMAR: el gate es lo primero de F0 y no se escribe una línea de código del player antes. `docs:check` deja 1 error ajeno (`blocks/banner/README.md:15`, «8 roles» por 9). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
be3cbf680e |
fix(sound): la invariante de un solo AudioContext, cerrada en attach y medida
`PLAN-sound-engine` daba por cableada la propiedad del motor «en los dos modos de arranque». En attach no lo estaba: `defineUixServices` registraba `motion` y `scene` junto a `dom` pero NO `sound`, y `defineEngineSemantic` no reenviaba `soundEngine`. Una app en attach con sonido activo tenía el motor privado del canal MÁS `uix.sound` — dos motores, y dos contextos en cuanto el reproductor primase el suyo. `attachActiveUix` DOCUMENTABA el footgun en un comentario en vez de cerrarlo, mientras `contracts.ts` ya prometía `singleContextPerDocument`. - `defineUixServices` declara `sound` junto a `motion` / `scene`, y `defineEngineSemantic` lo toma como `serviceDependencies: ['dom', 'sound']` y lo reenvía. Degrada igual que `dom`: sin el servicio declarado llega `undefined` y el canal vuelve a crear el suyo (construcción directa y tests). - La nota de `attachActiveUix` pasa de describir el footgun a describir el cierre, y deja escrito el único caso que sigue vivo: un esquema de servicios a mano que declare `events` sin `sound`. - Fila `events` de `contracts.ts` con `serviceDependencies`, y el assert DERIVA de la tabla en vez de hardcodearla — que es lo que su propia doctrina pide. - El estudio de sema (`/temas/sema`) enchufa `uix.sound`. Era la única página capaz de ejercitar la inyección y no lo hacía. MEDIDO en Chrome, no deducido. Con el motor privado, cada edición del draft reconstruye el `EngineSemantic`, el `dispose()` del canal cierra el contexto y el siguiente disparo abre otro: 9 ediciones dejaron **10 contextos creados y 9 cerrados**, perdiendo además la caché de samples decodificados en cada una. Con el motor compartido: **1 contexto, 0 cierres**, y 24 osciladores construidos en 6 ciclos — el earcon sigue vivo. Dos guards, y los dos VISTOS FALLAR sin el cableado. Es la lección de la retirada de F2, donde una medición vacua se presentó como prueba: - `active-uix.svelte.test.ts`: el `events` de la app prima el motor de la app (quitando el reenvío: `expected null not to be null`). - `sound-e2e.test.ts`: un motor inyectado sobrevive a 3 reconstrucciones con UN contexto y sin `close()`; sólo lo cierra quien lo creó. Verificación: `client` 130/130 · 842/842 · `server` 262/264 · 3718/3722 — los 3 rojos son ajenos (`aura`, `menubar`, `radio-group`) y el cuarto fue un timeout de `soma-attr-audit` por contención, verde aislado en 3,1 s · `check` en la baseline exacta (73 errores, 0 propios). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
6920684d09 |
refactor(sound): extraer el motor Web Audio de sema al art `$sound`
`sema/chans/sound.ts` tenía 407 líneas de las que ~255 (≈63%) eran maquinaria Web Audio —ciclo de vida del AudioContext, síntesis, samples, desbloqueo por gesto— mezclada con doctrina perceptiva. Deuda arrastrada, con tres síntomas medidos: el import era estático, así que toda app con sema metía el sintetizador en el bundle aunque el sonido estuviera apagado (que es el default); faltaba ciudadanía que `$scene` ya resuelve; y la costura de inyección (`audioContextFactory`) llevaba ahí sin usar desde el principio. Mismo movimiento que `$motion` hizo desde eidos: el art se lleva el RUNTIME, la capa conserva sus DATOS y su doctrina. - `$sound` / `EngineSound`: UN AudioContext por documento (los navegadores los limitan y el gesto de desbloqueo es por contexto), síntesis de earcon, samples con caché y fallback a síntesis, `autoSuspend` OPT-IN —suspender con la pestaña oculta es correcto para earcons y erróneo para contenido, así que es decisión de quien compone— y aviso cuando un segundo contexto va vivo. Puertos `SoundDom` / `SoundTimers` inyectados; no importa ningún otro art ni nada de `$uix/sema`, que es la prueba objetiva del corte. - `SoundChannel`: 407 → 136 líneas. Solo doctrina: el gate de `prepare`, la política de reducción y el reparto «el canal resuelve el NIVEL, el motor aplica la ganancia». Sema no gana ni un import: recibe el motor por puerto. - `uix.sound` en standalone y attach con `ownsSound` (idioma ya shipped: `ownsMotion` / `ownsScene`), fila `sound` en la tabla ejecutable `contracts.ts`, y `defineEngineSound()` para el camino de app. - Tests nuevos: `engine-sound.test.ts` (11), `sound-port.test.ts` (guard de deriva de tipos + la regla de propiedad) y `sound-e2e.test.ts`, que recorre `emit -> cascada -> canal -> art -> grafo real`: el camino que las 16 suites previas no cubrían porque paraban en canales falsos. Sin `diagnostics.ts` ni `errors.ts`, y es decisión: espejo de `$scene`, aquí todo fallo es degradación documentada, no error de programador. Verificación: 17 suites / 198 tests · `check` en la baseline exacta (73 errores, 0 propios) · `sound.test.ts` verde SIN tocar un solo assert, que era el criterio de que el movimiento fue value-preserving. Planes: `PLAN-sound-engine.md` (completo, con el registro de la revisión adversarial E-1..E-7), `PLAN-audio-player.md` (aparcado tras el análisis del reproductor, con sus correcciones en cabecera) y `CONTINUE-sound-engine.md` (handoff). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
3097cfcb69 |
revert: deshacer la auditoría entera — se hizo sin leer la doctrina
Revert de los 7 commits de la sesión del 2026-07-29/30: |
2 months ago |
|
|
c9b83d0257 |
docs(morfo): qué hace pública a una parte, y tres hallazgos retirados
`morfo.md` §Step 2 — la guía de AUTORÍA, donde el autor decide — documentaba sólo `'public'` y `'virtual'` y omitía `'private'`. La sección de referencia sí tenía los tres, pero nadie declara partes leyendo la referencia. El criterio que queda escrito: **lo que hace pública a una parte es que el consumidor la decida, con independencia de la sintaxis**. La composición (`<Dialog.Trigger>`) es la vía habitual, no la única: un primitivo de API plana gobierna sus partes por props y snippets — `<Badge dot>` decide la parte Dot, `icon` la parte Icon. Ambas son públicas. `'private'` queda para lo que nada de lo que escribe el consumidor alcanza (el Track de `Spinner`, el Sentinel de `Sticky`). Importa el matiz porque la primera versión de esta nota decía lo contrario — que el test era mecánico y que sin `<Componente.Parte>` la parte no es pública — y eso habría reclasificado mal a Badge y a todo primitivo de API plana. ## Handoff: tres hallazgos retirados Los tres salieron de heurísticas de una sola vía dadas por hechas sin abrir el código. Es el mismo fallo que la auditoría denuncia en los guards, cometido al redactar el informe: - **D-3 `AgentTimersPort.once`** — el puerto SÍ está satisfecho: `defineActiveAgent()` adapta `core.timers.schedule` a `once` en la raíz de composición, que es el patrón previsto. No había nada roto. - **«5 derivas de scope»** — `avatar-group`, `path-trace` y `rotate-align` están implementados; el framework co-loca sub-componentes dentro del directorio del padre y la heurística asumía «directorio == kebab». - **«29 morfos con `kind:'public'` irreal»** — medía si existe `<Componente.Parte>` e ignoraba props y snippets. Sin recuento fiable hasta cruzar las tres vías. Añadido: `npm run lint` nunca ha pasado. Separando el ruido CRLF del checkout Windows (`--end-of-line auto`) quedan 1544 ficheros con deriva real de contenido. Es decisión del usuario porque el arreglo toca todo el repo de golpe. Y la lección de método: **un analizador que no puede resolver algo tiene que decir «no verificado», no emitir un hallazgo.** Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
c39170abbf |
fix(uix): la auditoría del sistema — lo que los guards no veían
Auditoría clean-room de todo ActiveUIX (excluido `web/`), componente a
componente. Lo que sale de aquí no es una lista de bugs: es un patrón.
El framework validaba que lo escrito fuese VÁLIDO, no que lo declarado
se CUMPLIESE — y sus guards fallaban ABIERTOS.
## El colapso de las uniones de props (95 → 0)
Un `Props` de eidos es `{ …props propias… } & <atributos nativos>`.
Cuando el elemento declara un atributo homónimo, la intersección funde
ambos y una unión estrecha contra el `string` nativo COLAPSA a `string`.
Causa: `Without<T, U> = Omit<T, keyof U>` invocado como `Without<T, {}>`
— `Omit<T, never>`, un no-op — 433 veces en soma; sólo 3 con argumento
real. Invisible para `svelte-check`: ensanchar un tipo no es un error,
es una garantía perdida.
Medido: 95 props en 72 componentes. `<Avatar color="nonsense">`
compilaba. `ComboboxInput.size` chocaba con el `<input size>` numérico
y era inusable. Migrado con codemod sobre AST (nunca regex) a
`Own & Omit<Nativos, keyof Own>`: 92 tipos en 73 ficheros + carousel a
mano. `check` no se movió.
Garantía nueva: `eidos/prop-surface.test.ts` (PROP-1) compara los
literales de la anotación del autor contra los de la propiedad pública.
Verificado que falla reintroduciendo el defecto.
## Los cuatro guards que fallaban abiertos
- `translations:check` crasheaba en CADA ejecución de su historia — un
stripper de comentarios borraba `//` dentro de strings. Sustituido por
import dinámico. Al arrancar destapó 8 slots `texts` sin traducción.
- `soma-attr-audit` agotaba el timeout de 5 s: sin veredicto, verde por
omisión.
- `component-audit` D-7.4 hacía `continue` mudo cuando el tipo no
resolvía. Ahora resuelve con el checker de TypeScript
(`scripts/prop-unions.ts`): puntos ciegos de 124 → 3.
- `component-audit` R-1.1: el regex casaba `[data-motion='reduce']` y
daba PASS por el motivo equivocado.
Regla adoptada: un guard que no puede evaluar TIENE que decirlo. El
informe lleva ahora bloque «Not verified» y recuento en el resumen.
## D-1 · tooltip y D-2 · card, cableados
`tooltip` declaraba 3 eventos `emerge` que nadie emitía. Ahora emiten;
`present` pasa a `sequence: 'post'` — con `'pre'` el hold de ~240 ms
gateaba el montaje del propio overlay.
`card` declaraba `commit-select` sin emisor posible (scope sin soma).
Puente headless en `soma/components/card/` con la forma ya establecida
por `menu-dial` / `onion-menu`: eidos posee estado y render, soma posee
sólo el `SomaRuntime` que emite.
## Documentación: 22 mentiras corregidas
`docs/` afirmaba guards inexistentes (`NO_MISSING_PROVIDER_TESTS`),
APIs con firma equivocada y un modelo de Motion que el código no
implementa. Corregido en CANON, arquitectura, glosario, theming/motion,
checklist y los README de `motion` / `callout` / `arts/motion`.
## Además
- CardGroup: la descripción se metía en la primera celda del grid.
- Motion: `data-state` siempre estampado, salida real en `leave()`,
token fantasma `--motion-stagger-each-default` eliminado.
- `engine-motion`: `handoffState` Map → WeakMap (fuga por nodo).
- `mockup`: primitivo crudo → token de rol (R-4.6).
- 5 catálogos de traducción que faltaban.
Handoff: `docs/process/CONTINUE-audit-2026-07-29.md`.
Batería: check 0 errores en src · vitest server 3701/3701 ·
docs:check 0/0 · component:audit 161 PASS / 2 NEEDS-WORK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
31580f3b39 |
docs(spec): el contrato de delegación como especificación normativa (DRAFT)
Fase 1 del plan de corrección, y materialización de D-AG.12a firmada ayer: `docs/spec/delegation-contract.md`, versionada por fecha (2026-07-28). QUÉ ES 50 requisitos `AG-1…50` en RFC-2119, con escalera de estabilidad por sección (stable | provisional | reserved), identificadores estables que NUNCA se renumeran ni se reciclan (un requisito retirado conserva su número), y la exigencia de que ambos vehículos de conformance CITEN el identificador que incumplen — «una suite que no nombra lo que falla no acredita a nadie». La doctrina pasa a ser el PORQUÉ y lo declara explícitamente: si doctrina y spec discrepan sobre un requisito, manda la spec y la doctrina se corrige. Annex A lleva las dos reclamaciones de frontera con estado `claimed` (ninguna materializada) y su gate de promoción: ≥2 FORMAS DE DOMINIO con manifiesto conforme — y dice por qué dos participantes de texto no valen: un formato probado solo sobre texto secuencial asume texto secuencial en silencio. Annex B mapea el modelo de amenazas a requisitos concretos en vez de dejarlo como prosa. §0.6 declara la posición honesta (actos secuenciales, cliente- first, generalidad en prueba). LO QUE LA ESCRITURA SACÓ A LA LUZ — y no estaba en ninguna lista - **AG-35 (presencia)**: el invariante correcto NO es «Aura montada» sino «alguna superficie está expresando el ciclo». El playground de agnt ya expresa el ciclo SIN Aura, y exigir el componente lo rechazaría siendo correcto. Además resuelve el problema técnico: el motor es un arte sin DOM y no puede saber qué hay montado, pero sí puede contar adjuntos. - **AG-41 (elicitación)**: especificado y NO cumplido, con el porqué estructural — la escalada lleva razón tipada pero no la pregunta, y la resolución es binaria y no lleva respuesta. No era falta de superficie. - **AG-44**: «ningún estado se distingue solo por el color» sube de decisión de componente a requisito del contrato (ya lo fija `shapes.test.ts`). §D — DECISIONES ABIERTAS, VISIBLES Un DRAFT puede llevarlas; al promocionar, §D debe quedar VACÍA. Son dos y ninguna la firmo yo: `SHOULD` vs `MUST` en AG-35 (con recomendación razonada: SHOULD + política que lo eleve a MUST, porque rehusar por defecto rompería los 30 tests del motor y le quitaría al app el derecho a elegir su superficie) y la forma del canal de elicitación en AG-41. Registrada en el índice del corpus. `docs/README.md` lo edita otra sesión en paralelo: stageada SOLO mi fila (backup → HEAD → mi cambio → add → restaurar su versión + mi cambio), verificado que lo suyo queda sin stagear. Verificado: docs:check 542 docs, sin errores nuevos (el único es el ajeno preexistente de callout). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
2 months ago |
|
|
d38bd63faa |
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
ee02c5c55f |
docs(agent): D-AG.12 y §5b firmadas — la spec, y quién prueba el eje
Dos decisiones que estaban pendientes, firmadas tras investigación a fondo
(precedente externo verificado a fecha, no de memoria).
D-AG.12 — EL CONTRATO COMO REFERENCIA
Spec normativa en `docs/spec/` (RFC-2119 + IDs `AG-n` + escalera
stable|provisional|reserved + versionado por fecha), en estado DRAFT para que
F4b se construya CONTRA ella y no al revés. La doctrina se queda con el PORQUÉ.
Conformance en DOS vehículos, que es lo que evita que la palabra sea retórica:
kit de fixtures del protocolo (PORTABLE — un tercero lo corre contra SU
implementación; semilla ScriptedAgentTransport) + agent-check (lint del repo),
ambos citando IDs de requisito en sus fallos. Las dos claims de frontera se
nombran con marcador claimed|shipped porque hoy ninguna está materializada.
Gate de promoción a 1.0: ≥2 formas de dominio + claims shipped + fixtures
verdes. Respaldo externo: MCP ya no promueve a Final sin escenario en su suite
(SEP-2484) y opera lifecycle formal con ventana de 12 meses; AG-UI se volvió
referencia por ADOPCIÓN (AWS Bedrock AgentCore, Microsoft Agent Framework), no
por publicar suite. La spec se escribe antes para gobernar; el título se gana
después.
§5b — QUIÉN PRUEBA EL EJE: form + table
El gate de F4 pedía «2 manifiestos reales» sin asignar dueños; esto rellena ese
hueco, no enmienda nada. El criterio deja de ser disponibilidad y pasa a ser
dificultad: un formato lo decide su caso peor. Y form+table no son preferencia,
son la ANATOMÍA del contrato — los args de una capacidad SON schemas (sium →
toJsonSchema, ya construido en F1) y un run ES una transacción sobre un
conjunto de ediciones. Form además dobla: agente→componente (args contra
campos) y componente→agente (elicitación) son la misma proyección en los dos
sentidos.
Chat y palabras quedan como CONSUMIDORES, no jueces. El riesgo que esto evita
era real: ambos son texto lineal, así que un manifiesto probado solo ahí
habría asumido texto en silencio (rango como selección, ediciones
secuenciales, args string) y el caso peor habría aparecido tarde. Inventario
al firmar: 170 morfos, 0 con capacidad delegate declarada.
COROLARIO — la elicitación no depende de superficie conversacional
Una pregunta tipada es un formulario en contexto (Form.AutoFields), descartable
y no-modal, que es justo lo que exige el contrato a11y (WCAG 2.2.4). La nota de
protocol.ts («materializes with the conversation surface») describía UNA opción
y se leía como necesidad: corregida ahí y en la doctrina §7.
DESCARTADO
Entrada de delegación global siempre-disponible: roza el modo ambiental, que
está FIRMADO FUERA (⚖️1), y no se recupera por la puerta de atrás. La entrada
es un patrón con pieles montado en app-land — mismo veredicto que la
review-card.
Handoff `continue-agente-2026-07.md` reescrito para mañana: la spec DRAFT es lo
primero, luego el kit de fixtures, luego F4b con los dueños ya firmados.
Verificado: agent 30/30 · agent:check limpio · docs:check sin errores nuevos
(el único es el preexistente ajeno de callout).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
e10eed98cc |
uix(aura): la superficie visible del agente — anillo de custodia, orbe-contorno y desenlaces expresados
Rediseño dirigido por el usuario sobre un banco de mockups con el orbe REAL
corriendo dentro. Seis frentes, todos con maquinaria existente:
ANILLO DE CUSTODIA (parte nueva `Aura.Ring`, morfo+soma+eidos)
El estado se lee como FORMA y no solo como tinte: raíl punteado en reposo,
arco que respira al ofrecerse, segmentos = pasos del plan (laten), dos arcos
enfrentados y QUIETOS esperando tu revisión (la quietud lo separa de acting),
arco que gira actuando, anillo cerrado + pulso saliente cuando te necesita.
Envuelve al Indicator (es su marco de referencia, no un hermano posicionado a
mano). El movimiento continuo son loops REGISTRADOS (data-animation-style),
nunca @keyframes de receta (R-4.5) — vienen con su puerta de reduced-motion,
donde el dash sigue distinguiendo el estado. El attr va en los hijos del SVG,
jamás en la parte: motionAttrs() estampa además data-state, del morfo ahí.
EL ORBE ES UN CONTORNO
`thickness` en el efecto orb; el hueco es real (alfa propia) sobre cualquier
fondo. Se va la rama claro/oscuro y el parámetro backgroundColor: el shader
mezclaba con el fondo y derivaba el alfa del canal máximo, o sea pintaba un
disco OPACO del color de la superficie justo en el centro (en claro un círculo
relleno; en oscuro el azul del seed). Y la receta deja de pintar su disco en
cuanto monta el canvas (:has(canvas)) — solo sobrevive donde no hay render
vivo: SSR, tests, effect={false}, sin WebGL.
COLOR = TOKENS DE ROL, PINTADO Y NO ROTADO
colorA/colorB/colorDeep (solid / solid-hover / element) resueltos con
eidos.resolveToken y pasados como hex, que es el seam que el propio arte
documenta. El hue-delta anterior se medía en OKLCH y se aplicaba en YIQ: risk
salía VERDE junto a su propio anillo naranja.
ONION EN EL IDIOMA DEL ORBE
Prop `look="solid" | "luminous"` (defecto solid: nadie más se entera). En
luminous el sector no tiene relleno — filo de luz (pasada borrosa + nítida) y
etiqueta en el color del sector; la cuña sigue siendo área táctil. El trigger
con snippet propio va BARE: sin disco, sin sombra FAB y sin el salto del hover
(el orbe ES el trigger, no su contenido). Medio anillo bottom-center y radios
44/40. El halo respira (loop pulse) y en commit-select da UNA pasada de ping
pivotando en el centro real del menú — la firma genérica de commit anima
box-shadow, que sobre un path de SVG no hace nada.
DESENLACES EXPRESADOS (el hueco real: un fallo era indistinguible de un éxito)
Tres eventos terminales con intent INTRÍNSECO — delegate-return (fulfill) /
-aborted (sin intent: un decline es ausencia, no pérdida) / -failed (loss) —
en vez de un intent dinámico, que no tiene dónde vivir: TriggerOptions no
lleva intent y el fromProp del morfo ata a un prop PÚBLICO, no a estado de
máquina. Puerto `runs?` opcional en AuraAgentSource porque activeRun ya es
null al cerrar (§0: sin él, anuncio genérico). Primeras firmas de la familia
delegate en el registro; la de loss es MOVIMIENTO (return-recoil: retirada +
descenso), no tinte — no estrena ningún hue.
MAPA SEMÁNTICO DE COLOR (demo)
Un hue = un significado en toda la superficie: gris=sin agencia ·
naranja=tu revisión · morado=él actúa · rojo=escalated (exclusivo) ·
teal=tu sí. El anillo de autoridad reusa los colores de la custodia con el
MISMO sentido (Review=risk porque vivirás en reviewing; Auto=primary porque
actúa) y los declines pasan a neutral — canon: el intent de un cancel es
`absent`. Además el anillo en reposo DELEGA (las tres autoridades abren el
run) y se cierra por transición, no siempre que no hay run.
Dos guardas del framework corrigieron el diseño por el camino:
- BK-FRAME-NO-INTENT rechazó dar intent a una familia de marco sin
justificación escrita (excepción A-1): queda el intentRationale — el motor
no emite nada y no hay evento evaluable que enmarcar, así que el retorno
consolidado es el ÚNICO sitio expresivo del desenlace.
- fallbackTarget gana sobre el target del morfo (contrato de TriggerOptions):
el sello caía en el provider. Los terminales pasan el Ring cuando existe.
Verificado en Chrome real (ventana delante: en segundo plano el navegador
congela rAF y ResizeObserver y las medidas mienten): seis estados en claro y
oscuro, y un error de transporte REAL apuntando a un puerto muerto →
delegate-return-failed · loss · return-recoil sobre el Ring · región
«Agente: falló — control devuelto».
Tests 478 (morfo 114 · soma/aura 11, +5 casos de desenlace · eidos 353),
eidos-lint 0 invalid / 0 class-hooks nuevos, morfo:vocabulary limpio,
check en los 73 errores ajenos preexistentes.
Pendiente (handoff actualizado): la jerarquía tipográfica del Status (D1
llevaba dos líneas y solo se hizo el anillo) · el pulso de commit-select se
corta si el consumidor cierra el anillo al elegir (necesita retención tipo
Presence) · D5 la superficie de composición del encargo, decisión abierta.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
e187ab6d72 |
docs(agent): F3 cerrada · doctrina «The review surface» — split framework/app
Cierra los dos flecos de F3 tras la corrección del usuario (la review-card con diff era anclaje en dev-tools, sin sentido a nivel framework): - agent.md §7 gana «The review surface»: el FRAMEWORK posee los estados del ciclo expresados perceptualmente (reviewing/escalated con afordancias authorize/reject/cancel — Aura ya las da) + la semántica de «propuesto por otro actor» + los anuncios atribuidos; el APP posee el CONTENIDO de lo que se revisa, en su propio dominio (un documento enseña sus bloques propuestos, un calendario sus eventos pendientes). Una tarjeta-diff estilo git es legítima en una aplicación concreta; jamás canon. - §6 fatiga de aprobación enmendada en el mismo sentido (el cambio concreto en el dominio del app, nunca prosa). - PLAN: F3 CERRADA — review resuelta como doctrina sin componente nuevo; `allowedFamilies` del primer componente actuado reubicada a F2 (su hogar natural es el piloto). Siguiente: F4 por la vía del chat. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
6be7195f0f |
feat(agent): eje agéntico — motor $agent (F1) + Aura + promoción uix.scene
Materializa el eje ortogonal agéntico del ecosistema: la 8ª familia semántica
`delegate` («¿quién actúa ahora?») deja de estar sin materializar. El agente es
OTRO ACTOR (LLM, macro, regla, workflow) que actúa por la MISMA API pública del
provider; la ruta de llamada no se bifurca, la concreción semántica depende del
actor.
F0 · Doctrina
- docs/architecture/agent.md — la doctrina permanente (capítulo comparativo de
referencias + bibliografía de seguridad, máquina estados=verbos, contrato de
participación, fila §0, a11y, amenazas).
- docs/process/{PLAN,TRIAGE,INFORME}-agent — plan de ejecución con todas las
decisiones firmadas (D-AG.1–11 + ⚖️1/2/3), triage de 4 revisores externos, e
informe autocontenido para revisión externa.
F1 · Motor ($libs/actor + arts/agent, 24º arte)
- EngineAgent puro (sin DOM/runes, portable a servidor): máquina D-AG.4
(suggest/review/auto · escalated con reason+timeout · returned outcome-tipado
· kill switch · autorización journaled aunque sea auto), tool-loop D-AG.5
(acts secuenciales, fallos→verbos, unknown-capability acotado, idempotencia
por callId, presupuestos acts/turns/wall-clock vía puerto de timers), techo de
autonomía por origen no confiable (F8b).
- ActiveAgent (sesión reactiva, contrato ActiveEngine) · protocolo v1 espejo
AG-UI (5 categorías + dirección tipada + reservas) · ScriptedAgentTransport
determinista (adapters/ fuera del barrel) · journal WAL + puerto de trazas
OTel · emisor sium→JSON Schema.
- Acuñación del actor (⚖️2/F6b): ActorToken opaco en $libs/actor (hoja bajo
orca/agent), registro privado WeakMap — los forjados resuelven a null;
costura `actor?` en TriggerOptions→SemanticSignal (runtime copia verbatim;
sema no resuelve). defineActiveAgent (service-factory app-level, timers del
core — cero setTimeout a pelo).
F3 · Aura — primer componente del eje (ruta 9 fases 0–6)
- El materializador canónico de `delegate` (reservado en scene §F6): morfo con
los eventos del ciclo (offer/escalate-untilAction/return + sustain-processing
stateBound) — PRIMER morfo del ecosistema que emite familia delegate; provider
soma que observa un puerto estructural (sin importar $agent — degradación
total); orb eidos = aurora $scene modulada por estado (§F6) con fallback CSS
= render de reduced-motion; live region única atribuida (WCAG 4.1.3); cancel
compone Button (asChild). Demo v2 sobre el MOTOR REAL con compuertas
deterministas. Orb-size derivado de la primitiva Avatar (32/40/48).
D4 · Promoción uix.scene (Aura llegó)
- defineEngineScene + superficie ActiveUix/ActiveEidos/contracts; el orb prefiere
el motor compartido (presupuesto de escenas global) con fallback por-superficie.
Gates: arts/agent 18/18 · aura 6/6 · arts:check 24 · eidos battery + recipe
30/30 + eidos-lint aura 0 inválidos · morfo:vocabulary + esquemas · smoke aura
PASS · verificado en navegador (ciclo completo + estampa delegate en DOM +
aurora WebGL pintando). Los tokens de recipe base.ts + CSS generado ya entraron
en HEAD vía una sesión concurrente.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
647db398ee |
blocks(plan): enmiendas E-1..E-5 firmadas — F1=8 (+nav-tree), F2=14, paridad=v1
Firmas del usuario (2026-07-21) tras el dossier de referencia: - E-1: componente canonico nav-tree (F1.8, data-driven — precedente Menubar) resuelve la colision sidebar-app vs arbol-docs/B-5; fiche nueva con fase 0 obligada contra nuestro tree-view; docs-shell compone NavTree. - E-2: suelo de paridad del dossier = alcance v1 (regla en intro F1). - E-3: F2 pasa de 10 a 14 (banner · team · contact · content-section); fiches nuevas + galeria actualizada (verificada en navegador: 28 badges). - E-4: distribucion registry/llms.txt/MCP → iniciativa propia (next-features §9; ese archivo sigue fuera del commit por diff foraneo). - E-5: ⌘K = listener app-land documentado (nota en F4.1). Doctrina: promotion path de architecture/blocks.md actualizado a 8. Verificacion: docs:check 0/0 (512). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
0697596046 |
blocks(tier): F0 · infraestructura del tier (doctrina + $blocks + guard + galería)
Tier nuevo aprobado 2026-07-21 (D-BLK firmadas; D-BLK.1 enmendada: vive en src/uix/blocks/). Plan de ejecución completo en docs/process/PLAN-blocks.md. - docs/architecture/blocks.md — doctrina E1 (regla de admisión canon-vs-block, contrato B-1..B-11, promotion path); espejo de packs.md - alias $blocks → src/uix/blocks (vite + svelte.config + tabla CLAUDE.md) - src/uix/blocks/README.md — mapa del tier + template de README de block (B-9) - scripts/blocks-check.ts + npm run blocks:check — guard mecánico del contrato B con self-test de detectores (fixtures inline; 5532 archivos de canon/arts/libs/packs escaneados para dirección de imports) - web/routes/blocks/ — galería índice del catálogo planificado + layout de bootstrap propio (+layout@.svelte, espejo mínimo del de /uix sin packs sema) Verificación: docs:check 0/0 (511) · svelte-check 76E/51W = baseline exacto · blocks:check verde · galería comprobada en navegador (a11y-tree + estilos computados en light y dark; captura de píxel bloqueada por panel suspendido). Fuera del commit: fila E1 en docs/README.md y §8 en docs/next-features.md (diff foráneo de otra sesión sin separar — ver registro del plan). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
63b371d5ce |
feat(packs+text): incorporate the animation collection — Ambient pack + text-effects family + docs
Two streams, split by what the animation touches:
STREAM A — decorative backgrounds → the pack tier
- arts/scene: a consolidated scene runtime ($scene) that owns, once, the
citizenship every ad-hoc background reinvented or skipped (frame loop,
off-view pause, DPR cap, mandatory reduced-motion, WebGL context
loss/restore, scene budget, teardown). SceneDom port (adom satisfies it),
webgl/webgl2/canvas2d drivers + a custom-pipeline extension (vertexShader +
draw + glContext.depth/dprCap) for real geometry (beam, particles, dither,
grid, eter, pixel-blast, hyperspeed). 32 effects as shared resources.
- src/packs/ambient: the first pack — <Ambient effect="…"> mounts a registered
effect; the P contract (P-1..P-6) guarded by scripts/packs-check.ts; colors
are token-aware (P-4). One-way dependency, removable-by-construction.
- resolveToken extended to semantic color slots (--color-{role}-{slot}) so
consumers resolve theme tokens to concrete colors (the P-4 half).
STREAM B — animations over real text → canon
- Six components (count-up + text-{gradient,circular,blur,focus,scramble}):
each a morfo + eidos recipe (where there's styling) + demo. CountUp is a
service component (counts through uix.format.numbers). The five Text* are
passive decoratives. Upgrades over the seeds: SR hardening (real text
visually-hidden + aria-hidden decoration), a11y fix (no fake role=button),
measurement discipline (cached rects via dom.measure, no reflow storm),
reduced-motion, ecosystem citizenship (eidos.dom/timers, no raw platform).
- MorfoElement gains 'p'.
DOCS
- docs/architecture/packs.md (pack tier, admission rule, P contract, Aura
promotion path); docs/decisions/design-text-effects.md (the family design
record) + indexed in decisions.md / README.md; glossary entries
(scene/Ambient/Aura/text effects); scene README custom-pipeline + authoring
bridge; motion-guide content-effects note; strata tables acknowledge packs.
Gates: component:audit 141/0/0 · docs:check 0/0 · scene tests 9/9 · packs:check
0/36 · check 0 own errors. Verified in browser (32 effects mount+compile;
6 text components SSR+hydrate, CountUp re-formats by locale live, TextGradient
resolves token stops to OKLCH via var()).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
b55ca6eecb |
fix(cleanroom): F2+F3+F4-C+SEM-4s1 — lote mecánico, censos con guard, corpus documental y el close polimórfico de los pickers VIVO
F2 — lote mecánico (13 ítems): - DEP-2 ogl eliminado (0 imports) · DEP-1 clsx inlineado como toClassString propio + suite de contrato (props.test.ts; soma.md §12 cerrado). - THM-7: los 5 selectores manuales de sema.md reescritos con semaSelector (los ejemplos [data-toast-root] apuntaban a un part INEXISTENTE — la deriva que el builder previene, demostrada en el propio doc). - MOR-1 escape isomorfo + validación de attr-names en semaSelector + 9 tests (selectors.test.ts, matches() real con comillas/corchetes) · MOR-2 partMarkerAttr = única fuente compilador↔builder + test de paridad · MOR-3 _resetCompileCache borrado (0 usos). - SOM-2 keydown continue en match sin handler + keyboardFixtureMorfo · SOM-1 no-await de handlers (censo async = 0; contrato V1 cumplido) + pin. - SEM-2 trigger pre-attacha catch con logger (void trigger sin unhandled rejection; throw intacto para awaiters) + pin · SEM-3 fallback muerto de applyDominance → skip defensivo + timer tope de awaitExpression cancelado · SEC-1 adjudicado YA implementado (assertCssVariableValue desde 2026-05-11) + pin del path de VALOR. - accordion → outline (§32; su outline:none dejaba CERO anillo en HCM) — verificado en vivo · THM-6 radius-full 9999px · EID-4 recuentos 33. F3 — censos con guard: - SOM-3 cerrado: announcer + image-provider migrados a scheduler-preferred (consumidores cableados: date/time-field vía soma.uix.timers; avatar/image vía eidos.timers — verificado en vivo); guard de timers ENSANCHADO de soma/components a TODO soma y pasado a EVIDENCIA (setTimeout exige .schedule( en el fichero — layers/ y datetime/ escapaban del ámbito viejo). - THM-5: R-4.7 nueva (válvula same-line /* important: <razón> */, escaneo comment-blanked) + las 15 declaraciones anotadas con su razón + canon recipe-contract §3/§4. - SOM-4 adjudicado: el censo/guard YA existían (49 pins); knob/mask-field/ timeline pinneados (overrides documentados en call-site); media-player Batch-4 (35 hits, cero renderProps) = único batch restante, registrado. - THM-4 doctrinado en eidos.md §unused (comportamiento/composición = legítimo; deuda = eje visual sin consumidor; hotspots por lotes). F4-C — corpus documental (decisiones de usuario aplicadas): - DOC-3: los 15 enlaces muertos resueltos (repoint a la edición FINAL trackeada / des-link históricos) · docs:check I6-links WARN→ERROR. - DOC-1: tabla «Build contract» MIGRADA a component-guide con estados modernizados (A3–A5 → LIVE + guards de hoy); banners reapuntados; citas de CANON/sema.md historificadas; lápida-redirect en el §13 del fósil. - DOC-4: hold chain → holds.ts · FAQ event:* SUPERSEDED por signatures · gradient añadido a los DOS capstones (sextet real) · nota de paleta de demo-authoring corregida (universalPaletteDecls + decisión THM-2 = mecanismo universal como sucesor del tracker borrado). - DOC-5/6: recuentos anti-frágiles datados · §4.11 dup → §4.12 · Known gaps historificado · N-6/N-7 recuperadas de git (d68d2c45^) y canonizadas en eidos.md §pickers · authoring E2 → canon/tsc.md · air-old des-linkado · EID-3 (placement) en la fila RTL · AUX-2 disabledDom documentado. SEM-4 sesión 1 — el close polimórfico de los pickers, VIVO (D.11): - Reconciliación: los morfos ya no declaran close (delegated al Popover, de-dialoged 06-27); el agujero real era el cierre programático bypaseando dismissWith → save/cancel/select eran perceptualmente SILENCIOSOS. - Fix: PickerShellHandle.setPopoverDismiss + closeWith(cause) en los 5 providers (14 sitios; select/commit → 'save' = commit.save+fulfill, cancel → 'cancel' = emerge; fallback raw para headless) + UN inyector en el eidos PickerShell root (norma N-8). Picker genérico fuera a propósito (ya suena commit-set/cancel por diseño S9). - Verificado en vivo (date-picker): Done → close·commit·fulfill·active · Cancel → close·emerge · cierre real. Gates: matriz 141/141 (los 6 morfos nuevos de la pista de texto paralela también PASS) · contracts 38/38 · eidos 314 · sema 178 · morfo 94 · docs:check 0/0 con I6 en error · baseline propio 57. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |