# CONTINUE — tier blocks (handoff, act. 2026-08-06)
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
**Estado: F1 CERRADA (8/8) · F2 CERRADA (15/15) · saneamiento de la auditoría
CERRADO · fase 4 del saneamiento (newsletter coordina) HECHA.**
Los 15: site-header · hero · feature-grid · feature-split · pricing ·
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B
El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con
`validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada
mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba
valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la
verificacion con los tres campos vacios, justo el hueco que la doctrina existe
para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM:
sincrono y sin escribir errores (`form.validate()` habria encendido los tres
campos en rojo en el primer frame).
`state.test.ts` fija la maquina con 9 casos — primer test unitario del tier,
porque `state.ts` es su primera logica pura: el orden de prioridad, que solo
`ready` deja enviar, y que todo estado bloqueado tiene frase.
Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas
que posee un block que coordina (estado, forma de sus datos, palabras), B-5
admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados
como idlangref con fallback ingles) y la convencion de servicios acotada: el
traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los
blocks de layout no coordinan nada.
i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas
de arranque (galeria y previews, que duplican el boot) y la demo se lee en
castellano en vez de caer al fallback ingles.
Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y
bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico
— `verification` con reto / sin reto, porque omitir el prop no es lo mismo que
pasar un estado que nunca verifica.
Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un
campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con
los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la
verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test
unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status`
el mismo y el veredicto del app no se sostiene (hilo de canon abierto).
Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 ·
svelte-check sin errores propios · prettier limpio en los ficheros propios.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
testimonials · faq · stats-band · cta · newsletter · site-footer · banner · team ·
contact · content-section.
## ⚠️ LO PRIMERO: la fuente viva es el LEDGER, no el documento del 2026-08-01
**`docs/process/AUDIT-blocks-ledger.md`** — 90 filas con id estable `A-01…A-90` .
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: **41 ARREGLADO · 34 CONFIRMADO · 12 REFUTADO · 3 DATO** . Cero
pendientes: los 90 están medidos. 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.
## Qué queda, por orden
1. **Fase 5 del saneamiento — endurecer `blocks:check`** (lo único del plan sin
empezar): lista blanca de importaciones, README completo con declaración de
landmark, ficha en `catalog.ts` . Cada regla con su fixture negativo en el
`selfTest()` , que el guard ya corre en cada ejecución.
2. **Los 34 CONFIRMADOS sin arreglar** (4 ALTA · 15 MEDIA · 9 BAJA · resto):
sobre todo `stats-band` (4), `pricing` (4), `faq` , `site-header` ,
`site-footer` (3 cada uno). Cada fila lleva mecanismo y evidencia en el ledger.
3. **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** (dimensión CRUZADA), que sigue siendo la condición de
cierre de F2 en el plan y que el usuario dejó fuera de alcance.
4. **F3 del plan original** (10 blocks de aplicación).
## 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).
feat(blocks): `contact` — el primer block que COORDINA (estado, datos y palabras)
WIP declarado: el block funciona y renderiza, pero el arco interactivo completo
no está verificado todavía. Lo que falta está en `CONTINUE-blocks.md`, en orden.
## El cambio de doctrina
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 se veía en este mismo block:
con el estado fuera, cada `disabled` era una expresión distinta montada en el punto
de uso, y aparecían huecos — un envío que no se podía hacer y nadie decía por qué.
Eso cambia lo firmado en `docs/architecture/blocks.md` (B-5/B-7, D-BLK). Queda
ANOTADO como pendiente de escribir en el contrato: propagarlo sin actualizarlo
sería deriva.
## Lo que ahora posee el block
- **La máquina de estado** (`state.ts`): `incomplete · unverified · verifying ·
rejected · ready · sending · sent`, derivada de la validez del `Form`, del
veredicto de verificación y del envío. Con dos registros EXHAUSTIVOS —
`CONTACT_REASON` y `CONTACT_ACTION`— que hacen imposible un estado mudo: no se
puede añadir un estado que bloquee el envío sin darle su frase, porque el
`Record` obliga.
- **La forma de los datos** (`schema.ts`): nombre · correo · mensaje, con las
etiquetas como idlangref. El app lo sustituye pasando su `form` + `schema`.
- **Las palabras**, por el traductor (`#?blocks.contact.…|fallback`), la misma
puerta que usa el canon para sus `texts:`.
Partes nuevas: `Contact.Fields` (los tres campos del esquema propio),
`Contact.Submit` (bloqueo Y etiqueta desde el estado, nunca desde una expresión en
el punto de uso) y `Contact.Reason` (la frase del estado). `Contact.Form` toma el
handle del contexto en vez de pedirlo otra vez.
La demo adelgaza en consecuencia: se le cayeron el esquema, las etiquetas, el
`disabled`, el flag de envío y la frase. Le queda lo que es de un app — las
palabras de la página, los datos de contacto, el reto que elige y qué significa
«enviar».
## De paso, dos arreglos de composición
- El `TextArea` iba dentro de `Field.Control` y salían DOS marcos: pinta su propia
carcasa (borde, radio y relleno son de su recipe). `Field.Control` es la carcasa
del `Field.Input` desnudo.
- El `Form.ErrorSummary` vacío dejaba una caja naranja: su raíz pinta borde de
riesgo en cuanto el form es inválido, aunque la lista esté vacía.
## Dos hilos de canon abiertos (medidos, sin causa raíz)
- Con `progressive`/`onSubmit`, un envío inválido bloquea y mueve el foco pero no
expone mensajes; con `onChange` sí, pero saltan los tres campos al escribir en
uno. La máquina tapa el agujero de cara al usuario; el hueco sigue.
- El provider de `ProofOfHuman` escribe `status` él mismo, así que el veredicto que
devuelve el app no se sostiene y los slots `stage*` no renderizaron nunca.
Gates: `blocks:check` verde (14 blocks) · `svelte-check` sin errores propios ·
prettier limpio. Verificado que la sección renderiza (section · form · 3 campos ·
submit · cero errores de página) en un dev server limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
> ⚠️ **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.
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B
El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con
`validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada
mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba
valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la
verificacion con los tres campos vacios, justo el hueco que la doctrina existe
para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM:
sincrono y sin escribir errores (`form.validate()` habria encendido los tres
campos en rojo en el primer frame).
`state.test.ts` fija la maquina con 9 casos — primer test unitario del tier,
porque `state.ts` es su primera logica pura: el orden de prioridad, que solo
`ready` deja enviar, y que todo estado bloqueado tiene frase.
Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas
que posee un block que coordina (estado, forma de sus datos, palabras), B-5
admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados
como idlangref con fallback ingles) y la convencion de servicios acotada: el
traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los
blocks de layout no coordinan nada.
i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas
de arranque (galeria y previews, que duplican el boot) y la demo se lee en
castellano en vez de caer al fallback ingles.
Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y
bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico
— `verification` con reto / sin reto, porque omitir el prop no es lo mismo que
pasar un estado que nunca verifica.
Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un
campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con
los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la
verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test
unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status`
el mismo y el veredicto del app no se sostiene (hilo de canon abierto).
Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 ·
svelte-check sin errores propios · prettier limpio en los ficheros propios.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
> Ya está ESCRITA en el contrato (`architecture/blocks.md` §«Coordination»).
feat(blocks): F2.3 `feature-grid` — el primer block compound del tier
`<FeatureGrid>` + `.Header` + `.Items` + `.Item` + `.ItemIcon`/`.ItemTitle`/
`.ItemText`: una cabecera sobre una rejilla responsive de features
(icono · título · texto). Es la sección que responde «qué hace», la nº1 tras el
hero.
Primer block COMPOUND del tier, y aplica la regla de forma que fijamos: el
`.Item` se REPITE (el app mapea sobre N features) → gana sub-componentes, donde
`hero`/`site-header` usan slots de snippet (partes fijas de layout). Las partes
no coordinan —sin contexto ni estado entre ellas—: la rejilla es del padre, las
celdas del app. `.Items` es el envoltorio honesto de la rejilla (`AutoGrid`), que
deja la cabecera fuera sin un `grid-column: 1/-1` a pelo.
- El block coloca (Section · Container · AutoGrid · Surface · Heading · Text); el
app pone todo el contenido por children (B-7).
- `.Items` fluido por `minChildWidth` (tantas columnas como quepan) o `columns`
fijas; `.Item` `align` start/center; `.ItemIcon` chip `Surface`; `.ItemTitle`
`Heading` h3; `.ItemText` `Text` apagado.
- `align` de sección (center/start) coloca la cabecera coherente con las columnas.
Dos cosas encontradas al construir, resueltas:
- **Los sub-componentes de bloque extienden los props del componente canon que
envuelven** (`BoxProps`, `StackProps`, `HeadingProps`…), **no
`HTMLAttributes`**: el `style: string|null` del atributo HTML crudo choca con
el `style: string` del canon al hacer spread (+ "union type too complex").
- **`.ItemIcon` por defecto `solid`, no `soft`**: el soft-primary en claro es
casi blanco (oklch 0.99) → el chip era invisible; solid da el chip con glifo
on-solid (la tinta de contraste la pone `Surface`).
Demo (`web/routes/blocks/feature-grid/`): full-bleed + ruta `preview`, con
control de columnas (fluido/2/3/4), align, nº de items y dir; 6 features con
iconos del canon.
Hueco a decisión del usuario (en los Gaps del block): el **feature-split/
alternante** (texto junto a un screenshot, lados alternos) — la brecha nº1 del
dossier — es otra disposición (filas de 2 columnas, no rejilla de iconos):
probablemente un block hermano `feature-split`. Presentado, no resuelto.
Verificado en navegador (Playwright, módulos frescos): center/start × claro/
oscuro × LTR/RTL, columnas fluidas y fijas, 3/4/6 items, chips visibles.
`blocks:check` verde (3 blocks) · `svelte-check` sin errores propios.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
(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.)
fix(blocks): lo que la auditoría encontró DENTRO del tier
Una auditoría multi-agente (7 dimensiones doctrinales + refutación adversarial)
confirmó 32 hallazgos sobre la pasada de calidad. Esto corrige los que caen
dentro de `blocks/`; la deuda de fuera queda REGISTRADA, no tocada
(`PLAN-blocks-quality.md` §6).
- **B-7 roto en `pricing`**: el block traía tres strings castellanas propias como
defaults del toggle — dos visibles y un nombre accesible. Hardcodeaba un idioma
DENTRO del framework y esquivaba `langs`, en el mismo block cuyo README predica
«cero strings propias». Ahora los tres son props REQUERIDAS y las pone la app.
- **El block pintaba**: `pricing.Plan` metía `box-shadow` por `style=` porque
`Card` no expone elevación. Retirado; el hueco queda registrado como candidato
a canon en vez de falseado.
- **El escalonado estaba roto y el comentario mentía**: puse `spring-pop` (driver
JS) en los planes, y un driver JS no lee el `animation-delay` donde vive el
stagger — entraban TODOS a la vez mientras el comentario afirmaba lo contrario.
Vuelto al preset CSS: verificado, índices 0,1,2 a 70ms.
- **Comentario falso en la demo del hero**: atribuía al `intent` visual una
consecuencia sonora/háptica que ningún camino produce (el morfo del Button dice
explícitamente que `contact-activate` NO lleva intent). Reescrito.
- **Composition maps mentían**: los 6 blocks que ahora componen `Motion` no lo
decían; el hero seguía diciendo `Heading` (es `Display`), sin `Backdrop` ni
`decor`, y declaraba DIFERIDO un preset de cromo de media que ya está shipeado
(`Mockup`). Al día, sin filas duplicadas ni datos rancios (el chip de
feature-grid es `solid`, no `soft`).
- **Handoff y plan**: el plan seguía prescribiendo un `<Reveal>` que esta misma
sesión creó y retiró; el handoff daba `feature-split` por «sin decidir» estando
hecho, y los gates con números falsos.
Verificado: `blocks:check` verde (7) · `svelte-check` sin errores propios · en el
navegador, pricing escalona de nuevo, las etiquetas vienen de la app y ninguna
tarjeta pinta sombra.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2 months ago
feat(blocks): `banner` — el aviso de arriba, y tres defectos del canon que no existían
F2.11. El block más FINO del tier a propósito: cuando el canon ya tiene la pieza,
el block es colocación y nada más. Reenvía la superficie entera del `Banner` con
`Omit<BannerProps, 'children'>` —sin re-declarar `intent`/`variant`/`size` ni
estrecharlos—, lo mete en columna con `Container` (`width="100%"` +
`paddingX={0}`: a sangre por fuera, en columna por dentro), envuelve la fila con
`Wrap` en vez de aplastarla, y cablea `Banner.Close` solo si llega `onDismiss`.
La visibilidad NO es suya: es la decisión que ya tomó el componente
(«composición, no un booleano `dismissible`»), así que el app envuelve en su
`{#if}`. Un block que guardara ese estado sería una segunda fuente de verdad.
Tampoco emite sema: el canon declara cero eventos para `Banner` a propósito, y un
aviso persistente sin descarte es tan legítimo como uno descartable.
La demo lo enseña **encima del `site-header` de verdad**, con página para
desplazarse. Un aviso dentro de un recuadro no se parece a un aviso.
## El hallazgo que la fase 0 anticipó
`Banner` estampa `role="banner"` DESPUÉS de sus rest props, así que no se puede
relajar a una región normal. Con el `site-header` en la misma página —que es la
única colocación que shipean las referencias— quedan **dos landmarks `banner`**.
Medido en la vista previa: dos. Lo único que puede hacer un app hoy es nombrarlos
con `aria-label`, y eso hace la demo.
## Y una lección de método que costó una sesión
Reporté tres «defectos del canon» —`aria-label` ausente en `Banner.Close`, `type`
desaparecido de todo `Button`, `IconButton` tragándose el `onclick`— y **los tres
eran falsos**. La causa era la misma: medir demasiado pronto.
Los `data-variant` / `data-size` los escribe el componente de eidos al renderizar,
así que están desde el primer frame; `type`, `aria-label` y los handlers los
aplica la **runtime del morfo en un efecto posterior**. A ~1s: `type` ausente en
3 de 3 botones y ningún clic disparando. A ~6s: `type="button"`,
`aria-label="Descartar"` y el descarte funcionando.
La espera válida es un atributo que solo pueda haber puesto la runtime
—`waitForFunction(() => boton.hasAttribute('type'))`—, no que el nodo exista ni
que se vea. La regla queda afinada en `CONTINUE-blocks.md`, que ya avisaba de
esperar la condición y no el reloj: fallé al elegir la condición.
De paso, un aviso de soma que sí era real y era mío: `Tabs.List` sin nombre
accesible en `BlockDemo.svelte`. Afectaba a las 12 demos del tier.
Verificado en navegador: los 4 intents, los 3 tratamientos, `descartable` sí/no
(con `no` el botón deja de renderizarse, no se oculta), claro/oscuro/RTL y 420px,
la tira siempre por encima de la cabecera, cero desbordamiento horizontal, el
descarte con la página recolocándose, y los controles de la demo moviendo la
vista previa en línea. Gates: `blocks:check` verde (12 blocks) · `svelte-check`
sin errores propios · `docs:check` sin errores míos · prettier limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Todo commiteado y pusheado a `gita/alpha-0.1-sec-dom` .
feat(blocks): F2.3 `feature-grid` — el primer block compound del tier
`<FeatureGrid>` + `.Header` + `.Items` + `.Item` + `.ItemIcon`/`.ItemTitle`/
`.ItemText`: una cabecera sobre una rejilla responsive de features
(icono · título · texto). Es la sección que responde «qué hace», la nº1 tras el
hero.
Primer block COMPOUND del tier, y aplica la regla de forma que fijamos: el
`.Item` se REPITE (el app mapea sobre N features) → gana sub-componentes, donde
`hero`/`site-header` usan slots de snippet (partes fijas de layout). Las partes
no coordinan —sin contexto ni estado entre ellas—: la rejilla es del padre, las
celdas del app. `.Items` es el envoltorio honesto de la rejilla (`AutoGrid`), que
deja la cabecera fuera sin un `grid-column: 1/-1` a pelo.
- El block coloca (Section · Container · AutoGrid · Surface · Heading · Text); el
app pone todo el contenido por children (B-7).
- `.Items` fluido por `minChildWidth` (tantas columnas como quepan) o `columns`
fijas; `.Item` `align` start/center; `.ItemIcon` chip `Surface`; `.ItemTitle`
`Heading` h3; `.ItemText` `Text` apagado.
- `align` de sección (center/start) coloca la cabecera coherente con las columnas.
Dos cosas encontradas al construir, resueltas:
- **Los sub-componentes de bloque extienden los props del componente canon que
envuelven** (`BoxProps`, `StackProps`, `HeadingProps`…), **no
`HTMLAttributes`**: el `style: string|null` del atributo HTML crudo choca con
el `style: string` del canon al hacer spread (+ "union type too complex").
- **`.ItemIcon` por defecto `solid`, no `soft`**: el soft-primary en claro es
casi blanco (oklch 0.99) → el chip era invisible; solid da el chip con glifo
on-solid (la tinta de contraste la pone `Surface`).
Demo (`web/routes/blocks/feature-grid/`): full-bleed + ruta `preview`, con
control de columnas (fluido/2/3/4), align, nº de items y dir; 6 features con
iconos del canon.
Hueco a decisión del usuario (en los Gaps del block): el **feature-split/
alternante** (texto junto a un screenshot, lados alternos) — la brecha nº1 del
dossier — es otra disposición (filas de 2 columnas, no rejilla de iconos):
probablemente un block hermano `feature-split`. Presentado, no resuelto.
Verificado en navegador (Playwright, módulos frescos): center/start × claro/
oscuro × LTR/RTL, columnas fluidas y fijas, 3/4/6 items, chips visibles.
`blocks:check` verde (3 blocks) · `svelte-check` sin errores propios.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Dónde vive cada cosa:
feat(blocks): F2.2 `hero` — center · split · background, por slots
Segundo block del tier. API por SLOTS DE SNIPPET, no compound: la forma se
razonó con el usuario y con el paisaje de referencia. Compound se reserva para
partes que COORDINAN (estado/contexto/ARIA entre ellas — Accordion, Dialog); un
hero son cinco slots de layout que no se hablan entre sí y que el root arregla,
así que snippets. Además el campo entero shippea marketing como copy-paste
plano —nadie aplica compound a una sección—, y los slots por zona son la
historia de personalización que distingue al tier del copy-paste.
`<Hero layout="center|split|background" level container size>` +
`eyebrow/title/description/actions/media/background/children`.
- El block **envuelve** título y subtítulo en `Heading`/`Text`: así posee el
`id` que nombra el `<section aria-labelledby>` y el nivel del encabezado,
mientras la app pone las palabras (B-7). `eyebrow`/`actions`/`media` son
contenido libre (ahí los componentes del canon SON la API).
- `center` = `Stack` centrado, media debajo (ancho-capado); `split` = `Grid` de
dos columnas (una sola sin media), apila en estrecho.
- **`background` (cover)** — añadido por scope-approval del usuario: la media a
sangre detrás de la copy, con velo de contraste (`--color-overlay` a
`--opacity-scrim`), texto `on-solid` y `object-fit: cover` vía un `<style>`
justificado (D-BLK.2). Son las ÚNICAS reglas que el block posee, todas sobre
tokens del ecosistema — cero color a mano. Capas por orden de fuente, sin
`z-index`.
Demo (`web/routes/blocks/hero/`): full-bleed en la página + ruta `preview` para
anchos de dispositivo, cada prop un control vivo, y el backdrop del layout cover
dogfooda el sistema de color — es un `Surface color="primary" gradient` (finish
aurora = `--gradient-aurora`, derivado de los roles del tema; cambia con la
paleta). Mini-site compartido por las dos superficies (`HeroSite.svelte`).
Encontrado al componer, registrado no resuelto: `Box`/`Surface` `flex`/`grow`
no hicieron crecer un hijo flex (bars a 0-width, `flex: 0 1 auto`; la prop no la
usa ningún componente shipped). La demo usó `Grid` (tracks `1fr`). Flag en el
README del block y en el handoff para revisar el cableado de `--box-flex`.
Verificado en navegador (Playwright headless, módulos frescos): center/split/
background × claro/oscuro × LTR/RTL, media on/off, y el landmark nombrado
(región con `aria-labelledby` que resuelve al título). `blocks:check` verde
(2 blocks) · `svelte-check` sin errores propios.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **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}/`).
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
feat(blocks): `contact` — el primer block que COORDINA (estado, datos y palabras)
WIP declarado: el block funciona y renderiza, pero el arco interactivo completo
no está verificado todavía. Lo que falta está en `CONTINUE-blocks.md`, en orden.
## El cambio de doctrina
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 se veía en este mismo block:
con el estado fuera, cada `disabled` era una expresión distinta montada en el punto
de uso, y aparecían huecos — un envío que no se podía hacer y nadie decía por qué.
Eso cambia lo firmado en `docs/architecture/blocks.md` (B-5/B-7, D-BLK). Queda
ANOTADO como pendiente de escribir en el contrato: propagarlo sin actualizarlo
sería deriva.
## Lo que ahora posee el block
- **La máquina de estado** (`state.ts`): `incomplete · unverified · verifying ·
rejected · ready · sending · sent`, derivada de la validez del `Form`, del
veredicto de verificación y del envío. Con dos registros EXHAUSTIVOS —
`CONTACT_REASON` y `CONTACT_ACTION`— que hacen imposible un estado mudo: no se
puede añadir un estado que bloquee el envío sin darle su frase, porque el
`Record` obliga.
- **La forma de los datos** (`schema.ts`): nombre · correo · mensaje, con las
etiquetas como idlangref. El app lo sustituye pasando su `form` + `schema`.
- **Las palabras**, por el traductor (`#?blocks.contact.…|fallback`), la misma
puerta que usa el canon para sus `texts:`.
Partes nuevas: `Contact.Fields` (los tres campos del esquema propio),
`Contact.Submit` (bloqueo Y etiqueta desde el estado, nunca desde una expresión en
el punto de uso) y `Contact.Reason` (la frase del estado). `Contact.Form` toma el
handle del contexto en vez de pedirlo otra vez.
La demo adelgaza en consecuencia: se le cayeron el esquema, las etiquetas, el
`disabled`, el flag de envío y la frase. Le queda lo que es de un app — las
palabras de la página, los datos de contacto, el reto que elige y qué significa
«enviar».
## De paso, dos arreglos de composición
- El `TextArea` iba dentro de `Field.Control` y salían DOS marcos: pinta su propia
carcasa (borde, radio y relleno son de su recipe). `Field.Control` es la carcasa
del `Field.Input` desnudo.
- El `Form.ErrorSummary` vacío dejaba una caja naranja: su raíz pinta borde de
riesgo en cuanto el form es inválido, aunque la lista esté vacía.
## Dos hilos de canon abiertos (medidos, sin causa raíz)
- Con `progressive`/`onSubmit`, un envío inválido bloquea y mueve el foco pero no
expone mensajes; con `onChange` sí, pero saltan los tres campos al escribir en
uno. La máquina tapa el agujero de cara al usuario; el hueco sigue.
- El provider de `ProofOfHuman` escribe `status` él mismo, así que el veredicto que
devuelve el app no se sostiene y los slots `stage*` no renderizaron nunca.
Gates: `blocks:check` verde (14 blocks) · `svelte-check` sin errores propios ·
prettier limpio. Verificado que la sección renderiza (section · form · 3 campos ·
submit · cero errores de página) en un dev server limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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.*` .
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B
El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con
`validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada
mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba
valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la
verificacion con los tres campos vacios, justo el hueco que la doctrina existe
para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM:
sincrono y sin escribir errores (`form.validate()` habria encendido los tres
campos en rojo en el primer frame).
`state.test.ts` fija la maquina con 9 casos — primer test unitario del tier,
porque `state.ts` es su primera logica pura: el orden de prioridad, que solo
`ready` deja enviar, y que todo estado bloqueado tiene frase.
Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas
que posee un block que coordina (estado, forma de sus datos, palabras), B-5
admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados
como idlangref con fallback ingles) y la convencion de servicios acotada: el
traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los
blocks de layout no coordinan nada.
i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas
de arranque (galeria y previews, que duplican el boot) y la demo se lee en
castellano en vez de caer al fallback ingles.
Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y
bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico
— `verification` con reto / sin reto, porque omitir el prop no es lo mismo que
pasar un estado que nunca verifica.
Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un
campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con
los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la
verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test
unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status`
el mismo y el veredicto del app no se sostiene (hilo de canon abierto).
Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 ·
svelte-check sin errores propios · prettier limpio en los ficheros propios.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**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: **el traductor es el único
servicio sancionado**. La sección dice también a quién NO aplica.
feat(blocks): `contact` — el primer block que COORDINA (estado, datos y palabras)
WIP declarado: el block funciona y renderiza, pero el arco interactivo completo
no está verificado todavía. Lo que falta está en `CONTINUE-blocks.md`, en orden.
## El cambio de doctrina
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 se veía en este mismo block:
con el estado fuera, cada `disabled` era una expresión distinta montada en el punto
de uso, y aparecían huecos — un envío que no se podía hacer y nadie decía por qué.
Eso cambia lo firmado en `docs/architecture/blocks.md` (B-5/B-7, D-BLK). Queda
ANOTADO como pendiente de escribir en el contrato: propagarlo sin actualizarlo
sería deriva.
## Lo que ahora posee el block
- **La máquina de estado** (`state.ts`): `incomplete · unverified · verifying ·
rejected · ready · sending · sent`, derivada de la validez del `Form`, del
veredicto de verificación y del envío. Con dos registros EXHAUSTIVOS —
`CONTACT_REASON` y `CONTACT_ACTION`— que hacen imposible un estado mudo: no se
puede añadir un estado que bloquee el envío sin darle su frase, porque el
`Record` obliga.
- **La forma de los datos** (`schema.ts`): nombre · correo · mensaje, con las
etiquetas como idlangref. El app lo sustituye pasando su `form` + `schema`.
- **Las palabras**, por el traductor (`#?blocks.contact.…|fallback`), la misma
puerta que usa el canon para sus `texts:`.
Partes nuevas: `Contact.Fields` (los tres campos del esquema propio),
`Contact.Submit` (bloqueo Y etiqueta desde el estado, nunca desde una expresión en
el punto de uso) y `Contact.Reason` (la frase del estado). `Contact.Form` toma el
handle del contexto en vez de pedirlo otra vez.
La demo adelgaza en consecuencia: se le cayeron el esquema, las etiquetas, el
`disabled`, el flag de envío y la frase. Le queda lo que es de un app — las
palabras de la página, los datos de contacto, el reto que elige y qué significa
«enviar».
## De paso, dos arreglos de composición
- El `TextArea` iba dentro de `Field.Control` y salían DOS marcos: pinta su propia
carcasa (borde, radio y relleno son de su recipe). `Field.Control` es la carcasa
del `Field.Input` desnudo.
- El `Form.ErrorSummary` vacío dejaba una caja naranja: su raíz pinta borde de
riesgo en cuanto el form es inválido, aunque la lista esté vacía.
## Dos hilos de canon abiertos (medidos, sin causa raíz)
- Con `progressive`/`onSubmit`, un envío inválido bloquea y mueve el foco pero no
expone mensajes; con `onChange` sí, pero saltan los tres campos al escribir en
uno. La máquina tapa el agujero de cara al usuario; el hueco sigue.
- El provider de `ProofOfHuman` escribe `status` él mismo, así que el veredicto que
devuelve el app no se sostiene y los slots `stage*` no renderizaron nunca.
Gates: `blocks:check` verde (14 blocks) · `svelte-check` sin errores propios ·
prettier limpio. Verificado que la sección renderiza (section · form · 3 campos ·
submit · cero errores de página) en un dev server limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
docs(blocks): cada block declara su posicion bajo la doctrina de coordinacion
Y `content-section.Media` pasa a seguir al texto por defecto.
DEFAULT: `.Media` era `width='wide'`, asi que una figura se salia de la columna
SALVO que dijeras lo contrario. Al reves de lo que hacen todas las referencias
(en `@tailwindcss/typography` una imagen dentro de `prose` queda capada a la
medida y salirse exige el truco del `translate`; en `markdown-body` de GitHub,
`max-width: 100%` de la columna). Ahora el default es `measure` — la figura
sigue al texto y el escape se PIDE. Cambiado en los cuatro sitios que decian lo
contrario: la parte, la ruta `preview`, el estado inicial del control de la demo
(una demo que pisa el default en silencio ensena algo que el block no hace solo)
y la doc. Verificado: 528 = ancho del texto exacto a 1011/1280/1920, y `wide` /
`full` siguen funcionando cuando se piden.
DOCUMENTACION: los 15 READMEs declaran ahora su posicion bajo §«Coordination»,
y el mapa real no es uniforme —
- Coordinan: `contact` (maquina + esquema + palabras) y `pricing` (el periodo
por contexto). Pero en pricing nada se puede BLOQUEAR, asi que no necesita
maquina ni palabras: coordinar no es siempre una maquina.
- Comparten CONFIGURACION, no estado: `team` (`align`) y `content-section` (la
medida).
- Costura bindable hacia el canon: `faq` (`value` → `Accordion`) y
`site-header` (`mobileOpen` → `Drawer`). Reenviar no es poseer.
- No poseen nada: banner · cta · feature-grid · feature-split · hero ·
site-footer · stats-band · testimonials. Decirlo explicitamente es el punto:
inventarle estado a un block que no coordina nada es el error contrario.
- `newsletter` queda como CANDIDATO ABIERTO: coloca un envio que puede quedar
bloqueado sin que nada diga por que — el `disabled` lo monta el app en el
punto de uso — que es exactamente el hueco que cerro `contact`. Es anterior a
la doctrina (block del 30-07, doctrina del 31-07). Registrado, no asumido: se
toca con decision del usuario.
El README del tier apunta a la doctrina y el handoff recoge el mapa completo.
Gates: blocks:check verde (15) · docs:check 0 · svelte-check sin errores en lo
tocado · prettier limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**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.
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B
El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con
`validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada
mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba
valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la
verificacion con los tres campos vacios, justo el hueco que la doctrina existe
para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM:
sincrono y sin escribir errores (`form.validate()` habria encendido los tres
campos en rojo en el primer frame).
`state.test.ts` fija la maquina con 9 casos — primer test unitario del tier,
porque `state.ts` es su primera logica pura: el orden de prioridad, que solo
`ready` deja enviar, y que todo estado bloqueado tiene frase.
Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas
que posee un block que coordina (estado, forma de sus datos, palabras), B-5
admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados
como idlangref con fallback ingles) y la convencion de servicios acotada: el
traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los
blocks de layout no coordinan nada.
i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas
de arranque (galeria y previews, que duplican el boot) y la demo se lee en
castellano en vez de caer al fallback ingles.
Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y
bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico
— `verification` con reto / sin reto, porque omitir el prop no es lo mismo que
pasar un estado que nunca verifica.
Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un
campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con
los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la
verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test
unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status`
el mismo y el veredicto del app no se sostiene (hilo de canon abierto).
Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 ·
svelte-check sin errores propios · prettier limpio en los ficheros propios.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
⚠️ 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á.
feat(blocks): `contact` — el primer block que COORDINA (estado, datos y palabras)
WIP declarado: el block funciona y renderiza, pero el arco interactivo completo
no está verificado todavía. Lo que falta está en `CONTINUE-blocks.md`, en orden.
## El cambio de doctrina
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 se veía en este mismo block:
con el estado fuera, cada `disabled` era una expresión distinta montada en el punto
de uso, y aparecían huecos — un envío que no se podía hacer y nadie decía por qué.
Eso cambia lo firmado en `docs/architecture/blocks.md` (B-5/B-7, D-BLK). Queda
ANOTADO como pendiente de escribir en el contrato: propagarlo sin actualizarlo
sería deriva.
## Lo que ahora posee el block
- **La máquina de estado** (`state.ts`): `incomplete · unverified · verifying ·
rejected · ready · sending · sent`, derivada de la validez del `Form`, del
veredicto de verificación y del envío. Con dos registros EXHAUSTIVOS —
`CONTACT_REASON` y `CONTACT_ACTION`— que hacen imposible un estado mudo: no se
puede añadir un estado que bloquee el envío sin darle su frase, porque el
`Record` obliga.
- **La forma de los datos** (`schema.ts`): nombre · correo · mensaje, con las
etiquetas como idlangref. El app lo sustituye pasando su `form` + `schema`.
- **Las palabras**, por el traductor (`#?blocks.contact.…|fallback`), la misma
puerta que usa el canon para sus `texts:`.
Partes nuevas: `Contact.Fields` (los tres campos del esquema propio),
`Contact.Submit` (bloqueo Y etiqueta desde el estado, nunca desde una expresión en
el punto de uso) y `Contact.Reason` (la frase del estado). `Contact.Form` toma el
handle del contexto en vez de pedirlo otra vez.
La demo adelgaza en consecuencia: se le cayeron el esquema, las etiquetas, el
`disabled`, el flag de envío y la frase. Le queda lo que es de un app — las
palabras de la página, los datos de contacto, el reto que elige y qué significa
«enviar».
## De paso, dos arreglos de composición
- El `TextArea` iba dentro de `Field.Control` y salían DOS marcos: pinta su propia
carcasa (borde, radio y relleno son de su recipe). `Field.Control` es la carcasa
del `Field.Input` desnudo.
- El `Form.ErrorSummary` vacío dejaba una caja naranja: su raíz pinta borde de
riesgo en cuanto el form es inválido, aunque la lista esté vacía.
## Dos hilos de canon abiertos (medidos, sin causa raíz)
- Con `progressive`/`onSubmit`, un envío inválido bloquea y mueve el foco pero no
expone mensajes; con `onChange` sí, pero saltan los tres campos al escribir en
uno. La máquina tapa el agujero de cara al usuario; el hueco sigue.
- El provider de `ProofOfHuman` escribe `status` él mismo, así que el veredicto que
devuelve el app no se sostiene y los slots `stage*` no renderizaron nunca.
Gates: `blocks:check` verde (14 blocks) · `svelte-check` sin errores propios ·
prettier limpio. Verificado que la sección renderiza (section · form · 3 campos ·
submit · cero errores de página) en un dev server limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B
El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con
`validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada
mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba
valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la
verificacion con los tres campos vacios, justo el hueco que la doctrina existe
para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM:
sincrono y sin escribir errores (`form.validate()` habria encendido los tres
campos en rojo en el primer frame).
`state.test.ts` fija la maquina con 9 casos — primer test unitario del tier,
porque `state.ts` es su primera logica pura: el orden de prioridad, que solo
`ready` deja enviar, y que todo estado bloqueado tiene frase.
Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas
que posee un block que coordina (estado, forma de sus datos, palabras), B-5
admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados
como idlangref con fallback ingles) y la convencion de servicios acotada: el
traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los
blocks de layout no coordinan nada.
i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas
de arranque (galeria y previews, que duplican el boot) y la demo se lee en
castellano en vez de caer al fallback ingles.
Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y
bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico
— `verification` con reto / sin reto, porque omitir el prop no es lo mismo que
pasar un estado que nunca verifica.
Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un
campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con
los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la
verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test
unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status`
el mismo y el veredicto del app no se sostiene (hilo de canon abierto).
Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 ·
svelte-check sin errores propios · prettier limpio en los ficheros propios.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 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.
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
feat(blocks): `contact` — el primer block que COORDINA (estado, datos y palabras)
WIP declarado: el block funciona y renderiza, pero el arco interactivo completo
no está verificado todavía. Lo que falta está en `CONTINUE-blocks.md`, en orden.
## El cambio de doctrina
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 se veía en este mismo block:
con el estado fuera, cada `disabled` era una expresión distinta montada en el punto
de uso, y aparecían huecos — un envío que no se podía hacer y nadie decía por qué.
Eso cambia lo firmado en `docs/architecture/blocks.md` (B-5/B-7, D-BLK). Queda
ANOTADO como pendiente de escribir en el contrato: propagarlo sin actualizarlo
sería deriva.
## Lo que ahora posee el block
- **La máquina de estado** (`state.ts`): `incomplete · unverified · verifying ·
rejected · ready · sending · sent`, derivada de la validez del `Form`, del
veredicto de verificación y del envío. Con dos registros EXHAUSTIVOS —
`CONTACT_REASON` y `CONTACT_ACTION`— que hacen imposible un estado mudo: no se
puede añadir un estado que bloquee el envío sin darle su frase, porque el
`Record` obliga.
- **La forma de los datos** (`schema.ts`): nombre · correo · mensaje, con las
etiquetas como idlangref. El app lo sustituye pasando su `form` + `schema`.
- **Las palabras**, por el traductor (`#?blocks.contact.…|fallback`), la misma
puerta que usa el canon para sus `texts:`.
Partes nuevas: `Contact.Fields` (los tres campos del esquema propio),
`Contact.Submit` (bloqueo Y etiqueta desde el estado, nunca desde una expresión en
el punto de uso) y `Contact.Reason` (la frase del estado). `Contact.Form` toma el
handle del contexto en vez de pedirlo otra vez.
La demo adelgaza en consecuencia: se le cayeron el esquema, las etiquetas, el
`disabled`, el flag de envío y la frase. Le queda lo que es de un app — las
palabras de la página, los datos de contacto, el reto que elige y qué significa
«enviar».
## De paso, dos arreglos de composición
- El `TextArea` iba dentro de `Field.Control` y salían DOS marcos: pinta su propia
carcasa (borde, radio y relleno son de su recipe). `Field.Control` es la carcasa
del `Field.Input` desnudo.
- El `Form.ErrorSummary` vacío dejaba una caja naranja: su raíz pinta borde de
riesgo en cuanto el form es inválido, aunque la lista esté vacía.
## Dos hilos de canon abiertos (medidos, sin causa raíz)
- Con `progressive`/`onSubmit`, un envío inválido bloquea y mueve el foco pero no
expone mensajes; con `onChange` sí, pero saltan los tres campos al escribir en
uno. La máquina tapa el agujero de cara al usuario; el hueco sigue.
- El provider de `ProofOfHuman` escribe `status` él mismo, así que el veredicto que
devuelve el app no se sostiene y los slots `stage*` no renderizaron nunca.
Gates: `blocks:check` verde (14 blocks) · `svelte-check` sin errores propios ·
prettier limpio. Verificado que la sección renderiza (section · form · 3 campos ·
submit · cero errores de página) en un dev server limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
⚠️ **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.
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
feat(blocks): `contact` — el primer block que COORDINA (estado, datos y palabras)
WIP declarado: el block funciona y renderiza, pero el arco interactivo completo
no está verificado todavía. Lo que falta está en `CONTINUE-blocks.md`, en orden.
## El cambio de doctrina
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 se veía en este mismo block:
con el estado fuera, cada `disabled` era una expresión distinta montada en el punto
de uso, y aparecían huecos — un envío que no se podía hacer y nadie decía por qué.
Eso cambia lo firmado en `docs/architecture/blocks.md` (B-5/B-7, D-BLK). Queda
ANOTADO como pendiente de escribir en el contrato: propagarlo sin actualizarlo
sería deriva.
## Lo que ahora posee el block
- **La máquina de estado** (`state.ts`): `incomplete · unverified · verifying ·
rejected · ready · sending · sent`, derivada de la validez del `Form`, del
veredicto de verificación y del envío. Con dos registros EXHAUSTIVOS —
`CONTACT_REASON` y `CONTACT_ACTION`— que hacen imposible un estado mudo: no se
puede añadir un estado que bloquee el envío sin darle su frase, porque el
`Record` obliga.
- **La forma de los datos** (`schema.ts`): nombre · correo · mensaje, con las
etiquetas como idlangref. El app lo sustituye pasando su `form` + `schema`.
- **Las palabras**, por el traductor (`#?blocks.contact.…|fallback`), la misma
puerta que usa el canon para sus `texts:`.
Partes nuevas: `Contact.Fields` (los tres campos del esquema propio),
`Contact.Submit` (bloqueo Y etiqueta desde el estado, nunca desde una expresión en
el punto de uso) y `Contact.Reason` (la frase del estado). `Contact.Form` toma el
handle del contexto en vez de pedirlo otra vez.
La demo adelgaza en consecuencia: se le cayeron el esquema, las etiquetas, el
`disabled`, el flag de envío y la frase. Le queda lo que es de un app — las
palabras de la página, los datos de contacto, el reto que elige y qué significa
«enviar».
## De paso, dos arreglos de composición
- El `TextArea` iba dentro de `Field.Control` y salían DOS marcos: pinta su propia
carcasa (borde, radio y relleno son de su recipe). `Field.Control` es la carcasa
del `Field.Input` desnudo.
- El `Form.ErrorSummary` vacío dejaba una caja naranja: su raíz pinta borde de
riesgo en cuanto el form es inválido, aunque la lista esté vacía.
## Dos hilos de canon abiertos (medidos, sin causa raíz)
- Con `progressive`/`onSubmit`, un envío inválido bloquea y mueve el foco pero no
expone mensajes; con `onChange` sí, pero saltan los tres campos al escribir en
uno. La máquina tapa el agujero de cara al usuario; el hueco sigue.
- El provider de `ProofOfHuman` escribe `status` él mismo, así que el veredicto que
devuelve el app no se sostiene y los slots `stage*` no renderizaron nunca.
Gates: `blocks:check` verde (14 blocks) · `svelte-check` sin errores propios ·
prettier limpio. Verificado que la sección renderiza (section · form · 3 campos ·
submit · cero errores de página) en un dev server limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## Dos hilos de canon abiertos (medidos, sin causa raíz)
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
feat(blocks): `contact` — el primer block que COORDINA (estado, datos y palabras)
WIP declarado: el block funciona y renderiza, pero el arco interactivo completo
no está verificado todavía. Lo que falta está en `CONTINUE-blocks.md`, en orden.
## El cambio de doctrina
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 se veía en este mismo block:
con el estado fuera, cada `disabled` era una expresión distinta montada en el punto
de uso, y aparecían huecos — un envío que no se podía hacer y nadie decía por qué.
Eso cambia lo firmado en `docs/architecture/blocks.md` (B-5/B-7, D-BLK). Queda
ANOTADO como pendiente de escribir en el contrato: propagarlo sin actualizarlo
sería deriva.
## Lo que ahora posee el block
- **La máquina de estado** (`state.ts`): `incomplete · unverified · verifying ·
rejected · ready · sending · sent`, derivada de la validez del `Form`, del
veredicto de verificación y del envío. Con dos registros EXHAUSTIVOS —
`CONTACT_REASON` y `CONTACT_ACTION`— que hacen imposible un estado mudo: no se
puede añadir un estado que bloquee el envío sin darle su frase, porque el
`Record` obliga.
- **La forma de los datos** (`schema.ts`): nombre · correo · mensaje, con las
etiquetas como idlangref. El app lo sustituye pasando su `form` + `schema`.
- **Las palabras**, por el traductor (`#?blocks.contact.…|fallback`), la misma
puerta que usa el canon para sus `texts:`.
Partes nuevas: `Contact.Fields` (los tres campos del esquema propio),
`Contact.Submit` (bloqueo Y etiqueta desde el estado, nunca desde una expresión en
el punto de uso) y `Contact.Reason` (la frase del estado). `Contact.Form` toma el
handle del contexto en vez de pedirlo otra vez.
La demo adelgaza en consecuencia: se le cayeron el esquema, las etiquetas, el
`disabled`, el flag de envío y la frase. Le queda lo que es de un app — las
palabras de la página, los datos de contacto, el reto que elige y qué significa
«enviar».
## De paso, dos arreglos de composición
- El `TextArea` iba dentro de `Field.Control` y salían DOS marcos: pinta su propia
carcasa (borde, radio y relleno son de su recipe). `Field.Control` es la carcasa
del `Field.Input` desnudo.
- El `Form.ErrorSummary` vacío dejaba una caja naranja: su raíz pinta borde de
riesgo en cuanto el form es inválido, aunque la lista esté vacía.
## Dos hilos de canon abiertos (medidos, sin causa raíz)
- Con `progressive`/`onSubmit`, un envío inválido bloquea y mueve el foco pero no
expone mensajes; con `onChange` sí, pero saltan los tres campos al escribir en
uno. La máquina tapa el agujero de cara al usuario; el hueco sigue.
- El provider de `ProofOfHuman` escribe `status` él mismo, así que el veredicto que
devuelve el app no se sostiene y los slots `stage*` no renderizaron nunca.
Gates: `blocks:check` verde (14 blocks) · `svelte-check` sin errores propios ·
prettier limpio. Verificado que la sección renderiza (section · form · 3 campos ·
submit · cero errores de página) en un dev server limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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.
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B
El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con
`validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada
mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba
valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la
verificacion con los tres campos vacios, justo el hueco que la doctrina existe
para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM:
sincrono y sin escribir errores (`form.validate()` habria encendido los tres
campos en rojo en el primer frame).
`state.test.ts` fija la maquina con 9 casos — primer test unitario del tier,
porque `state.ts` es su primera logica pura: el orden de prioridad, que solo
`ready` deja enviar, y que todo estado bloqueado tiene frase.
Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas
que posee un block que coordina (estado, forma de sus datos, palabras), B-5
admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados
como idlangref con fallback ingles) y la convencion de servicios acotada: el
traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los
blocks de layout no coordinan nada.
i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas
de arranque (galeria y previews, que duplican el boot) y la demo se lee en
castellano en vez de caer al fallback ingles.
Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y
bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico
— `verification` con reto / sin reto, porque omitir el prop no es lo mismo que
pasar un estado que nunca verifica.
Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un
campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con
los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la
verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test
unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status`
el mismo y el veredicto del app no se sostiene (hilo de canon abierto).
Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 ·
svelte-check sin errores propios · prettier limpio en los ficheros propios.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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.
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
## 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.
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| # | 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
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
```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` .
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
## 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.**
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **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.
feat(blocks): `banner` — el aviso de arriba, y tres defectos del canon que no existían
F2.11. El block más FINO del tier a propósito: cuando el canon ya tiene la pieza,
el block es colocación y nada más. Reenvía la superficie entera del `Banner` con
`Omit<BannerProps, 'children'>` —sin re-declarar `intent`/`variant`/`size` ni
estrecharlos—, lo mete en columna con `Container` (`width="100%"` +
`paddingX={0}`: a sangre por fuera, en columna por dentro), envuelve la fila con
`Wrap` en vez de aplastarla, y cablea `Banner.Close` solo si llega `onDismiss`.
La visibilidad NO es suya: es la decisión que ya tomó el componente
(«composición, no un booleano `dismissible`»), así que el app envuelve en su
`{#if}`. Un block que guardara ese estado sería una segunda fuente de verdad.
Tampoco emite sema: el canon declara cero eventos para `Banner` a propósito, y un
aviso persistente sin descarte es tan legítimo como uno descartable.
La demo lo enseña **encima del `site-header` de verdad**, con página para
desplazarse. Un aviso dentro de un recuadro no se parece a un aviso.
## El hallazgo que la fase 0 anticipó
`Banner` estampa `role="banner"` DESPUÉS de sus rest props, así que no se puede
relajar a una región normal. Con el `site-header` en la misma página —que es la
única colocación que shipean las referencias— quedan **dos landmarks `banner`**.
Medido en la vista previa: dos. Lo único que puede hacer un app hoy es nombrarlos
con `aria-label`, y eso hace la demo.
## Y una lección de método que costó una sesión
Reporté tres «defectos del canon» —`aria-label` ausente en `Banner.Close`, `type`
desaparecido de todo `Button`, `IconButton` tragándose el `onclick`— y **los tres
eran falsos**. La causa era la misma: medir demasiado pronto.
Los `data-variant` / `data-size` los escribe el componente de eidos al renderizar,
así que están desde el primer frame; `type`, `aria-label` y los handlers los
aplica la **runtime del morfo en un efecto posterior**. A ~1s: `type` ausente en
3 de 3 botones y ningún clic disparando. A ~6s: `type="button"`,
`aria-label="Descartar"` y el descarte funcionando.
La espera válida es un atributo que solo pueda haber puesto la runtime
—`waitForFunction(() => boton.hasAttribute('type'))`—, no que el nodo exista ni
que se vea. La regla queda afinada en `CONTINUE-blocks.md`, que ya avisaba de
esperar la condición y no el reloj: fallé al elegir la condición.
De paso, un aviso de soma que sí era real y era mío: `Tabs.List` sin nombre
accesible en `BlockDemo.svelte`. Afectaba a las 12 demos del tier.
Verificado en navegador: los 4 intents, los 3 tratamientos, `descartable` sí/no
(con `no` el botón deja de renderizarse, no se oculta), claro/oscuro/RTL y 420px,
la tira siempre por encima de la cabecera, cero desbordamiento horizontal, el
descarte con la página recolocándose, y los controles de la demo moviendo la
vista previa en línea. Gates: `blocks:check` verde (12 blocks) · `svelte-check`
sin errores propios · `docs:check` sin errores míos · prettier limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **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
feat(blocks): contact CERRADO — la coordinacion entra en el contrato B
El estado se le pregunta al ESQUEMA, no al Form. `form.isValid` con
`validationBehaviour: 'progressive'` significa «aun no se ha encontrado nada
mal»: un formulario vacio e intacto no tiene errores, asi que se declaraba
valido y `incomplete` era INALCANZABLE al cargar — la seccion pedia resolver la
verificacion con los tres campos vacios, justo el hueco que la doctrina existe
para cerrar. Ahora la raiz valida los valores con `validateSync` de SIUM:
sincrono y sin escribir errores (`form.validate()` habria encendido los tres
campos en rojo en el primer frame).
`state.test.ts` fija la maquina con 9 casos — primer test unitario del tier,
porque `state.ts` es su primera logica pura: el orden de prioridad, que solo
`ready` deja enviar, y que todo estado bloqueado tiene frase.
Contrato B (architecture/blocks.md): seccion «Coordination» con las tres cosas
que posee un block que coordina (estado, forma de sus datos, palabras), B-5
admite el esquema por defecto, B-7 enmendado (posee las palabras de SUS estados
como idlangref con fallback ingles) y la convencion de servicios acotada: el
traductor es el UNICO servicio sancionado. Dice tambien a quien NO aplica — los
blocks de layout no coordinan nada.
i18n por la via real: el arnes registra el namespace `blocks` en las DOS cadenas
de arranque (galeria y previews, que duplican el boot) y la demo se lee en
castellano en vez de caer al fallback ingles.
Ademas: README y pestanas de doc rehechos al reparto nuevo, ficha F2.13 y
bitacora en PLAN-blocks.md, y el control vivo que faltaba para un prop publico
— `verification` con reto / sin reto, porque omitir el prop no es lo mismo que
pasar un estado que nunca verifica.
Verificado en navegador, arco completo y las dos ramas: sin reto vacio → un
campo → tres campos (se desbloquea) → «Enviando…» → «Enviado»; con reto, con
los tres campos llenos el boton sigue bloqueado y la razon pasa a «Resuelve la
verificacion de arriba». `verifying` y `rejected` quedan cubiertos por test
unitario, no por el reto en vivo: el provider de ProofOfHuman escribe `status`
el mismo y el veredicto del app no se sostiene (hilo de canon abierto).
Gates: blocks:check verde (14 blocks) · vitest 9/9 · docs:check 0 ·
svelte-check sin errores propios · prettier limpio en los ficheros propios.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
«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.
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **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).
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
- **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.
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **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.
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
- **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.
feat(blocks): content-section cierra F2 — la rejilla de escape, y las escalas tipograficas salen de Text
El ultimo block de sitio, y el que mas cerca estuvo de ser un envoltorio:
`Section` + `Container` + `Prose` no habria anadido nada, porque `Prose` ya trae
su medida de lectura. Lo que falta en el ecosistema es el ESCAPE — dentro de un
`Container` nada puede ser mas ancho que el, asi que una figura a sangre hay que
sacarla del articulo y ponerla de hermana, y el orden de lectura se rompe.
La seccion es una rejilla de 5 pistas (canalon · flanco · MEDIDA · flanco ·
canalon) y cada parte dice hasta donde llega: `measure` col 3, `wide` col 2/5,
`full` col 1/-1. Todas las longitudes son tokens del sistema (`--measure-*`,
`--container-width-*`, `--container-padding-inline`). `.Body` y `.Media` son
compound porque SE REPITEN; la cabecera son slots de snippet. `.Body` apaga la
medida de `Prose`: dos duenos del mismo ancho se pelean y ganaria el mas
estrecho en silencio.
Los tres ejes son RESPONSIVE (`measure`, `wide`, y el `width` de cada parte),
resueltos con `eidos.resolve()` — el mismo camino que usan los primitivos de
eidos, asi que los unicos breakpoints en juego son los canonicos. Es lo que un
valor unico no puede decir: `{ base: 'full', md: 'wide' }` = foto a sangre en
movil y contenida de `md` arriba.
CANON: un componente no es libreria de otro. `Heading` importaba CINCO escalas
tipograficas de `Text` (`TextTracking`, `TextLeading`, `TextWrap`,
`TextNumeric`, `TextMeasure`) y al tipar este block repeti la violacion. Las
cinco se mueven a `eidos/lib/types.ts`, que es donde su propio encabezado dice
que van y donde ya esta documentado el precedente del mismo fallo (`ColorRole`
re-escrito a mano que derivo a 8 miembros contra 9). Sin shim de compatibilidad:
`text` y `heading` las importan de la libreria.
Cuatro defectos que solo dijo el navegador:
1. `gap` separa tambien las COLUMNAS y una parte que las cruza se lleva esos
huecos encima — `wide` media 1088 en vez de los 1024 del contenedor con el
que debe alinearse, y a 420px `full` llegaba a 532 dentro de una rejilla de
404 y hacia scrollear la pagina. Van `rowGap` + `columnGap={0}`.
2. Una pista `min(medida, 100%)` ignora sus propios canalones: a 420px la
central se quedaba los 404 enteros y la rejilla se iba a 436.
3. Un token de container es un ancho EXTERIOR: con `--container-width-lg` a
pelo la figura `wide` sobresalia 16px por cada lado respecto al texto de la
seccion hermana de debajo, a 1280/1440/1920. La pista ancha resta ahora
`2 * --container-padding-inline` (992 en `lg`) y el desfase es 0. Lo cazo el
usuario mirando la pantalla: yo habia medido el block contra si mismo, no
contra la pagina.
4. `100%` significa algo distinto dentro de cada parte — en `measure`/`wide` ya
es una pista, en `full` es la rejilla entera con canalones —, asi que capar
el pie igual en los dos casos lo dejaba a 340 contra una columna de 372 en
uno y a 404 en el otro.
Verificado en navegador: barrido 375→1920 sin desbordamiento; alineacion
pie↔prosa identica en 3 viewports × 3 medidas × 3 anchuras (18/18); figura
`wide` a ras del texto de la seccion hermana en 1280/1440/1920; y el valor
responsive cambia exactamente en el `md` canonico (a sangre a 767, contenida a
768). Claro, oscuro, 420px y 1920 mirados, y la geometria confirmada tambien en
el Chrome del usuario.
Semantica propia `figure`/`figcaption` (no interactivos → estructura de
documento, B-8) con el hueco senalado: el canon no tiene primitiva `Figure`.
Contexto de CONFIGURACION, no de estado — es un block de layout y no coordina
nada.
Gates: blocks:check verde (15 blocks) · svelte-check sin errores en lo tocado ·
eidos 28/29 suites (la que falla es `audio-player` sin morfo, del hilo de audio,
ajena) · docs:check 0 · prettier limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **`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.
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **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
Siguen sin decidir, cada una entra por el contrato B con **fase 0 ligera**
(mirar ≥2 catálogos del dossier y anotar adoptado/descartado en el README):
1. **Tabla de comparación** de features× planes en `pricing` (recurre en las refs;
sería un hermano `pricing-table` ).
2. **Cita en spotlight** en `testimonials` (la variante modal de TODAS las refs;
el grid es minoría, 2 de 8 en TW).
3. **Lista estática 2/3 columnas** en `faq` (6 de 7 en TW no son acordeón).
4. ** `form-in-hero` ** (slot de alta al boletín dentro 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»).
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
docs(process): el handoff de blocks, reescrito para entrar en frío mañana
El fichero se había convertido en una pila cronológica: la cabecera seguía
contando el estado de hace tres blocks y los gates decían «8 blocks». Ahora es un
punto de entrada.
- **Estado honesto arriba**: 11 de **15** (el plan fijó 14; `feature-split` entró
como hermano con scope-approval, así que el denominador real es 15), qué queda
con su §, y dónde vive cada cosa (plan, contrato, dossier, deuda, READMEs).
- **F2.11 `banner` con fase 0 en pasos**: leer primero el README del componente
`Banner` que YA existe —si el descarte, la persistencia o el `role` son suyos,
el block los compone, no los reinventa—, contrastar ≥2 catálogos, decidir la
forma, y enseñarlo ENCIMA del header, que es donde vive.
- **La regla de forma del tier** en su propia sección, con los ejemplos ya
shipeados de cada lado (repiten / coordinan / slots).
- **Los 8 hallazgos de canon (F15–F22) en una tabla**, cada uno con su número
medido, en vez de repartidos por cinco párrafos. Más los dos huecos viejos que
siguen abiertos (`flex` que no crece, `Group` que no apila).
- **Tres reglas nuevas de las que costaron sangre**: esperar la CONDICIÓN y no un
timeout (con Form+Field+SIUM la hidratación tarda ~2,5s y un screenshot
temprano fotografía el panel invisible); Playwright headless SÍ vale para foco y
teclado reales, `page.fill()` no; y un slot que se apaga se pasa como
`undefined`, con el aviso de que `{#snippet signup()}` sombrea un prop del mismo
nombre.
- **Gates remedidos, no heredados**: `blocks:check` 11 blocks, `svelte-check` sin
errores propios, y los 3 fallos de `contracts.test.ts` verificados uno a uno
como ajenos (escrituras DOM en soma, data-attrs hardcodeados, claves camelCase
de `aura`) — ninguno apunta a `blocks/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
docs(blocks): registro de F2.1, doctrina de demo del tier y handoff
Documentación al día de lo que se cerró hoy y handoff para retomar mañana.
- `PLAN-blocks.md` §7: **F2.1 `site-header` HECHA** con sus siete commits, la
fase 0 contra el dossier §P1, el landmark que le faltaba a `NavigationMenu`
(arreglado en el canon, no parcheado en el block), el hueco del CTA que
navega y parece botón (registrado, no falseado) y el defecto de framework
que destapó la demo.
- `theming/changelog.md` §46: las 44 variables de cascada de `Box` dejan de
heredarse. Un `Section` regalaba su padding a cada descendiente —la galería
arrastraba ~300px de aire desde F0— y los hijos heredaban anchos y `display`
ajenos. `@property { inherits: false }`, radio verificado sin regresiones.
Lección: una variable que un componente escribe para SÍ MISMO debe declararse
`inherits: false`; si no, deja de ser un prop y se vuelve un contagio.
- `architecture/blocks.md` B-9: la demo de un block se construye sobre el
harness compartido y el block se enseña A SANGRE — nunca dentro de un marco
con relleno ni de una caja con scroll, porque eso cambia lo que el block
hace.
- `src/uix/blocks/README.md`: anatomía de la demo (harness, `{Name}Site`, ruta
`preview`, `DocRow`, catálogo único, ejes en el shell).
- `CONTINUE-blocks.md` (nuevo): handoff — qué toca (F2.2 `hero`), la plantilla
de ficheros para copiar, las reglas que ya costaron sangre (a sangre, iframe
solo para anchos de dispositivo, cada prop un control, nada de backticks en
`<Text>`, ojo con las variables que heredan), la deuda declarada que es
decisión del usuario y el estado exacto de los gates.
Gates al parar: `blocks:check` verde · `svelte-check` 73 errores, todos deuda
ajena (0 propios) · `vitest src/uix/eidos` 353/353 · `contracts.test` con los
3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
## Estado de gates al parar (2026-08-06, commit `866089a40`)
- `npm run blocks:check` **verde, 15 blocks / 114 ficheros** .
- `npx vitest run src/uix/blocks/` — **20/20** (`contact/state.test.ts` 12 +
`newsletter/state.test.ts` 8).
- `npx vitest run src/uix/eidos src/uix/blocks` — **402/402** .
- `npm run docs:check` — **0 errores, 0 avisos** (617 docs).
- `svelte-check` : **75 errores / 54 avisos = LA LÍNEA BASE** , ninguno en `blocks/`
ni en lo que toqué del canon. ⚠️ Esa cifra SE MUEVE: con otras sesiones vivas en
el mismo árbol se midió 75, 76, 77 y 80 en el mismo día. Mídela justo antes y
justo después de tu cambio, nunca contra un número recordado.
- `prettier` : limpio en todo lo del commit.
- Fallos AJENOS vivos al parar (**no son del tier**): 7 en `contracts.test.ts` +
`soma-attr-audit` (barrel de `waveform` , MOR-4, escrituras DOM, data-attrs,
namespaces camelCase de `aura` ) y un `__adv-verify-textarea-pack.test.ts` sin
trackear de otra sesión.
## ⚠️ 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.
docs(blocks): auditoria del tier congelada — 29 confirmados, 50 sin verificar
Auditoria de los 15 blocks con 50 agentes: dos dimensiones en paralelo —doctrina
(contrato B + regla de coordinacion, por lectura) y percepcion (sema en vivo en
navegador, interaccion por interaccion)— y despues un verificador ESCEPTICO por
hallazgo, con el encargo de refutarlo y la lista de falsos positivos conocidos
del repo.
90 en bruto → 40 verificados → 29 CONFIRMADOS + 11 refutados (28%)
Ese 28% es la razon de existir de la fase adversarial: sin ella habrian entrado
once acusaciones falsas.
⚠️ 50 hallazgos quedaron SIN VERIFICAR, 18 de ellos marcados ALTA. El tope de 40
lo puso mi script, no el trabajo. No son menos graves: estan sin comprobar.
`AUDIT-blocks-2026-08-01.md` es la UNICA copia — el journal del workflow era de
sesion y ya no existe. Incluye los refutados con su motivo para que nadie los
vuelva a reportar.
Lo gordo confirmado:
- `contact` importa `$libs/forms`, fuera de la lista de la frontera dura 1.
- El nombre accesible del submit de `contact` esta congelado en «Enviar» en los
cinco estados: `Form.Submit` impone su `aria-label`, asi que las palabras de
estado que el block existe para poseer NO llegan al lector de pantalla. El
block cumple su promesa solo a la vista.
- `site-header` congela la pagina: con el cajon abierto, cruzar el `breakpoint`
esconde cajon/overlay/trigger con `display:none` pero deja el Drawer abierto y
el scroll del body bloqueado. Reproducido con swipe tactil real rotando un
telefono; la unica salida es Escape, que en tactil no existe.
- `hero` en `layout="background"`: CTA secundaria a 1.78:1, bajo AA, y el token
que la demo escribe a mano para arreglarlo es inerte.
- `height="100%"` sobre `Card` es INERTE (atributo HTML, no prop) en `pricing` y
`testimonials`: las tarjetas no igualan alto mientras README y demo afirman lo
contrario.
- B-8 sin declarar (landmark + jerarquia de encabezados) en seis READMEs.
La dimension CRUZADA —los 15 en una pagina compuesta— NO se ejecuto, y es ademas
la condicion de cierre de F2 del plan.
La auditoria confirmo su propia premisa: la capa perceptiva no se habia ejercido
NUNCA porque la galeria arrancaba sin packs de sema desde F0, y ahi esta lo mas
feo (todavia sin verificar): el nav del header emitiendo `commit-select`+`affirm`
con earcon audible al pasar el RATON por encima, un `Enter` en `newsletter`
emitiendo dos `commit-submit` con intents contradictorios, y 4 de 7 elementos del
header mudos.
El handoff `CONTINUE-blocks.md` arranca ahora por la auditoria, con el orden
recomendado (verificar los 50 → arreglar por severidad → pagina compuesta →
catalogo → v2) y la deuda declarada del arnes, que triplica la cadena de arranque.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
---
## 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í.