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 }
62 Commits (613215fe7c9e98de878ade2f44271bb55844281a)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
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 |
|
|
f8dd8ff810 |
fix(morfo,sema)!: M1(ii) gradient-picker — el reset delegado deja de declararse dos veces (S-14)
El morfo declaraba `commit-reset`, el pack le dedicaba una regla (settle + tick) y los dos README lo tableaban — y el provider no lo emitia. Lo unico que suena en el Clear es el `commit-reset` del Picker generico COMPUESTO, con la firma del pack `picker`. La ficha S-14 ofrecia dos salidas y su premisa comparativa era falsa, medido: los hermanos color/date/time-picker SI emiten su propio reset (date-picker cableado el 2026-08-11) — pero NO componen el Picker generico: definen su propio pickerShellHandle y son dueños de su transaccion. Gradient-picker la DELEGA entera (su docblock: «The transaction — open / commit / cancel / clear — lives in the composed generic PickerProvider»), asi que la emision sigue a la propiedad: el reset es del picker. Cablear el gemelo habria dado DOBLE firma por un gesto (dos estampados + dos sonidos casi simultaneos — la clase que file-upload pago), y PickerProvider no ofrece hook para silenciar el suyo. Retirada completa, con la palabra explicita del autor: el evento fuera del morfo (una nota en su lugar dice por que y hacia donde), la regla muerta fuera del pack, las filas fuera de los dos README (el de eidos gana la frase correcta: los eventos propios son los del DOMINIO — presets — y el Clear es del picker), la excepcion fuera del censo y el ultimo id fuera de la deuda D9. Con esto la parte (ii) de M1 esta COMPLETA — las 7 resoluciones de la cola medida el 2026-08-12: tooltip x3 CABLEADOS (receta F4 + silencio visual minimo medido), virtual-list/grid x3 `emission: 'host'` (la decision escrita de sus providers, ahora expresable), gradient-picker RETIRADO (delegacion). INERT_EVENT_DEBT y EMISSION_EXCEPTIONS quedan VACIAS por primera vez, cada una con su lapida narrando las resoluciones. M1 entero cerrado: el contrato dice quien dispara cada evento, y ningun evento declarado miente. Sin cambio de conducta: el Clear sonaba por el picker y sigue sonando igual. Verificado: guards 339 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · docs 0/624 · prettier: los dos ficheros limpios en HEAD quedan limpios. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
c73cae0227 |
feat(morfo,soma): M1(ii) virtual-list/grid — el scroll declara su emisor: el host
Los tres `handle-scroll*` de virtual-list/grid estaban en la deuda del D9 como «declarados y nunca sonados». Mal clasificados: la resolucion YA estaba escrita en los dos providers como decision con su porque — el listener de scroll dispara por pixel y la familia handle es ['sound','haptic'] (trinquete `step` + tick), asi que una emision por defecto seria un clic-clic continuo bajo scroll con inercia; «apps that want scroll-driven sema feedback should wire it themselves with their own throttle / debounce policy». Lo que faltaba no era el emisor: era un sitio DECLARABLE para esa decision. Es exactamente el caso de diseño del valor `host` de D.2, estrenado aqui: el contrato declara la superficie, el emisor es la aplicacion anfitriona. - `emission: 'host'` en los tres eventos, con el porque en el docblock. - Los dos comentarios de provider apuntan al flag y corrigen la cita desfasada (`activeChannels=['haptic']` — hoy son dos canales, lo que hace la conclusion MAS cierta, no menos). - La promesa del host es viable, verificado: `provider.runtime` es campo publico — un app puede emitir con su propia politica. - Deuda D9: -3 ids. Queda UNA entrada en INERT_EVENT_DEBT: gradient-picker commit-reset (S-14), la ultima resolucion de la parte (ii). Sin cambio de conducta: cero emisiones nuevas, cero navegador que medir. Verificado: guards 309 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · prettier: virtual-list.ts limpio en HEAD queda limpio; los otros 3 ya fallaban. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
daa6461d62 |
feat(soma,morfo,eidos): M1(ii) tooltip — los tres eventos existen por fin, y el silencio queda donde estaba firmado
El morfo declaraba emerge-present / emerge-dismiss / emerge-dismiss-escape y
nadie los emitia: el SILENT del pack nunca casaba, la puerta de temas que su
propio docblock documenta no podia funcionar, y el preset present-rise que
declara `event: ['emerge-present',...]` jamas pintaba. La excepcion del censo
lo decia desde agosto: «Today's silence is accidental, not the declared
design».
El re-analisis pedido por el autor cazo lo que la primera propuesta no vio:
la firma present-rise es GLOBAL (base.css:6520), la entrada por data-state es
el workaround de la emision ausente (el comentario del provider lo dice
literal), y la semantica autorizada distingue hover (entrada) de focus
(instant-open, «appears with no entrance — its semantics») — cosa que el
evento no distingue. De ahi la opcion B firmada: el estado conserva la
entrada; la emision añade la superficie que faltaba.
Cableado con la receta F4: `present` pre→post — y SIN su `commits` (fijaba
'delayed-open' y pisaria el 'instant-open' del camino focus; los overlays
binarios conservan el suyo porque su valor es total, desviacion declarada);
dismiss ×2 ganan `targetFallback: [trigger]` (Presence sostiene el content por
la salida; el fallback cubre la carrera). Provider: emision SOLO en
transiciones reales — un open-timer cancelado no presenta nada que retirar, un
re-hover abierto no re-emite — y `handleClose` lleva la causa para distinguir
el Escape.
MEDIDO en navegador, los tres caminos y la fuga exacta:
hover present CONTENT · entrada autorizada corriendo (scale-in+fade-in),
present-rise NO — el preset gana la cascada; miedo al doble
movimiento REFUTADO en el camino delayed
focus present CONTENT con instant-open y present-rise CORRIENDO — la fuga
predicha, confirmada → silencio visual MINIMO en la receta, scoped a
[data-state='instant-open'][data-event-phase='active'];
re-medido: anims [] y la entrada delayed intacta
leave dismiss CONTENT via Presence · salida autorizada (scale-out+
fade-out), dismiss-fade no
Escape dismiss-escape CONTENT · desestampado del hold a ~240ms
Deudas limpiadas en el mismo commit (el guard lo exige): 3 ids de
INERT_EVENT_DEBT + 3 excepciones de EMISSION_EXCEPTIONS. Quedan 4 de la parte
(ii): virtual-list/grid ×3 y gradient-picker commit-reset (S-14).
Verificado: soma navegador 1278✓/1 (el timeout ajeno) · tooltip 4/4 ·
sema+morfo+contracts 552✓/6 ajenos · check 69 = base aislada, diff VACIO ·
prettier: morfo/tooltip.ts limpio en HEAD queda limpio; los 3 avisos ya
fallaban en HEAD.
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 |
|
|
13246a2c23 |
refactor(sema,morfo)!: M6 — los alias deprecados mueren; un nombre por concepto
`IntentExpectedFamily` y `SemaEventLabel` eran alias de compatibilidad de `IntentRequiredFamily` y `SemaEventKey`, y el codigo nuevo seguia importando el nombre viejo — la mitad de los tipos hablaba el vocabulario de antes del renombrado. La regla dura del repo (no backward-compat shims: actualiza los consumidores y borra el camino viejo) decide el destino: 13 usos migrados a los canonicos y los DOS alias borrados del tipo y del barrel. La unica duda doctrinal se resolvio antes de tocar: D.3 (book-deviations:489) nombra `IntentExpectedFamily`, pero DESCRIBE la implementacion con el nombre que existia entonces — registro historico, no prescripcion. El precedente es la casa misma: se ha renombrado vocabulario entero sin reescribir los registros de decisiones. Ademas, y declarado en la exposicion: `semaIntentExpectedFamilySchema` (const local de morfo/schema.ts, mismo vocabulario viejo) pasa a `semaIntentRequiredFamilySchema` — y el guard S-34 de contracts.test.ts, que lo busca por nombre LITERAL, se actualiza en el mismo commit; separarlos habria dejado el guard ciego un commit entero. Los 2 `as never` de compile.ts se retiran: eran vestigio de antes de que existiera `DepSink` (un `() => void` encaja en `(value: string) => void`), y el compilador lo confirma. Y `_resetCompileCache`, tercera pata de la ficha, resulto YA borrado — cero apariciones en src/; se anota para que no vuelva a la cola. Censo previo al borrado: cero consumidores de los alias en src/, web/ y scripts/ fuera de los 13 migrados. Las funciones `is/parse/toSemaEventLabel` y el tipo `ParsedSemaEventLabel` conservan su nombre: son API propia, no el alias, y su renombrado quedo explicitamente FUERA de lo firmado. Verificado: sema+morfo+contracts 541 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · prettier: types.ts era mio y queda limpio; compile.ts y contracts.test.ts ya fallaban en HEAD y no se reformatean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
eda3b977b1 |
test(contracts): S-34 — el validador de runtime queda atado al const de politica de intent
CLAUDE.md declara `SEMA_FAMILY_POLICY` fuente unica y promete que «editing it reshapes the discriminated unions». El lado del TIPO lo cumple (`IntentRequiredFamily` deriva del const); el lado del RUNTIME no: el esquema sium que valida los 172 morfos reescribe la misma particion con literales fijos (`morfo/schema.ts:205-216`). Hoy coinciden literal a literal — no hay bug vivo — pero editar el const moveria la puerta de compilacion y dejaria la de runtime en la particion vieja, en silencio. Es la clase de deriva que costo dos meses y medio con `form.*`: copiar el canon en vez de leerlo. El arreglo «obvio» —derivar el esquema del const— se descarta con su razon: eso compra una importacion de VALOR morfo → sema, y esa arista es hoy deliberadamente solo de tipos (morfo es «the contract / DNA; pure TypeScript, no runtime», theming/channels.md §3). Pagar acoplamiento de runtime entre capas para cerrar una deriva latente es mal cambio. Los dos lados se atan donde YA conviven: el test de contratos entre capas. El guard expande las uniones del esquema (incluida la anidada `transitional`) y las compara con las dos particiones derivadas del const. Visto FALLAR antes de darlo por bueno: moviendo `handle` a `required` en el const, el guard reporta «expected ['commit','signal'] to deeply equal ['commit','handle','signal']». Verificado: contracts 6 fallos = los 6 AJENOS de la base, sin nuevos. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
14a169357b |
fix(contracts): el guard D9 mira tambien la vista de eidos, que en los componentes-vista ES el emisor
El guard de eventos inertes construia su blob con `soma/components/<comp>` mas
libs/datetime/layers. Para los componentes-vista (metrics, menu-dial,
onion-menu) no hay clase provider: la vista de eidos registra las partes y
emite. Censados los 255 eventos declarados, son 4 los que se emiten SOLO desde
eidos — menu-dial.commit-select, onion-menu.commit-select y los dos de drill de
onion-menu.
Dos de ellos pasaban por casualidad: el nombre estaba entrecomillado en un
COMENTARIO del factory de soma. Al reescribir ese docblock en
|
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 |
|
|
eda25a4866 |
fix(morfo,soma): las 4 ultimas de D11 — el guard se queda sin excepciones
`DEFAULT_ELEMENT_DEBT` esta vacio: las 28 partes que el censo encontro dicen ya la verdad sobre el elemento que renderizan. Estas cuatro pedian un cambio con consecuencias, y la leccion es que «que lado esta mal» solo se contesta mirando. chronos.event-chip — el morfo tenia razon, faltaba el puente ------------------------------------------------------------ El motivo estaba escrito desde el 2026-08-10: el chip CONTIENE el tirador de redimensionado y el modelo de contenido de un `<button>` no admite un control anidado, asi que es `div` + `role='button'`. Lo que faltaba era el «tabindex propiedad de soma» que ese mismo comentario promete y que nunca existio. El provider emite ahora `tabindex: 0` y un `onkeydown` que convierte Enter/Espacio en un clic — con `preventDefault` sobre el espacio, que si no desplaza la pagina— y el componente pasa a ser el `div` que su contrato declara. tooltip.trigger — probe el morfo, me equivoque, y lo corrigio la captura ------------------------------------------------------------------------ Declaraba `button`, asi que renderice un `<button>`. La captura mostro el cromado nativo alrededor del disparador: un recuadro que antes no estaba. La receta llama a esta parte «a focusable shell» y difiere el anillo de foco «to the wrapped element». Es un ENVOLTORIO: el control real del consumidor va DENTRO, y un `<button>` envolviendo un `<button>` es el mismo anidamiento invalido del chip de chronos. El lado equivocado era el morfo. Declara `div`, y la a11y la dan el `tabindex: 0` del provider y el `aria-describedby` del morfo, que es lo que el patron APG de tooltip pide de verdad. stepper.item — no era declaracion, era patron ---------------------------------------------- Un `div` SIN rol dentro de un `role='tablist'`. El patron real es `list (tablist) > item > trigger (tab)`, y un elemento generico entre un tablist y sus tabs rompe la relacion de posesion. Es `role='presentation'` ahora: el envoltorio sale del arbol, igual que un `<li>` dentro de un `role='menu'`. El idioma ya estaba en 8 componentes del catalogo, no lo invento. color-picker.channel-input — una declaracion que nada podia satisfacer ----------------------------------------------------------------------- Declaraba `input` mientras compone `ColorField.Input`, que es `div` + `role='group'` — el contenedor de segmentos, la misma forma que el `input` de date-field. Las piezas editables son las partes hermanas `channel-segment`. Verificado ---------- Guard en verde CON LA LISTA VACIA. Navegador: el trigger de tooltip vuelve a ser texto plano (sin cromado), enfocable, y abre al foco; consola limpia en tooltip y chronos. 2246 tests del uix (los 6 fallos de `contracts.test.ts` siguen siendo de la otra sesion), `check` 70 errores en linea base — una pasada intermedia dejo 71 por un `SomaKeyboardEvent` sin importar en chronos. `docs:check` 0/623. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
ff86971a07 |
fix(morfo,soma,eidos): una flecha, una forma — y `defaultElement` deja de mentir en 24 partes
D12 · las dos flechas sueltas
-----------------------------
`internal/arrow.svelte` dibuja poligono MAS un contorno trazado; context-menu y
link-preview rendian un `<svg>` propio con solo el poligono. Dos de las siete
flechas del sistema no tenian contorno — medido: link-preview no tenia `<path>`
ninguno mientras tooltip si.
Ahora componen la primitiva, asi que las siete son el mismo `<span>` con el mismo
`<svg>` y las siete declaran `span`. Las recetas pasan a direccionar las formas
por dentro (`polygon { fill }`, `path { stroke }` con `non-scaling-stroke`):
un `fill` sobre el envoltorio no puede pisar los atributos de presentacion que
poligono y path llevan encima.
⚠️ El arreglo destapo otro defecto. Al componer la primitiva la flecha
DESAPARECIO: `<span>` de 0x0, sin `<svg>` dentro. Los wrappers de eidos de estos
dos pasaban `{@render children?.()}` INCONDICIONALMENTE, y la primitiva lee
cualquier snippet de children como «el consumidor trae su propio glifo» y se
salta el suyo. popover y tooltip no pasan children en absoluto, por eso nunca les
paso. Los dos son condicionales ahora. Es la regla «eidos wrappers need
conditional children», reaprendida midiendo en vez de leyendo.
Medido despues: los dos dan `<span>` 10x5 con `<svg>` 10x5, poligono relleno y
`<path>` con trazo, identicos a tooltip (el control). Consola limpia.
D11 · 24 de 28
--------------
Eran 28, no 24 — mi cifra anterior estaba mal.
Dieciseis son el morfo yendo detras de un componente que ya renderiza lo mas
correcto (`header`, `p`, `span`, `button`): edicion de declaracion, el DOM no se
mueve.
Ocho parecian del grupo contrario Y NO LO ERAN. `tree-view.item` declara `li` y
rinde `div`, si — pero tambien declara `role='treeitem'`, y lo mismo
`file-upload` con `list`/`listitem`, `feed` con `article`, `stepper.list` con
`tablist`, `tree-view.branch-content` con `group`. La a11y NO esta rota: el rol
explicito carga la semantica que daria el elemento nativo. La declaracion era
aspiracional. Cambiar el DOM a listas nativas es una mejora deliberada aparte,
no algo que colar en un pase de verdad-del-contrato.
Las 4 que quedan piden un cambio con consecuencias, y cada una lo lleva escrito
en `DEFAULT_ELEMENT_DEBT`:
- `chronos.event-chip` — aqui el morfo es el que tiene razon, con el motivo
escrito desde el 2026-08-10: el chip CONTIENE el tirador de redimensionado y
el modelo de contenido de un `<button>` no admite un control anidado. El
componente rinde `<button>` igual. Pero el «tabindex propiedad de soma» que ese
mismo comentario promete NO EXISTE en la parte, asi que cambiar el elemento hoy
canjearia HTML invalido por un control sin foco.
- `tooltip.trigger` — `button` declarado, `div` con `tabindex: 0` y sin rol
rendido. Un boton nativo se trae `type=submit` dentro de formularios.
- `stepper.item` — `div` sin rol dentro de un `role='tablist'`. Problema de
patron, no de declaracion.
- `color-picker.channel-input` — compone `ColorFieldInput`, no renderiza un
elemento.
Verificado: guard de `defaultElement` en verde con las 4 de deuda; 2246 tests del
uix (los 6 fallos de `contracts.test.ts` siguen siendo de la otra sesion);
`check` 70 errores, linea base.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2 months ago |
|
|
114a2dd633 |
fix(morfo,soma): `defaultElement` era un contrato que no vigilaba nadie
D7 y D8 no eran dos defectos: eran dos instancias de la misma clase. Un censo
del catalogo encontro que `defaultElement` MIENTE en decenas de partes, y es un
contrato — `assertContract`, las recetas y quien lee el morfo lo toman como el
tag que aterriza en el DOM.
D7 · cinco Labels, no dos
-------------------------
El morfo declara `label` y se renderizaba `<div>` en date-field,
date-range-field, time-field, time-range-field y color-picker.
La otra mitad de la fila era FALSA: decia que la doc promete un par `for`/`id`
que nadie emite. No existe tal promesa — `date-field/types.ts:158` dice lo
contrario y bien («Prefer `aria-labelledby` with a `DateField.Label`»). La fila
habia mezclado el hallazgo del `Field` generico, que si emite `for` condicional.
El nombre accesible ya estaba cableado por `aria-labelledby` desde el
`role="group"` (que es el mecanismo correcto: `for=` no puede apuntar a un
grupo) y el clic-a-enfocar ya lo hacia el provider.
Riesgo comprobado antes de tocar: `<label>` es `inline` y `<div>` es `block`.
Dos reciben `display: inline-flex` de `field.css`; los otros tres son items de
flex o grid y el navegador los blockifica. Medido despues en los cinco demos:
ninguno queda `inline`, ninguno arrastra un `for` que no pueda honrar, y la
maquetacion no se mueve.
D8 · los siete arrow, no uno
----------------------------
La sospecha de deuda sistemica era cierta. Los SIETE declaraban `div` y ninguno
renderiza un `div`: cinco componen `internal/arrow.svelte`, cuyo elemento de
parte es un `<span>`, y dos rinden un `<svg>` propio. Cada morfo declara ahora
lo que su componente renderiza.
El guard, y el falso verde que casi cuela
-----------------------------------------
Sin guard esto vuelve a derivar, asi que hay uno en `contracts.test.ts` con las
24 partes restantes enumeradas en `DEFAULT_ELEMENT_DEBT` — visible y sin poder
crecer — mas un detector anti-podredumbre que falla si una entrada de la lista
deja de estar rota.
El primer borrador se fue en FALSO VERDE. Tomaba «el tag del ultimo `{:else}`»,
y el `<svg>` decorativo de `internal/arrow.svelte` no lleva spread: la busqueda
fallaba, el tag volvia `undefined` y una regresion inyectada en `popover.arrow`
PASABA. Se descubrio inyectandola antes de fiarse. La regla correcta es que el
elemento de la parte es EL QUE DIFUNDE LOS PROPS. Con ella el guard encontro 8
violaciones mas que el censo a mano.
Segunda correccion: hay partes polimorficas por diseño — `media-player.media`
rinde `audio` o `video`, `breadcrumb.link` `span` o `a`. El guard lee TODAS las
ramas y da el contrato por bueno si el declarado esta entre ellas; leer solo la
primera las marcaba como mentira. Eso saco a `chat-composer.input` de la lista
de deuda, donde yo lo habia metido por error — lo dijo el propio detector.
Las 24 que quedan (D11) se parten en dos y por eso no van en bloque: unas el
morfo va detras del componente (arreglo mecanico), otras el componente va detras
del morfo con peso de a11y — `tree-view.item` declara `li` y rinde `div`,
`stepper.list` declara `ol`, `tooltip.trigger` declara `button`. Eso es
semantica de lista y de control: son decisiones, no ediciones.
Queda abierto D12, tambien del censo: `internal/arrow.svelte` dibuja poligono
MAS un contorno trazado, y las dos flechas propias solo el poligono. Dos de
siete flechas del sistema no tienen contorno. El arreglo es que compongan la
primitiva compartida, pero es un cambio visual y no se improvisa.
Verificado: 2240 tests del uix en verde (los 6 fallos de `contracts.test.ts` que
se ven en el arbol son de la otra sesion — waveform, media-player, audio-player,
menubar, aura, radio-group, tabs — y el guard nuevo no esta entre ellos);
`check` 70 errores, linea base; `docs:check` 0/623.
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 |
|
|
fc84305c22 |
feat(direction): el mecanismo pertenece al morfo — la declaracion exige el cable
El campo obligatorio `dir: SomaRuntimeDir | null` de 4.1 conservaba el gate a
costa de ~116 lineas de ceremonia: 67 `dir: null` en fuente + 47 en tests
obligaban a ~75 componentes SIN direccion a pronunciarse sobre un eje que no
les concierne, y `parts` era un `readonly string[]` sin validar — un typo
estampaba nada, en silencio (fail-open). El usuario lo dijo antes que la
auditoria: "los dir no deben ser obligados de pasar".
Ahora lo declara el MORFO, que es donde viven los contratos:
direction: {} // 46 morfos — estampa en 'provider'
direction: { parts: ['trigger'] } // dropdown-menu (su raiz no renderiza)
direction: { parts: ['content'] } // drawer · float-panel (pintan en portal)
direction: { parts: [...,'input'] } // css-field · number-field (dos caras)
y el TIPO se computa de la declaracion, con el generico que ya existia inerte
en soma.runtime<M>:
morfo declara direction => dir: Active<Direction|undefined> REQUERIDO
no declara => dir?: never PROHIBIDO
Olvidar el cable no compila; cablear un eje a un componente sin direccion
tampoco. Y compileMorfo valida direction.parts contra el arbol declarado —
fail-CLOSED: el typo que antes estampaba nada ahora revienta al registrar.
El colapso: 49 getters `{ get: () => this.opts.dir.current }` -> `dir:
this.opts.dir`; los 67+47 `dir: null` MUEREN. El estampado gana su sitio en el
tipo de la superficie (`SomaRuntimePart.props` incluye `dir?: Direction`), asi
que los casts `(props as Record...).dir` de los tests sobran.
El gate cazo en el acto, y las dos presas eran reales:
- `command`: 4.1 le dejo `dir: null` en el runtime y el estampado manual
inline en el assert (la linea no estaba sola y el barrido no la vio) — un
componente CON direccion que habia quedado fuera del mecanismo.
- 5 runtimes SECUNDARIOS del mismo morfo (accordion item, nav-tree item,
emoji-picker cell, context-menu root-runtime, feed sentinel): mismo morfo,
misma exigencia — reciben la afirmacion del dueno (el estampado solo aterriza
en direction.parts, que esos runtimes no registran).
Guards nuevos:
- compile.test: normalizacion a ['provider'], partes explicitas, y el
fail-closed del typo (4 casos).
- direction-census.test: prop publica `dir?: Direction` <=> morfo.direction,
en ambos sentidos, con las excepciones FIRMADAS y sus razones (field-langs:
LangSpec; waveform: delega en el Slider; popover/tooltip/link-preview:
overlays puros cuya pintura estampa la capa flotante en el wrapper
portalizado — declararles direction seria el atributo inutil contra el que
4.1 avisaba). El propio censo cazo a esos tres: la excepcion quedo escrita
el mismo dia que el guard.
Verificado en Chrome con la fuente nueva (el morfo): dropdown-menu estampa en
el TRIGGER, css-field en raiz E input, drawer con pagina rtl por prefs deja el
content SIN atributo y computa rtl por herencia — el modelo del flip intacto.
check 77 = linea base · morfo+soma+active-uix 1401/1402 (el 1 es
soma-attr-audit, flaky bajo carga, pasa aislado) · rtl:check 1 (palabras) ·
docs:check 0/566.
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 |
|
|
41e1f28306 |
feat(prefs): el `lang` del documento sigue al idioma, como ya hacia el `dir`
`src/app.html` trae `<html lang="en">` hardcodeado y NADIE lo actualizaba: con arabe seleccionado la pagina quedaba correctamente espejada (`dir="rtl"`, que la proyeccion si escribe) pero anunciada en el idioma equivocado. El `lang` gobierna seleccion de fuentes, corte de palabras, glifos de comilla y lo que lee CUALQUIER lector de pantalla. `lang` viaja con `dir` porque responden a la misma pregunta sobre el documento y el navegador lee LOS DOS del DOM. La dimension `language` ya estaba en el esquema —es de donde `prefs` deriva la direccion— asi que el slot ya estaba disponible: el valor efectivo es un tag BCP-47, que es justo lo que el atributo toma. Un esquema SIN la dimension `language` no produce slot y no se proyecta nada, asi que una app que gobierne el `lang` desde el servidor (i18n por routing) queda intacta. Cambia el contrato declarado: `prefsDomProjection.ownsAttrs` pasa a `['dir','lang','data-motion','data-sound','data-haptic']`, con su guard en `contracts.test.ts` y el README de prefs. Medido en Chrome por el camino real (el selector de idioma de la shell): ar -> lang=ar/dir=rtl · en -> lang=en/dir=ltr · es -> lang=es/dir=ltr. Un solo `language.set` mueve los dos: el `dir` por derivacion, el `lang` directo — y eso queda fijado en `dom-projection.test.ts`. Sin tocar el `<main lang="en">` de las demos: NO es el defecto, es correcto — la prosa de las demos esta en ingles (decision de 6.4). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
2 months ago |
|
|
2de1718c3b |
refactor(langs): words se pliega en palabras — y el nombre sale del corpus
El pack words desaparece: sus strings de motor entran en palabrasLangs y las guardas (ACTIVE_DEV_TRACK, WIP_TRACKS) dejan de listar 'words'. Las menciones en comentarios de recetas, READMEs, SPECs, demos y docs de proceso pasan a Palabras. Co-Authored-By: Claude Fable 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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
4addfd7dbf |
fix(cleanroom): P1s ejecutados (EID-1/EID-2/SEM-1/MOR-4/THM-1/THM-3) + auditoría + plan scene/ambient
Clean-room fixes (2026-07-11, sobre los P1 de la auditoría):
- EID-1: el anillo de foco de la fundación migra a `outline` (§32) — el último
box-shadow del catálogo; :where()-wrapped para que las recetas siempre ganen;
doctrina actualizada en theming/reference + changelog.
- EID-2: opacidades literales de archetypes.css → var(--opacity-disabled/hover).
- SEM-1: política de live-region unificada — la urgencia va SOLO por intent
(threat/loss → assertive), runtime y AnnounceChannel ya coinciden.
- MOR-4: card-group con morfo propio (scope eidos) + guard de enumeración.
- THM-1: component-audit + recipe-css-contract escanean los *.css hermanos
(calendar-select, color-picker-spectrum, date-range-picker-time…); R-2.1
caza también hsl()/oklch(); violaciones de los secundarios corregidas.
- THM-3: canario de censo de toggle volteado (lint.test); commit-block promovido
a firma global block-shake; regen de generated/base.css.
Docs nuevos:
- docs/audit-new-10-july/audit-active-uix.md — auditoría clean-room 2026-07-10
del ecosistema (hallazgos P1–P3 con evidencia ruta:línea, matriz y gap analysis).
- docs/process/PLAN-scene-ambient-pack.md — plan por fases: motor arts/scene +
pack Ambient (45 animaciones de demos) + vía de promoción agéntica (Aura);
gate de decisiones D1–D9.
- docs/Disenando_lo_que_ocurre_FINAL.{docx,pdf} — edición FINAL del libro.
- README.md raíz (esqueleto de paquete).
Excluidos deliberadamente de este commit: words/palabras/chronos (pista
separada), web/routes/alpha (terminado, sin commitear por decisión), rt.css
(volcado de referencia Radix Themes).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
bdd17f04df |
feat(float-panel): runtime discipline + keyboard gesture semantics — contracts.test 37/37 GREEN
The convergence session's second half (with field commit-submit landed in
|
3 months ago |
|
|
cb554ae2e7 |
fix(contracts): catalogue-guards census — 12 -> 3 failures (knob/menu-dial/onion-menu + unmasked debt)
The "3 ajenos" census. What the 12 pre-existing failures really were: a few
genuine violations + several guards CRASHING on menu-dial's missing
exports.ts (ENOENT) and hiding their true violation lists. All addressed
except the two runtime-discipline files (see tail).
- knob: soma barrel export + the missing soma README (headless contract —
parts/ARIA/absolute angular keyboard/props/published CSS vars/sema table).
- menu-dial + onion-menu: joined SOMA_INTERNAL_PRIMITIVES — both are
documented provider-less bridges (keyboard dispatch + nav maths + emission;
the eidos root owns state and render). The scope guard now subtracts the
internal set from BOTH sides so a bridge may truthfully keep 'soma' in its
morfo scope without being a public namespace. Self-alias imports fixed;
the two morfo docblocks realigned to the implementation ("soma owns open"
was drift — the bridge files own dispatch/emission only; onion-menu's
stale "family-default, no pack" tail also corrected, S3b shipped its pack).
- Public barrels decoupled from provider implementation filenames (the
picker `internals.ts` house pattern) for the 6 offenders: calendar,
cropper, field-langs, gradient-builder, gradient-picker, image-adjustments.
- listSourceFiles now skips ACTIVE_DEV_TRACK paths (words/palabras/chronos)
— the track doctrine already said catalogue guards skip them; the
file-scanning guards just didn't.
- data-archetype added to the known morfo attrs (compiler-emitted from part
archetype declarations); the four field-overlay picker triggers
(color-picker, date-range-picker, time-picker, time-range-picker) declared
archetype 'trigger' while stamping 'field-trigger' at runtime — morfos
aligned to the date-picker/ntp precedent (contract now matches the DOM;
zero runtime change, the stamp already won).
- float-panel: the consumer-placed data-float-panel-no-drag opt-out marker
is now declared in the morfo (presence entry with the consumer-marker
note) — the NON_DRAG_SELECTOR contract stops being invisible.
Remaining tail (3 failures, deliberately deferred to a dedicated
runtime-discipline session with browser gesture verification):
float-panel-provider direct style writes + raw timer (drag/resize hot path
-> dom.apply/uix.timers migration) and media-player media-provider raw
listeners (-> dom.listen).
Gates: contracts 34/37 (was 25/37) · component-audit 102 PASS · vocab exit 0
· type baseline 57 · eidos-lint invalid 0 on the four touched pickers ·
compile + eidos contract tests 42/42.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
|
|
8e3a67bdb4 |
feat(metrics): S9 metrics+suite — internal-bridge classification + apg + dossier + suite (initiative close)
- contracts.test: metrics joins SOMA_INTERNAL_PRIMITIVES (with picker-shell) — its soma layer is a deliberate provider-less runtime bridge (documented in-place: "no provider/state class"; morfo scope ['sema','eidos'] on purpose), NOT a public component namespace. Classification fix, not an exemption of debt: the barrel/exports/README guards stop mis-demanding a canonical Provider shape it was designed not to have. Also fixes the bridge's $soma self-alias import (the internals guard). Contracts: 13 -> 12 failures, ZERO metrics mentions (the remaining 12 belong to knob/menu-dial/onion-menu). - morfo: apg 'none — data display readout' (C5 form) — clears A-1.4; the README's stale "A-1.4 N/A" audit note updated to point at the declaration. - README upgraded to dossier form (Baseline Untitled-UI/Tremor/Chakra Stat, Comparativa with the goodTrend decoupling as the differentiator, Decisiones incl. the internal-bridge classification, Gaps with the D-1.2 demo union routed to the cross-catalog demo census). - Suite (the ficha's F-2): resolveDeltaIntent extracted to a pure helper (delta-intent.ts, consumed by Metrics.Delta) + delta-intent.test.ts 4/4 (direction/valence decoupling, goodTrend flip, flat neutral, explicit override) + metrics.svelte.test.ts (the live-update bridge: signal.notify carries the prop-source intent; the morfo's requiresLiveRegion contract). Gates: component-audit metrics clean except D-1.2 (demo census) · eidos-lint invalid 0/class-hooks 0 · type baseline 57 · contracts 25 pass/12 pre-existing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
3 months ago |
|
|
45ae36919a |
feat(gradient): S9 gradient-* — graduate from ACTIVE_DEV_TRACK + apg + dossiers (initiative close)
The initiative's real remaining scope (verified 2026-07-10 against the repo; the "uncommitted work" memory was stale — builder landed in |
3 months ago |
|
|
b3d0e29037 |
feat(gradient): GradientBuilder editor polish + presets; land gradient WIP
GradientBuilder (this session): - Stop color editor uses the canonical ColorField value row (swatch + styled Select), not a native select; rails at 90% width + xl track. - Composable footer: ColorPicker.Cancel / ColorPicker.Close + trash remove-stop, as a sibling of PickerShell.Body. - Surface-agnostic by default (variant='ghost'): no card, adapts to host. - '+' add-stop on the kind-switch row; inserts between the selected stop and the next; rail click-to-add re-enabled (insert at cursor). - GradientBuilder.StopList composable stops list (rendered by default). - presets prop (Gradient[]) rendered below the editor (flex, not tabs); save-to-presets with a maxPresets cap; the action button flips Save <-> Delete when a saved preset is selected (ColorPicker mechanic). Also lands the in-progress gradient tree so HEAD is self-consistent (langs/components/index.ts already imported ./gradient-picker, whose file was untracked). gradient-builder + gradient-picker stay on ACTIVE_DEV_TRACK; the GradientPicker 2-tab redesign is next (see gradient-builder/CONTINUE.md). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
c474fc6bae |
feat(eidos): gradient axis (6th builder) + elevation-scaled opacity + GradientBuilder scaffold
Gradient axis — Phase 1 (engine + dogfood):
- $libs/gradient: canonical Gradient model + gradientToCss serializer, pure and
zero-dep, shared by the theming axis and the (WIP) GradientBuilder. Stops
reference color ROLES → a --gradient-{name} re-tints with the seed and flips
light/dark for free. Default `in oklch` interpolation.
- build-gradient: role-derived factories (deepen/sheen/halo/aurora mesh) +
ActiveEidos.applyGradients() (the 6th runtime builder, in ThemeSeed/applyTheme).
- shimmer migrated to `in oklch`. Dogfood: /demos/cristal replaces its ~12
hardcoded gradients with applyGradients role tokens.
Opacity = function of elevation (depth cue `translucency`):
- New translucency depth-plane cue, sibling of shadow/blur: frost opacity now
scales with elevation (foundation overlay 68% / modal 80%; cristal 52→66→80)
via --depth-{plane}-translucency, consumed by the frost rule. base.css regenerated.
GradientBuilder component — Phase 2 (scaffold, WIP):
- morfo (contract) + soma provider state machine over the Gradient model: stops
add/move/remove/recolor, kind, angle, pointer drag; every stop a keyboard-
accessible role=slider thumb. Marked ACTIVE_DEV_TRACK until eidos/picker/demo land.
Docs: $libs/gradient README + THEMING §gradient-axis / §translucency + token table.
Demo cristal: scroll-reveal via uix.motion spring + hover glow; Select z-index
ladder; aurora/title → role-derived tokens.
contracts.test: ACTIVE_DEV_TRACK now filtered uniformly in both soma collectors.
check: 0 new errors. Tests: $libs/gradient + build-gradient + base.css sync green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
8606232125 |
fix(eidos): THEME-SYS-1 — consolidate overlay z-index into a named scale (+ guard + docs)
The ~12 overlay recipes hardcoded an ad-hoc parallel z-index scale (raw integers
60–99/1200) that duplicated nothing reusable and had drifted out of order
(tooltip 76 < dropdown 80 — a tooltip painted BEHIND a dropdown). They are now a
named scale.
- `STATIC_Z_INDEX_OVERLAY` (static.ts) → emitted as `--z-index-overlay-{inline,
backdrop,content,floating,tooltip,detached,toast}`. A SEPARATE scale from the
global `--z-index-*` ladder (which orders the depth planes) — overlays portal
to <body> as siblings of modals, so they share one flat low band where each
rung sits just above the modal scrim. Mapping them to the 300–900 ladder would
hide a dropdown/select/popover opened INSIDE a dialog (dropdown 300 < modal
700); the combobox recipe already warned about this. `tooltip` now sits above
`floating` (fixes the inversion); `toast` stays above the soma FloatPanel band.
- Every overlay recipe token (`content-z`/`overlay-z`/`inline-z`/`toaster-z`/
`preview-z`) now references `var(--z-index-overlay-*)` — zero raw integers.
dialog/drawer gain an explicit `content-z` rung (drops the `calc(... + 1)`).
- Guard (contracts.test.ts, "overlay z-index against raw integers"): a recipe
`*-z` token must reference the scale, never a bare integer. Proven to catch
drift (a raw `'76'` makes it fail). Local `z-index: 0..5` (avatar/tabs/sticky)
is intra-component relative stacking — out of scope, stays.
- Docs: THEMING.md §35 rewritten to describe the consolidated scale + the
flat-band rationale + the guard; token table gains `--z-index-overlay-*`;
testing-and-tooling.md documents the catalogue guards (VG-8/SYS-1/A31/A30/
THEME-SYS-1).
Stacking order verified from the resolved CSS (deterministic z compare: content
70 < floating 80 < tooltip 90 < toast 1200; dropdown-in-dialog preserved). A
live browser check was blocked by a port conflict with another session's server.
check: 0 new type errors; the 5 guards green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
3 months ago |
|
|
a2ae2c2007 |
fix(uix): SYS-A30-EFFECT — register inputId directly, not in a $effect (+ guard)
date/time/color-field assigned `field.inputId.current = opts.id.current` inside a `$effect` — the A30 anti-pattern (an effect for a one-shot id registration is wasteful + order-fragile). `opts.id` is available at construction and the parent Field context resolves synchronously, so this is a plain constructor assign. Mirrors the 12 field-family providers that already do it right (the fix is byte-equivalent to number-field:583-587 / css-field). Verified by that equivalence + the field providers' 15/15 tests (incl. color-field's input/label wiring test); a live browser check was blocked by a port conflict with another session's dev server. Guard (A30, contracts.test.ts): flags `$effect(... inputId.current = opts.X.current ...)`. - Proven to catch drift: reverting date-field to the effect form makes it fail. - Scoped to an `opts.*` source on purpose: file-upload registers `this.hiddenInputId` (empty '' until a child part mounts) inside a $effect — that source is genuinely async, so the effect is JUSTIFIED, and the `opts.*` requirement leaves it green without an exception list. (The guard surfaced file-upload during development, which is how the distinction was found.) check: 0 new type errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
ec100c4bb8 |
test(uix): add VG-8 + A31 regression guards; fix the toolbar A31 the guard found
Two more catalogue invariants in contracts.test.ts, locking in audit fixes: - VG-8: every component morfo must be declared `as const satisfies Morfo`, never `: Morfo` (the annotation widens the literal so the schema can't check it — how alert-dialog-001 shipped). Green; proven to catch drift (annotating toggle as `: Morfo` makes it fail). - A31: a per-item membership predicate (`isSelected` / `isItemPressed` / …) must not do `.current.includes(...)` — that is O(N²) per render. Lift a Set, use `.has()`. The A31 guard immediately surfaced a real violation the human audit missed: `toolbar-provider` ToolbarGroupProvider.isItemPressed did `this.opts.value.current.includes(value)`, called from the per-item `isPressed` $derived — the exact O(N²) pattern, in a component not on the audit's 9-item A31 list. Fixed with the same lift (`pressedSet` + `.has()`). That catch IS the guard's proof. Both guards exclude the active-dev-track set so they stay green for the maintained catalogue. check: 0 new type errors; toolbar provider tests 3/3. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
3 months ago |
|
|
e378e14a62 |
test(uix): add SYS-1 scope-drift regression guard
A catalogue invariant in contracts.test.ts: every maintained component that
ships an eidos recipe (a `.svelte` wrapper or `.css` under
`eidos/components/{kebab}/` — README-only dirs don't count) MUST declare
`'eidos'` in its morfo `scope`. This is the prevention for the 62-component
drift the audit found and just fixed — turns "an agent has to re-audit" into
"the build goes red".
- Green now (scope-drift = 0 after
|
3 months ago |
|
|
0d66e5a4e7 |
fix: address architectural audit findings + sync ecosystem docs
Audit: src/audit-opus-4-6-26.md. All non-words findings remediated. Code: - E1: navigation-menu indicator data-state visible -> open (render bug; the active underline was permanently invisible). Clears the only invalid lint selector. - SO1 + E2: raw `new ResizeObserver` -> ActiveDom.observeResize in carousel-provider and the canvas-text useContainerWidth hook (+ s-text / s-text-virtual-list pass eidos.dom). iframe/popup-safe, lifecycle-tracked. - A1: defineUixServices now registers `motion`, so attach-mode app.motion is real and the active-uix fallback becomes the true edge case (test guard updated). - A2/A4: contracts.ts pins `motion` + `announce` in ActiveUixServiceContract + publicSurface; dispose() comment corrected. - SO2: carousel drops the hardcoded `transform 300ms ease-out` (the recipe already handles it via [data-dragging]); also fixed the recipe's undefined `--duration-base` token -> `--duration-slow` (it was masked by the inline). - S1: HapticChannel reduced-motion via an injected ActiveDom port (mirrors SoundChannelDom) instead of global matchMedia. - T1: 10 sites repointed `$libs/dom` -> `$adom` (sema x5 + its tests x3, active-uix value import, arts/prefs). - M2 / S2 / S3: dead code removed (button `states:['idle','loading']`, SemaRuntimeChannelId, SEMA_VALENCED_FAMILY_LIST). Docs: - Motion-as-service reflected across the ecosystem: CLAUDE.md (aliases + arch + service note), arts/README, active_architecture, soma SOMA_ARCHITECTURE, eidos README, eidos-motion.md. - X1: CLAUDE.md "5 canonical channels" (false) -> the single canonical narrative (8 book channels; Sema runs 2 + visual meta-channel, Eidos materializes 5). - X2/X3/X4: sema/README (8 families + intentRequirement/intentGuidance split), types.ts JSDoc (SEMA_INTENT_POLICY -> SEMA_FAMILY_POLICY), engine.ts cascade 6->5, alias table ($frontend out, $lang->$langs, +$clipboard). - E4: codex_audit.md HISTORICO banner. Deferred: SU2 (test-only layering, not a build violation); Words M1/T5/E3 (WIP). Verify: npm run check -> 1 pre-existing error (grafito), 0 new; sema 152/152, contracts 31/32 (1 pre-existing words), carousel 4/4, motion 22/22. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
4 months ago |
|
|
bceaef41e0 |
Theming: add `scaling` zoom axis (Radix parity), separate from density
Introduce a global zoom axis independent of density, in parity with Radix
Themes' `scaling` (90/95/100/105/110%). Scaling zooms px metrics INCLUDING
typography (font-size, icon-size, space, control-height); density only moves
layout rhythm + control height and leaves text fixed. The two axes compose
multiplicatively.
- config-types: SCALING_KEYS / ScalingKey / DEFAULT_SCALING; DensityPrimitiveSet
drops the dead `scale` + `contentScale` (kept spaceScale, controlScale).
- primitives/static: STATIC_SCALING (0.9..1.1).
- render-css: appendScaledMetricDeclarations wraps metrics in
calc(<raw>[ * var(--density-x-scale)] * var(--scaling)); appendScalingDeclarations
emits --scaling-{key} + --scaling default; renderScalingBlocks emits
[data-scaling] blocks. line-height/radius/border/shadow excluded.
- config + contract: prune the removed density scalars.
- active-eidos: `scaling` / `scalingSource` options, getScaling() on the
preference source, data-scaling projection + dispose cleanup.
- docs: THEMING.md section 23 + 20.1 reconcile; README density/scaling; SCALING_RFC.md.
Verified: npm run check (0 new errors), vitest eidos (0 new regressions),
browser cascade at 90/100/110 scales font/space/control x0.9/x1.1 and leaves
radius/border fixed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
4 months ago |
|
|
681e056102 |
fix(uix-audit): close 4 pre-existing issues found in system audit
System-wide audit of `src/uix/*` + `src/arts/adom` (excluding demos and web/routes). Findings + fixes documented in `src/uix/AUDIT_REPORT_2026-05-27.md`. **Fixed**: 1. `src/uix/eidos/lint.test.ts`: "every eidos CSS component has a morfo file" test extended to walk `morfo/internal/` as well as `morfo/components/`. The picker-shell relocation (audit-codex P1 #5) moved picker-shell to internal but the test wasn't updated. 2. `src/uix/contracts.test.ts`: two pre-existing failures resolved. (a) Picker WithChild regex updated to accept the `WithChild<Omit<PrimitiveDivAttributes, ...> & {...}>` shape applied in the audit-codex P0/P1 picker ProviderProps refactor. (b) `collectPublicSomaComponentDirs` now skips `picker-shell` as an internal primitive (symmetric to `collectKnownMorfoDataAttrs` which now walks `morfo/internal/` to include the picker-shell data-attrs). 3. `src/uix/soma/components/command/command-provider.svelte.ts`: restored missing `createAttrs(commandMorfo)` import. Was the only ERROR in `npm run check` baseline. 4. `src/uix/soma/components/dialog/types.ts`: JSDoc comment updated to reflect polymorphic close shape (cosmetic alignment with the 2026-05-27 refactor). **Result**: - `npm run check`: 1 ERROR → **0 ERRORS** ✓ - `npx vitest run src/uix`: 5 failed → **2 failed** (both 100% in user's words/* WIP, not framework code) - Polymorphic close + persistence + a11ySemantic rollout: **zero drift detected** across morfos / providers / sema cascades / tests. **NOT actioned** (documented in report): - 6 pre-existing `morfo:check` failures (avatar / color-picker / date-picker / dropdown-menu / table / tree-grid). All require domain decisions (drop 'idle' state vs add it to morfo enum, etc.). - 1 navigation-menu data-state='visible' eidos selector drift. - ~14 README/`.md` files with stale Dialog/Drawer/Popover descriptions. - Words editor doctrinal cleanup (user's WIP). - Tooltip polymorphic alignment (optional, no consumer benefit today). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
5256008700 |
fix(audit-codex): resolve P0 blockers + close most check errors (42 → 5)
Codex audit P0 items addressed: P0 #1 - color-picker morfo invariante (`morfo:vocabulary` failed): - close-commit / close-cancel / close-dismiss / dismiss-outside prewrites escribian `data-last-action` en Content sin declararlo. Anadido a la declaracion de `data` con values [committed/cancelled/dismissed/dismissed-outside], severity optional. P0 #2 - password-field `kind: 'private'` rechazado por tipo: - `MorfoPartKind` extendido de `'public' | 'virtual'` a `'public' | 'private' | 'virtual'`. Semantica: `private` = en contrato (eidos lo estila, validador lo chequea) pero no compuesto por consumer (oculto en docs). Justifica el StrengthMeterLabel sub-part. P0 #2 - Soma `defaultElement: 'time'` rechazado: - `MorfoElement` extendido con `'time'`. format-date y relative-time morfos lo usan. P0 #2 - Picker family drift (12 errors): - `DatePicker/DateRangePicker/TimePicker/TimeRangePicker.ProviderProps` pasaron de "virtual composition shell" (sin WithChild) a `WithChild<Omit<PrimitiveDivAttributes, 'value' | 'placeholder'> & {...}>` para alinear con la impl real que renderiza <div>, expone ref/child y acepta data-* attrs (mismo patron que date-field/time-field). - contracts.test.ts §1.1.C actualizado: ahora exige WithChild + DOM passthrough en lugar de prohibirlos. Coherente con date-field/time-field. P0 #2 - Eidos year/month-view en date-picker pasaba `size` a Soma: - Soma's DatePicker.YearView no modela `size` como prop (delega a YearGrid.Provider que tampoco). Cambiado a `data-size={...}` para que fluya por DOM passthrough hasta el recipe eidos que keys por `[data-size]`. P0 #2 - `KEYS.Enter` y `KEYS.Space` no existen: - table-provider usa `KEYS.ENTER` y `KEYS.SPACE` (ya correctos en consts). P0 #2 - `Snippet` import faltante en time-range-picker types. P0 #5 - tmp/lexical contaminaba `npm run check`: - `tsconfig.json` anadido `exclude: ['tmp/**', ...]`. svelte-check ya no carga los `svelte.config.js` de lexical demos (faltaban deps). Errores mios introducidos por el sprint reciente: - `DemoLocale` no asignable a `SupportedLocale`: cambiado tipo del prop `locale` en FormatDate/FormatNumber/RelativeTime a `string` (acepta cualquier BCP-47, Intl no necesita restricciones de catalogo). - `RelativeTimeUnit` con `'auto'` no asignable a `Intl.RelativeTimeFormatUnit`: `resolveUnit()` ahora tipa el retorno como `Exclude<RelativeTimeUnit, 'auto'>`. Errores pendientes (5): - `floating/shell.ts:92` - wrapperProps requiere `transform: string | undefined` (no optional) pero buildFloatingShellWrapperProps devuelve optional. - `date-picker-year-view.svelte` / `date-picker-month-view.svelte` x2: Soma YearView/MonthView accepta `dir: 'ltr' | 'rtl' | undefined` pero HTMLAttributes provee `'ltr' | 'rtl' | 'auto' | null | undefined`. Conflict en passthrough. Estos 3 son drift entre Soma type strict y HTML passthrough laxo. Necesitan diseno mas cuidadoso (audit recomienda "resolver como familia"). Estado: 42 errors → 5 errors. morfo-check pasa. morfo:vocabulary pasa (con warnings ya conocidos de words). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
2b3f3e093e |
uix: rename morfo.translations → morfo.texts (idlangref discipline)
Morfo now declares text slots as absolute idlangrefs in `texts: Record<string, LangRef>`,
not literal multilingual records. Multilingual content moves out of morfo entirely
into per-component catalogs under `src/uix/langs/components/{kebab}.ts`, merged
globally by `ActiveUix` via the new `componentLangs` barrel. Morfo stays a pure
abstract surface; localization lives in its own layer.
- Type: `translations?: LangNode` → `texts?: Record<string, LangRef>` in
`src/uix/morfo/types.ts`. Doc and test fixtures updated.
- Catalog: new `src/uix/langs/` directory with `common.ts` (was `langs.ts`),
`components/` (one file per migrated morfo, plus a barrel), and an aggregate
`uixLangs`. `ActiveUix` registers both namespaces at boot.
- Registry: `registerMorfo` no longer carries a translations fingerprint and
no longer extends `ActiveLangs` dynamically. `connectMorfoTranslations` and
`MorfoTranslationTarget` deleted from the public morfo barrel.
- Validation: schema.ts drops the relative-translationRef-in-morfo.translations
check; catalog presence is enforced by the new `scripts/translations-check.ts`
(wired as `npm run translations:check`).
- Audit: `scripts/component-audit.ts` rule A-1.3 now validates `texts.label`
idlangref shape (`#?components.{kebab}.label|Fallback`) and explicitly errors
on any surviving legacy `translations:` field.
- Migration: `scripts/migrate-translations-to-texts.ts` is the one-off Node
script that produced the 42 morfo rewrites and 42 catalog files.
- Tests: `morfo/registry.test.ts` rewritten around the simpler contract;
`active-uix.svelte.test.ts` reflects boot-time catalog availability instead
of lazy per-morfo extension.
Verification: `npm run check` (0 errors), `npm run translations:check`
(0/0), `npm run morfo:check` (36/36 routed). The 4 pre-existing failures
in `test` are unrelated (date-picker types, css regen, range-calendar
clear, color contract).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
7a85ea59c2 |
Align Soma public wrappers with providers
|
5 months ago |
|
|
f276ef964f |
Guard Sema runtime boundaries
|
5 months ago |
|
|
e3689c1080 |
Remove Soma common langs facade
|
5 months ago |
|
|
e709316380 |
Narrow Soma root barrel
|
5 months ago |
|
|
e7bb709e83 |
Keep Soma internals off public alias
|
5 months ago |
|
|
2fb1dd78c9 |
Guard Soma component README links
|
5 months ago |
|
|
8baa422974 |
Guard Soma wrapper export contract
|
5 months ago |
|
|
96edd41545 |
Tighten Soma public component surface
|
5 months ago |
|
|
ca9359c32f |
Add ActiveClipboard service for Soma clipboard
|
5 months ago |
|
|
48149ccf02 |
Route Soma DOM actions through ActiveDom
|
5 months ago |
|
|
847151387e |
Remove Soma external facades
|
5 months ago |
|
|
487ec9b5c9 |
Share Soma list typeahead buffers
|
5 months ago |