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 }
33 Commits (1f8ce293bb3234790b1986f5d9f8f950a21dc5c4)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
43b3e8a501 |
uix(theming): correcciones de la revisión de F2-B — el empate no se resuelve de oficio
Auditoría de la sesión con medición. Cinco correcciones; la primera toca código y es la que importa. 1. **`link-preview`: RESTAURADAS `font-family` y `line-height`.** Las retiré como «declaraciones muertas» apoyándome en los precedentes de `table.selected-row-fg` y del trigger de `gradient-picker`, pero **no son el mismo caso**: allí perdían por ESPECIFICIDAD, aquí empatan a (0,1,0) con el plano `[data-depth='overlay']` y decide el ORDEN DE CARGA. Retirarlas no es neutral — **fija el empate a favor del plano** y con él su 1.25 donde la receta pide `--leading-normal` (1.5), que es justo la decisión que §12.9 tiene pendiente para VEINTE componentes. Un empate por orden de carga se registra, no se resuelve dentro de un commit de tokenización. Y la medición era más débil de lo que el commit daba a entender: estable en cinco recargas **en DEV**, sin verificar en un bundle de producción. Restauradas → **0 diffs contra el estado PRE-SESIÓN** (550 valores × 5 tallas), censo de vuelta a 100 %, y las dos adjudicadas en el ledger con esa razón. §12.9 y el README dicen ahora que el par se deja declarado A PROPÓSITO. 2. **Marcas `R-5.x exception:` en 16 READMEs.** El gate del plan es «censo al 100 % o excepción escrita», y la válvula que `component-audit` lee es esa marca (`readmeException`), no la prosa. Tenía la razón documentada en veredicto, README y commit, pero sin la marca: cuando F3 active R-5.1/5.2, los 16 habrían dado error. Una marca por clase medida — forward de paleta THM-2 · conmutador de dos fuentes · eje propiedad de una capa · identidad de layout. Los tres al 100 % (`code`, `label`, `link-preview`) no la necesitan. 3. **Continuidad restaurada.** El handoff seguía diciendo «45 %» y «empieza por grid-list», y `PLAN §8` no tenía una sola línea de la sesión pese a que el protocolo lo pide por componente: una sesión nueva habría arrancado con el mapa viejo. Reescrita la agenda de `CONTINUE-theming.md` (cifras, la revisión adversarial pendiente COMO PRIMERA TAREA, la cola restante, las 8 firmas), registrado el bloque en `PLAN §8` con su tabla de 22 commits y sus seis lecciones, y actualizada la memoria del eje. 4. **`recipe-contract §4`** documenta las SEIS capacidades que R-5.4 ganó en el bloque, cada una nombrando la forma en que el instrumento mentía, más la lección que generaliza: una sonda que inspecciona pocos nodos pasa en falso — `picker-shell` medía CERO y reportaba éxito. 5. **«un paso por debajo desde lg» → «desde md»** en `tree-view`, `virtual-list` y `virtual-grid` (README + ficha). El escalón empieza en md (md→sm); los valores eran correctos (0 diffs), el texto no. Lo que esta revisión NO arregla, y queda como primera tarea del eje: **F2-B sigue sin su revisión adversarial §7.7**. Sus 21 componentes llevan sólo la medición de quien los escribió, y el plan la exige antes de cerrar un bloque. El handoff nombra las dos hipótesis concretas que hay que atacar. Gates: censo global 50 % · `--names` DESVIADAS 0 sobre 3.993 claves · `component:audit` 162 PASS / 4 NEEDS-WORK (los cuatro preexistentes, ninguno con un commit de esta sesión en su código) · R-5.4 verde en los 22 · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero limpio. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
2 months ago |
|
|
679dfdb291 |
uix(theming): revisión adversarial de F2-A — 8 hallazgos de 34, y nace R-5.4
Pase escéptico del bloque (§7.7, molde Sidebar) con medición propia: worktree
con SOLO el CSS revertido a la base del bloque (DOM de demo constante — prueba
más dura que la del autor), sonda x2 por componente para fijar el suelo de
ruido. 34 hipótesis · 26 refutadas · 8 reales.
Lo que NO se pudo romper: el «default idéntico» aguanta en los ocho (0 diffs;
command 8.091 · table 13.050 · media-player 3.277 · gradient-picker 609 ·
listbox 2.813 · carousel 2.842; los 3 de tree-grid y los 5 de feed salen
idénticos corriendo DOS veces el mismo código → instrumento); 0 tokens
huérfanos de 336; el bundle ES 1:1 con los primitivos; el «verificado a mano»
de gradient-picker era verdad (15/16 alcanzan al montar); sin doble animación;
eidos-lint 0 invalid.
Los 4 de código, arreglados con diff de computed VACÍO (re-medido: command
8.091 y carousel 2.842 valores, 0 diffs):
1. command: el re-point de `--command-radius` dentro del Dialog era una
declaración muerta con comentario falso — la misma regla pone el radio a 0
y el input lee `--command-input-radius`, nunca este token (medido:
`--dialog-content-radius: 9999px` no movía NADA; `--command-radius: 77px`
movía la paleta inline y no la del dialog). Retirado; comentarios y README
reescritos con lo medido.
2. carousel: la regla vertical del indicador activo llevaba el `2.25` a pelo
mientras la horizontal leía `active-indicator-scale` — el token alcanzaba
media superficie (scale:6 → horizontal 18→48px, vertical 18→18px). Ahora
lo leen las dos.
3. carousel: el trío `item-gap*` no podía ganar NUNCA — soma estampa `gap`
INLINE desde la prop (lo necesita para el flex-basis; reglas casadas por
CDP: la de la receta y encima INLINE gap:0rem). Retirados los 3 tokens y
las 2 declaraciones muertas (la base y el gap:0 de ghost): 0 diffs, que es
la prueba. Canal de valor de soma, como --gp-current-gradient. Censo
carousel 35 knobs · 23 públicos · 77 % · 27 claves; global 43 % intacto
(5.190 · 2.102).
4. table: `striped-row-bg` no alcanza NADA en la demo publicada —
`:nth-of-type(even)` cuenta los <tr> de RowDetail y las filas de datos
quedan impares (medido tr a tr: diez inmunes). Arreglarlo mueve píxel →
PENDIENTE DE FIRMA (candidato `:nth-child(even of [data-table-row])`);
adjudicado en el ledger mientras tanto.
El instrumento cargaba 22 falsos negativos de 26 «no effect», con tres causas
medidas, y la cuarta era de la sonda:
- el paso de abrir hacía CLIC en el input, y un clic de ratón sobre un input
de texto SÍ casa `:focus-visible` — la regla de foco pintaba el borde y el
token de reposo leía muerto (era el «falso negativo sin causa» de §13);
- el override se escribía solo en `[data-{c}]`, y las partes que cuelgan de
`{c}-root` no lo recibían;
- `::before`/`::after` eran invisibles (el aro de buffering, el spinner);
- la no-determinación de feed no era una animación sin localizar: era la demo
aún CARGANDO — el auto load-more mantiene [data-busy] ~3 s y la firma
`commit-settle` anima box-shadow sobre el nodo medido. Con la espera de
asentado: 0 diffs y 0 nodos ausentes sobre 6.264 valores, dos corridas.
Con eso NACE EL GUARD R-5.4 (la deuda que §13 pedía desde gradient-picker, y
que carousel convirtió en patrón — un estilo inline de otra capa es invisible
para todo análisis estático): `npm run theming:sentinel -- <c> <url>`
(scripts/theming-sentinel.ts, promovido de `__`; blur tras abrir, override en
todos los nodos del componente, pseudos, pase de hover, settle) + ledger
scripts/theming-sentinel-exceptions.ts con UNA razón medida por token — muerto
sin adjudicar = exit 1, excepción STALE = aviso. Los ocho componentes en
verde: command 50/57 · table 41/45 · media-player 46/61 · tree-grid 45/47 ·
gradient-picker 9/25 · listbox 20/21 · carousel 26/27 · feed 43/50, con 46
excepciones adjudicadas. El guard ya me corrigió a mí: cinco entradas mías
salieron STALE (los control-height-* de media-player SÍ alcanzan) y las borró;
y dos muertos nuevos los produjeron los propios arreglos (el blur apaga
focus-input-border; el settle desmonta el spinner de feed) — adjudicados con
su porqué. Doctrina en recipe-contract §4.
También: tres cifras de especificidad de next-features §12 recontadas (la
banda de tree-grid es (0,8,0); la tinta seleccionada del arquetipo es (0,4,0);
y el `item` de table vive en la fila Y EN LA CELDA — elementsFromPoint pone el
velo en la celda), el comentario de listbox que mandaba pisar el privado
`--_listbox-max-height` apunta ahora al público, y quedan registrados en §13
los dos `header-z` con literal '2' y el spinner de media-player a 720ms
mientras el gemelo de feed lleva tokens.
Guards: censo global 43 % intacto · audit --only command+carousel PASS ·
eidos-lint 0 invalid (command 16/12, carousel 29/9, listbox 12/7) · vitest
eidos 434/435 (el rojo es el conocido skin-media-player) · rtl 0/180 · docs
0/813 · check 0 errores en tocados (72 globales de otras sesiones) · prettier
limpio en lo nuevo (los desvíos de command.css/carousel.css ya estaban en
HEAD y no se tocan).
Pendiente de firma (CONTINUE §«PENDIENTE DE FIRMA»): el striped inerte, el
arquetipo en fila+celda, y que soma lea el gap de un token.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
bd032534be |
feat(census): los primitivos tipográficos se miden contra su capa — pieza 0 de F2-A
D-TH.2-b, la firma que va ANTES del bloque porque cambia el suelo del censo: medirla a mitad de camino falsearía todos los antes/después. `heading` marcaba «36 knobs, 0 % alcanzable» y eso no era deuda: era una lectura equivocada. Su receta resuelve cada eje como `var(--_heading-font-size, var(--style-h2-font-size))` — el privado es el escape POR INSTANCIA que el wrapper escribe desde una prop, y el named style es la superficie de tema, ya pública y viva. Acuñar `--heading-*` para espejarla sería un alias por eje × nivel: la clase que mató la purga del changelog §39. El criterio de qué ES un primitivo tipográfico se midió, no se supuso: o la receta SELECCIONA por `data-style` (`heading`, `text`, `s-text` — el named style es su API) o está atada entera a UN style (`code` → `--style-code-*`, `display` → `--style-hero-*`, `label` → `--style-label-*`). `code-block` queda FUERA a propósito: lee un par de tokens de style para su texto pero posee cromo de caja real, y eso sí es suyo. Detalle que costó una vuelta: la comprobación va ANTES de la de privado. Leer el privado primero puntúa el primitivo entero como inalcanzable cuando es completamente temable — por la capa que lo posee. antes 5.199 knobs · 1.837 públicos (37 %) · 894 privados · 232 sistema después 5.199 knobs · 1.837 públicos (38 %) · 788 privados · 338 sistema **Gate**: 106 knobs pasan de `private` a `system` en los SEIS primitivos y **cero** de los otros 156 se mueve — verificado componente a componente contra la versión en HEAD, no a ojo. Alcance `—` (nada que poseer) para heading, text y display; `s-text` al 100 %; `code` y `label` bajan a 7 y 2 knobs de deuda real. Informe regenerado (171 veredictos a mano intactos), veredicto de heading anotado como ejecutado, y recipe-contract §1 gana el párrafo que fija el criterio — incluida la frontera: un menú que lee `--style-label-font-family` para una etiqueta NO es un primitivo y no entra en el conjunto. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
7781d6e4c8 |
feat(audit): R-5.3 — la gramática de los nombres deja de ser prosa
El canon de nombres llevaba meses documentado y sin guard, y la medición del
2026-07-01 ya decía qué le pasa a un canon así: deriva entre el 30 y el 85 %.
Había derivado. El codemod de ayer lo normalizó; esto es lo que impide que
vuelva.
R-5.3 valida una FORMA, no una lista — ahí se separa del guard de eventos, que
comprueba pertenencia a un vocabulario cerrado. La forma es: tinta = `fg`,
modificador interactivo DELANTE, y detrás lo dimensional y contextual.
No reimplementa la gramática: la consume de `theming-census --names`, que es
la misma fuente sobre la que corrió el codemod. Dos implementaciones de una
gramática son dos gramáticas que acaban discrepando — y este repo ya pagó esa
factura con `commit-resize`, un hook muerto tres meses en una receta con todos
los tests en verde.
Entra en `error` directo, sin rampa `warn`, porque su deuda murió en el mismo
pass (precedente R-4.4). Las claves en cola de migración a la capa de estado se
reportan APARTE: no son deuda de nombre, son knobs que van a desaparecer, y
renombrar lo condenado es churn.
Muta-prueba de tres caras, que es lo único que distingue un guard de un guard
que pasa sobre el vacío:
`-bg-hover` con valor de acento → ROJO
`trigger-color` → ROJO
`primary-solid-hover` → VERDE (canónica: COLOR_ROLE_SLOTS pone
el modificador detrás por construcción)
La tercera es la que importa: es el fallo que un codemod ingenuo habría
cometido sobre las 47 claves de rol, `button` entero incluido.
Doctrina en el mismo pass: recipe-contract §1 gana las dos filas que le
faltaban (tinta y estado) más la frase que las gobierna y las dos familias con
gramática propia; §4 gana la fila R-5.3; theming §6.7 una nota fechada que
acota el principio de plataforma del px/py a los ejes dimensionales. El
checklist de cierre declara la regla — lo cazó `docs:check` con su propio
guard I5, que exige que toda regla del audit esté declarada allí.
Lo que NO entra, y por qué: el tercer muro (el tipo en `defineRecipes`, molde
`PhysicalAxisKey`) está escrito y probado, y dispara sobre 17 claves — los
hovers neutros que la firma 3 manda migrar. Meterlo hoy rompería `npm run
check` a todo el mundo por una deuda que ya tiene dueño y fecha. Entra cuando
la migración a la capa de estado las vacíe; son dos líneas entonces.
component:audit 163 PASS · 3 NEEDS-WORK (badge, mockup, motion — los tres
sin tocar por esto, R-5.3 pasa en los 166)
docs:check 0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
a11805d434 |
docs(eidos): la doctrina alcanza a la jornada — dos guards nuevos, el patron de capa, y EID-3 deja de estar pendiente
Barrido de lo que la sesion cambio y la documentacion todavia no decia. `testing-and-tooling.md` — los dos guards nuevos entran en la tabla de «que atrapa cada script», con su reparto explicito: `layer:check` mira el valor computado (quien gana la cascada) y declara su hueco (la geometria, porque `getComputedStyle` da el valor USADO y un `inset: auto` se lee como pixeles); `shared-layer-contract.test.ts` mira el texto, y existe por lo que el navegador no puede ver — un `env()` ya sustituido devuelve `"0px"` en escritorio. `component-guide.md` fila RTL — deja de remitir a «EID-3 exception» y enuncia la regla: dos rejillas NOMBRADAS, `Position` fisica y `LogicalPosition` logica, y la pregunta que decide entre ellas («¿tiene que voltearse para un lector de derecha a izquierda?»). Estrechar con `Extract<>`, nunca redeclarar. `canon/recipe-contract.md` — la fila de z-index flotante distinguia mal: la banda `--z-index-overlay-*` es de overlays PORTALED. El cromo fijado al viewport que no portala es otra cosa y tiene su peldano (`affix`, 150). Y el item 9 del checklist de recetas decia «si flota → una rung de overlay», que era incompleto. `eidos/components/README.md` — el patron de CAPA COMPARTIDA, que no estaba escrito en ningun sitio pese a tener dos ejemplares vivos (`list-surface` y `affix`). Sus cuatro reglas, tres de ellas aprendidas rompiendose: enganchar en el attr de capa y no en la identidad (o `morfo-check` suelda al consumidor a un contrato ajeno), un eje = token publico + ranura, el puente reafirma `position` si el primitivo compuesto declara uno, y se guarda con dos redes porque ninguna basta sola. `audit-active-uix.md` — EID-3 pasa a RESUELTO, conservando el hallazgo original debajo. Era un P3 de julio cerrado «como excepcion» pero marcado en su propia tabla resumen como «pendiente de doctrina explicita». Ya no lo esta. `PLAN-affix.md` §7 — lo que vino DESPUES de cerrar el plan, que es casi todo lo interesante: las dos migraciones y sus dos lecciones, el peldano de z, el patron de tokens, la canonizacion de la rejilla y los dos guards. Ninguna estaba prevista en el plan; todas salieron de auditar lo construido. docs:check 0/627. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
7ea913ab11 |
refactor(eidos): la rejilla de colocacion logica se canoniza — cinco declaraciones a mano pasan a una
`lib/types.ts` ya tenia `Position`, la rejilla 3x3 FISICA, con su idioma `Extract<Position, …>` y sus consumidores (Dialog, Drawer, Toast). Lo que no tenia nombre era la otra: la LOGICA, escrita a mano cinco veces — `AffixPlacement`, `FabPlacement`, `MenuDialPlacement`, `OnionPlacement` y las cuatro esquinas de `AvatarBadgePosition`—, coincidiendo por mantenimiento manual y no por contrato. Anadir una zona a una no llegaba a las otras cuatro. Ahora hay `LOGICAL_POSITIONS` / `LogicalPosition` junto a `POSITIONS` / `Position`, y las cinco estrechan desde ella. Ambas pasan a ser consts para que `docs:vocabularies` las genere: eran las unicas props visuales compartidas que faltaban en `docs/canon/vocabularies.md`, que ya listaba tamanos, variantes, paletas, arquetipos y el vocabulario sema entero. NO son dos grafias de lo mismo, y por eso ninguna sustituye a la otra: son dos COMPORTAMIENTOS. `top-left` es la izquierda de la pantalla y no espeja nunca; `top-start` sigue la direccion de lectura — medido hoy, de `left: 0` a `right: 0` en RTL. La regla de eleccion queda escrita en los dos typedoc y en el canon generado: ¿tiene que voltearse para un lector de derecha a izquierda? Una tira fijada en `bottom-end` va en el borde de salida en ambas direcciones; un panel que se abre a la derecha fisica porque ahi hay sitio, no. Con eso se cierra EID-3, que el audit de julio dejo asentado como excepcion pero marcado «placement fisico — pendiente de doctrina explicita». La doctrina ya no esta pendiente: son dos rejillas nombradas con la regla de cuando usar cada una. Sin cambio de comportamiento: las uniones resultantes tienen los mismos miembros (`MenuDialPlacement` = la rejilla + `static`; `FabPlacement` = las cuatro esquinas + `static`; `OnionPlacement` y `AffixPlacement` = la rejilla entera; `AvatarBadgePosition` = las cuatro esquinas). check 69 = base intacta · docs:check 0/627 · component:audit los cinco PASS · layer:check 0/3 · rtl 0/178 · smoke 311/311. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
0d10b983e5 |
revert(sema): la libreria de sonido vuelve byte a byte — la retirada no era de esta sesion
Revierte la mitad D10 de `5571f3bf7` por orden del autor. La sesion era normalizar nombres de evento; la retirada de los 3 resolvers de gesto nacio de perseguir un homonimo que cree yo mismo al renombrar `SoundDirection` → `SoundContour` sin mirar el namespace de destino, y se ejecuto sin leer primero el handoff vivo del eje (`CONTINUE-sound-engine.md` — la memoria del proyecto lo marca PRIMERO) ni `PLAN-audio-player-v2.md` (gate firmado), que ni siquiera sabia que existia. La verificacion posterior salio limpia — ninguno de los seis documentos no leidos nombra los resolvers — pero limpia por suerte no es limpia por metodo. Restaurados byte a byte a su estado anterior al borrado (diff contra `5571f3bf7^` = 0 lineas): - `sema/sounds.ts` (160 lineas otra vez: resolvers, `DragSoundParams`, `SoundContour`, `clamp`, `lerp`) · `sounds.test.ts` (los 2 tests de arrastre) · `exports.ts` (los 5 re-exports) - las 4 prosas que se habian reescrito describiendo el borrado: `sema/types.ts`, `sema/components/splitter.ts`, `soma/splitter-provider.svelte.ts`, `arts/motion/types.ts` - `scripts/docs-vocabularies.ts` + `docs/canon/vocabularies.md` regenerado - la nota fechada que se añadio al §4 de `PLAN-sound-engine.md` Lo que NO se revierte, porque no depende de la libreria: D9 (el acarreo de chronos) y el guard de eventos inertes — verificados en verde tras el revert. El ledger deja constancia: D10 pasa a CONFIRMADO (retirada REVERTIDA), con el analisis conservado como evidencia PARA la sesion propia del eje de sonido — `git log -S` (un solo commit en la vida de los resolvers), `book-deviations.md` dandolos por borrados el 2026-08-06, los dos planes cerrado/rechazado — y la regla explicita: sounds.ts no se toca sin una sesion que empiece por su handoff. Verificado: sema 300/300 (con los tests restaurados dentro), guard de eventos inertes en verde, `check` 70 errores (linea base), `docs:check` 0/623. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
5571f3bf7e |
feat(soma,sema): el acarreo de chronos habla, los 3 resolvers se van, y un guard cierra la clase
D10 · no era una decision abierta: era una firmada sin ejecutar
---------------------------------------------------------------
Mi correccion de mi propia propuesta estaba sobre-corregida: lei la linea 95 de
un plan y la 367 de otro, no los documentos.
- `PLAN-sound-engine.md` esta CERRADO (2026-07-30) y su columna «sema conserva»
miente en 4 de 5 celdas: `SOUND_LIBRARY` no existe, `SOUND_TUNINGS` se retiro
el 2026-08-06 (con guard que lo mantiene retirado), el sonido salio de la
cascada, y «que firma para que familia × intent» lo sustituyo la ley del
nombre. Es un inventario del 30 de julio, no un mandato.
- `PLAN-audio-player.md` esta ⛔ GATE RECHAZADO — SUSPENDIDO, y su cabecera dice
«nada de lo de abajo se implementa».
- La fuente que gobierna es `docs/decisions/book-deviations.md`, de DECISIONES,
que el 2026-08-06 escribio en pasado: «un gesto continuo suena por REPETICION …
y los tres resolvers se borraron con el». `git log -S` sobre `sounds.ts`
devuelve UN SOLO COMMIT en toda su vida (mayo): nunca se borraron. El registro
dio por ejecutado lo que no se ejecuto.
Retirados los tres, `DragSoundParams`, el `SoundContour` de sema, sus exports,
los dos tests que solo se probaban a si mismos y los helpers `clamp`/`lerp` que
quedaron huerfanos: `sounds.ts` pasa de 160 a 79 lineas. Con ellos cae el
homonimo `SoundContour` — el nombre queda para el art, sin renombrar nada. El
plan cerrado no se reescribe, pero su §4 lleva ya una nota fechada porque me
engaño a mi.
D9 · el acarreo habla
---------------------
`handle` pregunta «¿estoy manipulando directamente este objeto?», y una familia
que no responde su propia pregunta ha fallado en lo unico para lo que existe
(CANON §2). Chronos es el componente mas manipulable del catalogo y hablaba solo
el suelte: el acarreo entero estaba mudo. El *handle invisible*.
No se autora nada — `handle` ya declara `sounds: { default: 'step' }` y los dos
canales, asi que el RITMO es el sonido. Estrangulado a ~14 Hz como slider y
splitter, y ANCLADO: los destinos son partes repetidas, asi que es aplicacion
directa de la ley de `e5811e422`.
Donde el acarreo NO es de chronos: arrastrar un chip con puntero pasa por el
`DragDrop` compuesto, que declara tres eventos y emite tres, sin acarreo continuo
(CANON regla 6). Dos componentes narrando un gesto seria peor. Asi que
`handle-drag` es el acarreo de TECLADO y `handle-resize` cubre las dos vias.
Medido: el arrastre del tirador con puntero da 6 `handle-resize` en 6 pasos,
todos sobre `event-resize-handle`.
El guard, y dos hallazgos que salieron de medirlo
--------------------------------------------------
`book-deviations.md` tipifica el defecto hermano como «mecanicamente comprobable
y merece guard». Este es la misma forma un piso arriba, y es la clase que produjo
D1, D3, D4 y D9. La prueba NO es `trigger('literal')` — chronos despacha por
variable y eso me dio dos falsos negativos durante la auditoria; se comprueba que
el nombre aparezca como literal donde el provider alcance: su directorio soma mas
las librerias compartidas (sin `$libs/selection` en el ambito, combobox y select
se leen como inertes y no lo son).
D13 — quedan 7 eventos inertes en 4 componentes, enumerados en
`INERT_EVENT_DEBT`, que no son exenciones sino la cola. El peor: `tooltip` 3 de
3, cero `trigger(` en todo su directorio, abriendo y cerrando sin firma alguna.
D14 — midiendo D9 salio un defecto anterior: `restoreChipFocus` reenfoca en un
`queueMicrotask` que corre ANTES de que Svelte reponga el nodo, asi que enfoca la
nada y el foco cae al `<body>`. La segunda flecha ya no llega al manejador y el
Escape tampoco: la interaccion que el propio componente anuncia («Grabbed. Arrow
keys move, Enter drops, Escape cancels») muere al primer paso. No lo toco aqui —
es de foco, no de firma, y merece su propia medicion en navegador real.
Verificado: guard visto fallar deshaciendo D9 (caza los dos eventos); `check` 70
errores en linea base; 2250 tests del uix — los 6 fallos de `contracts.test.ts`
siguen siendo de la otra sesion; `docs:check` 0/623 tras regenerar
`vocabularies.md`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
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 |
|
|
968fff9cdf |
feat(morfo,soma,eidos): chronos deja de tener partes que el morfo no conoce
C3 cierra con `more-link` y C4 con los seis marcadores que eidos estilizaba sin declaracion. Los tres botones sin fuentes (`day-add`, `edit-action`, `search-button`) comparten una clase; `undo`/`redo` se cablean sobre la del nav; `timegrid` pasa a `time-view` porque estaba a un guion de la parte `time-grid` que vive dentro; `peek-panel` y `day-peek` quedan como partes de display puro con marcador inline. Cada una renderiza un `<button>` real cuando el morfo lo declara: una `Region` que cae a `<div>` pondria el `type` declarado sobre un no-boton. La lupa deja de ser un adorno sin `onclick`: abre una paleta `Command.Dialog` sobre TODOS los eventos, no un filtro de la vista, con `goToDate` nuevo en el provider — la generalizacion de la que `goToday` era un caso particular. El texto buscable viaja en `keywords` porque el scorer de Command lee value + keywords y NUNCA los hijos renderizados, asi que se busca por titulo, por tipo y por fecha. Y tres etiquetas que mentian en silencio: `translationRef` / `commonRef` normalizan la clave LITERAL y no consultan ningun mapa, asi que `prevMonth` / `nextMonth` / `moreEvents` y `action.undo` / `action.redo` caian al fallback ingles en una pagina `es`. Corregidas contra el catalogo, con `common.buttons.undo` / `redo` anadidas. Verificado: check 74 = base (delta cero) · 458 vitest · docs:check 0/621 · rtl:check 0/177 · eidos-lint chronos morfo-backed 32 -> 44, invalid 0 · navegador limpio en las tres vistas, con el A/B del undo medido (18 jun -> drag -> 25 jun -> undo -> 18 jun -> redo -> 25 jun) y el salto de la paleta (noviembre -> elegir Retro -> junio). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
86fa9a1b77 |
refactor(sema)!: el sonido se ELIGE, no se modula — evento.intent = sonido
Directiva del autor, sin matices: «todo el sistema de sonido es una puta
mierda... la unica solucion es evento sonido, evento.intent = sonido, y
punto y nada de mierdas de que si el intent modifica nada».
Tenia razon, y lo que lo destapo fue oirlo: los dieciseis nombres del
catalogo anterior sonaban practicamente igual. Medido despues: cinco de
ellos eran LA MISMA nota de 700 Hz a cinco volumenes, y las variantes de
direccion se separaban 20 Hz. El modelo de modulacion producia teoria
bonita y mush audible.
LA REGLA ENTERA, y no hay mas:
nombre = per-emit ?? cascada ?? pack ?? morfo ?? familia[verbo] ?? familia.default
sonido = pack[`${nombre}.${intent}`] ?? pack[nombre] ?? nada
Dos busquedas. El intent SELECCIONA un sonido entero; si no existe la
variante se desprecia y suena la base; si no hay base, silencio. Un
`threat` no es un `tick` con mas aspereza: es otro sonido —mas grave, mas
rasposo, descendente— y no se pueden confundir.
EL TIER DEL VERBO es lo que vacia los packs. La tabla de familia se
indexa por verbo (`emerge.close → 'close'`), asi que ni un solo overlay
escribe su cierre. De ~165 reglas de sonido quedan 30, en 16 de los 71
packs — y las 135 borradas no decian mas que el defecto. Para que el
verbo llegue, ahora VIAJA en la señal: se calculaba y se tiraba con un
`void` (hallazgo S-40), el tipo prometia que se emitia y era falso.
CATALOGO NUEVO: 10 bases + 6 variantes, cada una un sonido diseñado
entero. Medido en el grafo de audio real: de 240 Hz (`tick.loss`) a
1900 Hz (`step`), casi tres octavas, y las variantes evaluativas traen su
propio modulador de aspereza — texturas distintas, no un tono movido.
100% sintesis a proposito: el pack por defecto no lleva binarios, funciona
sin red y no puede dar 404. Los .wav quedan como pack de EJEMPLO.
EL GUARD QUE FALTABA: `distinguishability` exige que dos entradas se
separen en al menos DOS ejes perceptuales. Nacio en rojo contra mi propio
catalogo —ocho pares casi gemelos— y dirigio el diseño hasta separarlos.
Una instantanea no lo habria cazado: cada valor era exactamente el que la
tabla decia; lo que faltaba era una RELACION entre entradas.
GESTOS POR REPETICION (idea del autor). Un arrastre ya emite a su ritmo,
asi que toca `step` (18 ms) por emision: la velocidad del gesto ES la del
trinquete, como una rueda fisica. Mueren los tres resolvers que
sintetizaban tono desde posicion y velocidad —la ultima aritmetica del
sistema— y `handle` queda EXENTA de la memoria de frecuencia, que si no
estrangularia el trinquete al primer arrastre. Perdida firmada: el ritmo
lleva la velocidad, pero la posicion ya no mapea a altura.
NADIE ESCRIBE PARAMETROS, ni la app. `SemaSignatureOverride.sound` y
`SemaCascadeRule.sound` aceptan un nombre o SILENT. La rendija de autoria
inline «para la personalizacion» sobraba: un producto REGISTRA su sonido
(nombre + definicion, una vez) y lo nombra.
⚠️ BUG PROPIO durante la cirugia: al reemplazar el bloque del sonido borre
tambien la aplicacion de deltas de intent, y el HAPTICO dejo de recibirlos.
Lo cazaron sus tests. Restaurado — el haptico sigue modulando porque su
vocabulario es categorico y sus deltas cambian el KIND, que tambien es
seleccion.
MUERE: los deltas de sonido del intent, las bases de sonido de familia, la
capa 1.5, `op:'add'` para sonido, `mergeSoundOverride`, y
`d7-intent-survives.test.ts` — vigilaba una inversion que ya no puede
existir.
VERIFICADO: sema+morfo+soma+sound 1108/1108 · docs:check 0/618 · la pagina
`/uix/docs/sound` reescrita y medida en navegador (commit+threat →
tick.threat; commit+affirm → tick, el intent se desprecia; emerge+close →
close sin una linea de pack; sustain → silencio). Los 7 fallos de la suite
completa son los ajenos y preexistentes de contracts.test.
⚠️ NO SE HA OIDO. Todo esta medido en el grafo de audio, no con un oido.
Si `open` suena a lo que debe sonar un panel abriendose es tu decision, y
se afina en un fichero.
⚠️ El estudio `/temas/sema` compila pero edita firmas crudas: su modelo es
el viejo. Queda por reconvertir.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
769e426c5e |
refactor(sema)!: el sonido se NOMBRA — un catalogo, una palabra por componente
F1-F5 del PLAN-sound-names, por orden directa del autor. El componente
dice UN NOMBRE y nada mas; si ese nombre es una receta sintetizada o un
.wav no es asunto suyo.
sound: { gain: {op:'add',value:-0.25}, -> sound: 'settle'
centroid: {op:'add',value:2800},
decay: {op:'add',value:20} }
EL CATALOGO (src/uix/sema/sound-names.ts) fusiona SOUND_LIBRARY y
SOUND_TUNINGS en 16 nombres + SILENT: cinco niveles, cinco direcciones,
cuatro caracteres, dos samples. Los nombres NO llevan familia delante y
esa ley se retira POR SU MOTIVO: `{family}.{tail}` existia porque un
tuning era un delta sobre una familia; un nombre es una base, y el mismo
`soft` sirve a un commit y a un emerge porque la familia sigue poniendo
su identidad debajo.
EL REORDEN es la pieza que importa. Los packs corrian los ULTIMOS y en
modo replace, asi que la eleccion del componente borraba el perfil
evaluativo del intent — medido: un dialog con `threat` sonaba igual que
uno neutro. Ahora el nombre se aplica sobre la base de familia y ANTES de
los deltas de intent:
base de familia -> EL NOMBRE -> intent -> evento -> app
Con el nombre debajo, D.7 y S-07 dejan de PODER ocurrir. No se arreglan:
se disuelven. `sound-names.test.ts` lo certifica para los 16 nombres, no
para los 3 packs que un guard anterior muestreaba.
LA PUERTA es el tipo: `Sema['cascade'].sound` acepta un nombre o SILENT.
Al estrecharlo, el compilador enumero la migracion entera — 173 errores
en 70 de los 71 packs — y ninguno podia colarse. `SemaCascadeRule` se
queda abierto a proposito: `overrides.cascade` es la puerta de
personalizacion de la app, y una app puede legitimamente autorar un
sonido que el catalogo no lleva. El framework se aplica a si mismo la
regla estricta.
MEDIDO ANTES DE TOCAR NADA: 214 reglas autoraban 33 firmas distintas; 92
(43%) no decian mas que un volumen; 69 pisaban el `contour` del intent y
no podian hacer otra cosa (es un enum, solo admite reemplazo); 5 packs
llevaban un `centroid +2800` copiado que dejaba su commit en 4600 Hz —
mas brillante que `signal`, la familia mas brillante del mapa— porque era
el residuo aritmetico de un sample borrado (4600-1800).
Las colapsaciones de firma van una a una en PLAN §5.1 con su consecuencia
audible; cada una se revierte editando UNA linea. La unica grande es
deliberada: navegar un calendario sonaba a gain 0.18, el valor mas alto
de todo el sistema —mas que una alerta—, residuo de una de las diez
cabezas de tuning que mentian (S-39). Ahora es `soft.rise`.
GUARDS: `sounds-grammar.test.ts` deja de vigilar una gramatica que ya no
existe y pasa a vigilar que ningun pack AUTORE (censo sobre las fuentes,
con los comentarios fuera — la prosa que menciona la forma vieja no es la
forma vieja, y escanear texto crudo marco cinco doc blocks). El invariante
3 del censo de packs pasa de «la cabeza del tuning no miente» a «el nombre
existe». Bug propio cazado por su test: `namedSound` devolvia la entrada
del catalogo POR REFERENCIA — una mutacion de un consumidor redefinia lo
que un nombre significa para toda la sesion.
VERIFICADO: sema+morfo+sound 437/437 · check 75 = baseline exacto ·
docs:check 0/618 · suite completa 7 fallos frente a 8 de la base sin mis
cambios (los 6 de contracts.test son ajenos y preexistentes).
QUEDA F6: retirar D.7/S-07 con su motivo, reescribir sema.md §tunings y
escribir el limite fisico del WAV (playSample solo lee sampleUrl y gain,
luego sobre un .wav el intent solo mueve el volumen).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
b9d73c19b5 |
feat(sema): los toggles recuperan su CONTACTO — la voz vuelve al momento que el libro le da
Tras leer el corpus entero de sema y del motor de sonido (los tres planes, el
handoff vivo, sema.md, CANON, channels, el README del art y D.7/D.8 literales),
que es la precondicion que me estaba saltando.
1) EL CONTACTO EXISTE (directiva del autor: «1 su contact-press»)
El libro le da a un Toggle DOS momentos, y el codigo solo tenia uno:
- cap. 23 §9 empareja el Toggle con `contact.press` Y `commit.toggle`;
- cap. 22 nombra «un switch que muestra estado pressed» como contact;
- CANON §8 regla 1: «contact precede al resultado cuando hay accion directa».
switch, toggle y toggle-group declaran ahora `contact-press` (family contact,
verbo press, sequence 'pre', SIN intent por cap. 22 §11) ademas de su
`commit-toggle`. Los providers los disparan en ese orden. Solo 4 de 91 morfos
con eventos expresaban `contact`; ahora 7.
Y el reparto de la voz cambia de sitio, que es el fondo del asunto:
- el CONTACTO habla — `contact.soft` (gain 0.08, clave nueva): el «click breve
opcional» de la tabla de canales del cap. 22, un tercio de una pulsacion de
boton para que un flip nunca pese mas que una activacion;
- el COMMIT calla — `SILENT`. Es lo que pide la tabla de canales del cap. 23
(«sonido: cierre opcional»), lo que exige el Criterio 12 (ch. 7 §13) de un
control que se pulsa docenas de veces por sesion, y lo que D.5 decidio en
mayo. La consecuencia la lleva `data-state` + color, la huella de ch. 23 §3.3.
`commit.medium` —la clave que invente ayer para que sonaran— queda RETIRADA:
huerfana en cuanto la voz se mudo al contacto.
MEDIDO en navegador: un clic emite `contact-press` -> `commit-toggle`, el estado
pasa a checked, y se levantan 2 osciladores con pico 0.08. UNA sola voz, la del
contacto; el commit no sintetiza nada.
⚠️ REVERSION DOCTRINAL DECLARADA: `compile.test.ts` afirmaba, citando el ANEXO
Tabla 1, que «Toggle/Switch declara un UNICO evento; el contacto es IMPLICITO en
el gesto». El cuerpo del libro dice lo contrario donde trata el caso. El test se
actualiza dejando escrita la reversion y su procedencia, para que quien lea el
anexo sepa por que el codigo diverge.
2) D.7 VUELVE A SER CANON SIN VERSOS SUELTOS (directiva del autor)
`proof-of-human` canonizaba samples en familia `commit`: `sound()` devuelve una
firma COMPLETA y la cascada la aplica en `replace`, asi que aplastaba la
modulacion por intent que la propia entrada existe para proteger — un `fulfill`
perdia su +300 Hz / ascending / +0.05, y el fallback sintetico sonaba ciego al
intent tambien. Su cabecera afirmaba literalmente lo contrario de lo que hacia.
Ahora compone tunings con `{ op: 'add' }`: el intent conserva sus primitivos y
la regla solo aporta caracter. Un producto que quiera el ping lo registra desde
la cascada de app (capa 5b), donde un sample SI es recurso legitimo.
3) D.8 REESCRITA Y FIRMADA
La entrada decia «los packs DEBEN respetar el activeChannels del family … anadir
`sound` a `handle` tambien [es incoherente]», y el framework hace justo eso,
prescrito por sema.md §componentes continuos. La ley queda:
- `activeChannels` es el DEFAULT perceptual de la familia, no una prohibicion.
- `channels` es la palanca por evento/regla y ajusta en las DOS direcciones.
Restringir: dialog `close-after-fail`, transporte del player, VirtualList.
Ampliar: el pack del slider sobre `handle`.
- Ampliar exige justificacion perceptual escrita. La del slider es canonica: el
sonido del arrastre no es firma fija sino payload por emision — por eso
`handle` no declara `base.sound`, y por eso sema conserva TRES resolvers de
gesto cuyo unico proposito es ese sonido.
- PROHIBIDO: declarar una firma de canal que la activacion no incluye. Deja
reglas INERTES. Es comprobable por guard. Infractoras: 2 del dialog + 3 mas.
Y Announce: la propia D.8 dejo la puerta abierta («si en el futuro necesita ser
pluggable … entonces vale convertirlo en canal formal. Hoy no»). Ese futuro
llego el 2026-07-04. No fue deriva; fue no volver a escribirlo aqui.
VERIFICADO: sema+morfo 337/337 · docs:check 0/616 · check 76 = base 75 + 1 del
fichero huerfano de la auditoria · `morfo:vocabulary` limpio · prettier: el
unico aviso mio formateado, los demas ya sucios en HEAD.
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 |
|
|
46ce557836 |
docs(opts): la guia enseña el puente v2, partOpts y el reparto de callbacks
Wrapper Pattern con bindProps<XOpts> target-typed y partOpts para la parte trivial; nueva seccion «Callback conventions» (bindable→setter por construccion, evento puro→aparte); la via OptsFromProps+Preserve en Types. La nota del contrato de direccion deja de exigir el cable aparte: dir entra en la bolsa como pass-through con la llamada visible en la init. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
8d1909b166 |
feat(direction): eidos comparte el contexto, y el corpus registra el endgame
La entrada eidos se parte igual que la de soma: `activeEidosDir(dir)` devuelve la AFIRMACION (prop -> ancestro) y publica en el MISMO DirectionContext — un chart eidos-only dentro de un subarbol soma afirmado (o al reves) resuelve el mismo hecho. La cola matematica va a `resolveEidosDir(dir, eidos.prefs)`, con la vista de prefs cacheada por instancia (WeakMap): construirla dentro de una funcion pura llamada por $derived era una alocacion por pasada. createChartRtl consume las dos mitades por su lado: `attr` estampa la afirmacion cruda (ausente hereda del <html> proyectado), `current`/`anchor` resuelven la cola. Corpus: - direction-contract.md §2 pasa del campo obligatorio de 4.1 al mecanismo del morfo (declaracion -> tipo condicional -> estampado; secundarios del mismo morfo; el censo como guard), §7 documenta el contexto compartido y la tabla de entradas partidas, y el checklist §8.3 pide morfo + cable. - docs/decisions.md registra la ratificacion D1-D4 del 2026-08-05 con las cuatro decisiones y su porque. - CONTINUE-direction-runtime.md §11 cierra el handoff: tabla de fases con commits, verificacion final medida, la cola pospuesta por D4 y las trampas nuevas (pathspec SIEMPRE en rama compartida; el detector de huerfanos y la ruta del import; la vista en $derived). check 77 = linea base · morfo+direction suites 131/131 · docs:check 0/566. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
01cc13d412 |
feat(direction): el flip — el atributo es la afirmacion, y undefined ACTUA
La cadena se parte en la afirmacion. `activeDir` devuelve ahora SOLO lo que
alguien eligio (prop -> contexto del ancestro) y ahi se detiene: eso es lo que
el runtime estampa y lo que cruza portales. La preferencia de la app ya no
viaja en el atributo de cada componente — llega a la pagina UNA vez, por la
proyeccion automatica de P2, y todo lo demas HEREDA.
Por que: con prefs dentro de la cadena por-componente, "nadie afirmo" era
inalcanzable — el boot por defecto siempre tiene la dimension direction, asi
que getDir() casi nunca era undefined y cada raiz del canon estampaba
dir="ltr" en una pagina sin afirmaciones. El atributo nunca llegaba a estar
AUSENTE, la herencia del DOM nunca llegaba a trabajar, y de ahi la clase
entera de islas y reenvios que este eje llevo meses parcheando.
La matematica conserva la preferencia como VALOR (no puede leer el DOM) en un
solo helper:
afirmacion -> soma.prefs.getDir() -> 'ltr' resolveDir()
- activeDir(dir) pierde el parametro soma: 57 wrappers, y 49 quedan sin
`const soma = Soma.get()` huerfano (limpiados con deteccion de identificador,
no de ruta — la primera pasada se tropezaba con 'core/soma.svelte').
- 45 colas `opts.dir.current ?? 'ltr'` -> resolveDir(this.opts.dir, this.soma).
- Los 4 getDirectionalKeys de submenus que leian opts.dir.current directo
pasan a resolvedDir — con undefined degradaban a ltr EN SILENCIO bajo rtl
ambiental.
- command: 4.1 le habia dejado `dir: null` en el runtime y el estampado manual
inline en el assert (la linea no estaba sola y el barrido no la vio). Ahora
el runtime estampa y el inline se va.
- listbox / navigation-menu: el alias assertedDir muere — tras el flip,
opts.dir ES la afirmacion.
- date-range-field-input / time-range-field-input: el reenvio dir= al Field
compuesto en sitio sobra — DirectionContext lo lleva.
- 7 wrappers renombran `resolvedDir` -> `assertedDir`: el nombre mentia.
- picker / gradient-picker: activeDir(() => undefined) = la afirmacion del
ancestro o nada — mejor que el prefs-siempre-concreto de antes.
MEDIDO en Chrome, el modelo entero:
auto UN solo [dir] en toda la pagina: el <html> de la proyeccion
(antes lo llevaba casi cada raiz del canon)
prefs rtl select sin atributo, computa rtl por herencia; el panel
portalizado SIN atributo y computa rtl (heredo del html a
traves del body)
prop rtl trigger del dropdown estampa rtl, el panel portalizado
estampa rtl (la afirmacion cruza), submenu data-side=left,
cero islas dentro
matematica slider SIN atributo con pagina rtl: click al 25% fisico da
75 y ArrowRight BAJA — resolveDir lee prefs sin que el
atributo lo haga
SSR curl de select y accordion: cero dir= en el payload
El contrato: §1 documenta la cadena partida (activeDir -> afirmacion;
resolveDir -> matematica), §6 la proyeccion automatica + la semilla y el
parrafo de SSR — el <html dir> del primer render pertenece a app.html o al
servidor (modelo react-aria).
check 77 = linea base · suites soma+active-uix+prefs+contracts 1362 tests:
los 6 de contracts y soma-attr-audit son el conjunto preexistente (A/B dos
veces en fases previas; attr-audit pasa aislado) · media-player 17/17 tras
hacer resolveDir defensivo con mocks sin prefs · rtl:check 1 (palabras) ·
docs:check 0/566.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
9158d07964 |
feat(direction): el eslabon de contexto — la composicion hereda por fisica
La cadena gana el eslabon que el usuario dejo aparcado y ahora ratifica
(2026-08-05): prop -> afirmacion del ancestro (DirectionContext) -> prefs.
Cada activeDir PUBLICA su afirmacion (prop ?? heredada) y consulta la del
ancestro antes de caer a prefs.
Por que: la cadena por-componente metia prefs en la ruta del ATRIBUTO, y el
boot por defecto SIEMPRE tiene la dimension direction (deriva del idioma,
fallback ltr) — asi que "nadie afirmo" era inalcanzable en la practica y cada
hijo del canon estampaba ltr dentro de un subarbol afirmado rtl, cortando la
herencia. La otra sesion lo midio dos veces en el campo: un menu compuesto en
sitio aterrizaba en la preferencia global (LTR chrome dentro de un player RTL),
y el panel portalizado resolvia por su cuenta aunque arreglaras lo primero. Un
solo eslabon cierra los dos agujeros, porque la capa flotante ya lleva el
opts.dir del dueno — que ahora incorpora el contexto.
Con la fisica, las convenciones se borran:
- los 3 reenvios a mano de 4.3 (natural-time-picker-panel, emoji-picker-content
x3, color-field-format-select) — el contexto los hace
- el enlace manual del submenu (dir ?? parentMenu?.opts.dir.current) — idem
- los 3 canarios dir= de media-player que la otra sesion dejo puestos a
proposito ("si al quitarlos el panel vuelve a salir LTR, el mecanismo no
llego al portal")
El canario canta: su repro exacto (media-player, manual parts, direction rtl,
float de subtitulos) da panel rtl SIN el dir= — y el volume float igual.
Medido ademas en Chrome: islas 0 en emoji-picker, natural-time-picker y
color-picker sin ningun reenvio; sliders de canal en espejo exacto (hue 210 a
0.417 del borde fisico); submenu de dropdown con data-side=left en RTL
derivado del contexto.
Regla de colocacion (documentada en direction.ts y el contrato): activeDir lee
y publica contexto de Svelte, asi que corre SIEMPRE en la init del wrapper —
nunca en constructores de provider. Los tests de provider construyen directo y
no se enteran; los 91 mocks de Soma.require() sobreviven porque getOr(undefined)
cae a traves de prefs.
Test nuevo (direction.svelte.test.ts + harnesses reales, 5/5): el hijo hereda
la afirmacion, su prop la pisa, sin afirmacion queda undefined (nunca un
default), sin ancestro cae a prefs, y la publicacion es REACTIVA.
check 77 = linea base · 71/71 en los 9 ambitos tocados · rtl:check 1
(palabras, preexistente) · docs:check 0/566. El contrato §1 pasa a cuatro
eslabones y §1 "Composition carries the assertion" documenta la fisica.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
5983b00d5f |
feat(direction): el estampado pertenece al runtime — y olvidarlo ya no compila
Era la ultima convencion manual del eje: una linea por proveedor, 49 copias de
ella, y 20 de 55 componentes que simplemente la habian olvidado. Una convencion
que hay que repetir 49 veces y que un tercio del catalogo incumple no es una
convencion — es una abstraccion que falta.
VEHICULO A, y el handoff tenia razon desde el principio. Yo habia recomendado B
(un AttrPlan del compilador de morfo) con el argumento de que A deja `dir`
invisible para `contracts.cssSelectors` y `eidos-lint`. Medido: FALSO. El linter
sólo clasifica selectores `[data-*]`, y las recetas leen la direccion con
`:dir()`, una pseudo-clase que no toca ninguno de los dos guards. El coste que
le achacaba a A no existe.
Y B no llegaba donde yo decia. Reparto real de los 49 sitios segun la bolsa que
expanden:
renderProps() 21 un AttrPlan les llegaria
runtimePart.props 27 NO les llegaria
ninguna 1
O sea B aterriza en 21 de 49 y exige migrar 28 componentes de `.props` a
`renderProps()` — que no es cosmetico: `renderProps()` emite TODOS los attrs del
morfo de esa parte. A, en cambio, inyecta en `partPropsForRegistration`, que
incluyen LAS DOS bolsas, y llega a 48 de 49 desde un solo punto. Su precedente
es literal: `id`, el marcador, `data-archetype` y `reg.attachment` ya se
inyectan ahi sin declaracion en morfo.
EL GATE ES DE TIPOS, que es el premio de verdad:
dir: SomaRuntimeDir | null // OBLIGATORIO en SomaRuntimeSources
`null` es la unica salida y dice algo cierto: este runtime no estampa direccion.
Olvidarlo es un error de compilacion, no un defecto que nadie ve hasta que
alguien abre el componente en RTL.
`dir.parts` responde la otra mitad de la pregunta del contrato — QUE elemento
lleva la pintura — por defecto `['provider']`. El censo dice que 44 de 49 son la
raiz; los cinco que no: dropdown-menu ['trigger'] (su raiz no renderiza
elemento), drawer y float-panel ['content'] (pintan en el portal), number-field
y css-field ['provider','input'].
VERIFICADO en Chrome real, pagina en LTR y direccion movida por el control de la
demo:
tabs auto -> ltr prop rtl -> rtl vuelta a auto -> ltr
dropdown-menu el TRIGGER pasa a rtl y el panel portalizado tambien
number-field raiz e input, los dos a rtl
drawer el content portalizado a rtl por la cadena de prefs
accordion sigue la cadena y vuelve, sin una sola linea en su proveedor
check: 77 con mis cambios vs 79 sin ellos (A/B con stash). El -2 esta explicado:
uno era un error preexistente de `date-range-picker-provider.svelte.test.ts` que
indexaba el tipo `{ readonly dir }` de unos props que ahora son mas anchos; el
otro es que `media-player` ya no puede compilar sin el campo (su unica linea
tocada, +1/-1, es mecanica).
Tests: 1271/1278 en `src/uix/soma` + contracts. Los 7 fallos son el conjunto
preexistente, A/B contra arbol limpio: los 6 de `contracts.test.ts` salen
identicos sin mis cambios y `soma-attr-audit` pasa aislado (flaky bajo carga).
⚠️ Un fallo mio que cazaron los tests y conviene no repetir: los proveedores con
la forma de una linea `soma.runtime(morfo, {})` recibieron `dir: null` en vez del
getter, y 12 componentes con direccion se quedaron sin estampar. Lo vieron cinco
tests de "exposes root props". Un barrido con dos formas sintacticas necesita
las dos en el mapa, no una.
Estampados a mano restantes en el catalogo: 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
c73669cde7 |
feat(direction): la composicion lleva la afirmacion — tambien en sitio
4.2 cerro el caso del portal. Faltaba el gemelo: un componente que MONTA otro componente del canon arranca dentro de si una segunda cadena. Sin la afirmacion del dueño esa cadena salta la prop, aterriza en prefs y estampa un `dir` propio — y un `dir` estampado CORTA la herencia de todo lo que cuelga. El padre afirma rtl, el hijo estampa ltr, y el subarbol se parte. Medido con las demos ya cableadas, escaneando cada panel en RTL en busca de elementos que estampen una direccion distinta a la del envoltorio flotante: natural-time-picker 1 isla [data-slider] emoji-picker 3 islas [data-command] + [data-toggle-group] x2 color-field 1 isla [data-color-field-format-select] (un Select) Preexistente: mientras el panel entero era LTR la isla no se veia. Lo que hizo 4.2 fue volverla visible. El canon pasa a enunciar UNA regla con DOS vehiculos, elegidos por geometria y no por gusto (direction-contract.md §1, "Composition carries the assertion"): a traves de un portal -> la capa flotante, una vez por overlay composicion en sitio -> la prop `dir`, en el punto de llamada Lo que se entrega es siempre la afirmacion CRUDA (`opts.dir.current`), nunca `resolvedDir`: asi "nadie afirmo" sigue siendo distinguible y la cola de prefs del hijo sigue corriendo. Verificado en Chrome real, pagina en LTR y direccion movida por el control de la demo: los tres paneles pasan a 0 islas y el area del color-picker computa por fin `rtl`. Los dos sliders de canal siguen en espejo exacto — hue 210 cae a 0.583 del borde izquierdo en LTR y a 0.417 en RTL, alpha al maximo pasa de 1.0 a 0.0 — o sea §9.12 no se ha movido. check 78 = linea base · 59/59 en los 8 ambitos · rtl:check 1 (palabras) · docs:check 0/565. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
64df3d7b37 |
feat(direction): la propagacion pertenece a la capa flotante
Un panel portalizado se renderiza FUERA del subarbol de su dueño, asi que la
herencia no lo alcanza: la afirmacion del dueño solo llega si alguien la lleva.
Eso estaba delegado en cada consumidor — siete envoltorios Content componian el
enlace al padre a mano — y quien COMPONE un popover no tenia por donde hacerlo.
Medido con <DatePicker dir="rtl"> sobre una pagina LTR: el wrapper flotante
estampaba dir="ltr", el picker-shell y su pie se disponian en LTR, y solo el
calendario espejaba. Panel partido por la mitad, el fallo del §2 del contrato.
La capa lo compone ahora una sola vez:
el dir propio de la superficie -> el dir afirmado por el proveedor DUEÑO -> omitir
- FloatingProviderOpts.dir el dueño baja su afirmacion (ya pasada por
activeDir, o sea prop -> prefs)
- FloatingContent.assertedDir donde se encuentran; lo leen los DOS estampados
del wrapper (rama nativa y rama JS)
- createFloatingShellRoot lo reenvia; es la puerta de 8 de los 10
FloatingProvider. Los 2 submenus toman el del
menu padre.
FloatingContentOpts.dir pasa a ser la prop CRUDA del Content. Resolverla ahi la
haria casi siempre concreta (prefs) y el dueño no ganaria nunca el fallback.
Se borran los 7 enlaces compuestos a mano con su soma / activeDir / parentRoot.
Sobrevive uno a proposito: dropdown-menu-sub-content lo necesita para
effectiveSide, que es matematica y quiere valor resuelto.
popover, tooltip y link-preview aceptan dir en su raiz. No estampan nada — no
renderizan elemento —; el valor existe para cruzar el portal, y es lo que
permite que los 9 componentes que componen un popover empujen su direccion al
panel.
Borrados 9 resolvedDir muertos en los proveedores Content: nadie los leia (toda
la matematica direccional consume el de la RAIZ) y el cambio de semantica de
opts.dir dejaba su docblock mintiendo.
Demos: las 7 que aceptan dir lo pasan ahora a la instancia — time-picker,
time-range-picker y color-picker no tenian control ninguno; emoji-picker y
natural-time-picker lo tenian solo en el arnes. Sin eso el camino de la PROP,
el unico que ejercita el portal, no era observable.
Verificado en Chrome real, direccion movida por el control de la demo:
date-picker Clear/Cancel/Close 15/102/209 -> 200/92/15 espejo exacto
date-range-picker 448/217/17
sin regresion dropdown-menu (raiz y submenu, data-side=left en RTL),
context-menu, menubar, select, sidebar (flyout), y tooltip
en auto sigue estampando ltr — la cola de prefs sobrevive
check 78 = linea base (A/B con stash) · 158/158 en los 20 ambitos tocados ·
rtl:check 1 (palabras, preexistente) · docs:check 0/565.
Queda documentado en el handoff un defecto PREEXISTENTE de otra familia que
esto vuelve visible: un componente eidos que monta otro componente del canon en
SITIO no le reenvia el dir, y el hijo estampa ltr cortando la herencia del
panel (natural-time-picker/Slider, emoji-picker/Command+ToggleGroup,
color-field/Select). No hay precedente de ese reenvio en eidos: es doctrina
nueva y movimiento propio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
21055bd3cc |
fix(direction): la regla de estampado era demasiado estrecha — 16 componentes la incumplian
Decision del usuario (opcion B). La regla de §2 decia «estampa si el recipe usa `:dir()`», y esa premisa es FALSA: `:dir()` no es el unico lector. Tambien leen la direccion computada del elemento las propiedades logicas (`margin-inline-*`, `text-align: start`) y —lo que lo hace casi universal— `flex-direction: row`, que se INVIERTE. En la practica, cualquier componente con maquetacion horizontal depende de la direccion. La regla honesta, ya escrita: quien acepta `dir` lo estampa. La pregunta no es SI, es EN QUE ELEMENTO — el que lleva la pintura. Y para eso hay cuatro casos, ahora documentados: raiz normal · superficie PORTALIZADA (el wrapper flotante, que la capa ya estampa porque fuera del subarbol no puede heredar) · componente con LAS DOS mitades, que necesita las dos · y geometria aplicada desde JS, que no necesita atributo. Censo: de 55 que aceptan `dir`, 20 no estampaban. Clasificados uno a uno leyendo recipe + wrapper + donde renderiza cada parte: - 16 ESTAMPAN ahora, cada uno en la parte que lleva la pintura. Los sub-partes (drawer→Content, float-panel→Content, dropdown-menu→Trigger porque su raiz no renderiza elemento) llevan comentario nombrando la pintura que cubren. - 4 NO estampan y es CORRECTO: popover, tooltip, link-preview y context-menu. Su unica pintura direccional es el panel portalizado, que ya estampa la capa flotante; y el root de context-menu no renderiza elemento ninguno. DOS DEFECTOS QUE EL BARRIDO DESTAPO, uno de ellos MIO: 1. `sidebar` pasaba el valor RESUELTO al motor flotante. Esa linea la escribi yo en §9.19: arregle que se saltara la prop y deje el otro defecto, que siempre es concreto. La capa estampa lo que recibe, asi que un flyout SIN afirmacion recibia `dir="ltr"` — forzando su subarbol a LTR dentro de una pagina RTL, que es exactamente lo que el crudo existe para impedir. Era el UNICO de once consumidores flotantes que lo hacia. De paso desaparece su `resolvedDir`: sidebar no hace matematica direccional, toda su pintura es CSS. 2. La afirmacion de la raiz NO LLEGABA al panel portalizado. `select`, `combobox` y `context-menu` resolvian su Content con `activeDir(() => dir, soma)`, sin paso por el padre — asi que `<Select dir="rtl">` movia el trigger y dejaba la lista atras. Antes del barrido no se notaba porque NINGUNA de las dos mitades estaba afirmada y coincidian por heredar las dos del `<html>`; estampar el trigger lo habria hecho VISIBLE. Compuesto el enlace en el punto de llamada, que es la forma que §1 imprime y que `dropdown-menu` y `menubar` ya usaban. El resolutor sigue sin paso por el padre. Y `color-picker`, que es el mismo caso con una regla `:dir()` REAL varada: el area mirroring vive en el panel portalizado (`color-picker.css:298,335`) mientras la matematica del thumb y el arrastre salen de la raiz. Sin el enlace, el thumb se voltea y el gradiente de debajo no — justo lo que avisa el comentario de la receta. Su wrapper ya reestampaba `data-color` por esta misma razon; le faltaba `dir`. MEDIDO en Chrome (select): la raiz pasa de NO tener atributo a estamparlo, y trigger y panel coinciden en las tres posiciones del toggle. Estructuralmente ya no pueden divergir: ambos leen `opts.dir` de la misma raiz. `check` sin errores en ninguno de los 20 (el total 80 son 77 de base mas 3 de `media-player`, que edita otra sesion). 90 tests de los componentes tocados en verde. `rtl:check` 1, el de `palabras`. `docs:check` 0/0. QUEDA, reportado y sin tocar: ningun picker de eidos reenvia `dir` a su `PopoverContent`, asi que el cromo del `picker-shell` dentro del portal (`picker-shell.css:78`, el split del pie) queda sin cubrir en los siete. Los controles interiores (Calendar, los Slider) se salvan porque reciben el crudo por su cuenta. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
e9454bebe7 |
docs(direction): la excepcion que el usuario veto — :dir() que traduce una prop logica
«NO añadas la direccion al avatar porque tambien depende de donde se situa el badge». Tiene razon, y mi regla de §2 —«si el recipe usa :dir(), acepta y estampa `dir`»— es demasiado ancha: mandaba al siguiente a hacer justo eso. NO toda regla `:dir()` afirma una direccion. `Avatar.Badge` expone la esquina con `position` en terminos LOGICOS (`top-start` / `top-end`), asi que el ancla (`inset-inline-*`) ya se voltea sola; `:dir(rtl)` solo invierte el SIGNO del `translate` fisico que hace sobresalir la insignia —por eso las cuatro reglas llevan su marcador `rtl-physical:`, es el caso sancionado de RTL-1, no un selector de lado—. `Image` es lo mismo con otro mecanismo: `object-position` no tiene palabra clave logica, asi que `:dir(rtl)` traduce a `left`/`right` el `start`/`end` que el consumidor ya eligio. En los dos, una prop `dir` seria un SEGUNDO mando para lo que el primero ya decide, y pelearia en vez de componer: el ancla logica resuelve por la direccion ambiente mientras la prop dice fijarla. Esa regla `:dir()` necesita la direccion que REALMENTE maqueto el elemento, que es la heredada, por diseño y no por descuido. El test que queda escrito: si el componente tiene vocabulario logico propio para el eje, no lleva `dir`. Un `Switch` no lo tiene —su pulgar viaja en un sentido y la direccion es el unico input—, asi que ahi si es significativo. Un `Avatar.Badge` tiene `position`, asi que no. Barrido: de los 13 recipes con `:dir()`, SOLO `avatar` e `image` tienen ademas prop logica start/end. La excepcion son exactamente esos dos. Corrige tambien lo que reporte como hallazgo: «avatar e image usan `:dir()` sin aceptar `dir`» NO es un defecto ni una decision pendiente, es el diseño correcto. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
43a7880832 |
feat(direction): las charts aceptan `dir` y corren la cadena canonica
Decision del usuario. Yo habia recomendado lo contrario —dejarlas y ablandar el
contrato— y me equivocaba en el diagnostico: no era que la cadena no valiese
para eidos, era que **le faltaba el punto de entrada**.
`activeDir(getter, soma)` pide un `Soma`, y una chart solo tiene un
`ActiveEidos`. Y `ActiveEidos.prefs` es un `ActivePrefs` crudo, que no tiene
`getDir()` —ese metodo vive en la vista que mira a soma—. Por eso la cadena
"no era ejecutable" y por eso alguien puso ahi un `getComputedStyle`. La
adaptacion entre las dos superficies es TODA la diferencia:
src/uix/eidos/direction.ts → activeEidosDir(getter, prefs)
Mismos dos eslabones, mismo `Direction | undefined`, misma cola `'ltr'` en el
consumidor. Abre la puerta tambien a `avatar` e `image`, que son el mismo caso.
Fuera la lectura del DOM y fuera la copia byte-a-byte que `chart.svelte` tenia
del mismo mecanismo —dos fuentes de verdad para un hecho, y ademas el observer
vigilaba `<html dir>` mientras la lectura era del elemento, asi que un ancestro
que volteara en caliente no se veia nunca—.
⚠️ EL HALLAZGO QUE SOLO SALE MIDIENDO: **estampar `dir` en un `<svg>` no hace
nada.** El atributo lo mapea a `direction` la hoja UA de HTML, que no alcanza a
los elementos SVG: medido en Chrome, un `<svg dir="ltr">` bajo un ancestro
`dir="rtl"` sigue computando `rtl`. Yo lo habia puesto ahi y el comentario
afirmaba que servia. Va en el envoltorio HTML (`<figure data-chart-figure>`,
`<div data-chart-frame>`). Importa porque `text-anchor` es LOGICO: con el
estampado en el nodo equivocado la matematica espeja y las etiquetas no —el
fallo de §2 con otro disfraz—.
MEDIDO en Chrome, heatmap: LTR `ene/feb/mar` en x=26/90/154, celdas 26‥858;
RTL en 851/787/723, celdas 6‥838. Funnel: poligonos de 0‥226 a 193‥420,
etiquetas de 249 a 171. El toggle del topbar sigue volteandolas, ahora por
prefs y no por el DOM.
COSTE ACEPTADO, verificado: envolver una chart en `<div dir="rtl">` sin pasar la
prop ya no la voltea. Falla de forma CONSISTENTE —el estampado del envoltorio
gana sobre el ancestro, asi que matematica y pintura siguen de acuerdo— y no
con las etiquetas cruzando el grafico.
§7 del contrato deja de ser una divergencia declarada y pasa a ser la tabla de
los dos puntos de entrada. Su justificacion era ademas falsa en dos puntos: no
era "el unico sitio del catalogo" (quedan 4 mas, uno vivo en
`field-langs-switcher`), y decia que una chart no se podia voltear por subarbol
cuando `direction` se HEREDA y si se podia.
`check` 77 = linea base. `docs:check` 0/0. Tests de eidos + libs/plots: 424/425,
el fallo es `audio-player` sin morfo, preexistente y ajeno.
⚠️ No pude ver los pixeles: el panel del navegador no compone frames.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
1cb61567e5 |
feat(direction): RTL-2 — el guard del doble volteo, la forma que se me colo dos veces
Cierra §10.1, la ultima deuda del eje. `dee2c9e3a` arreglo los dos defectos pero
dejo la FORMA sin guard, y es la que sobrevivio a la migracion §6.3 y a que yo
diera el eje por cerrado en §9.19.
LA FIRMA — dentro de un bloque cuyo prelude tiene `:dir()`, la misma familia
logica (`border`/`padding`/`margin`/`inset`-`inline`) declarada en LAS DOS caras,
EXACTAMENTE UNA con valor neutro (`0`, `auto`, `none`, `initial`, `unset`,
`revert`). Ese desequilibrio ES el espejo cancelado: la propiedad ya se habia
volteado cuando la regla matchea, asi que reubicarla la devuelve al punto de
partida y la deja en el borde opuesto al de sus hermanas.
Es mas estrecha que «bloque `:dir()` con puras logicas» a proposito. Una regla
que CAMBIA un valor bajo RTL sin reubicarlo es legitima —asimetrico por diseño
existe—, y las dos caras con valor son un autor describiendo dos bordes reales.
Lo que delata el defecto es el PAR, una cara apagada. Hay un test negativo por
cada uno de esos casos, que son los que evitan que la regla se vuelva ruido.
LAS TRES DECISIONES que el handoff dejaba abiertas:
- `:dir(ltr)` tambien entra. Igual de sospechosa, y no anade ruido.
- Marcador PROPIO, `rtl-mirror: <reason>`. Reutilizar `rtl-physical:` mentiria:
aqui no hay nada fisico, y un marcador que miente es peor que ninguno. Mismo
mecanismo (`exemptLines` toma ahora el patron por parametro), dos vocabularios.
Un test comprueba que el marcador de RTL-1 NO exime a RTL-2.
- Regla aparte, `lintRtlMirror()` con su propio `RtlMirrorFinding`. Los 14 tests
de RTL-1 quedan intactos y el tipo lleva los campos que importan
(`family`/`neutral`/`payload`) en vez de forzar los de RTL-1.
VERIFICACION, en este orden:
- `rtl-lint.test.ts` 27/27 (14 de RTL-1 intactos + 13 nuevos).
- RECALL con el runner COMPLETO contra el arbol pre-arreglo: restaure los dos
ficheros de `dee2c9e3a^` sobre el arbol, corri `rtl:check` y los devolvi con
`git checkout` en el mismo bloque. **2/2 cazados** (`feed.css:147`,
`tree-view.css:211`). Probar la funcion no basta: el runner es lo que corre.
- `rtl:check` sobre HEAD: 1 error, el de `palabras`, preexistente y excluido.
- `docs:check` 0/0 sobre 564 docs. `check` 77 = linea base.
Un defecto de presentacion salio al hacerlo: el `calc()` multilinea de tree-view
partia el mensaje por el primer salto. Los campos que RTL-2 emite colapsan el
whitespace; con test.
Actualizados los textos que el handoff avisaba que quedarian obsoletos: el
`enforcement:` y §4 del contrato, la fila RTL del build contract, la fila X-1.6
del checklist, y los docblocks de `rtl-lint.ts` y `rtl-check.ts` — que son
doctrina, no adorno.
⚠️ Sigue siendo cierto lo que NINGUNA de las dos reglas ve: leen texto CSS. Un
`transform` inline escrito por JS, un preset de motion compartido y la geometria
SVG siguen necesitando el ojo en RTL.
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 |
|
|
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 |
|
|
53b6f629f2 |
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
116594e063 |
feat(docs): S1 — generated canonical-vocabulary appendix (STUMBLES #1)
An agent building a component from the docs-book alone could not assign archetypes / holds / haptic kinds: the closed sets live only in code consts and the corpus (correctly) forbids copying them into prose, so they were invisible from the docs. STUMBLES #1, the top stumble. Fix: docs/canon/vocabularies.md is GENERATED from the consts by 'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled out (generated = no drift objection), which every other doc links to. It covers part archetypes (with a one-line role each), sema families with their default hold/persistence + per-intent overrides, verbs by family, intents, the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes, variant archetypes, and the shared common.* strings. - scripts/docs-vocabularies.ts imports the consts (robust — no fragile JSDoc/regex parsing) and formats them; the generation is an exported function so docs-check can compare. - Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the authoritative one-liners the appendix reads — descriptions become first-class data instead of inline union JSDoc a tool would have to parse. - docs-check gains I7: it regenerates in-memory and fails if the committed file drifts from the consts (negative-tested: corrupt -> error, regen -> green). The appendix's own counts pass I1, so I1 doubles as a second guard. - Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md (doctrine here, enumerated lists there), architecture/morfo.md (pick a part's archetype), and the component-audit minimum-package list (STUMBLES #8). docs:check 0 errors; npm run check unchanged in my files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
29b121236a |
docs(book): F7.6 (2/2) — final link sweep + complete book TOC; decisions/ batch COMPLETE
The global sweep caught the last corpus links still pointing at pre-book paths: morfo chapter's s2.bis cross-ref (src/uix/README -> overview.md), authoring's E1 stratum row (active_architecture -> docs/architecture/), the related: frontmatter of decisions.md and canon/recipe-contract.md (THEMING/THEMING_GUIDE -> theming/reference + guide), and component-audit's typographic-vertebration pointer (eidos README -> architecture/eidos.md with the translated heading). README map completed as the book TOC: every chapter now has a row — theming/motion.md (the model) and theming/changelog.md (the chronicle) were the two missing. Remaining old-path references live only in chronicle docs (theming/changelog, process/) and resolve via stubs; the 11-warn baseline is all foreign targets (MOTION_SERVICE_RFC demo routes, deleted THEMING_AUDIT/PENDIENTES). F7.6 complete. The book tree is fully populated: architecture/ (8) + canon/ (2) + theming/ (7) + rfcs/ (7) + guides/ (4) + decisions/ (2). Next and last: F7.7 CLAUDE.md thinning — diff to the user first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
6307ec86f0 |
docs(book): F7.3 (9/9) — THEMING reference translated; theming/ + canon/ batch COMPLETE
src/uix/eidos/THEMING.md (1499 L, Spanish) translated to English as docs/theming/reference.md, same s1-s38 numbering: the mental model, s1.bis (theming lives in eidos — the 2-of-3 derivation and the canonical split), the CSS layers, the 7 token layers, the 9 roles, the size canon (universal 1:1 + label step-down + container cap + the icon scale), the naming conventions, the s7/s8/s9/s13/s15/s17/s18 stubs repointed into the book (canon/tsc, theming/guide, theming/notes, theming/changelog, theming/motion), runtime overrides + builders, bundle/purge, validation tooling, s14 motion (the pickup lift), s16 anti-patterns, s19 variants canon, and the s20-s38 standing-decision stubs. One internal reconciliation applied: the s35 stub cited the control/compact/dense size archetypes that s5 declares superseded by the universal 1:1 — aligned. One stale v1 DEMO_AUTHORING s12.8 citation in s5 replaced by the v2 s6 parity rule. Stub at the old path carries the full s1-s38 map (the most sN-cited doc in the repo: code comments, CLAUDE.md, RFCs). Corpus links swept (CANON, docs map, comparison, glossary, eidos chapter, canon/tsc, theming/guide + notes). docs:check 0 errors (260 docs). F7.3 complete: docs/canon/ (tsc, recipe-contract) + docs/theming/ (reference, guide, notes, channels, motion, motion-guide, changelog). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
f4639a36fa |
docs(book): F7.3 (1/2) — canon/ chapter: TSC + Recipe Contract translated
src/uix/eidos/TSC.md (334 L) and RECIPE_CONTRACT.md (131 L), both Spanish, translated to English as docs/canon/tsc.md + docs/canon/recipe-contract.md (the E2 visual canon inside the book tree). Thin stubs at the old paths (code comments and component-audit R-4.x provenance keep resolving); corpus links swept (docs map E2 rows, authoring anti-copy law, building-a-component phase 5, comparison, getting-started, glossary, eidos chapter). docs:check 0 errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |