Fase 0 de la corrección: higiene, sin decisiones pendientes de firma.
EL MOTOR PURO
La tabla de formas del anillo vivía dentro de un `<script module>` sin
exportar — o sea, imposible de testear. Sale a `shapes.ts` siguiendo el
precedente de la casa (OnionMenu ya tiene sus engines puros `geometry.ts` /
`color.ts` con test al lado). No es abstracción nueva: es mover código que ya
estaba separado a un sitio donde un test pueda alcanzarlo.
EL TEST QUE FALTABA
`RING_SHAPES` es LA afirmación del componente — «ningún estado se distingue
solo por el color» — y no la sostenía nada: los 353 de eidos son de contrato
y no la tocan. Ahora hay 8 casos, y el central compara SILUETAS (raíl · dash ·
loop · halo, con el color deliberadamente ausente) y exige que ningún par de
estados comparta la suya. Los demás fijan las decisiones de diseño que se
tomaron con motivo: el raíl permanece en reposo (la presencia recede, nunca
desaparece), `reviewing` está QUIETO mientras `acting` gira (es lo que los
separa cuando el tinte solo los aleja ~22° de hue), `escalated` es el único
con anillo cerrado y halo, los loops son solo los registrados, y el halo
arranca en el borde del orbe para que el `ping` (2×) aterrice en el anillo.
Verificado en negativo, como el guard: igualando `reviewing` a `acting` el
test falla con el mensaje exacto — «acting is indistinguishable from
reviewing without colour».
GAPS DESFASADOS
El README de soma listaba dos gaps YA HECHOS (el wrapper de eidos y la
comparativa, de fases cerradas) y los dos READMEs seguían diciendo que la
elicitación «se materializa con la superficie de conversación» — que es
justo lo que la firma de ayer (§5b) descartó. Corregido, y de paso queda
escrito POR QUÉ nunca se materializó, que no era falta de superficie: la
escalada lleva una razón tipada pero NO la pregunta, y
`resolveEscalation(runId, 'continue'|'cancel')` no tiene por dónde llevar una
RESPUESTA. Hasta que existan esas dos costuras, `escalated` se puede mostrar
pero no responder — con el punto 4 del contrato a11y firmado (elicitaciones
descartables/posponibles) incumplido.
Verificado: eidos 360/361 · el único fallo es `backdrop`, componente SIN
TRACKEAR de otra sesión en curso (dos `#000` crudos en su recipe), ajeno ·
eidos-lint aura 0 inválidos / 0 class-hooks · check en los 73 preexistentes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>