|
|
# CONTINUE — tier blocks (handoff, act. 2026-08-18)
|
|
|
|
|
|
**Estado: F1 CERRADA (8/8) · F2 CERRADA EN UNIDADES (15/15) · saneamiento
|
|
|
CERRADO ENTERO · fase 5 (guard endurecido) HECHA · fase 7 (contraste doctrinal)
|
|
|
CERRADA 15/15 · renombrado del eje de tamaño HECHO · A-99/A-98/A-93/A-68
|
|
|
ARREGLADAS · F2b (ola de variedad) ✅ CERRADA ENTERA 2026-08-18, los cuatro
|
|
|
tramos — y con su tramo D queda cerrado también el cierre que F2 debía y no
|
|
|
cumplió: la página compuesta.**
|
|
|
|
|
|
**18 blocks vivos**: site-header · hero · feature-grid · feature-split · pricing ·
|
|
|
testimonials · faq · stats-band · cta · newsletter · site-footer · banner · team ·
|
|
|
contact · content-section · **logo-cloud** · **article-grid** ·
|
|
|
**cookie-consent**. Más **una página compuesta**: `/blocks/landing`, con 14 de
|
|
|
ellos.
|
|
|
|
|
|
---
|
|
|
|
|
|
## Por dónde entrar mañana
|
|
|
|
|
|
**Lo que TÚ dejaste pedido, y va primero: «repasar cada bloque».** Un repaso
|
|
|
block a block, con el tier ya completo y la página compuesta como banco de
|
|
|
pruebas. **No hay ficha escrita para ese repaso**: su forma se acuerda antes de
|
|
|
empezar — qué orden, qué se mira en cada uno, y si lo que salga se escribe en el
|
|
|
ledger o en el README de cada block. Pregunta antes de arrancar.
|
|
|
|
|
|
Detrás, por orden: [§Qué queda, por orden](#qué-queda-por-orden) — las **24 filas
|
|
|
CONFIRMADO** del ledger (110 filas: 70 ARREGLADO · 24 CONFIRMADO · 13 REFUTADO ·
|
|
|
3 DATO), de las que **5 nacieron el 2026-08-18** en la página compuesta.
|
|
|
|
|
|
⚠️ Las cifras «96 filas / 64-19-13» que este handoff traía eran una foto vieja.
|
|
|
Las de arriba están contadas hoy sobre el fichero.
|
|
|
|
|
|
## ⚠️ LO PRIMERO: la fuente viva es el LEDGER, no el documento del 2026-08-01
|
|
|
|
|
|
**`docs/process/AUDIT-blocks-ledger.md`** — filas con id estable desde `A-01`.
|
|
|
El documento del 2026-08-01 se conserva (sus reclamaciones y evidencias son
|
|
|
válidas y el ledger las indexa) pero **su clasificación NO vale**: tenía los
|
|
|
veredictos desemparejados de sus hallazgos por un join por POSICIÓN, y el journal
|
|
|
del workflow era de sesión. Se reprodujo en vivo al re-verificar — el journal
|
|
|
devuelve los resultados en un orden distinto al de entrada.
|
|
|
|
|
|
Estado del ledger (2026-08-17): **64 ARREGLADO · 19 CONFIRMADO · 13 REFUTADO**
|
|
|
(96 filas: las 90 originales + A-91…A-99, nacidas del contraste doctrinal). Cero
|
|
|
pendientes: todas están medidas. La tasa real de refutación fue del **13%**, no
|
|
|
del 28%.
|
|
|
|
|
|
**La regla que hay que respetar al escribir en él: unión por `id`, SIEMPRE.**
|
|
|
Ningún proceso vuelve a casar dos listas por posición. Un verificador que no
|
|
|
devuelve su id no escribe fila.
|
|
|
|
|
|
## El hallazgo grande de la re-verificación
|
|
|
|
|
|
**De las 12 ALTA de percepción, 8 tenían la causa raíz en el CANON, no en los
|
|
|
blocks.** Los blocks componen bien; lo que estaba roto era el arco perceptivo del
|
|
|
canon. Todos arreglados ya (ver abajo), pero la lección se queda: cuando una
|
|
|
auditoría de un tier acusa al tier, comprueba en qué capa vive el defecto.
|
|
|
|
|
|
## Lo hecho en la sesión del 2026-08-06 (commit `866089a40`)
|
|
|
|
|
|
**Canon** — `Link` (la prop `color` era inerte en `subtle`/`plain`, el hover
|
|
|
clavaba primary, el `:active` quedaba tapado) · `Card` (prometía `BoxProps` sin
|
|
|
aplicarlos: `height="100%"` era un atributo inerte; ahora el tipo dice la verdad y
|
|
|
el eje de tamaño va por `buildSizeStyle`, helper compartido) · `Form.Submit` y
|
|
|
`Form.Reset` (componen el `Button` del canon, así que la acción principal de un
|
|
|
formulario recupera el `contact-activate` en el gesto; y el `aria-label` genérico
|
|
|
deja de pisar el texto propio — WCAG 2.5.3 para todo consumidor, verificado con el
|
|
|
AX tree de Chrome) · `Field` (dejaba de duplicar `commit-submit` dentro de un
|
|
|
`Form`) · `NavigationMenu` (el `commit-select` se mueve al `Link`, el despliegue
|
|
|
habla como `emerge-open/close` en vez de fingir una selección por hover, y el pack
|
|
|
casa por fin con la parte que recibe el estampado — antes sonaba a la ganancia
|
|
|
base, 10× lo diseñado) · `Badge` (el ✕ compone `IconButton`; la altura del chip
|
|
|
pasa a ser la del control) · **la fundación**: `[data-on]` arrastra la propiedad
|
|
|
`color`, no solo las variables.
|
|
|
|
|
|
**Blocks** — hero 1.61:1 → 6.61:1 · contact separa `incomplete` de `invalid` (el
|
|
|
camino de error por campo era inalcanzable por construcción) y su frase llega a la
|
|
|
AT · site-header ya no congela la página al cruzar el breakpoint · site-footer
|
|
|
emite su commit · pricing no se vacía · cta llega a sangre de verdad · newsletter
|
|
|
COORDINA.
|
|
|
|
|
|
**Doctrina** — la frontera dura 1 nombra `$libs/forms`; el contrato B admite un
|
|
|
**segundo servicio, el anunciador** (`uix.announce`): un block que posee las
|
|
|
palabras de sus estados tiene que poder decirlas.
|
|
|
|
|
|
## Fase 7 — contraste doctrinal de los 15 — **CERRADA 15/15**
|
|
|
|
|
|
Encargo del usuario: contrastar cada block con la documentación del sistema **a
|
|
|
nivel de cada componente que compone y en conjunto**, siguiendo la guía de
|
|
|
creación de blocks y **con la composición como estrategia válida**, comprobando
|
|
|
las directrices de eidos, sema, morfo y soma — más **motion**, que faltaba en la
|
|
|
rejilla y detectó él preguntando si había leído su doctrina. No la había leído;
|
|
|
el primer veredicto sobre coreografía salió sin ella y hubo que retirarlo.
|
|
|
|
|
|
**Detalle completo, rejilla de siete ejes y fichas: `AUDIT-blocks-ledger.md`
|
|
|
§Fase 7.** Aquí sólo el estado y cómo seguir.
|
|
|
|
|
|
### ⚠️ Lo primero: el instrumento mentía
|
|
|
|
|
|
Las mediciones en navegador del 2026-08-09 se hicieron contra un dev server con
|
|
|
caché de Vite **anterior a arreglos ya commiteados**. Al re-medir con servidor
|
|
|
limpio cayeron **A-36, A-69 y A-85**. El delator fue aritmético: las duraciones
|
|
|
medidas (7867/3567 ms, ratio 2.21) eran EXACTAMENTE las que el comentario del
|
|
|
arreglo cita como estado anterior. **Servidor recién arrancado antes de medir**,
|
|
|
y si un número reproduce el «antes» documentado con demasiada exactitud,
|
|
|
sospechar del instrumento antes que del código.
|
|
|
|
|
|
### Estado: los 15 con la rejilla completa
|
|
|
|
|
|
**Ninguno de los 15 tiene un defecto de composición.** Componen canon y lo
|
|
|
componen bien; todo lo que salió vive una capa más abajo (canon) o en las FICHAS
|
|
|
del propio ledger. Ese es el resultado de la fase, y conviene no perderlo: la
|
|
|
auditoría original acusaba al tier.
|
|
|
|
|
|
⚠️ Cómo se llegó aquí importa, porque me equivoqué en el camino: durante días
|
|
|
declaré «conformes» siete blocks de los que sólo había medido UN eje (dónde cae
|
|
|
el `{...rest}` y quién estampa `data-stagger`). Un barrido no es la rejilla.
|
|
|
Se corrigió en el ledger y en este handoff cuando él preguntó «¿ya has
|
|
|
terminado?». Lo que el contraste produjo:
|
|
|
|
|
|
- **Cinco disposiciones corregidas** — A-55 (mecanismo falso: el desvío lo causa
|
|
|
el ancho que resta el `Close`, no unos márgenes auto que miden `0px`), A-09 (su
|
|
|
arreglo apagaría la detección live), A-68 (no es arreglable desde el block sin
|
|
|
romper B-6), A-60 (correcta, y con el camino que nombra el propio `AutoGrid`),
|
|
|
A-61 (su segunda mitad ya la cerró `uix.announce`).
|
|
|
- **Tres filas nuevas** — A-91 (`banner` hereda el eje `intent`/`color` sin
|
|
|
registrarlo), A-92 (una restricción del modelo de cascada que vive en un
|
|
|
comentario de `pricing` y falta en §D.13 de la doctrina de motion), A-93
|
|
|
(`Motion` no expone su momento «visto», que es lo que desbloquearía A-68).
|
|
|
|
|
|
⚠️ Al retomar: **la composición es estrategia válida** (norma del usuario). Un
|
|
|
block que sólo coloca canon no es defectuoso por serlo; sólo lo es si al componer
|
|
|
se salta un contrato o reinventa un primitivo. Y el eje motion se contrasta con
|
|
|
§D.11 (la cascada ES `[data-stagger]` + preset + regla de fundación — modelo
|
|
|
CERRADO, no un atajo), §D.13(1) hijos directos, §D.13(2) ENTER-ONLY, y §2
|
|
|
KNOWN-FRAGILE (firma y stagger sobre UN mismo nodo se pisan).
|
|
|
|
|
|
## Lo hecho en las sesiones del 2026-08-16/17 (commits `4caf1100a`, `d02021aee`)
|
|
|
|
|
|
### 1 · El eje de tamaño dice de QUIÉN es (`4caf1100a`)
|
|
|
|
|
|
`size` significaba dos cosas en el tier: el aire de la `Section` en trece blocks
|
|
|
y la altura de la tira en `banner` — misma prop, distinta escala (`BannerSize`
|
|
|
no tiene `xl`). Renombrado: `size`→**`sectionSize`** (13), `container`→
|
|
|
**`containerSize`** (14), `minColumnWidth`→**`minChildWidth`** en `site-footer`.
|
|
|
`size` queda libre y sólo lo usa `banner`, donde SÍ es el tamaño del block.
|
|
|
|
|
|
**La regla vive ya en `architecture/blocks.md` §Conventions**: un block que
|
|
|
reenvía el eje de tamaño de una pieza interna lo nombra `{pieza}Size`, y un eje
|
|
|
que el canon ya nombra se reenvía CON SU NOMBRE.
|
|
|
|
|
|
Método que funcionó y conviene repetir: **renombrar los TIPOS primero** y dejar
|
|
|
que `svelte-check` señale cada consumidor. Cazó los cinco call sites que
|
|
|
quedaban. ⚠️ Y cazó un error mío: el primer patrón era demasiado ancho y renombró
|
|
|
`size` en siete sub-partes donde ese `size` es el del `Card` o el `Text` que
|
|
|
envuelven. Revertidas antes de seguir.
|
|
|
|
|
|
### 2 · Dos animadores que no se hablaban y un cluster que no envolvía (`d02021aee`)
|
|
|
|
|
|
**A-99 + A-98 — `Group` no aplicaba NINGUNO de los dos defaults que su README
|
|
|
promete** (`align: center`, `wrap: wrap`): nunca se los pasaba a `Flex`, que cae
|
|
|
a `nowrap`+`stretch`. De ahí que las acciones de `hero` no apilaran a 375px.
|
|
|
Arreglado en `group.svelte` con el mecanismo que ya usaba para `direction`, y con
|
|
|
`attached` invirtiendo a `nowrap` (el recipe cuadra radios y solapa bordes
|
|
|
asumiendo UNA línea). El censo decidió la forma: **`ButtonGroup` es el único
|
|
|
`Group` en columna de todo el ecosistema**, así que su `align="stretch"` vive en
|
|
|
su call site y no como default condicional. Medido: `hero` pasa a 2 líneas y la
|
|
|
acción secundaria de 124px truncados a 156,9px.
|
|
|
|
|
|
**A-68 + A-93 — los contadores corrían detrás de `opacity: 0`.** `CountUp` trae
|
|
|
su propio IO (threshold 0) y `Motion` usa otro (0.1 / −10%); un IO no mira la
|
|
|
opacidad. `Motion` publica ya su momento visto por contexto
|
|
|
(`motion/context.ts`, calcado de `cascade/context.ts`) y `stats-band-value` lo
|
|
|
pasa a `startWhen`. Medido en la ventana que discrimina (banda a 6px dentro del
|
|
|
viewport): contadores a 0/0/0 en la llegada y tras 2,6 s, donde antes acababan
|
|
|
en 12.121/330/47. Al revelarse, la primera cifra al **0%** — antes 97,8%. **A-69
|
|
|
intacta**: aterrizaje a 1781 ms, dispersión 0.
|
|
|
|
|
|
⚠️ **Residuo documentado**: las cifras que el stagger revela después llegan al
|
|
|
16,8% y **35,4%**. Estimé «≤10%» y me equivoqué — el muelle es sobreamortiguado
|
|
|
y cubre mucho recorrido al principio. Es el precio del aterrizaje conjunto que
|
|
|
A-69 firmó; no baja sin reabrirla.
|
|
|
|
|
|
### 3 · Tres errores de método de estas sesiones (no los repitas)
|
|
|
|
|
|
1. **Medir un default en una demo que lo pisa no es medir un default.** Escribí
|
|
|
en la ficha de A-99 que «el `align` sí se cumple, luego el recipe carga». El
|
|
|
`center` salía del control de la propia demo, que lo pasa explícito.
|
|
|
2. **El panel del navegador oculto suspende el IntersectionObserver**, no sólo
|
|
|
el rAF. Una primera medición de A-68 dio «contadores congelados» y era la
|
|
|
suspensión, no el arreglo: con el panel oculto el IO de `CountUp` tampoco
|
|
|
dispara, así que el resultado no distinguía una cosa de la otra. La vía
|
|
|
fiable es **Playwright headless** (como las fichas originales), resolviendo
|
|
|
`playwright` desde el `package.json` del repo si la sonda vive en el scratchpad.
|
|
|
3. **`Motion` es `once: true`.** Empujar la banda bajo el pliegue DESPUÉS de
|
|
|
cargar no sirve: su observador ya disparó. El espaciador tiene que existir en
|
|
|
el primer pintado (`addInitScript` con una hoja de estilo).
|
|
|
|
|
|
## Lo hecho en la sesión del 2026-08-09 — fase 5, el guard endurecido
|
|
|
|
|
|
`blocks:check` deja de vigilar sólo la forma del tier y pasa a vigilar su
|
|
|
frontera, su documentación y su catálogo. Tres reglas, cada una con fixture
|
|
|
negativo en el `selfTest()`:
|
|
|
|
|
|
1. **Lista blanca de importaciones (B-4)** — la frontera dura 1 era prosa; ahora
|
|
|
es código. Permitido: `$uix`, los arts públicos, `$libs/forms`, `svelte` /
|
|
|
`svelte/elements` y los relativos del propio block. **Los arts se DERIVAN de
|
|
|
`src/arts/*`**, no se listan a mano — una lista escrita se queda atrás el día
|
|
|
que aterriza un art (y este proyecto ya pagó dos veces esa lección). El tier
|
|
|
ya cumplía: cero violaciones al encenderla. Cierra la disposición de A-05.
|
|
|
2. **README completo (B-9) + declaración de landmark (B-8)** — las cuatro
|
|
|
secciones de la plantilla y un párrafo `**Landmark + headings**`. Al
|
|
|
encenderla salieron **7 de 15** sin declaración: los tres de A-14 más
|
|
|
`banner`, `content-section`, `cta`, `newsletter` y `site-footer`, que
|
|
|
nombraban «landmark» en una celda de tabla pero no declaraban nada. Escritas
|
|
|
leyendo la fuente de cada uno. **A-14 → ARREGLADO.**
|
|
|
3. **Ficha en `_lib/catalog.ts` (B-9)** — el árbol y el catálogo tienen que
|
|
|
coincidir en los DOS sentidos: un block que el catálogo no publica es
|
|
|
invisible para el raíl, y un slug publicado sin block detrás es un enlace
|
|
|
muerto.
|
|
|
|
|
|
De paso, **el escáner deja de leer los comentarios como código**: los `index.ts`
|
|
|
documentan su uso con un `// import { Cta } from '$blocks/cta'`, y una lista
|
|
|
blanca que lee prosa habría empezado a acusar a los ejemplos. (No era teórico:
|
|
|
esos 15 comentarios ya entraban en el escaneo de B-10 y sólo se salvaban porque
|
|
|
cada ejemplo cita su propio block.)
|
|
|
|
|
|
**Verificado en ROJO sobre el árbol real, no sólo contra los fixtures** — que es
|
|
|
la única prueba de que una regla nueva no es decorativa: un `zod` y un
|
|
|
`$libs/days` metidos en `hero/types.ts` salen con su línea exacta mientras el
|
|
|
mismo import dentro de un comentario NO salta; el catálogo falla en las dos
|
|
|
direcciones (`shipped: false` sobre un block vivo, slug fantasma); y al romper
|
|
|
`isAllowedSpec` a propósito el self-test aborta el guard en vez de dar verde.
|
|
|
|
|
|
## Qué queda, por orden
|
|
|
|
|
|
**Las 19 filas CONFIRMADO vivas**, agrupadas por lo que hace falta para cerrarlas.
|
|
|
Cada una lleva mecanismo y evidencia en su ficha del ledger — **léela antes de
|
|
|
re-medirla** (perdí una sesión re-midiendo A-67 sin leer la suya, que ya tenía el
|
|
|
análisis completo).
|
|
|
|
|
|
### a) Bloqueadas por decisión TUYA — no las toco sin tu firma
|
|
|
|
|
|
| Fila | Qué hay que decidir |
|
|
|
| --- | --- |
|
|
|
| **A-67** `faq` | Dos earcons simultáneos al cambiar de pregunta. Puerta ya elegida en la ficha: un nuance `emerge-collapse-swap` (morfo + provider + pack). **Cae en el eje de sema**, así que se ejecuta en una sesión suya, no aquí. |
|
|
|
| **A-47** `cta` (ALTA) | Anillo de foco. Por doctrina es **un eje de configuración**: se endurece con un VALOR en `color.focus.ring`, «never a per-component CSS change» — que es justo lo que hacía el intento revertido en `e468e764b`. |
|
|
|
| **A-09** `stats-band` | Su disposición APAGARÍA la detección live: el contrato exige `value` para detectarla. El defecto real es el anuncio crudo en `metrics-value.svelte:40`, que es canon. |
|
|
|
|
|
|
### b) Bloqueada por una pieza que no existe
|
|
|
|
|
|
- **A-95** `banner` — con `affix="top"` la tira tapa la cabecera pegada. No se
|
|
|
arregla en ninguno de los dos blocks: falta la pieza que POSEE las alturas de
|
|
|
página. Mantine lo resuelve en su `AppShell` (`header={{ height }}`); nuestro
|
|
|
equivalente es el block **`app-shell`, F3.1, sin construir**. El plan ordena
|
|
|
ya F3 con `app-shell` PRIMERO por esto (y porque F4.1 lo especializa); la
|
|
|
página compuesta (F2b tramo D) es donde A-95 se re-mide.
|
|
|
|
|
|
### c) Las 5 que dejó la página compuesta (F2b-D, 2026-08-18)
|
|
|
|
|
|
Todas medidas, todas CONFIRMADO, ninguna tocada — salen de componer 14 blocks en
|
|
|
un documento y de construir `cookie-consent`:
|
|
|
|
|
|
| Fila | Qué |
|
|
|
| --- | --- |
|
|
|
| **A-106** `Button` (canon) | Acepta `ref` en su tipo y NUNCA lo reenvía: `bind:ref` tipa bien y no ata nada. **Forma exacta de A-94.** |
|
|
|
| **A-107** `Switch` (canon) | El morfo no tiene parte de etiqueta: la etiqueta visible es hermana y no es diana de clic. Todos los consumidores del repo repiten el mismo apaño. |
|
|
|
| **A-108** `Dialog` (canon) | Devuelve el foco al nodo que lo tenía al abrir; si el disparador se desmontó, acaba en el `body` — y la trampa sigue armada un frame más. |
|
|
|
| **A-109** `Banner` (canon) | DOS landmarks `banner` en toda página con cabecera; el `role` se estampa DESPUÉS de los rest props. |
|
|
|
| **A-110** `feature-split` | Único block de sección sin `.Header`: sus filas emiten `h2` y la sección se queda sin nombre. |
|
|
|
|
|
|
### d) Las 15 restantes — trabajo normal
|
|
|
|
|
|
`A-11` site-header · `A-29` pricing+testimonials · `A-48` banner · `A-53` hero ·
|
|
|
`A-55` banner · `A-60` site-footer · `A-61` contact · `A-62` team · `A-65`
|
|
|
pricing · `A-71` site-footer · `A-74` feature-split · `A-75` pricing · `A-79`
|
|
|
site-footer (SOSP) · `A-92` pricing · `A-96` banner.
|
|
|
|
|
|
⚠️ Dos avisos sobre este grupo: **A-55 no es un defecto visible** (20,5px = 1,6%
|
|
|
del ancho; él lo ve centrado y tiene razón), y **A-65/A-75 son el mismo patrón**
|
|
|
— re-emitir sobre un control que ya emite.
|
|
|
|
|
|
### e) La ola F2b — CERRADA ENTERA 2026-08-18
|
|
|
|
|
|
**La fuente es `PLAN-blocks.md` §F2b** — no dupliques aquí sus tablas. Cuatro
|
|
|
tramos, en orden: **A** variantes de suelo (~~V4 hero `form`~~ ✅ · ~~V8
|
|
|
single-price~~ ✅ · ~~V5 cta split~~ ✅ · ~~V2 testimonials spotlight~~ ✅ ·
|
|
|
~~V3 faq-lista~~ ✅ · ~~V1 `Pricing.Compare`~~ ✅ — **TRAMO A CERRADO 6/6 el
|
|
|
2026-08-17; entra por el tramo B**) · **B** ✅ **CERRADO 2026-08-17** (15 matrices + guard encendido y probado en rojo) — la matriz de
|
|
|
equivalencia variante-a-variante en el README de cada block, con recetas
|
|
|
demostradas en demo (el entregable central: responde a la brecha de
|
|
|
cardinalidad sin competir en dumps) · **C** ✅ **CERRADO 2026-08-18** (~~`logo-cloud`~~ ✅ con el eje `data-ink` en la fundación · ~~`blog`~~ ✅ como **`article-grid`** — el nombre se decidió en su fase 0 — · ~~`cookie-consent`~~ ✅ con su fase 0 legal: las 5 reglas son el TIPO y 8 tests, y **una sola llamada requerida `onDecision(consent, via)` en vez de las dos de la ficha** — es MÁS fuerte, porque el camino de rechazo lo renderiza el block y no el consumidor, que con dos callbacks podía pasar un no-op) **C** tres blocks nuevos — `logo-cloud`
|
|
|
(reabierto: las refs lo shippean estático), `blog`, `cookie-consent` (fase 0
|
|
|
legal profunda; la ley vive en el TIPO) — y `onboarding` NO entra (receta de
|
|
|
`wizard`) · **D** ✅ **CERRADO 2026-08-18** — `web/routes/blocks/landing/`, 14 de 18 blocks a sangre, medidas en su propio README. **F2b QUEDA CERRADA ENTERA.** Lo que dejó: A-95 re-confirmado con cifras (solape 49px = la cabecera entera; falta `app-shell` F3.1) · `Newsletter.Reason` arreglado (2,33 → 8,83:1 sobre el panel: la frase del bloqueo no se leía) · dos gaps nuevos (los DOS landmarks `banner` del canon · `feature-split` sin `.Header`, el único block de sección sin ella) · el catálogo aprendió `kind: 'page'` con guard probado por mutación. ⚠️ **medir una página con motion de viewport**: `fullPage` no hace scroll y sale EN BLANCO, y saltar al final tampoco dispara el observador — se recorre en pasos.
|
|
|
|
|
|
Las 4 filas que la sección «Deuda a decisión tuya» de este handoff listaba
|
|
|
llevan ahí su firma (V1 parte, no hermano · V2/V3 `layout` · V4 slot). V6/V7
|
|
|
(stats-band split-with-image y timeline) salieron a F5: el dossier declara ese
|
|
|
block «Paridad OK».
|
|
|
|
|
|
⚠️ **Lo que dejó V4 y sirve para las que vienen**:
|
|
|
|
|
|
- **Dos gaps de canon** (README del hero §«Found while composing»):
|
|
|
`Form.Submit` no reenvía al `Button` ni `intent` ni eje de anchura — el botón
|
|
|
no llena su celda cuando la fila colapsa, y `newsletter` mide lo mismo, así
|
|
|
que es del canon, no del block. El `justify` de `Grid` es `justify-content`,
|
|
|
no `justify-items`: no se arregla desde fuera. La demo usa `style`, que sí se
|
|
|
reenvía.
|
|
|
- **El contexto de inversión mordió por segunda vez**: bajo `background`, un
|
|
|
`Text` sin `color` explícito mide **1.62:1**; con `on-solid`, 10:1. Bajo
|
|
|
`background` la tinta se pone explícita SIEMPRE.
|
|
|
- **La sonda de contraste**: mide sobre PÍXELES PINTADOS, nunca parseando
|
|
|
colores. Dos sondas dieron cifras seguras y falsas antes (un parser rgb sobre
|
|
|
un sistema que emite `oklch`, y `canvas.fillStyle`, que tampoco normaliza
|
|
|
oklch aquí → medía NEGRO contra todo: 20.46:1 en claro y 1.11:1 en oscuro,
|
|
|
ficción las dos). Control de sanidad: la etiqueta del submit tiene que salir
|
|
|
a **5.18:1**, la cifra que el repo ya documenta para `primary`. Vive en
|
|
|
`scratchpad/v4-final.mjs`.
|
|
|
- **El preview cambia de tema por `?mode=dark`**, no por `prefers-color-scheme`
|
|
|
ni tocando `data-theme` a mano (el boot es el dueño; forzar el atributo NO
|
|
|
repinta y da dos medidas idénticas que parecen «no hay diferencia»).
|
|
|
|
|
|
⚠️ **Y lo que dejó V8, que vale para TODA la ola**: **una fila «diferida» puede
|
|
|
ser una medición equivocada, no un aplazamiento.** El README de `pricing` decía
|
|
|
desde julio que el plan único «ya lo cubre el layout» y era falso — la rejilla
|
|
|
es `auto-fill` y RESERVA las pistas que caben aunque nadie las ocupe (un plan
|
|
|
solo: card de 315px con 677px de vacío al lado). Nadie había mirado ese block
|
|
|
con menos de tres planes. Antes de dar por buena una disposición vieja de
|
|
|
cualquier README, mídela.
|
|
|
|
|
|
⚠️ **Y dos cosas del árbol COMPARTIDO, medidas el 2026-08-17**: otra sesión está
|
|
|
a medias con un componente **`background`** (toca `morfo/`, `langs/` y
|
|
|
`eidos/generated`), así que `svelte-check` da 73 con cosas que no son del tier —
|
|
|
la base real sin nadie es **60**. Y **no hagas `git stash --include-untracked`
|
|
|
aquí**: arrastra su trabajo sin commitear. Si necesitas una base, mide por
|
|
|
fichero.
|
|
|
|
|
|
### e) Cabos y fases
|
|
|
|
|
|
1. **Tres cabos concretos**:
|
|
|
- Un **desbordamiento de texto en las cards del card-group** que el usuario ve
|
|
|
y que NO se reprodujo a 1280 en claro ni en oscuro. Falta el ancho de ventana.
|
|
|
- Los **hermanos del `Field`** (`password-field`, `search-field`, `textarea`)
|
|
|
declaran su propio `commit-submit` copiando el patrón: pueden duplicar igual
|
|
|
dentro de un `Form`. Se comprueba en un comando con la sonda (abajo).
|
|
|
- **La página compuesta** dejó de ser un cabo suelto: es el tramo D de F2b.
|
|
|
2. **F3 del plan original** (10 blocks de aplicación), ya con orden recomendado
|
|
|
en el plan: **`app-shell` PRIMERO** (desbloquea A-95 y F4.1); `data-table` y
|
|
|
`kanban` al final por su fase 0 obligada.
|
|
|
|
|
|
## ⚠️ El sonido CAMBIÓ DE MODELO el mismo día — léelo antes de tocar un pack
|
|
|
|
|
|
Mientras esta sesión trabajaba, la de sema rehízo el sonido dos veces. Modelo
|
|
|
vigente (`CONTINUE-sema-audit.md` §3.0-bis):
|
|
|
|
|
|
```
|
|
|
nombre = per-emit ?? cascada ?? pack ?? morfo ?? familia[verbo] ?? familia.default
|
|
|
sonido = pack[`${nombre}.${intent}`] ?? pack[nombre] ?? nada
|
|
|
```
|
|
|
|
|
|
- **`soundTuning()`, `SOUND_TUNINGS` y `sound()` están RETIRADOS**, con guard
|
|
|
propio (`sounds-grammar.test.ts`). El sonido ya no se modula: **se elige**. Un
|
|
|
intent selecciona OTRO sonido entero, no doblega el mismo.
|
|
|
- **El sonido lo decide el mapa, por VERBO** (`SEMA_MAP.families[f].sounds`), así
|
|
|
que **un pack escribe una regla sólo cuando DIFIERE del default**: de ~165
|
|
|
reglas a 30 en 71 packs. Un pack migrado es casi sólo selectores.
|
|
|
- **`allowedTargets`** en el morfo declara a qué partes puede viajar el estampado.
|
|
|
- Guard de **distinguibilidad**: dos nombres han de diferir en ≥2 ejes.
|
|
|
|
|
|
**Qué significa para la cola de este handoff**: nada se invalida — lo pendiente es
|
|
|
QUÉ evento se emite y SOBRE QUÉ parte, no cómo suena. Lo que cambia es el método:
|
|
|
si al arreglar un confirmado hiciera falta tocar el sonido, ya no se afina, se
|
|
|
NOMBRA; y si el nombre no existe, se registra en el catálogo antes de usarlo.
|
|
|
|
|
|
El pack de `navigation-menu` de esta sesión ilustra el encaje: la otra sesión lo
|
|
|
migró encima y **las tres reglas sobrevivieron sin su `sound:`** — la aportación
|
|
|
era a qué parte apunta cada una, y eso es ortogonal al modelo de sonido.
|
|
|
|
|
|
## La sonda de sema — `G:/tmp/sonda-sema.mjs`
|
|
|
|
|
|
Herramienta nueva, y la forma de contestar «¿esto suena?» sin discutirlo:
|
|
|
|
|
|
```bash
|
|
|
node G:/tmp/sonda-sema.mjs <url> <enter|click|hover> [selector]
|
|
|
```
|
|
|
|
|
|
Dice, por gesto, cuántos nodos de audio se crean y qué eventos se estampan con su
|
|
|
familia e intent. Con ella se midió el doble `commit-submit` del `Field` (2
|
|
|
eventos a 11 ms y 4 nodos de audio → 1 y 2) y el mudo del `NavigationMenu.Link`.
|
|
|
⚠️ Sesgo conocido: asocia los metadatos por ventana temporal de 30 ms, así que
|
|
|
puede atribuir el `intent` de un evento vecino. El `contact-activate` NO lleva
|
|
|
intent (morfo de Button, cap. 22 §11).
|
|
|
|
|
|
⚠️⚠️ **Y su límite, que el handoff de sema documenta como lección cara**: contar
|
|
|
nodos de audio **no es medir la salida**. Sirve para «¿emite o no emite?», que es
|
|
|
para lo que se usó aquí. Para «¿suena como debe?» hay que renderizar el grafo
|
|
|
offline (`OfflineAudioContext`) y medir el pico de muestra — ahí se descubrió que
|
|
|
una firma «silenciada» salía a −13.9 dBFS.
|
|
|
|
|
|
> ⚠️ **CAMBIO DE DOCTRINA (decisión del usuario, 2026-07-31).** Lee la sección
|
|
|
> «La doctrina cambió» antes de tocar nada: un block ya no es solo colocación.
|
|
|
> Ya está ESCRITA en el contrato (`architecture/blocks.md` §«Coordination»).
|
|
|
|
|
|
(El plan fijó 14; `feature-split` se añadió como block hermano con
|
|
|
scope-approval, así que el tier tiene 15 y el denominador honesto es 15.)
|
|
|
|
|
|
Commiteado en `alpha-0.1-dir-prefs` (`866089a40` + este handoff). SIN PUSH.
|
|
|
|
|
|
Dónde vive cada cosa:
|
|
|
|
|
|
- **Plan y bitácora por block**: `docs/process/PLAN-blocks.md` (fichas §F2.x +
|
|
|
registro cronológico al final, con el detalle de cada cierre).
|
|
|
- **Contrato del tier**: `docs/architecture/blocks.md` (B-1..B-11, D-BLK).
|
|
|
- **Suelo de paridad**: `docs/process/RESEARCH-blocks-references.md` (el dossier).
|
|
|
- **Deuda de canon congelada**: `docs/process/PLAN-blocks-quality.md` §6.
|
|
|
- **Decisiones por block**: el README de cada uno (`src/uix/blocks/{kebab}/`).
|
|
|
|
|
|
---
|
|
|
|
|
|
## La doctrina cambió: un block coordina, tiene estado y trae datos
|
|
|
|
|
|
Decisión del usuario, textual: **«si al final es una lista de componentes… el
|
|
|
bloque es coordinación, estado, y data también»**. El motivo, con el `contact`
|
|
|
delante: cuando el estado vive fuera, cada `disabled` es una expresión distinta
|
|
|
montada en el punto de uso y aparecen **huecos** — un envío que no se puede hacer
|
|
|
y nadie dice por qué.
|
|
|
|
|
|
Lo que cambia respecto de lo firmado en `docs/architecture/blocks.md` (B-5/B-7 y
|
|
|
D-BLK: «todo el contenido por snippets, cero cadenas propias, sin servicios»):
|
|
|
|
|
|
1. **El block posee el estado de su sección.** Una máquina única y EXHAUSTIVA,
|
|
|
derivada, en contexto. Sus partes la leen; ninguna la recalcula ni inventa un
|
|
|
`disabled`.
|
|
|
2. **El block trae la forma de sus datos** (el esquema por defecto). El app lo
|
|
|
sustituye pasando el suyo.
|
|
|
3. **El block habla**, con idlangrefs por el traductor
|
|
|
(`#?blocks.<block>.<clave>|fallback`), la misma puerta que usa el canon para
|
|
|
sus `texts:`. Fallback en inglés; el app traduce registrando `blocks.*`.
|
|
|
|
|
|
**ESCRITO** (2026-07-31): `architecture/blocks.md` gana la sección
|
|
|
«Coordination», B-5 admite que el block traiga la FORMA de sus datos, B-7 queda
|
|
|
enmendado (posee las palabras de SUS estados, como idlangref con fallback
|
|
|
inglés) y la convención de servicios se acota. La sección dice también a quién NO
|
|
|
aplica.
|
|
|
|
|
|
**Enmendado el 2026-08-06**: los servicios sancionados son DOS — el traductor,
|
|
|
que resuelve las palabras, y el anunciador (`uix.announce`), que las entrega a la
|
|
|
tecnología asistiva. Entró por la misma razón que el primero: un block que posee
|
|
|
las palabras de sus estados tiene que poder decirlas, y montar una región viva
|
|
|
propia re-implementa `Announce`.
|
|
|
|
|
|
**Alcance acordado**: `contact` primero como prueba de la forma — **HECHO**.
|
|
|
**Revisión documental de los 15 HECHA** (2026-08-01): cada README declara ahora
|
|
|
su posición bajo la doctrina, y el mapa real salió así —
|
|
|
|
|
|
- **Coordinan**: `contact` (máquina + esquema + palabras) · `pricing` (el periodo
|
|
|
por contexto, pero nada se puede BLOQUEAR ahí, así que no necesita máquina ni
|
|
|
palabras: coordinar no es siempre una máquina).
|
|
|
- **Comparten configuración, no estado**: `team` (`align`) · `content-section`
|
|
|
(la medida).
|
|
|
- **Costura bindable hacia el canon**: `faq` (`value` → `Accordion`) ·
|
|
|
`site-header` (`mobileOpen` → `Drawer`). Reenviar no es poseer.
|
|
|
- **No poseen nada**: banner · cta · feature-grid · feature-split · hero ·
|
|
|
site-footer · stats-band · testimonials.
|
|
|
- ✅ **`newsletter` COORDINA desde 2026-08-06** — era el candidato abierto de esta
|
|
|
lista y ya está cerrado. Máquina de cinco estados (`incomplete · invalid ·
|
|
|
ready · sending · sent`), dos `Record` exhaustivos de razón y acción, y
|
|
|
`Newsletter.Submit` / `Newsletter.Reason` como PARTES que leen el contexto: la
|
|
|
regla de forma del tier aplicada, no una excepción — compound se gana cuando las
|
|
|
partes repiten o **coordinan**. `state.test.ts` con 8 casos.
|
|
|
|
|
|
⚠️ Al revisarlos, el listón es el que dejó `contact`: **el estado se deriva de
|
|
|
una fuente y las partes lo leen**; si un estado puede bloquear algo, tiene frase
|
|
|
por `Record` exhaustivo. Y **no inventes estado donde no lo hay** — un block de
|
|
|
layout que no coordina nada se queda como está.
|
|
|
|
|
|
---
|
|
|
|
|
|
## F2.13 `contact` — CERRADO (2026-07-31)
|
|
|
|
|
|
Los seis puntos que quedaban están hechos: arco verificado en navegador · README
|
|
|
rehecho · pestañas de doc al día · namespace `blocks` registrado en el arnés ·
|
|
|
ficha §F2.13 + bitácora en `PLAN-blocks.md` · contrato B actualizado.
|
|
|
|
|
|
**Un defecto real, encontrado al verificar**: la máquina leía `form.isValid`,
|
|
|
que con `progressive` significa «aún no se ha encontrado nada mal» — un
|
|
|
formulario vacío e intacto no tiene errores, así que se declaraba válido y
|
|
|
`incomplete` era INALCANZABLE al cargar: la sección pedía resolver la
|
|
|
verificación con los tres campos vacíos. Justo el hueco que la doctrina existe
|
|
|
para cerrar. Se arregla preguntando al ESQUEMA (`validateSync` de SIUM: síncrono
|
|
|
y sin escribir errores; `form.validate()` habría encendido los tres campos en
|
|
|
rojo al cargar). Fijado con `state.test.ts` — 9 casos, **primer test unitario
|
|
|
del tier**, porque `state.ts` es su primera lógica pura.
|
|
|
|
|
|
Añadido de paso el control vivo que faltaba para un prop público:
|
|
|
`verification` con reto / sin reto. **Omitir el prop NO es pasar `idle`**: sin
|
|
|
él la sección no tiene paso de verificación; con un estado que nunca llega a
|
|
|
`verified` tienes una verificación que no pasa, que es otra cosa.
|
|
|
|
|
|
⚠️ **Ojo con el dev server de otra sesión**: el de `:5173` sirvió un módulo VACÍO
|
|
|
para `blocks/contact/index.ts` (500 «does not provide an export named Contact»)
|
|
|
porque tenía el grafo caducado tras crear ficheros nuevos. Arrancado uno propio en
|
|
|
otro puerto, la página va perfecta. Si mañana ves ese 500, es eso: no persigas el
|
|
|
código.
|
|
|
|
|
|
## Dos hilos de canon abiertos (medidos, sin causa raíz)
|
|
|
|
|
|
1. **`Form`/SIUM**: con `progressive`/`onSubmit`, un envío inválido bloquea y
|
|
|
mueve el foco pero **no expone mensajes** (`form.errors` vacío). Con `onChange`
|
|
|
sí aparecen —y traducidos—, pero saltan en los tres campos al escribir en uno;
|
|
|
con `onBlur` aparecen desde la carga. La máquina de estado del block TAPA el
|
|
|
agujero de cara al usuario (el botón dice qué falta), pero el hueco sigue ahí.
|
|
|
2. **`ProofOfHuman`**: el provider de soma escribe `status` él mismo
|
|
|
(`writableActive`), así que el veredicto que devuelve el app tras consultar a su
|
|
|
servidor no se sostiene, y los slots `stage*` no renderizaron en ningún estado.
|
|
|
El camino «rechazado» no es contable hoy desde el app. **Consecuencia medida en
|
|
|
`contact`**: sus estados `verifying` y `rejected` quedan cubiertos por test
|
|
|
unitario de la máquina, no por el reto en vivo.
|
|
|
|
|
|
---
|
|
|
|
|
|
## La regla de forma del tier (no la re-decidas)
|
|
|
|
|
|
Compound **solo** si las partes:
|
|
|
|
|
|
- **SE REPITEN** — el app mapea sobre N (`feature-grid.Item`,
|
|
|
`testimonials.Item`, `pricing.Plan`, `faq.Item`, `stats-band.Stat`,
|
|
|
`site-footer.Column`), o
|
|
|
- **COORDINAN** — se hablan por contexto (`pricing.Switch` ↔ `PlanPrice`).
|
|
|
|
|
|
Si no hacen ninguna de las dos: **slots de snippet** en la raíz (`hero`, `cta`,
|
|
|
`newsletter`, y la marca / alta / social / legal / extra de `site-footer`). El
|
|
|
plan original dibujaba varias de esas como partes; la desviación está registrada
|
|
|
en cada README.
|
|
|
|
|
|
---
|
|
|
|
|
|
## Los 8 hallazgos de canon que dejó el tier (congelados)
|
|
|
|
|
|
Restricción del usuario en vigor: **no tocar componentes ni librerías fuera de
|
|
|
`src/uix/blocks/` y `web/routes/blocks/`**. Están todos medidos en navegador
|
|
|
y listados en `PLAN-blocks-quality.md` §6 (F15–F22) — más los de motion (A1, A2,
|
|
|
A4, C7, C8, C10, D12, E14) de la pasada de calidad.
|
|
|
|
|
|
| # | Qué |
|
|
|
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
|
| F15 | `Surface variant='soft'` **no acota un panel**: su track queda a 0.002 de luminancia del fondo de página en claro, y `Surface` no tiene borde. `Card outline` sí acota pero no acepta `gradient` → «panel sosegado con borde» no tiene primitivo. |
|
|
|
| F16 | La ranura `contrast` de la paleta es **blanca en todo escalón sólido**, así que un lienzo de luminancia media deja el cuerpo bajo AA: `primary` 5.18 · `indigo` 5.21 · `plum` 4.75 pasan; `neutral` 3.32 · `secondary`/`slate` 3.30 · `teal` 3.07 fallan en claro. |
|
|
|
| F17 | **`Text align` es inerte por defecto**: renderiza un `span` y `text-align` no hace nada sobre caja inline. Hay que pedir `as="p"`. |
|
|
|
| F18 | **La fundación de eidos no trae reset de modelo de caja y lo asume del app.** Bajo `content-box`, `[data-field-control]` (`inline-size:100%` + padding) mide 30px más que su contenedor. Resuelto en app-land: `web/routes/blocks/_lib/reset.css`. |
|
|
|
| F19 | **`onValidSubmit`/`onInvalidSubmit` son no-op silenciosos** si se pasa un `form` ya construido: el componente solo los reenvía al `createForm` que hace él mismo. El handler va SIEMPRE en `createForm`. |
|
|
|
| F20 | Los mensajes de SIUM son **idlangref** (`#?sium.errors.email\|…`): resolver con `uix.langs.t(issue.message, issue.params)`. La demo de docs del `Form` los parte a mano y por eso siempre salen en inglés. |
|
|
|
| F21 | **Los primitivos de layout no pueden cambiar de elemento**: `Text`/`Heading` aceptan `as`, pero `Box` —y `Stack`/`Flex`/`Grid`/`Group`/`Wrap`/`Container`/`Section`— es un `<div>` fijo. Una columna de enlaces no puede ser `ul`/`li`. |
|
|
|
| F22 | **Un `Select` controlado muestra el VALOR crudo hasta abrirse una vez**: las etiquetas las registran los `Select.Item` al montarse y el `Content` portaleado está cerrado. Rodeo: el `child` de `Select.Value`. |
|
|
|
|
|
|
Y dos huecos anteriores que siguen abiertos: **`Box`/`Surface` `flex`/`grow` no
|
|
|
hacen crecer a un hijo flex** (se rodea con tracks `1fr` de `Grid`) y **`Group` no
|
|
|
apila** (usa `Flex direction={{ base: 'column', sm: 'row' }}`; `hero` todavía
|
|
|
compone sus acciones con `Group`).
|
|
|
|
|
|
---
|
|
|
|
|
|
## La plantilla ya existe — cópiala, no la reinventes
|
|
|
|
|
|
```text
|
|
|
src/uix/blocks/{kebab}/
|
|
|
├── README.md # Función · Mapa de composición · Decisiones · Gaps
|
|
|
├── index.ts # export del compound + tipos
|
|
|
├── types.ts # props (todo contenido entra por snippets, B-5/B-7)
|
|
|
└── {kebab}.svelte # composición: solo componentes del canon, sin CSS
|
|
|
|
|
|
web/routes/blocks/{kebab}/
|
|
|
├── +page.svelte # BlockDemo + controles vivos + pestañas de doc
|
|
|
├── {Name}Site.svelte # el block dentro de contenido REAL de producto
|
|
|
└── preview/
|
|
|
├── +layout@.svelte # `@` resetea el layout: la vista previa es su página
|
|
|
└── +page.svelte # sirve {Name}Site leyendo la URL
|
|
|
```
|
|
|
|
|
|
Y luego: marcar `shipped: true` en `web/routes/blocks/_lib/catalog.ts` (raíl y
|
|
|
galería leen esa única fuente) y `npm run blocks:check`.
|
|
|
|
|
|
---
|
|
|
|
|
|
## Reglas que ya costaron sangre (no las re-aprendas)
|
|
|
|
|
|
- **El block se enseña A SANGRE en la página.** Nada entre el block y el borde:
|
|
|
ni marco con relleno, ni caja con scroll, ni cromo pegajoso encima. Medido: un
|
|
|
`Card` desplazaba 21px un header con `offset: 0` (su recipe pinta con
|
|
|
`--card-padding-*`, que `padding={0}` de la capa Box no alcanza), y un
|
|
|
`position: sticky` dentro de un div con scroll es un comportamiento que nadie
|
|
|
vive. **Si un block se ancla a algo, se mide contra lo que se anclará en
|
|
|
producción.**
|
|
|
- **Verifica esperando la CONDICIÓN, nunca un timeout fijo.** Con `Form`+`Field`+
|
|
|
SIUM el dev server tarda ~2,5s en hidratar y un screenshot temprano fotografía
|
|
|
el panel todavía invisible (`data-animation-pending` puesto). Esperar a que ese
|
|
|
atributo desaparezca es la diferencia entre verificar y reportar un bug que no
|
|
|
existe.
|
|
|
- **Y la condición tiene que ser algo que escriba la RUNTIME del morfo, no el
|
|
|
render.** Los `data-variant` / `data-size` los pone el componente de eidos al
|
|
|
renderizar, así que están desde el primer frame; `type`, `aria-label` y los
|
|
|
handlers los aplica la runtime en un efecto POSTERIOR. Esperar a que exista el
|
|
|
nodo (o a que se vea) no basta: a ~1s medí `type` ausente en TODOS los `Button`
|
|
|
del árbol y ningún `onclick` disparando, y a ~6s los mismos botones tenían
|
|
|
`type="button"`, `aria-label="Descartar"` y el clic funcionando. Reporté tres
|
|
|
«defectos del canon» que no existían.
|
|
|
- **MATIZ CARO (2026-07-31): que lo escriba la runtime NO basta — tiene que no
|
|
|
existir en el SSR.** Esperé `button[type="submit"]` con atributo `type` como
|
|
|
puerta de hidratación en `contact`, y **ese atributo viene ya en el HTML del
|
|
|
servidor**: la espera se cumplía sobre el documento estático y yo tecleaba
|
|
|
antes de que Svelte tomara los inputs. Resultado: los valores entraban en el
|
|
|
DOM, `form.values` seguía vacío, y pasé varias rondas persiguiendo un fallo del
|
|
|
block que no existía (llegué a «arreglar» un `$derived` que estaba bien; lo
|
|
|
revertí al medirlo). **La puerta honesta**: algo que solo pueda haber escrito
|
|
|
el cliente — aquí `<style id="uix-blocks-display">`, que pone un `$effect` de
|
|
|
`BootUix` — más un ida y vuelta reactivo real antes de dar por hidratado.
|
|
|
- **Si instrumentas con `console.log` y no lo ves en el navegador, míralo en el
|
|
|
SERVIDOR.** Un `console.log` dentro de un `$derived` sale por el stdout del dev
|
|
|
server durante el SSR. Ver el log SOLO ahí es la prueba de que el componente no
|
|
|
está corriendo en cliente — fue lo que delató el diagnóstico anterior.
|
|
|
- **Playwright headless SÍ sirve para foco y teclado.** El pane suspendido no
|
|
|
(congela rAF y pierde `activeElement`), pero un `page.keyboard.type()` real
|
|
|
dispara `:placeholder-shown` y `:focus-within`: así se verificó que la etiqueta
|
|
|
flotante del `Field` sube al borde. `page.fill()` usa el setter nativo y NO los
|
|
|
dispara. Un script del scratchpad debe importar
|
|
|
`file:///G:/dev/svelte/vicen/node_modules/playwright/index.mjs` (no resuelve
|
|
|
`playwright` por ruta relativa).
|
|
|
- **Los anchos de dispositivo (375/768) van por la ruta `preview` en iframe**, y
|
|
|
es opt-in: en dev, dos documentos sin empaquetar a la vez agotan las
|
|
|
conexiones del navegador (`ERR_INSUFFICIENT_RESOURCES` mata las DOS páginas).
|
|
|
- **Cada prop público, un control vivo** en la demo; los ejes de sección (tema,
|
|
|
idioma, dirección, densidad) ya los da el shell, no los repitas.
|
|
|
- **Un slot que se apaga se pasa como `undefined`, no se renderiza vacío.**
|
|
|
Declara el snippet arriba y pásalo por prop
|
|
|
(`signup={activo ? banda : undefined}`): así el `{#if}` del block quita también
|
|
|
su hueco. Y **cuidado con el sombreado**: `{#snippet signup()}` pisa un prop
|
|
|
llamado `signup` dentro del componente.
|
|
|
- **Nada de backticks de markdown dentro de `<Text>`**: se ven literales. Lo que
|
|
|
es código va en `<Code>`.
|
|
|
- **Ojo con las variables de layout que heredan.** `justify` de `Group` se
|
|
|
hereda a los clusters anidados (pon `justify` explícito) y las de `Box` YA no
|
|
|
heredan desde 2026-07-23 (`docs/theming/changelog.md` §46).
|
|
|
- **Un hueco del canon se registra, no se falsea.** Si al componer falta algo,
|
|
|
va a los Gaps del block como candidato a canon y la demo usa lo que hay.
|
|
|
- **`gap` en un `Grid` separa también las COLUMNAS, y una parte que las cruza se
|
|
|
lleva esos huecos encima** (`content-section`, medido): con `gap={8}` y cinco
|
|
|
pistas, la parte que abarca 2/5 medía 1088 en vez de los 1024 del contenedor
|
|
|
con el que debía alinearse, y la de 1/-1 llegaba a 532 dentro de una rejilla de
|
|
|
404 y hacía scrollear la página a 420px. Si las columnas son un instrumento de
|
|
|
medida y no cosas puestas al lado: `rowGap` + `columnGap={0}`.
|
|
|
- **Y `100%` dentro de una pista no es `100%` de la rejilla.** Una pista
|
|
|
`min(medida, 100%)` se come sus propios canalones (a 420px la central se
|
|
|
quedaba los 404 enteros y la rejilla se iba a 436), y algo anidado dentro de una
|
|
|
parte ve el ancho de SU pista, no el de la rejilla — capar igual en los dos
|
|
|
casos descuadra uno de los dos. Los dos se cazaron midiendo, no leyendo.
|
|
|
- **Los números de layout se miden.** Tres defaults de `site-footer` (`container`,
|
|
|
la razón de la rejilla, el gap de columnas) salieron mal a la primera y solo el
|
|
|
navegador lo dijo. Si un default decide cuántas cosas caben en una fila, se
|
|
|
comprueba con el contenido real.
|
|
|
|
|
|
---
|
|
|
|
|
|
## Deuda del arnés de demos (mía, sin tocar)
|
|
|
|
|
|
- La galería (`web/routes/blocks/+layout@.svelte`) **arranca UIX en línea** en vez
|
|
|
de usar `_lib/BootUix.svelte`, que es lo que usan los previews: la misma cadena
|
|
|
escrita dos veces, y el motivo de que `reset.css` haya que importarlo en los dos
|
|
|
sitios.
|
|
|
- En `BlockDemo`, a ~1400px, el conmutador de anchos de dispositivo se solapa con
|
|
|
el párrafo que lo precede. Idéntico en los 11 blocks → es del arnés, no de un
|
|
|
block.
|
|
|
|
|
|
---
|
|
|
|
|
|
## Deuda a decisión tuya — SALDADA 2026-08-17
|
|
|
|
|
|
Las cuatro se firmaron en `PLAN-blocks.md` §F2b (tramo A) y ya no bloquean:
|
|
|
|
|
|
1. **Tabla de comparación** de features×planes → **`Pricing.Compare`**, PARTE
|
|
|
del block (el toggle de periodo vive en su contexto y B-10 impide que un
|
|
|
hermano lo lea). NO es el hermano `pricing-table` que esta lista sugería.
|
|
|
2. **Cita en spotlight** → `layout="spotlight"` en `testimonials`.
|
|
|
3. **Lista estática 2/3 columnas** → `layout="list"` en `faq`, con unión
|
|
|
discriminada en los tipos (A-94).
|
|
|
4. **`form-in-hero`** → slot `form` del hero.
|
|
|
|
|
|
Resueltas ya: feature-split/alternante (F2.3b, con `Mockup`) · hero con fondo
|
|
|
cover (layout `background`) · CTA que navega y parece botón (el `child` de
|
|
|
`Button` entrega un snippet `content`; `Button` sigue sin `href` y `Link` sigue
|
|
|
poseyendo la navegación — doctrina en el README de eidos Button §«CTA que
|
|
|
navega»).
|
|
|
|
|
|
---
|
|
|
|
|
|
## Estado de gates al parar (2026-08-17 — TODO COMMITEADO Y PUSHEADO)
|
|
|
|
|
|
- `npm run blocks:check` — **verde, 0 errores sobre 15 blocks / 115 ficheros**.
|
|
|
- `npm run docs:check` — **0 errores, 0 avisos** (629 docs).
|
|
|
- `svelte-check`: **72 errores / 59 avisos**, medido justo antes y justo después
|
|
|
de cada cambio — misma cifra, cero regresión, ninguno en lo tocado. ⚠️ Esa
|
|
|
cifra SE MUEVE: con otras sesiones vivas en el mismo árbol se midió 72, 74,
|
|
|
75, 76, 77 y 80 en días distintos. Mídela justo antes y justo después de tu
|
|
|
cambio, **nunca contra un número recordado**.
|
|
|
- Suite completa: **8 rojos en 3 ficheros, todos AJENOS** —
|
|
|
`contracts.test.ts` (6), `eidos/lint.test.ts` y `soma-attr-audit.test.ts`
|
|
|
nombran `waveform` / `skin-media-player` / `radio-group`, que entran por
|
|
|
`bf12b8a57` y `68a48a440` (sesión del media-player, componentes sin morfo), más
|
|
|
un timeout de `engine-orca` que huele a flaky. Ninguno cita nada del tier.
|
|
|
- `prettier`: los avisos que quedan son de ficheros **CRLF preexistentes**
|
|
|
(`motion.svelte`, `motion.css`, `motion/types.ts`, `motion/README.md`,
|
|
|
`AUDIT-blocks-ledger.md`) — ya fallaban en HEAD y formatearlos produciría un
|
|
|
diff del fichero entero. Se dejan como están, a propósito.
|
|
|
|
|
|
## Estado de gates al parar (2026-08-09, fase 5 — histórico)
|
|
|
|
|
|
- `npm run blocks:check` verde con las tres reglas nuevas, 15 blocks / 114
|
|
|
ficheros · `vitest src/uix/blocks/` 20/20 · `vitest src/uix/eidos
|
|
|
src/uix/blocks` 410/410 · `docs:check` 0/0 (618 docs) · `svelte-check` 74/54.
|
|
|
|
|
|
## ⚠️ El árbol está COMPARTIDO — cuidado al commitear
|
|
|
|
|
|
El commit `866089a40` incluye ficheros que **ya venían modificados** cuando
|
|
|
empezó la sesión: había trabajo sin commitear de otras sesiones sobre `blocks/` y
|
|
|
`eidos/`, y al editar encima no hay forma de separarlo por fichero. Lo que sí se
|
|
|
respetó: `morfo/{calendar,pagination,rating-group,toolbar,tree-view,schema,types}`,
|
|
|
`sema/components/textarea.ts` y `soma/tree-view` quedaron FUERA por ser de otra
|
|
|
sesión.
|
|
|
|
|
|
Antes de tocar sema o morfo, mira si hay otra sesión viva ahí. En este mismo día,
|
|
|
una arregló el emparejamiento de las reglas (`closest()`) y el renombrado que mató
|
|
|
el prefijo `form.` en las claves de sonido, y otra (ésta) cambió a qué parte
|
|
|
apuntan los eventos. Se complementaron por suerte, no por diseño.
|
|
|
|
|
|
---
|
|
|
|
|
|
## Sesión 2026-08-01 — lo que cambió
|
|
|
|
|
|
- **`content-section` HECHO** → F2 cerrada 15/15. Rejilla de escape de 5 pistas
|
|
|
(`measure` col 3 · `wide` col 2/5 · `full` col 1/-1), los tres ejes RESPONSIVE
|
|
|
por `eidos.resolve()`, y `.Media` con default **`measure`**: la figura sigue al
|
|
|
texto y salirse se PIDE, como en todas las referencias.
|
|
|
- **CANON tocado por orden del usuario**: un componente no es librería de otro. Las
|
|
|
cinco escalas tipográficas (`TextTracking`, `TextLeading`, `TextWrap`,
|
|
|
`TextNumeric`, `TextMeasure`) salieron de `components/text/types.ts` a
|
|
|
`eidos/lib/types.ts`. `Heading` ya las importaba de `Text` desde antes.
|
|
|
- **La galería EMITE**: arrancaba sin packs de sema desde F0 — el motor estampaba
|
|
|
`data-event-*` y nada era audible. Ahora `events: { sound, haptic, components }`
|
|
|
con la lista DERIVADA del barrel (`Object.values`), no escrita a mano: la lista a
|
|
|
mano del otro arnés se quedó atrás dos veces.
|
|
|
- **Los 15 READMEs declaran su posición** bajo la doctrina de coordinación. El mapa
|
|
|
no es uniforme y está en la sección de arriba.
|
|
|
|
|
|
### Lecciones de verificación que costaron esta sesión
|
|
|
|
|
|
1. **La puerta de hidratación no puede ser un atributo que el SSR ya manda.** Usé
|
|
|
el `type` del submit y la espera se cumplía sobre el documento estático: tecleé
|
|
|
antes de hidratar, los valores entraron en el DOM, `form.values` siguió vacío, y
|
|
|
perseguí un fallo inexistente varias rondas. La puerta honesta es algo que solo
|
|
|
escriba el cliente (`style#uix-blocks-display`).
|
|
|
2. **Medir el block contra SÍ MISMO no basta.** El defecto de los 16px de
|
|
|
`content-section` solo apareció al poner una sección normal debajo y comparar
|
|
|
bordes. Los 15 blocks están verificados cada uno contra sí mismo.
|
|
|
3. **Inspeccionar en una pestaña de fondo miente**: `visibilityState: 'hidden'`
|
|
|
suspende el rAF y deja `data-animation-pending` con `opacity: 0`, que parece un
|
|
|
fallo de visibilidad grave y no lo es.
|
|
|
4. **Screenshot Y MIRAR.** Generé la captura en oscuro y nunca la abrí.
|