You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/process/CONTINUE-direction-runtime.md

495 lines
25 KiB

docs(direction): handoff del refactor — la direccion deja de ser una convencion El usuario preguntó si lo entregado era lo mas logico y profesional. No lo es: el contrato es correcto y el MECANISMO para cumplirlo es teclear la misma linea en cada componente. 54 estampados a mano, 7 enlaces al padre a mano, 9 Content que re-arrancan la cadena por su cuenta, y 20 de 55 componentes que lo incumplian hasta `21055bd3c`. Una convencion que hay que repetir 54 veces y que 20 de 55 incumplian no es una convencion — es una abstraccion que falta. `docs/process/CONTINUE-direction-runtime.md`, para abrir en sesion nueva. Lleva LAS DOS COMPROBACIONES DE VIABILIDAD YA HECHAS, que es lo que separa un handoff util de una idea: - El runtime YA omite un atributo cuyo valor resuelve a `undefined` (`runtime.svelte.ts:519-522`). Es exactamente la semantica del estampado crudo. No hay que construir nada para eso. - El runtime YA inyecta atributos sin declaracion en morfo — `id`, el marcador, `data-archetype`, `reg.attachment` (`:538-551`), con el comentario que lo justifica. Asi que el movimiento NO exige tocar 55 ficheros de morfo. - La capa flotante tiene acceso al dueño: los sub-proveedores llevan `this.provider`. Y el reparto de los 11 consumidores es el diagnostico — 8 pasan el `dir` DEL PROPIO Content (la fuga), 3 el de la raiz (lo correcto). Dos movimientos, con el orden razonado (la propagacion primero: mas acotada, verificable de un vistazo, y BORRA codigo, asi que el segundo llega a un arbol mas limpio). Dos vehiculos posibles para el primero, sin decidir a proposito. Lleva ademas la linea base exacta de verificacion y las trampas ya pagadas: el crudo nunca resuelto, `dir` en un `<svg>` no hace nada, `OptsFromProps` quita el `undefined`, clasificar por grep se equivoca (me paso con `avatar`), y una migracion mecanica hereda los defectos que traduce. ⚠️ Anota lo que el refactor NO debe romper: los 4 que correctamente NO estampan (popover, tooltip, link-preview, context-menu) y la excepcion firmada por el usuario de `avatar`/`image`, que no aceptan `dir` a proposito. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
# CONTINUE — la dirección deja de ser una convención
Handoff para una sesión nueva. **Leer entero antes de tocar nada**: el refactor
es pequeño en líneas y grande en alcance — toca un mecanismo del que cuelga todo
el catálogo.
Contexto previo: `docs/process/CONTINUE-direction.md` (el eje entero) y
`docs/canon/direction-contract.md` (el contrato, que es lo que este refactor
automatiza). **No hay que releerlos para empezar**; lo que hace falta está aquí.
---
## 1. Por qué — los números
El contrato es correcto. El **mecanismo** para cumplirlo es teclear la misma
línea en cada componente, y por eso falla:
| | veces |
| -------------------------------------------------------------- | ------------ |
| Estampado del `dir` escrito a mano en un provider | **54** |
| Enlace al padre compuesto a mano en un envoltorio | **7** |
| `Content` portalizados que re-arrancan la cadena por su cuenta | **9** |
| Componentes que aceptaban `dir` y **no** lo estampaban | **20 de 55** |
Los 20 se arreglaron uno a uno (`21055bd3c`), leyendo receta y envoltorio de
cada uno. Encontrarlos costó una auditoría. **El siguiente componente que
alguien escriba volverá a tenerlo**, porque nada lo impide: la regla vive en la
cabeza del que lo escribe.
Una convención que hay que repetir 54 veces y que **20 de 55 incumplían no es
una convención — es una abstracción que falta**.
---
## 2. Las dos comprobaciones, HECHAS
Las dos condiciones de viabilidad están verificadas. No hay que repetirlas.
### 2.1 · ¿Puede el runtime emitir un atributo que a veces debe estar AUSENTE?
**Sí, ya lo hace.** `src/uix/soma/runtime.svelte.ts:519-522`, en `renderProps()`:
```ts
for (const plan of compiledPart.dynamicAttrs) {
const value = evalAttrPlan(plan, bindings);
if (value !== undefined) props[plan.attr] = value;
}
```
`undefined` ⇒ el atributo **no se escribe**. Es exactamente la semántica del
estampado crudo: nadie afirmó ⇒ no hay atributo ⇒ el elemento hereda.
`resolveProps()` (`:496-503`) hace lo mismo. **No hay que construir nada para
esto.**
### 2.2 · ¿Y sin declararlo en el morfo?
**También, y hay precedente literal.** `partPropsForRegistration()`
(`runtime.svelte.ts:538-551`) ya inyecta `id`, el marcador del componente,
`data-archetype` y `reg.attachment` **sin ninguna declaración en el morfo**, y
el comentario de `data-archetype` justifica por qué:
> `data-archetype` is part of static identity (cross-component classification,
> never mutates), so it ships through partProps — not through dom.apply.
Así que **el movimiento 1 NO exige tocar 55 ficheros de morfo.** Hay dos
vehículos posibles y la elección es de quien lo haga (§4.1).
### 2.3 · ¿Tiene la capa flotante acceso al dueño?
**Sí.** Los sub-proveedores llevan `this.provider` (verificado en `select` y
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
`popover`).
docs(direction): handoff del refactor — la direccion deja de ser una convencion El usuario preguntó si lo entregado era lo mas logico y profesional. No lo es: el contrato es correcto y el MECANISMO para cumplirlo es teclear la misma linea en cada componente. 54 estampados a mano, 7 enlaces al padre a mano, 9 Content que re-arrancan la cadena por su cuenta, y 20 de 55 componentes que lo incumplian hasta `21055bd3c`. Una convencion que hay que repetir 54 veces y que 20 de 55 incumplian no es una convencion — es una abstraccion que falta. `docs/process/CONTINUE-direction-runtime.md`, para abrir en sesion nueva. Lleva LAS DOS COMPROBACIONES DE VIABILIDAD YA HECHAS, que es lo que separa un handoff util de una idea: - El runtime YA omite un atributo cuyo valor resuelve a `undefined` (`runtime.svelte.ts:519-522`). Es exactamente la semantica del estampado crudo. No hay que construir nada para eso. - El runtime YA inyecta atributos sin declaracion en morfo — `id`, el marcador, `data-archetype`, `reg.attachment` (`:538-551`), con el comentario que lo justifica. Asi que el movimiento NO exige tocar 55 ficheros de morfo. - La capa flotante tiene acceso al dueño: los sub-proveedores llevan `this.provider`. Y el reparto de los 11 consumidores es el diagnostico — 8 pasan el `dir` DEL PROPIO Content (la fuga), 3 el de la raiz (lo correcto). Dos movimientos, con el orden razonado (la propagacion primero: mas acotada, verificable de un vistazo, y BORRA codigo, asi que el segundo llega a un arbol mas limpio). Dos vehiculos posibles para el primero, sin decidir a proposito. Lleva ademas la linea base exacta de verificacion y las trampas ya pagadas: el crudo nunca resuelto, `dir` en un `<svg>` no hace nada, `OptsFromProps` quita el `undefined`, clasificar por grep se equivoca (me paso con `avatar`), y una migracion mecanica hereda los defectos que traduce. ⚠️ Anota lo que el refactor NO debe romper: los 4 que correctamente NO estampan (popover, tooltip, link-preview, context-menu) y la excepcion firmada por el usuario de `avatar`/`image`, que no aceptan `dir` a proposito. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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
> ⚠️ **El reparto que decía este apartado era FALSO, y la fuga no estaba donde
> decía.** Censo real (§9.1): de los 11 `FloatingContent.create`, **10** pasan
> `dir: opts.dir` y **1** (`sidebar`) pasa `this.provider.opts.dir` — no 8 y 3.
> Y ese `opts.dir` **no era una fuga**: los 7 envoltorios `Content` que lo
> alimentan ya componían el enlace al padre a mano, así que la afirmación de la
> raíz SÍ cruzaba. Lo que no cruzaba era otra cosa (§9.1).
docs(direction): handoff del refactor — la direccion deja de ser una convencion El usuario preguntó si lo entregado era lo mas logico y profesional. No lo es: el contrato es correcto y el MECANISMO para cumplirlo es teclear la misma linea en cada componente. 54 estampados a mano, 7 enlaces al padre a mano, 9 Content que re-arrancan la cadena por su cuenta, y 20 de 55 componentes que lo incumplian hasta `21055bd3c`. Una convencion que hay que repetir 54 veces y que 20 de 55 incumplian no es una convencion — es una abstraccion que falta. `docs/process/CONTINUE-direction-runtime.md`, para abrir en sesion nueva. Lleva LAS DOS COMPROBACIONES DE VIABILIDAD YA HECHAS, que es lo que separa un handoff util de una idea: - El runtime YA omite un atributo cuyo valor resuelve a `undefined` (`runtime.svelte.ts:519-522`). Es exactamente la semantica del estampado crudo. No hay que construir nada para eso. - El runtime YA inyecta atributos sin declaracion en morfo — `id`, el marcador, `data-archetype`, `reg.attachment` (`:538-551`), con el comentario que lo justifica. Asi que el movimiento NO exige tocar 55 ficheros de morfo. - La capa flotante tiene acceso al dueño: los sub-proveedores llevan `this.provider`. Y el reparto de los 11 consumidores es el diagnostico — 8 pasan el `dir` DEL PROPIO Content (la fuga), 3 el de la raiz (lo correcto). Dos movimientos, con el orden razonado (la propagacion primero: mas acotada, verificable de un vistazo, y BORRA codigo, asi que el segundo llega a un arbol mas limpio). Dos vehiculos posibles para el primero, sin decidir a proposito. Lleva ademas la linea base exacta de verificacion y las trampas ya pagadas: el crudo nunca resuelto, `dir` en un `<svg>` no hace nada, `OptsFromProps` quita el `undefined`, clasificar por grep se equivoca (me paso con `avatar`), y una migracion mecanica hereda los defectos que traduce. ⚠️ Anota lo que el refactor NO debe romper: los 4 que correctamente NO estampan (popover, tooltip, link-preview, context-menu) y la excepcion firmada por el usuario de `avatar`/`image`, que no aceptan `dir` a proposito. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
## 3. Qué está mal hoy, en una frase
**El estampado y la propagación son dos convenciones manuales**, y el eje entero
—cuatro auditorías, tres barridos y unos veinte defectos— es lo que cuesta
mantenerlas a mano.
---
## 4. Los dos movimientos
### 4.1 · El estampado pertenece al runtime
**Objetivo**: que las 54 líneas escritas a mano se vuelvan cero, y que un
componente que acepte `dir` no pueda dejar de estamparlo.
**Vehículo A — inyección en `partPropsForRegistration`.** El provider registra
su parte pasando la dirección cruda; el runtime la emite junto a `id` y
`data-archetype`. Precedente exacto (§2.2). Mínimo código. Coste: el runtime
pasa a conocer un concepto que hoy no conoce.
**Vehículo B — un `dynamicAttr` universal en el compilador de morfo.** El
compilador añade a la parte `provider` un `AttrPlan` para `dir` con fuente
`fromProp: 'dir'`. Encaja con «morfo declara, soma ejecuta», y `renderProps()`
ya lo omitiría solo cuando resuelve `undefined` (§2.1). Coste: toca el
compilador y su caché; hay que mirar `contracts.cssSelectors`, porque el
conjunto cerrado de selectores que eidos puede usar se calcula ahí.
⚠️ **Decidir A o B es la primera tarea, y no está decidida.** Mi inclinación es
**A** por tamaño y por precedente, pero B es más fiel a la doctrina. Léete los
dos sitios antes de elegir.
⚠️ **Lo que NO debe pasar**: que el atributo aparezca en componentes que hoy
NO deben llevarlo. Son cuatro y están verificados: `popover`, `tooltip`,
`link-preview` y `context-menu` — su raíz no renderiza elemento o no pinta nada
direccional, y su pintura vive en el panel portalizado. Si el runtime lo emite
universalmente, esos cuatro ganan un atributo inútil. **Es la parte delicada del
movimiento 1**: la emisión tiene que colgar de que el componente ACEPTE `dir`,
no de que exista una parte `provider`.
⚠️ Y **la excepción del §2 del contrato**: `avatar` e `image` NO aceptan `dir` a
propósito (su `:dir()` sólo traduce a físico una prop lógica que ya eligió el
consumidor). Si acaban con la prop por efecto colateral, el refactor se ha
llevado por delante una decisión firmada por el usuario.
### 4.2 · La propagación pertenece a la capa flotante
**Objetivo**: que la afirmación de la raíz cruce el portal sin que nadie escriba
el enlace.
Hoy cada `Content` portalizado vuelve a arrancar `activeDir` desde cero, y por
eso la afirmación no cruza. La forma: **`FloatingContent` toma la dirección del
proveedor DUEÑO, y el `dir` propio del Content la pisa si existe.** Una
composición dentro de la capa en vez de once fuera.
Los 7 enlaces compuestos a mano (`dropdown-menu` ×2, `menubar`, `select`,
`combobox`, `context-menu` ×2) y el de `color-picker` se borran.
Y **cierra sin trabajo extra el pendiente que quedó reportado**: ningún picker
de eidos reenvía `dir` a su `PopoverContent`, así que el cromo del `picker-shell`
dentro del portal (`picker-shell.css:78`, el split del pie) está sin cubrir en
los siete. Con 4.2 deja de ser una tarea; sin 4.2 son seis parches idénticos.
---
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
> ✅ **4.2 EJECUTADA — ver §9.** Lo de abajo es el enunciado original, conservado
> porque el diagnóstico cambió al verificarlo.
docs(direction): handoff del refactor — la direccion deja de ser una convencion El usuario preguntó si lo entregado era lo mas logico y profesional. No lo es: el contrato es correcto y el MECANISMO para cumplirlo es teclear la misma linea en cada componente. 54 estampados a mano, 7 enlaces al padre a mano, 9 Content que re-arrancan la cadena por su cuenta, y 20 de 55 componentes que lo incumplian hasta `21055bd3c`. Una convencion que hay que repetir 54 veces y que 20 de 55 incumplian no es una convencion — es una abstraccion que falta. `docs/process/CONTINUE-direction-runtime.md`, para abrir en sesion nueva. Lleva LAS DOS COMPROBACIONES DE VIABILIDAD YA HECHAS, que es lo que separa un handoff util de una idea: - El runtime YA omite un atributo cuyo valor resuelve a `undefined` (`runtime.svelte.ts:519-522`). Es exactamente la semantica del estampado crudo. No hay que construir nada para eso. - El runtime YA inyecta atributos sin declaracion en morfo — `id`, el marcador, `data-archetype`, `reg.attachment` (`:538-551`), con el comentario que lo justifica. Asi que el movimiento NO exige tocar 55 ficheros de morfo. - La capa flotante tiene acceso al dueño: los sub-proveedores llevan `this.provider`. Y el reparto de los 11 consumidores es el diagnostico — 8 pasan el `dir` DEL PROPIO Content (la fuga), 3 el de la raiz (lo correcto). Dos movimientos, con el orden razonado (la propagacion primero: mas acotada, verificable de un vistazo, y BORRA codigo, asi que el segundo llega a un arbol mas limpio). Dos vehiculos posibles para el primero, sin decidir a proposito. Lleva ademas la linea base exacta de verificacion y las trampas ya pagadas: el crudo nunca resuelto, `dir` en un `<svg>` no hace nada, `OptsFromProps` quita el `undefined`, clasificar por grep se equivoca (me paso con `avatar`), y una migracion mecanica hereda los defectos que traduce. ⚠️ Anota lo que el refactor NO debe romper: los 4 que correctamente NO estampan (popover, tooltip, link-preview, context-menu) y la excepcion firmada por el usuario de `avatar`/`image`, que no aceptan `dir` a proposito. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 5. Orden, y por qué
**4.2 primero.** Es más pequeña, está más acotada (una capa, once consumidores)
y su verificación es directa: abrir un `Select` y comprobar que trigger y panel
coinciden. Además borra código, así que 4.1 llega a un árbol más limpio.
**4.1 después**, y sólo cuando 4.2 esté verde y commiteada.
---
## 6. Verificación — la línea base exacta
Antes de empezar, tomar estas medidas y **compararlas al final**. Cualquier
desviación hay que explicarla, no absorberla:
| | valor esperado |
| ------------------------------------ | -------------------------------------------------------------------------- |
| `npx svelte-check --threshold error` | **77** (la base; sube si otra sesión toca `media-player`) |
| `npm run rtl:check` | **1 error** — `palabras-chrome.css:446`, preexistente y excluido |
| `npm run docs:check` | 0 errores sobre 564 docs |
| Tests de los componentes tocados | verdes; `eidos/lint.test.ts` falla por `audio-player` sin morfo, **ajeno** |
⚠️ **En navegador, y no es opcional**: el censo del estampado NO se puede hacer
por grep. Dos componentes (`listbox`, `navigation-menu`) estampan a través de un
alias `assertedDir` y un grep del patrón literal los da como huecos. La
comprobación buena es leer el DOM:
```js
// con la demo abierta, por cada componente
document.querySelector('[data-select]').getAttribute('dir');
```
⚠️ **El panel del navegador tiene que estar VISIBLE.** Oculto no compone frames:
no hay capturas y **las transiciones CSS se congelan**, así que
`getComputedStyle(...).transform` devuelve la matriz identidad y parece un
defecto que no lo es. Me pasó dos veces.
---
## 7. Trampas ya pagadas — no volver a pisarlas
- **El estampado es el CRUDO** (`opts.dir.current`), nunca `resolvedDir`. Un
valor resuelto escribe `dir="ltr"` cuando nadie afirmó nada, forzando el
subárbol a LTR dentro de una página RTL. Pasó en `sidebar` y lo escribí yo.
- **`dir` en un `<svg>` no hace nada** — la hoja UA de HTML no alcanza a los
elementos SVG. Va en el envoltorio HTML. Medido en Chrome.
- **`OptsFromProps` quita el `undefined`** del prop público y destruye la
distinción entre «nadie afirmó» y `'ltr'`. Quien lo use declara `dir` aparte.
- **Clasificar esto por grep se equivoca.** Yo di `avatar` por defectuoso
leyendo el patrón en vez del componente, y el usuario lo cazó. Abre la receta,
abre el provider, mira dónde renderiza cada parte.
- **Una migración mecánica hereda los defectos que traduce.** El barrido de
§6.3 cambió doce selectores sin mirar los cuerpos y arrastró dos dobles
volteos hasta que los cazó el guard RTL-2.
---
## 8. Estado del árbol al escribir esto
Rama `alpha-0.1-dir-prefs`, con remoto `gita` (no hay `origin`). Último commit
del eje: `21055bd3c`. Sin tocar y **no commitear**: `.claude/settings.local.json`,
`src/arts/adom/__scratch-verify.ts`, `web/routes/alpha/`. `media-player` lo
edita otra sesión — excluirlo de todo staging.
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
---
## 9. Sesión 2026-08-04 — §4.2 ejecutada
Sin commitear. Rama `alpha-0.1-dir-prefs`, **HEAD se movió a `4755d97d1`
mientras trabajaba** (otra sesión, `media-player` + `audio-player`): esos
ficheros están fuera de este cambio y fuera de cualquier staging.
### 9.1 · El censo del §2.3 estaba mal, y la fuga era otra
Rehecho leyendo los ficheros, no el patrón:
| | |
| --- | --- |
| `FloatingContent.create` | **11** sitios en 9 ficheros |
| …pasando `dir: opts.dir` | **10** |
| …pasando `this.provider.opts.dir` | **1** (`sidebar`) |
| Envoltorios `Content` que componían el enlace al padre A MANO | **7** |
| Envoltorios `Content` SIN enlace | **3** — `popover`, `tooltip`, `link-preview` |
**Y esos 3 no eran un defecto**: sus raíces no aceptaban `dir` en absoluto, así
que no había nada de la raíz que heredar. O sea **entre los 11 consumidores del
motor flotante no había ninguna fuga**.
**La fuga estaba un piso más arriba: en quien COMPONE un popover.** Nueve
componentes crean un `PopoverProvider` internamente
(`color-picker`, `date-picker`, `date-range-picker`, `emoji-picker`,
`gradient-picker`, `natural-time-picker`, `picker`, `time-picker`,
`time-range-picker`) y **siete de ellos aceptan `dir`** — pero `PopoverProvider`
no tenía dónde recibirlo, así que la afirmación moría en el borde del portal.
Medido en Chrome ANTES, con `<DatePicker dir="rtl">` y la página en LTR:
```
[data-floating-wrapper] dir="ltr" ← el panel entero forzado a LTR
[data-picker-shell] direction ltr
[data-picker-footer] direction ltr Clear x=15 · Cancel x=102 · Close x=209
[data-calendar] direction RTL ← lo ÚNICO que espejaba
```
El calendario mirroreaba porque es un componente soma propio que recibe el `dir`
por el árbol de proveedores; el cromo que lo rodea —el `picker-shell`, cuyo pie
reparte con `margin-inline-end: auto` (`picker-shell.css:78`)— no. **Panel
partido por la mitad**, que es exactamente el fallo del §2 del contrato.
### 9.2 · La forma implementada
```
el dir propio de la superficie → el dir afirmado por el proveedor DUEÑO → omitir
```
- `FloatingProviderOpts.dir` — el dueño baja su afirmación (ya pasada por
`activeDir`, o sea `prop → prefs`, así que la cola de prefs queda intacta).
- `FloatingContent.assertedDir` — `opts.dir.current ?? provider.opts.dir.current`.
Los dos estampados del wrapper (rama nativa y rama JS) leen de ahí.
- `createFloatingShellRoot` lo reenvía; es la puerta de 8 de los 10
`FloatingProvider`. Los otros 2 (submenús de `dropdown-menu` y `context-menu`)
pasan el del menú padre.
⚠️ **El punto que decide si esto funciona**: `FloatingContentOpts.dir` pasa a ser
**la prop cruda del Content**, no el resultado de `activeDir`. Si el envoltorio
resolviera ahí, el valor sería casi siempre concreto (prefs) y el dueño **nunca**
ganaría el fallback. Los 10 envoltorios pasan ahora `readableActive(() => dir)`.
Consecuencias:
- **Los 7 enlaces compuestos a mano se borran**, y con ellos el `soma`, el
`activeDir` y el `parentRoot`/`parentMenu` de cada envoltorio.
- `popover`, `tooltip` y `link-preview` **aceptan `dir` en su raíz** (prop
pública nueva + opt). No estampan nada —no renderizan elemento—; el valor
existe para cruzar el portal, y es lo que permite que un picker empuje su
dirección al panel. Sin esa prop habría que escribir
`activeDir(() => undefined, soma)` en la raíz, que es peor y no da nada a cambio.
- Los 9 composers pasan su dir al `PopoverProvider`. `gradient-picker` y `picker`
no aceptan `dir` (§9.17 del eje), así que corren la cadena con el primer
eslabón ausente: `activeDir(() => undefined, soma)`.
**Queda UN `activeDir` con enlace al padre**, y a propósito:
`dropdown-menu-sub-content.svelte` lo necesita para `effectiveSide` (el lado por
defecto del submenú es un `left`/`right` concreto, o sea MATEMÁTICA, que sí
quiere valor resuelto). El ATRIBUTO ya no sale de ahí.
### 9.3 · Verificación
Medido en Chrome real, con la página en LTR y el `dir` movido por el control de
la demo (el camino real, nunca `setAttribute`):
| | antes | después |
| --- | --- | --- |
| `date-picker` · wrapper | `ltr` | **`rtl`** |
| `date-picker` · shell / footer | `ltr` | **`rtl`** |
| `date-picker` · Clear/Cancel/Close x | 15 / 102 / 209 | **200 / 92 / 15** |
| `date-range-picker` · Clear/Cancel/Close x | — | **448 / 217 / 17** |
Y a ojo: el panel entero espeja —cabecera, rejilla de días y pie—, no sólo el
calendario. **Sin regresión** en los que ya funcionaban, comprobados uno a uno:
`dropdown-menu` (raíz y submenú, con `data-side=left` en RTL), `context-menu`,
`menubar`, `select` (por prefs, las dos direcciones), y `tooltip` en `auto`, que
sigue estampando `ltr` — la cola de prefs no se perdió.
| guard | resultado |
| --- | --- |
| `npx svelte-check --threshold error` | **78 = línea base** (A/B con stash: 78 sin mis cambios) |
| Tests de los 20 ámbitos tocados | **158/158** |
| `npm run rtl:check` | **1** — `palabras-chrome.css:446`, preexistente |
| `npm run docs:check` | 0 sobre 565 |
| Pasada completa de vitest | 9 fallos, **ninguno mío** (A/B con el árbol limpio: los 6 deterministas de `contracts`/`eidos-lint` salen igual; `soma-attr-audit`, `orca` y `cookie-adapter` pasan aislados — flaky bajo carga) |
⚠️ La línea base de `check` es **78**, no 77 como decía §6 — con el
`__scratch-verify.ts` sin commitear en el árbol.
### 9.4 · Trampas de esta sesión
- **Lancé `prettier --write` sobre los 43 ficheros tocados y fue un error.**
Reformateó líneas PREEXISTENTES (imports colapsados, prosa de dos READMEs
reflowada: `popover/README.md` pasó de +7/−4 a +49/−46). Deshecho a mano hasta
devolver la diff a su forma quirúrgica. La regla del §5 del eje sigue viva:
**formatea sólo lo que ensuciaste, y mira el tamaño de la diff después.** Lo
único que se conserva del formateo son dos líneas MÍAS que superaban las 100
columnas.
- **Un click JS justo después de navegar no engancha**: hasta que Svelte hidrata,
`.click()` no dispara el handler. Y leer el atributo en la MISMA llamada que el
click devuelve el valor viejo — hay que separar en dos llamadas.
- **El control de dirección de algunas demos vive bajo la pestaña «eidos
System»**, no en la vista Live. Sin cambiar de pestaña no hay chips que pulsar.
### 9.5 · Lo que queda apuntado, sin tocar
1. **9 campos `resolvedDir` MUERTOS en los proveedores `Content`**
(`select` 495, `combobox` 577, `dropdown-menu` 314 y 1230, `context-menu` 252
y 995, `menubar` 563, `tooltip` 409, `link-preview` 268). Verificado que
**nadie los lee** — toda la matemática direccional consume
`this.provider.resolvedDir`, o sea el de la RAÍZ. Ya eran código muerto antes;
ahora además su docblock («Direction for this part's OWN math») describe algo
que `opts.dir` ya no es. **Borrarlos requiere tu palabra.**
2. **Agujero de observabilidad en 5 demos de picker**: `time-picker`,
`time-range-picker`, `color-picker`, `emoji-picker` y `gradient-picker` tienen
el control `dir` del arnés pero **no se lo pasan al componente**, así que la
mitad que ejercita el portal no se puede ver. Sólo `date-picker` y
`date-range-picker` lo pasan — y por eso son los dos que pude medir. Mismo
agujero que §8.2 del eje, misma clase de arreglo.
3. **`context-menu-sub-content` clava `side = 'right'`** (físico) mientras
`dropdown-menu-sub-content` lo deriva de la dirección. Incoherencia entre dos
componentes hermanos, preexistente y ajena a este cambio.
4. **§4.1 sigue entera y sin empezar** — el estampado del `dir` en el runtime, y
la elección Vehículo A vs B sigue sin tomar.
---
## 10. Auditoría de la sesión — lo que salió al re-verificarlo todo
### 10.1 · Código muerto: BORRADO
Los 9 `resolvedDir` de §9.5.1 están **eliminados**. Antes de borrar, verificado
que ninguno se lee: las 5 lecturas vivas de `resolvedDir` en esos ficheros son
todas `this.provider.resolvedDir` —o sea el de la RAÍZ— desde
`SelectTriggerProvider`, `MenuContentProvider`, `ContextMenuContentProvider`,
`MenubarMenuProvider` y `MenubarTriggerProvider`. Cero lecturas desde eidos,
blocks, packs, `web/routes` o tests. Quedan **5** declaraciones, las cinco de
raíces vivas.
### 10.2 · Las demos: las 5 cableadas
`time-picker`, `time-range-picker` y `color-picker` no tenían control de
dirección **en absoluto**; `emoji-picker` y `natural-time-picker` lo tenían en el
arnés pero **no se lo pasaban al componente**. Ahora las siete demos de picker
que aceptan `dir` lo pasan a la instancia, así que el camino de la PROP —el
único que ejercita el portal— es observable en todas.
⚠️ **Corrección a §9.5.2**: dije «5 demos con el control pero sin pasarlo». Falso
en dos sentidos: tres no tenían control, y `gradient-picker`/`picker` no aceptan
`dir` (§9.17 del eje), así que no entran.
### 10.3 · Censo de ISLAS — un defecto nuevo, PREEXISTENTE y de otra familia
Con las demos cableadas se puede al fin escanear cada panel en RTL buscando
elementos que estampen una dirección DISTINTA a la del envoltorio flotante:
| panel | islas |
| --- | --- |
| `date-picker` · `date-range-picker` · `time-picker` · `time-range-picker` | **0** |
| `natural-time-picker` | **1** — `[data-slider]` |
| `emoji-picker` | **3** — `[data-command]` + `[data-toggle-group]` ×2 |
| `color-picker` | **1** — `[data-color-field-format-select]` (un `Select`) |
**La forma es la misma que arregló §4.2, pero para composición EN SITIO en vez
de por portal**: un componente eidos monta otro componente del canon
(`natural-time-picker-panel.svelte` → `Slider.Provider`,
`emoji-picker-content.svelte` → `Command` + `ToggleGroup`,
`color-field-format-select.svelte` → `Select`) **sin reenviarle el `dir`**. El
hijo corre su propia cadena, cae en prefs, estampa `ltr` — y ese atributo CORTA
la herencia del panel, que sí es `rtl`.
Es **preexistente**: mientras el panel entero era LTR la isla no se veía. Lo que
hizo §4.2 fue volverla visible.
⚠️ **No lo he arreglado, y es deliberado**: no hay UN solo precedente en eidos de
reenviar `dir` a un componente compuesto (`grep "dir={" src/uix/eidos` da un
único resultado, y es `data-dir` de otra cosa). Arreglarlo es inventar el patrón
—doctrina nueva, no un parche— y toca 3 ficheros que este movimiento no toca.
**Es un movimiento propio.**
La misma clase de defecto vivía en la DEMO del `color-picker`, que monta `<Tabs>`
y un `<ColorField>` a mano dentro del panel: ahí sí lo arreglé, porque es demo y
porque si no el control de dirección que acabo de añadir enseñaría un panel medio
volteado y parecería defecto del framework.
### 10.4 · Verificación tras la auditoría
| | |
| --- | --- |
| `npx svelte-check --threshold error` | **78 = línea base** (A/B con stash: 78 sin mis cambios) |
| Tests de los 20 ámbitos tocados | **158/158** |
| `npm run rtl:check` | **1**, el de `palabras` |
| `npm run docs:check` | 0 sobre 565 |
| Islas en los 7 paneles | 4 paneles a 0; las 5 restantes documentadas arriba |
⚠️ **Trampa medida dos veces**: `svelte-check` dio **79** en una pasada y 78 en
la siguiente sin tocar nada. Era una CARRERA con la otra sesión guardando
ficheros de `media-player`. En una rama compartida, un número que no reproduce
no es un hallazgo — repite la medida antes de perseguirlo.
⚠️ Las 5 demos tocadas **ya fallaban `prettier --check` en HEAD**, así que no se
formatearon (§9.4).
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
---
## 11. ENDGAME ejecutado — 2026-08-05 (P1–P5, decisiones D1–D4 firmadas)
El plan completo vive en la sesión que lo ejecutó; lo durable está en el canon
(`direction-contract.md` §1/§2/§6) y en `docs/decisions.md`. Resumen operativo:
| fase | commit | qué |
| --- | --- | --- |
| P1 | `9158d0796` | **DirectionContext dentro de `activeDir`** — la cadena gana el eslabón del ancestro; los 3 reenvíos de 4.3, el enlace manual del submenú y los 3 canarios de media-player se BORRAN. El repro de la otra sesión pasa sin ellos. |
| P2 | `973d2c886` | **Proyección automática + semilla** — `createActiveUix` proyecta por defecto (`projectPrefs:false` opt-out; attach opt-in); `readPrefsEnvironmentFromDom` siembra `<html dir>` puesto a mano (`intent > env > derive > default`). Los 3 cableados manuales borrados en el MISMO commit. |
| P3 | `01cc13d41` | **El flip** — `activeDir(dir)` devuelve la AFIRMACIÓN (prop ?? ctx), sin prefs y sin parámetro soma; la matemática vive en `resolveDir(dir, soma)`. En `auto` la página entera lleva UN `dir` (el `<html>`). SSR: cero `dir` en el payload. |
| P4 | `fc84305c2` | **El morfo declara** — `direction: { parts }` en 51 morfos; `soma.runtime<M>` computa el requisito del tipo (declarado ⇒ requerido; no ⇒ prohibido); `compileMorfo` valida parts fail-closed; censo prop↔morfo como guard (`direction-census.test.ts`) con las excepciones firmadas (field-langs, waveform, popover/tooltip/link-preview). La ceremonia de 4.1 (67+47 `dir: null`, 49 getters) MUERE. |
| P5 | (este commit) | `activeEidosDir` gana el contexto (compartido con soma) y se parte igual (`resolveEidosDir`); charts al día; corpus + decisiones + memoria. |
**El estado final del eje**: nadie escribe `dir=` a mano; ausente hereda de
verdad; olvidar el cable no compila; el ambiente llega al DOM una vez. Los tres
`dir=` de media-player eran los canarios y CANTARON (panel rtl sin ellos).
### Verificación final medida
- `auto`: **1** `[dir]` en toda la página (el `<html>` proyectado) — antes ~cada raíz.
- prefs rtl: select y su panel portalizado SIN atributo, computan rtl por herencia.
- prop rtl: trigger del dropdown estampa, el portal cruza, submenú `data-side=left`, 0 islas.
- matemática: slider sin atributo con página rtl — click 25% físico → 75, `ArrowRight` baja.
- SSR (curl): cero `dir=` en select y accordion.
- `check` 77 = línea base en todas las fases · `rtl:check` 1 (palabras) · `docs:check` 0/566 · suites 1401/1402 (el 1: `soma-attr-audit`, flaky bajo carga, pasa aislado).
### La cola que QUEDA (pospuesta por decisión D4 o ajena)
feat(direction): el censo cierra — picker, gradient-picker y chat-log aceptan dir Los tres ultimos del catalogo sin la prop, pospuestos en D4 y firmados hoy: - `picker` (el generico transaccional): `dir?: Direction` publico; el wrapper corre `activeDir(() => dir)` en vez del `() => undefined` que solo seguia al ancestro. `PickerOpts` gana la opt y el TIPO arrastra a sus dos composers (gradient-picker y natural-time-picker pasan su afirmacion) — el generico condicional de P4 haciendo exactamente su trabajo. - `gradient-picker`: prop + cadena + morfo `direction: {}` + runtime. - `chat-log`: prop + cadena + morfo, y reenvia la afirmacion a los `FeedProvider`/`VirtualListProvider` que compone en triple registro — que la exigian en OPTS desde la normalizacion y nadie les pasaba nada. **Sus 2 errores de linea base MUEREN: check pasa de 77 a 75.** Solo la bolsa del chat-log se spreadea en el elemento compartido, asi que el estampado es suyo (morfo direction) y Feed/VirtualList reciben el valor para su matematica. El censo-guard (direction-census.test.ts) los cuenta sin excepcion nueva: prop y morfo declaran el mismo hecho en los tres. Demos: gradient-picker y chat-log tenian el control del arnes sin cablear — ahora pasan `{dir}`. Verificado en Chrome, pagina en LTR y direccion por el chip de la demo: gradient-picker root dir="rtl" · panel portalizado rtl · 0 islas chat-log auto -> atributo AUSENTE (hereda) · prop rtl -> estampa rtl picker via sus composers (no tiene demo propia) check 75 = 77 - 2 (los de chat-log, desviacion explicada: la base MEJORA) · censo + compile + suites de los ambitos 73/73 · rtl:check 1 (palabras) · docs:check 0/566. El handoff marca la cola D4 como CERRADA. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- ~~**Censo de API**~~ **CERRADO 2026-08-05 (tarde)** — los tres que quedaban:
`picker` y `gradient-picker` aceptan `dir?: Direction` (prop → `activeDir` →
morfo `direction: {}` → runtime; el genérico arrastra la opt a
natural-time-picker y gradient-picker por tipo), y `chat-log` acepta y
reenvía a los `Feed`/`VirtualList` que compone — **sus 2 errores de línea
base MUEREN: la base pasa de 77 a 75**. El censo-guard los cuenta sin
excepción nueva. Verificado en Chrome: gradient-picker root+panel `rtl` por
prop con 0 islas; chat-log AUSENTE en auto y `rtl` por prop.
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
- **Opts canónicas transversales** (bindProps v2) — eje propio; el censo
completo está en el reporte del agente de diseño de esta sesión.
- Deuda ajena intacta: 91 tests que falsean `Soma.require()` · cero tests de
charts · `smoke`/`perm:check` sin correr.
### Trampas nuevas de esta pasada, para no repetir
- `git commit` SIN pathspec en rama compartida se llevó por delante el índice
de la otra sesión una vez (b41669c43). Desde entonces: SIEMPRE `git commit --
<rutas>`.
- El detector de huérfanos por identificador se tropezó con la RUTA del import
(`core/soma.svelte` contiene "soma") — excluir líneas de import antes de
buscar usos.
- `readableActive`/vista de prefs construida DENTRO de una función pura llamada
por `$derived` = una alocación por pasada — cachear por instancia (WeakMap en
`resolveEidosDir`).
- El gate de P4 cazó a `command` (estampado inline en el assert que el barrido
4.1 no vio) y a 5 runtimes secundarios del mismo morfo — un tipo condicional
bien puesto encuentra lo que los barridos no.

Powered by TurnKey Linux.