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>