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

443 lines
28 KiB

docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
# CONTINUE — la cola de auditorías, y el protocolo con el que se trabaja
> **Kickoff para la sesión siguiente**: _«Lee `docs/process/CONTINUE-audit-queue.md`
> y empieza por §1 (S-33, firmado y sin ejecutar).»_
> **Fecha**: 2026-08-13 · Rama `alpha-0.1-dir-prefs`, pusheada en `eda3b977b`.
> ⚠️ **COMPARTIDA**: otra sesión trabaja en `blocks` **y** en un eje de
> dismissal (los `types.ts` de color-picker / time-picker / time-range-picker).
> `git reset -q` + stagear por lista explícita, y clasificar **por contenido del
> diff, nunca por nombre**.
> **Fuentes de los hallazgos**: [`AUDIT-sema-2026-08-05.md`](./AUDIT-sema-2026-08-05.md)
> (44 confirmados con refutación adversarial) ·
> [`CONTINUE-perceptual-surface.md`](./CONTINUE-perceptual-surface.md) §3 (la
> cola larga, con el detalle de cada eje). Este documento dice **cómo se
> trabaja, qué se firmó y por dónde se sigue**.
---
## 0 · EL PROTOCOLO — va primero porque cambió hoy, y no es opcional
**Por CADA modificación o acción, y ANTES de tocar nada**, se responden las
cinco preguntas del autor:
1. ¿Qué es lo que se va a solucionar?
2. ¿Has leído toda la documentación al respecto, o sólo estás presuponiendo?
3. ¿Qué afecta a otros componentes?
4. ¿Afecta a las doctrinas del ecosistema?
5. ¿Qué modificaciones planteas?
**Un item por mensaje, y se PARA a esperar firma.** Hoy propuse una «puerta»
que me dejaba auto-autorizar lo mecánico y presenté cuatro decisiones juntas en
una hoja resumida; el autor lo cortó en seco. No hay puerta: se expone y se
espera.
Y dos precondiciones que hoy costaron **dos rectificaciones completas**:
- **El SPEC del COMPONENTE es doctrina, y se busca ANTES de diagnosticar.** El
item de la «6ª fila fantasma» de chronos llevaba un mes en la cola y llegó a
estar FIRMADO; al ir a ejecutarlo apareció `SPEC.md` del componente diciendo
lo contrario en tres sitios coordinados. Se revocó. Una asimetría con la
familia NO es evidencia de deriva: puede ser la única decisión escrita.
- **Barrer `docs/` ANTES de proponer retirar nada.** Recomendé retirar
`intentRequirement: 'forbidden'` «igual que `collapse` y `lock`». Falso:
aquellos no tenían una línea en `docs/`; éste está en `CANON.md` §4, en
`architecture/sema.md` §, y en la tabla de **D.3**, decisión firmada del autor
en `decisions/book-deviations.md:472`.
Regla que resume las dos: **el código nunca corrige al canon; se le pone al
autor la CONTRADICCIÓN delante y decide él.**
---
## 1 · Mañana lo primero: S-33, FIRMADO el 2026-08-13 y sin ejecutar
**El defecto es `Exclude`, no «falta una rama».** `IntentOptionalFamily =
Exclude<SemaFamily, IntentRequiredFamily>` es una derivación de mundo abierto
—«todo lo que no es required»— así que una familia marcada `'forbidden'` cae en
el cubo OPCIONAL, donde el intent está permitido. Los dos guardianes de runtime
tienen el mismo hueco: `validation.ts:39` sólo comprueba `=== 'required'`, y
`event.ts:104-110` (`isSemaEvent`), si el intent está presente, devuelve
`isIntent(...) || isIntentBinding(...)` sin mirar la política.
**Medido con el `tsc` del proyecto** (probe con los dos estados del const, hoy y
endurecido). Un único error en todo el probe, y es el control de la derivación
actual:
```
intent-probe.ts(87,1): error TS2578: Unused '@ts-expect-error' directive.
```
es decir: con `contact` en `'forbidden'`, `{ family: 'contact', intent: 'threat' }`
**compila**. Lo demás del probe demostró las tres cosas que hacían falta:
| Probe | Resultado |
| --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `{contact,intent}` · `{contact}` · `{commit,intent}` con la tercera rama presente | compilan → **una rama tecleada sobre `never` no cuesta nada hoy** |
| `@ts-expect-error` sobre `{family:'commit'}` | se CONSUME → la puerta de `required` sigue intacta |
| `@ts-expect-error` sobre `{contact,intent}` con derivación POSITIVA y contact forbidden | se CONSUME → **flipar una línea del const cierra la puerta solo** |
| `{family:'contact'}` a secas, misma situación | compila → `'forbidden'` significa «familia usable, intent prohibido», no «familia inutilizable» |
docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**La receta firmada:**
1. `sema/types.ts` — derivar los tres cubos **positivamente** (`extends 'optional'`
/ `extends 'required'` / `extends 'forbidden'`), matando el `Exclude`, y añadir
la rama `{ family: IntentForbiddenFamily; intent?: never }` a `SemaEvent` y a
`MorfoEventSemantic` (`morfo/types.ts`).
2. `sema/event.ts:104` y `sema/validation.ts:39` — la condición simétrica en los
dos guardianes de runtime.
3. El esquema sium de `morfo/schema.ts` **no necesita nada hoy** (no hay literal
que añadir mientras ninguna familia sea `'forbidden'`), y si alguien flipa el
const **salta el guard de S-34** que se metió hoy y le dice que lo añada. Las
dos mitades cubiertas: los tipos se activan solos, el runtime avisa.
4. El probe se convierte en **test** (`sema/` o `contracts.test.ts`), para que
el día que alguien reintroduzca un `Exclude` el `@ts-expect-error` deje de
consumirse y falle.
Delta de conducta hoy: **cero, medido**. Lo que cambia es que la promesa del
canon («editing the const reshapes the discriminated unions») pasa a ser cierta.
⚠️ Y la razón por la que se descartó la alternativa que yo mismo había
recomendado —un guard que fallara si alguna familia declara `'forbidden'`— es
doctrinal, no de gusto: es **lint donde el tipo puede hablar**, y `CLAUDE.md`
§_Eidos drift defense_ dice que cuando una capa PUEDE expresar la restricción en
docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
TypeScript, hacerlo con un test es una violación de arquitectura. La misma
doctrina con la que ayer se cerró F3 borrando `targetOverride`.
---
## 2 · Lo cerrado hoy (6 commits, todos pusheados)
| Commit | Qué |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| `b8aa333fd` | **F3 CERRADA** — `targetOverride` BORRADO de `TriggerOptions` + `assertTargetOverride`; el compilador, ya como censo, cazó 9 usos en tests que el grep del scratch nunca escaneó |
| `c00e97b91` | **F5 CERRADA** — los tres sitios de las tres auditorías, medidos DESPUÉS de la campaña |
| `a15745491` | **chronos `fixedWeeks`: decisión REVOCADA** — el SPEC manda; no era un defecto |
| `44af38e6e` | **S-10** — el canal visual honra los servicios de su propia bolsa (`anidado ?? raíz`, como los otros tres) |
| `14a169357` | **guard D9** — mira también la vista de eidos, que en los componentes-vista ES el emisor |
| `eda3b977b` | **S-34** — el validador de runtime queda atado al const de política de intent, con el guard visto fallar |
docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Con F5 cerrada, **las seis fases del eje perceptual (F1–F6) están completas**.
---
## 3 · Decisiones ABIERTAS — esperan firma, no se tocan
Cada una necesita su exposición de cinco preguntas cuando se abra. Aquí sólo el
titular y la evidencia ya medida, para no re-descubrirla.
### S-35 · la forma etiqueta regala lo que la estructurada exige
docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
`SemaActionEvent = SemaEvent | SemaEventLabel` y la etiqueta incluye la familia
pelada: `'commit'` compila aunque `commit` sea `required`, y
`normalizeSemaEvent('commit')` devuelve `intent: null` — el documento que la
política declara inválido. `validateSemaEvent` remata devolviendo temprano ante
cualquier etiqueta (`validation.ts:29`). **Alcance medido**: `SemaActionEvent` no
tiene consumidores fuera de los tres ficheros del propio sema. Va pegado a S-33:
la misma unión, las mismas líneas.
fix(sema): S-30/S-38 — las cinco reglas hapticas abren la puerta que habian olvidado Cinco reglas escribian una firma haptica sobre familias cuyo activeChannels es ['sound'] — alertdialog (pulse 0.7/60), sheet movil (tap 0.4/24), editable shift-enter-mode, stepper shift-step y timeline emerge-reveal. El resolver instalaba la firma, HapticChannel salia por la puerta de entrada: vibrate() 0 veces desde su nacimiento, con la intencion comentada en los propios packs. La decision se tomo contrastando con el mundo real, no por limpieza: mi propuesta inicial era borrar los bloques y el autor la corrigio con las preguntas correctas — ¿que hacen las plataformas? ¿quien decide? Verificado en las fuentes: Apple HIG prescribe haptic de warning cuando aparece una alerta importante y tick de seleccion para cambios de valor discretos; Android pide moderacion pero con constantes CONFIRM/REJECT. O sea: el alertdialog y el stepper SON patrones de plataforma, y la intencion escrita en los packs era diseño, no deriva. El framework ademas ya deja la ultima palabra a quien toca: la puerta por regla (`channels`), la per-emit, y la haptica entera es opt-in del producto — ensanchar estas reglas no impone vibracion a nadie. Arreglo: `channels: ['sound', 'haptic']` en las cinco, cada una con su razon perceptual escrita. Los 5 waivers de CHANNEL_EXCEPTIONS se retiran: el guard del censo (punto 2, que estos waivers silenciaban desde 2026-08-06) queda re-armado y ES el aviso automatico para la proxima vibracion con puerta cerrada. El aviso del waiver («widening requires completing the firma») estaba ya pagado por la defensa S-31 de kindToPattern: un {kind:'tick'} parcial resuelve a valores finitos, nunca vibrate(NaN). Visto en rojo DOS veces antes de verde: el censo sin waivers listando exactamente las 5, y el e2e nuevo de resolver.test.ts (resolucion con el pack real + HapticChannel real: 1 vibracion por regla, patron finito) ejecutado contra los packs sin ensanchar via stash. Verificado: censo 67/67 · resolver 30/30 · sema+morfo+contracts 540 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · prettier limpio · docs 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### ~~S-30/S-38~~ · EJECUTADO 2026-08-13 — las cinco reglas ABREN LA PUERTA
fix(sema): S-30/S-38 — las cinco reglas hapticas abren la puerta que habian olvidado Cinco reglas escribian una firma haptica sobre familias cuyo activeChannels es ['sound'] — alertdialog (pulse 0.7/60), sheet movil (tap 0.4/24), editable shift-enter-mode, stepper shift-step y timeline emerge-reveal. El resolver instalaba la firma, HapticChannel salia por la puerta de entrada: vibrate() 0 veces desde su nacimiento, con la intencion comentada en los propios packs. La decision se tomo contrastando con el mundo real, no por limpieza: mi propuesta inicial era borrar los bloques y el autor la corrigio con las preguntas correctas — ¿que hacen las plataformas? ¿quien decide? Verificado en las fuentes: Apple HIG prescribe haptic de warning cuando aparece una alerta importante y tick de seleccion para cambios de valor discretos; Android pide moderacion pero con constantes CONFIRM/REJECT. O sea: el alertdialog y el stepper SON patrones de plataforma, y la intencion escrita en los packs era diseño, no deriva. El framework ademas ya deja la ultima palabra a quien toca: la puerta por regla (`channels`), la per-emit, y la haptica entera es opt-in del producto — ensanchar estas reglas no impone vibracion a nadie. Arreglo: `channels: ['sound', 'haptic']` en las cinco, cada una con su razon perceptual escrita. Los 5 waivers de CHANNEL_EXCEPTIONS se retiran: el guard del censo (punto 2, que estos waivers silenciaban desde 2026-08-06) queda re-armado y ES el aviso automatico para la proxima vibracion con puerta cerrada. El aviso del waiver («widening requires completing the firma») estaba ya pagado por la defensa S-31 de kindToPattern: un {kind:'tick'} parcial resuelve a valores finitos, nunca vibrate(NaN). Visto en rojo DOS veces antes de verde: el censo sin waivers listando exactamente las 5, y el e2e nuevo de resolver.test.ts (resolucion con el pack real + HapticChannel real: 1 vibracion por regla, patron finito) ejecutado contra los packs sin ensanchar via stash. Verificado: censo 67/67 · resolver 30/30 · sema+morfo+contracts 540 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · prettier limpio · docs 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**La decision se INVIRTIO al contrastarla con el mundo real y con el autor.** Mi
recomendacion era borrar los bloques; el autor pregunto que hacen iOS/Android y
quien decide — y la verificacion en las fuentes (Apple HIG: haptic de warning
para alertas; tick de seleccion para cambios de valor; Android: «less is more»
pero con constantes CONFIRM/REJECT) mas un hecho del propio framework (la
intencion estaba ESCRITA y comentada en los packs, y la haptica entera es
opt-in del producto) cambiaron el veredicto: las reglas llevaban la intencion
del disenador y solo habian olvidado la puerta. Arreglo: `channels: ['sound',
'haptic']` en las cinco, con la razon perceptual escrita en cada una; los 5
waivers de `CHANNEL_EXCEPTIONS` fuera (el guard del censo, re-armado, ES el
test que avisa si vuelve a pasar); y un e2e nuevo en `resolver.test.ts` que
prueba resolucion + `vibrate()` con valores FINITOS (la defensa S-31 cubre los
`{kind:'tick'}` parciales — el aviso del waiver ya estaba pagado). Visto en
rojo dos veces: el censo sin waivers listando las 5, y el e2e contra los packs
sin ensanchar.
### S-30/S-38 · (ficha original, cerrada)
docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Dialog ×2 (alertdialog y sheet), editable, stepper y timeline escriben
`haptic: {…}` sobre eventos de familia `emerge`/`shift`, cuyo `activeChannels`
es `['sound']`. Medido por el verificador con resolver y canal reales: firma
háptica instalada, `vibrate()` **0 veces**. Y el mapa es coherente en su diseño:
las familias que vibran declaran **base háptica Y canal** (contact, commit,
signal, handle); emerge y shift no declaran ninguna de las dos.
**Opciones**: (a) `channels: ['sound','haptic']` en las cinco (patrón canónico en
`slider`, `css-field`, `drag-drop`) · (b) borrar los bloques y escribir que
emerge/shift no vibran. **Mi criterio: (b)** — el mapa ya lo dice en dos sitios
independientes, y (a) reabriría por la puerta de atrás una decisión perceptual
(que un overlay te vibre en la mano) que nadie ha tomado. Con guard que cruce
cada regla contra el `activeChannels` alcanzable.
fix(sema,docs): S-19 — el puerto de anuncios encaja con su sumidero sin adaptador `AnnounceFn` pedia la prioridad en una bolsa de opciones y `ActiveUix.announce` la toma POSICIONAL, asi que el cableado que la doctrina prescribe —una sola region viva compartida, este canal como uno de sus dos emisores— no se podia escribir: `{ announce: uix.announce }` no compilaba. Y forzarlo era peor que no tenerlo: el sumidero recibia un OBJETO donde lee una prioridad, `liveRegionIds[obj]` es `undefined`, y TODO aterrizaba en la region polite — un `threat` dejaba de interrumpir, que es justo lo que un aviso critico no puede hacer. La firma pasa a posicional, como el sumidero y como la fuente equivalente de soma. Tres puertos, una sola forma. Cambio de conducta: CERO — el canal es opt-in y ninguna raiz lo enciende hoy. Visto en rojo antes de tocar: un test con la forma EXACTA de `uix.announce` (`(message, priority?, timeout?)`) recibia `{priority:'assertive'}` en el hueco de la prioridad. Tres aserciones existentes migran de bolsa a posicional — su contrato cambia, y era el contrato defectuoso. ⚠️ Mi analisis inicial estaba equivocado y el autor lo mando revisar. Habia concluido que el canal estaba muerto porque el runtime de soma no mete `message` en la señal. No lo mete, cierto, pero es DISEÑO: son dos emisores para dos publicos —soma cubre sus componentes, el canal cubre a quien usa `EngineSemantic` sin soma y escribe su propia señal— y precisamente por eso nada se anuncia dos veces. La propuesta de fusionarlos habria roto ese diseño; retirada antes de escribir una linea. `sema.md` §announce reescrito: fuera el aviso del adaptador (ya no hace falta), dentro la razon de los dos publicos, y el aviso que SI queda — ninguna raiz enchufa el canal, y encenderlo sin inyectar el anunciador añadiria un segundo par de regiones vivas junto al compartido. Eso es decision aparte, sin firmar. Verificado: announce 6 ✓ · sema+morfo+contracts 541 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · docs 0/624 · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### ~~S-19~~ · EJECUTADO PARCIAL 2026-08-13 — la firma alineada; encenderlo sigue ABIERTO
fix(sema,docs): S-19 — el puerto de anuncios encaja con su sumidero sin adaptador `AnnounceFn` pedia la prioridad en una bolsa de opciones y `ActiveUix.announce` la toma POSICIONAL, asi que el cableado que la doctrina prescribe —una sola region viva compartida, este canal como uno de sus dos emisores— no se podia escribir: `{ announce: uix.announce }` no compilaba. Y forzarlo era peor que no tenerlo: el sumidero recibia un OBJETO donde lee una prioridad, `liveRegionIds[obj]` es `undefined`, y TODO aterrizaba en la region polite — un `threat` dejaba de interrumpir, que es justo lo que un aviso critico no puede hacer. La firma pasa a posicional, como el sumidero y como la fuente equivalente de soma. Tres puertos, una sola forma. Cambio de conducta: CERO — el canal es opt-in y ninguna raiz lo enciende hoy. Visto en rojo antes de tocar: un test con la forma EXACTA de `uix.announce` (`(message, priority?, timeout?)`) recibia `{priority:'assertive'}` en el hueco de la prioridad. Tres aserciones existentes migran de bolsa a posicional — su contrato cambia, y era el contrato defectuoso. ⚠️ Mi analisis inicial estaba equivocado y el autor lo mando revisar. Habia concluido que el canal estaba muerto porque el runtime de soma no mete `message` en la señal. No lo mete, cierto, pero es DISEÑO: son dos emisores para dos publicos —soma cubre sus componentes, el canal cubre a quien usa `EngineSemantic` sin soma y escribe su propia señal— y precisamente por eso nada se anuncia dos veces. La propuesta de fusionarlos habria roto ese diseño; retirada antes de escribir una linea. `sema.md` §announce reescrito: fuera el aviso del adaptador (ya no hace falta), dentro la razon de los dos publicos, y el aviso que SI queda — ninguna raiz enchufa el canal, y encenderlo sin inyectar el anunciador añadiria un segundo par de regiones vivas junto al compartido. Eso es decision aparte, sin firmar. Verificado: announce 6 ✓ · sema+morfo+contracts 541 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · docs 0/624 · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**Mi primer analisis estaba mal y el autor lo mando revisar.** Dije que el canal
estaba muerto porque el runtime de soma no mete `message` en la señal; es cierto
que no lo mete, pero **es diseño, no defecto**: son DOS emisores para DOS
publicos (soma cubre sus componentes; el canal cubre a quien usa
`EngineSemantic` SIN soma y escribe su propia señal con el texto). Justo por eso
nada se anuncia dos veces, y mi opcion (b) —fusionarlos— habria roto el diseño.
Retirada.
HECHO: `AnnounceFn` pasa a prioridad POSICIONAL, como el sumidero y como la
fuente de soma, asi que `{ announce: uix.announce }` compila literal y sobra el
adaptador. Visto en rojo antes: un sumidero con la forma de `uix.announce`
recibia `{priority:'assertive'}` en el hueco de la prioridad — y ahi
`liveRegionIds[obj]` es `undefined`, o sea que TODO caia en la region polite y un
`threat` dejaba de interrumpir. 3 aserciones de test migradas de bolsa a
posicional. `sema.md` §announce reescrito: fuera el aviso del adaptador, dentro
la razon de los dos publicos.
QUEDA ABIERTO (decision, no bug): **ninguna raiz enchufa el canal**. Con el
puerto ya alineado es UNA linea en la raiz, pero encenderlo sin inyectar el
anunciador cae en las regiones propias del canal y añade un SEGUNDO par
`role=status`/`role=alert` junto al compartido.
### S-19 · (ficha original)
docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
`AnnounceFn` pide `(message, { priority })` y `uix.announce` es posicional
`(message, priority?, timeout?)`: el cableado canónico de `sema.md:762` es un
error de tipos, no hay adaptador, y **ninguna de las dos raíces** pasa `announce`
al engine. Si alguien fuerza el cast, `priority` llega como objeto,
`liveRegionIds[priority]` es `undefined` y todo cae en la región polite — un
`threat` deja de interrumpir.
**Son DOS decisiones y conviene no mezclarlas**: (i) alinear la firma + corregir
el doc — no cambia conducta, porque hoy nadie lo pasa; (ii) enchufar el canal en
las raíces, que **sí** cambia conducta y arriesga el «dos sumideros» que la
propia doctrina prohíbe, porque soma tiene su propio puerto de anuncio
funcionando (`soma.svelte.ts:99`, el de metrics/menu-dial/onion-menu).
**Mi criterio**: (i) ahora, (ii) como ítem con censo previo de quién anunciaría
por los dos caminos.
---
## 4 · La cola, en orden
fix(sema): S-30/S-38 — las cinco reglas hapticas abren la puerta que habian olvidado Cinco reglas escribian una firma haptica sobre familias cuyo activeChannels es ['sound'] — alertdialog (pulse 0.7/60), sheet movil (tap 0.4/24), editable shift-enter-mode, stepper shift-step y timeline emerge-reveal. El resolver instalaba la firma, HapticChannel salia por la puerta de entrada: vibrate() 0 veces desde su nacimiento, con la intencion comentada en los propios packs. La decision se tomo contrastando con el mundo real, no por limpieza: mi propuesta inicial era borrar los bloques y el autor la corrigio con las preguntas correctas — ¿que hacen las plataformas? ¿quien decide? Verificado en las fuentes: Apple HIG prescribe haptic de warning cuando aparece una alerta importante y tick de seleccion para cambios de valor discretos; Android pide moderacion pero con constantes CONFIRM/REJECT. O sea: el alertdialog y el stepper SON patrones de plataforma, y la intencion escrita en los packs era diseño, no deriva. El framework ademas ya deja la ultima palabra a quien toca: la puerta por regla (`channels`), la per-emit, y la haptica entera es opt-in del producto — ensanchar estas reglas no impone vibracion a nadie. Arreglo: `channels: ['sound', 'haptic']` en las cinco, cada una con su razon perceptual escrita. Los 5 waivers de CHANNEL_EXCEPTIONS se retiran: el guard del censo (punto 2, que estos waivers silenciaban desde 2026-08-06) queda re-armado y ES el aviso automatico para la proxima vibracion con puerta cerrada. El aviso del waiver («widening requires completing the firma») estaba ya pagado por la defensa S-31 de kindToPattern: un {kind:'tick'} parcial resuelve a valores finitos, nunca vibrate(NaN). Visto en rojo DOS veces antes de verde: el censo sin waivers listando exactamente las 5, y el e2e nuevo de resolver.test.ts (resolucion con el pack real + HapticChannel real: 1 vibracion por regla, patron finito) ejecutado contra los packs sin ensanchar via stash. Verificado: censo 67/67 · resolver 30/30 · sema+morfo+contracts 540 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · prettier limpio · docs 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
1. ~~**S-33**~~ EJECUTADO 2026-08-13 (`1a174d5a6`): los tres cubos derivados en
positivo + rama `intent?: never` + `intentRequirementOf()` (leer el const
directo ESTRECHA y el gate no compila — arista que la ficha no vio).
Desviacion declarada: `validation.ts` NO se toco (su rama de politica es
inalcanzable — hallazgo nuevo, abierto).
2. ~~**S-35**~~ EJECUTADO 2026-08-13 (`6a7554415`): `SemaEventKey` derivado de la
politica (bare solo sin required; pareja sin forbidden), `SEMA_EVENT_LABELS`
56→54, `isSemaEventLabel('commit')` false → `validateSemaEvent` lanza. Dos
hallazgos ABIERTOS de su paso: el vocabulario de etiquetas no tiene NINGUN
consumidor en produccion (¿se gana el sitio?), y la rama `required` de
`validation.ts:39-46` es codigo muerto (inalcanzable tras `isSemaEvent`).
fix(sema,docs): S-19 — el puerto de anuncios encaja con su sumidero sin adaptador `AnnounceFn` pedia la prioridad en una bolsa de opciones y `ActiveUix.announce` la toma POSICIONAL, asi que el cableado que la doctrina prescribe —una sola region viva compartida, este canal como uno de sus dos emisores— no se podia escribir: `{ announce: uix.announce }` no compilaba. Y forzarlo era peor que no tenerlo: el sumidero recibia un OBJETO donde lee una prioridad, `liveRegionIds[obj]` es `undefined`, y TODO aterrizaba en la region polite — un `threat` dejaba de interrumpir, que es justo lo que un aviso critico no puede hacer. La firma pasa a posicional, como el sumidero y como la fuente equivalente de soma. Tres puertos, una sola forma. Cambio de conducta: CERO — el canal es opt-in y ninguna raiz lo enciende hoy. Visto en rojo antes de tocar: un test con la forma EXACTA de `uix.announce` (`(message, priority?, timeout?)`) recibia `{priority:'assertive'}` en el hueco de la prioridad. Tres aserciones existentes migran de bolsa a posicional — su contrato cambia, y era el contrato defectuoso. ⚠️ Mi analisis inicial estaba equivocado y el autor lo mando revisar. Habia concluido que el canal estaba muerto porque el runtime de soma no mete `message` en la señal. No lo mete, cierto, pero es DISEÑO: son dos emisores para dos publicos —soma cubre sus componentes, el canal cubre a quien usa `EngineSemantic` sin soma y escribe su propia señal— y precisamente por eso nada se anuncia dos veces. La propuesta de fusionarlos habria roto ese diseño; retirada antes de escribir una linea. `sema.md` §announce reescrito: fuera el aviso del adaptador (ya no hace falta), dentro la razon de los dos publicos, y el aviso que SI queda — ninguna raiz enchufa el canal, y encenderlo sin inyectar el anunciador añadiria un segundo par de regiones vivas junto al compartido. Eso es decision aparte, sin firmar. Verificado: announce 6 ✓ · sema+morfo+contracts 541 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · docs 0/624 · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
3. ~~**S-30/S-38**~~ EJECUTADO · ~~**S-19**~~ EJECUTADO PARCIAL (firma alineada; encender el canal sigue abierto, ver §3).
feat(morfo,sema,contracts): M1(i) — el contrato dice QUIEN dispara cada evento (D.2) El morfo no podia decir quien emite un evento declarado: todo se presumia del runtime, y la verdad de las excepciones vivia fuera del contrato — en la lista de deuda del guard D9 y en las excepciones del censo de packs. Las tablas de eventos de los README prometian percepcion que no ocurre (la ficha M1, P1). Ejecuta la decision firmada D.2 (IMPLEMENTATION_CONTRACT): - `emission: 'runtime' | 'host' | 'external' | 'declared-only'` en MorfoEvent + literal en el schema sium. Ausente = 'runtime': las ~250 declaraciones existentes no se tocan y la presuncion sigue siendo la norma. - El guard D9 exime por DECLARACION en vez de por lista: solo los 'runtime' exigen emisor. Y si un evento eximido sigue en INERT_EVENT_DEBT, el guard FALLA con «FIXED — remove it», para que la deuda no sobreviva a su resolucion. - El pack-census gana el chequeo simetrico: una regla cuyo alcance son SOLO eventos 'declared-only' afina una percepcion que jamas estampara — muerta por definicion, sin lista de excepciones. 'host' NO cuenta como muerto: el anfitrion dispara por el runtime y la regla casa normal. Fixture con los dos casos (positivo y negativo con hermano runtime al alcance). - `morfo.md` §Step 5.5 lleva el apendice tecnico en los terminos que D.2 exige: la tabla de los cuatro valores × sus guards, y la nota doctrinal de por que NO es doctrina del libro — marcar un evento inerte como declared-only para callar al guard es ensanchar la deuda con otro nombre. La sonda temporal (flag sobre tooltip.emerge-present → correr → revertir) destapo un agujero real y lo cerro: el flag entre `name` y `semantic` hacia INVISIBLE el evento al regex del guard — ni censado ni exento. El regex tolera ahora la linea opcional, y la sonda termino dando la conducta disenada exacta: exencion + exigencia de limpiar la deuda. NADIE estrena el flag: los 7 de INERT_EVENT_DEBT quedan intactos y son la parte (ii) — siete decisiones perceptuales del autor, una a una (la de tooltip: ¿el componente mas ubicuo merece firma, o silencio declarado?). La instancia original de la ficha (los 5 pickers con close inerte) esta muerta desde la normalizacion de nombres. Verificado: suites 945 ✓ / 6 ajenos · docs 0/624 · check 69 = base aislada, diff VACIO · prettier: mis dos limpios siguen limpios, pack-census formateado (regresion mia), contracts y morfo.md ya fallaban en HEAD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
4. **§3.5 fable** — ~~`M1 emission`~~ **PARTE (i) EJECUTADA 2026-08-13**: el
flag `emission: 'runtime' | 'host' | 'external' | 'declared-only'` de D.2
existe en `MorfoEvent` + schema (ausente = `'runtime'`, las ~250
declaraciones intactas), el guard D9 exime por declaracion (y si el evento
eximido esta en la deuda, EXIGE limpiarla — visto con sonda temporal sobre
tooltip), el pack-census rechaza reglas sobre eventos `declared-only`
(fixture 2 casos; `host` NO es muerto), y `morfo.md` §Step 5.5 lleva el
apendice tecnico con la nota doctrinal de D.2. La sonda destapo y cerro un
agujero: el flag entre `name` y `semantic` hacia INVISIBLE el evento al
feat(soma,morfo,eidos): M1(ii) tooltip — los tres eventos existen por fin, y el silencio queda donde estaba firmado El morfo declaraba emerge-present / emerge-dismiss / emerge-dismiss-escape y nadie los emitia: el SILENT del pack nunca casaba, la puerta de temas que su propio docblock documenta no podia funcionar, y el preset present-rise que declara `event: ['emerge-present',...]` jamas pintaba. La excepcion del censo lo decia desde agosto: «Today's silence is accidental, not the declared design». El re-analisis pedido por el autor cazo lo que la primera propuesta no vio: la firma present-rise es GLOBAL (base.css:6520), la entrada por data-state es el workaround de la emision ausente (el comentario del provider lo dice literal), y la semantica autorizada distingue hover (entrada) de focus (instant-open, «appears with no entrance — its semantics») — cosa que el evento no distingue. De ahi la opcion B firmada: el estado conserva la entrada; la emision añade la superficie que faltaba. Cableado con la receta F4: `present` pre→post — y SIN su `commits` (fijaba 'delayed-open' y pisaria el 'instant-open' del camino focus; los overlays binarios conservan el suyo porque su valor es total, desviacion declarada); dismiss ×2 ganan `targetFallback: [trigger]` (Presence sostiene el content por la salida; el fallback cubre la carrera). Provider: emision SOLO en transiciones reales — un open-timer cancelado no presenta nada que retirar, un re-hover abierto no re-emite — y `handleClose` lleva la causa para distinguir el Escape. MEDIDO en navegador, los tres caminos y la fuga exacta: hover present CONTENT · entrada autorizada corriendo (scale-in+fade-in), present-rise NO — el preset gana la cascada; miedo al doble movimiento REFUTADO en el camino delayed focus present CONTENT con instant-open y present-rise CORRIENDO — la fuga predicha, confirmada → silencio visual MINIMO en la receta, scoped a [data-state='instant-open'][data-event-phase='active']; re-medido: anims [] y la entrada delayed intacta leave dismiss CONTENT via Presence · salida autorizada (scale-out+ fade-out), dismiss-fade no Escape dismiss-escape CONTENT · desestampado del hold a ~240ms Deudas limpiadas en el mismo commit (el guard lo exige): 3 ids de INERT_EVENT_DEBT + 3 excepciones de EMISSION_EXCEPTIONS. Quedan 4 de la parte (ii): virtual-list/grid ×3 y gradient-picker commit-reset (S-14). Verificado: soma navegador 1278✓/1 (el timeout ajeno) · tooltip 4/4 · sema+morfo+contracts 552✓/6 ajenos · check 69 = base aislada, diff VACIO · prettier: morfo/tooltip.ts limpio en HEAD queda limpio; los 3 avisos ya fallaban en HEAD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
regex del guard — ahora lo tolera. ⚠️ **Parte (ii): 3 de 7 resueltas** — ~~tooltip ×3~~ **CABLEADO 2026-08-13**
(la re-analisis del autor cazo lo que faltaba: la firma global
`present-rise` de base.css:6520 y que la entrada por `data-state` era el
WORKAROUND de la emision ausente, con la semantica focus=sin-entrada
colgando de ella). Receta F4: `present` pre→post SIN su commit (el commit
fijaba 'delayed-open' y pisaria el 'instant-open' del focus — los overlays
binarios si lo conservan), dismiss ×2 + `targetFallback: [trigger]`,
triggers solo en transiciones REALES (un timer cancelado no emite; un
re-hover abierto no re-emite), y la causa escape distinguida. MEDIDO en
navegador los 3 caminos: hover→present en CONTENT con la entrada autorizada
corriendo (scale-in+fade-in, present-rise NO — el preset gana la cascada);
focus→present con `instant-open` y present-rise CORRIENDO = la fuga exacta
→ silencio visual MINIMO en la receta, scoped a
`[data-state='instant-open'][data-event-phase='active']`, re-medido: anims
[]; leave→dismiss en CONTENT via Presence con la salida autorizada
(scale-out+fade-out, dismiss-fade no); Escape→dismiss-escape. El SILENT del
pack por fin CASA y la puerta de temas funciona. Deudas limpiadas: 3 ids
feat(morfo,soma): M1(ii) virtual-list/grid — el scroll declara su emisor: el host Los tres `handle-scroll*` de virtual-list/grid estaban en la deuda del D9 como «declarados y nunca sonados». Mal clasificados: la resolucion YA estaba escrita en los dos providers como decision con su porque — el listener de scroll dispara por pixel y la familia handle es ['sound','haptic'] (trinquete `step` + tick), asi que una emision por defecto seria un clic-clic continuo bajo scroll con inercia; «apps that want scroll-driven sema feedback should wire it themselves with their own throttle / debounce policy». Lo que faltaba no era el emisor: era un sitio DECLARABLE para esa decision. Es exactamente el caso de diseño del valor `host` de D.2, estrenado aqui: el contrato declara la superficie, el emisor es la aplicacion anfitriona. - `emission: 'host'` en los tres eventos, con el porque en el docblock. - Los dos comentarios de provider apuntan al flag y corrigen la cita desfasada (`activeChannels=['haptic']` — hoy son dos canales, lo que hace la conclusion MAS cierta, no menos). - La promesa del host es viable, verificado: `provider.runtime` es campo publico — un app puede emitir con su propia politica. - Deuda D9: -3 ids. Queda UNA entrada en INERT_EVENT_DEBT: gradient-picker commit-reset (S-14), la ultima resolucion de la parte (ii). Sin cambio de conducta: cero emisiones nuevas, cero navegador que medir. Verificado: guards 309 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · prettier: virtual-list.ts limpio en HEAD queda limpio; los otros 3 ya fallaban. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
del D9 + 3 excepciones del censo. ~~virtual-list/grid ×3~~ **RESUELTOS
2026-08-13 con `emission: 'host'`** — la resolucion YA estaba escrita en los
dos providers como decision con su porque («apps wire it themselves with
their own throttle policy»; per-pixel + familia handle = trinquete continuo
bajo inercia) y solo le faltaba un sitio DECLARABLE: es el caso de diseño
del valor `host` de D.2. La cita desfasada del comentario
(`activeChannels=['haptic']` — hoy es ['sound','haptic']) corregida al
tocarlo; la promesa del host es viable (`provider.runtime` es publico).
fix(morfo,sema)!: M1(ii) gradient-picker — el reset delegado deja de declararse dos veces (S-14) El morfo declaraba `commit-reset`, el pack le dedicaba una regla (settle + tick) y los dos README lo tableaban — y el provider no lo emitia. Lo unico que suena en el Clear es el `commit-reset` del Picker generico COMPUESTO, con la firma del pack `picker`. La ficha S-14 ofrecia dos salidas y su premisa comparativa era falsa, medido: los hermanos color/date/time-picker SI emiten su propio reset (date-picker cableado el 2026-08-11) — pero NO componen el Picker generico: definen su propio pickerShellHandle y son dueños de su transaccion. Gradient-picker la DELEGA entera (su docblock: «The transaction — open / commit / cancel / clear — lives in the composed generic PickerProvider»), asi que la emision sigue a la propiedad: el reset es del picker. Cablear el gemelo habria dado DOBLE firma por un gesto (dos estampados + dos sonidos casi simultaneos — la clase que file-upload pago), y PickerProvider no ofrece hook para silenciar el suyo. Retirada completa, con la palabra explicita del autor: el evento fuera del morfo (una nota en su lugar dice por que y hacia donde), la regla muerta fuera del pack, las filas fuera de los dos README (el de eidos gana la frase correcta: los eventos propios son los del DOMINIO — presets — y el Clear es del picker), la excepcion fuera del censo y el ultimo id fuera de la deuda D9. Con esto la parte (ii) de M1 esta COMPLETA — las 7 resoluciones de la cola medida el 2026-08-12: tooltip x3 CABLEADOS (receta F4 + silencio visual minimo medido), virtual-list/grid x3 `emission: 'host'` (la decision escrita de sus providers, ahora expresable), gradient-picker RETIRADO (delegacion). INERT_EVENT_DEBT y EMISSION_EXCEPTIONS quedan VACIAS por primera vez, cada una con su lapida narrando las resoluciones. M1 entero cerrado: el contrato dice quien dispara cada evento, y ningun evento declarado miente. Sin cambio de conducta: el Clear sonaba por el picker y sigue sonando igual. Verificado: guards 339 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · docs 0/624 · prettier: los dos ficheros limpios en HEAD quedan limpios. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
~~gradient-picker commit-reset~~ **RETIRADO
2026-08-13 (S-14 cerrado)** — y la ficha se corrigio con medida: los
hermanos color/date/time-picker SI emiten su reset (cableados 2026-08-11),
pero NO componen el Picker generico — son dueños de su transaccion.
Gradient-picker la DELEGA entera (su docblock lo declara), asi que el Clear
ya suena con el `commit-reset` del picker compuesto, y cablear el gemelo
habria dado DOBLE firma por un gesto (la clase file-upload). Retirados:
evento del morfo (con la razon en su lugar) + regla del pack + filas de los
dos README + la excepcion del censo + el ultimo id de la deuda.
**Con esto la parte (ii) esta COMPLETA y M1 CERRADO ENTERO: las dos listas
de deuda (INERT_EVENT_DEBT y EMISSION_EXCEPTIONS) quedan VACIAS por primera
vez**, cada una con su lapida explicando las 7 resoluciones. La
feat(morfo,sema,contracts): M1(i) — el contrato dice QUIEN dispara cada evento (D.2) El morfo no podia decir quien emite un evento declarado: todo se presumia del runtime, y la verdad de las excepciones vivia fuera del contrato — en la lista de deuda del guard D9 y en las excepciones del censo de packs. Las tablas de eventos de los README prometian percepcion que no ocurre (la ficha M1, P1). Ejecuta la decision firmada D.2 (IMPLEMENTATION_CONTRACT): - `emission: 'runtime' | 'host' | 'external' | 'declared-only'` en MorfoEvent + literal en el schema sium. Ausente = 'runtime': las ~250 declaraciones existentes no se tocan y la presuncion sigue siendo la norma. - El guard D9 exime por DECLARACION en vez de por lista: solo los 'runtime' exigen emisor. Y si un evento eximido sigue en INERT_EVENT_DEBT, el guard FALLA con «FIXED — remove it», para que la deuda no sobreviva a su resolucion. - El pack-census gana el chequeo simetrico: una regla cuyo alcance son SOLO eventos 'declared-only' afina una percepcion que jamas estampara — muerta por definicion, sin lista de excepciones. 'host' NO cuenta como muerto: el anfitrion dispara por el runtime y la regla casa normal. Fixture con los dos casos (positivo y negativo con hermano runtime al alcance). - `morfo.md` §Step 5.5 lleva el apendice tecnico en los terminos que D.2 exige: la tabla de los cuatro valores × sus guards, y la nota doctrinal de por que NO es doctrina del libro — marcar un evento inerte como declared-only para callar al guard es ensanchar la deuda con otro nombre. La sonda temporal (flag sobre tooltip.emerge-present → correr → revertir) destapo un agujero real y lo cerro: el flag entre `name` y `semantic` hacia INVISIBLE el evento al regex del guard — ni censado ni exento. El regex tolera ahora la linea opcional, y la sonda termino dando la conducta disenada exacta: exencion + exigencia de limpiar la deuda. NADIE estrena el flag: los 7 de INERT_EVENT_DEBT quedan intactos y son la parte (ii) — siete decisiones perceptuales del autor, una a una (la de tooltip: ¿el componente mas ubicuo merece firma, o silencio declarado?). La instancia original de la ficha (los 5 pickers con close inerte) esta muerta desde la normalizacion de nombres. Verificado: suites 945 ✓ / 6 ajenos · docs 0/624 · check 69 = base aislada, diff VACIO · prettier: mis dos limpios siguen limpios, pack-census formateado (regresion mia), contracts y morfo.md ya fallaban en HEAD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
instancia original de la ficha (5 pickers con close inerte) esta MUERTA
desde la normalizacion de nombres · ~~`M4`~~ **EJECUTADO 2026-08-13, con
fix(morfo): M4 — la pareja [true,false] solo se infiere donde es exacta `compileDataPlan` precalculaba el par [etiqueta-true, etiqueta-false] para todo stateRef+enum con `falseLabel = values.find(v => v !== trueLabel)`: exacto en un enum BINARIO (el otro miembro, da igual el orden) y dependiente del ORDEN de declaracion desde 3 valores. El diagnostico de la ficha se corrigio dos veces con medida: 1. «Hoy no muerde porque los stateRef booleanos usan enums binarios» — falso de premisa: hay 12 declaraciones con 3+ (aura x3, checkbox x2, image, meter x2, progress x2, tooltip x2). Pero tampoco muerden, por OTRA razon: bindean strings y el runtime solo usa la pareja con `typeof raw === 'boolean'`. En los 12 la pareja se calculaba y jamas se usaba. 2. El arreglo de la ficha («stateRef+enum exige exactamente 2 valores») habria PROSCRITO esas 12 declaraciones legitimas — la misma clase de error que M5. El peligro real es el emparejamiento booleano↔enum-no-binario, hoy inexistente, y el arreglo lo hace imposible EN SILENCIO: con 3+ valores la pareja no se calcula, un string pasa intacto (la clase viva), y un booleano LANZA MorfoInvariantError nombrando componente-attr y la salida declarativa — que no es un campo nuevo sino `v.mapRef(source, { true, false })`, que ya existia en el vocabulario. Doctrina S-09: un path malo lanza, no inventa (la alternativa era estampar `data-state="true"`). Del re-analisis, dos rectificaciones propias que quedan anotadas: mi primera propuesta invocaba un «guard de contratos» que NO existe (el mecanismo honesto es el throw), y mi censo de bindings verifico 2 de 12 — la prueba real es la suite entera: soma navegador 1278✓/1 (el timeout ajeno) con la pareja ya retirada de los 12, ninguno lanzo. Tests nacidos en rojo (2/3; el binario paso porque es la conducta de hoy): binario exacto con orden invertido · 3+ con string = passthrough · 3+ con booleano = throw con /mapRef/. Verificado: compile 44/44 · sema+morfo+contracts 550 ✓ / 6 ajenos · soma navegador 1278✓/1 · check 69 = base aislada, diff VACIO · prettier: ambos ficheros ya fallaban en HEAD, no se reformatean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
el DIAGNOSTICO CORREGIDO dos veces**: la premisa de la ficha era falsa (hay
12 declaraciones con enum de 3+, censadas: aura·checkbox·image·meter·
progress·tooltip) y su arreglo («exigir exactamente 2 valores») las habria
proscrito — son legitimas porque bindean STRINGS y el modo hace passthrough.
El peligro real es booleano↔enum-no-binario, hoy inexistente. Arreglo: la
pareja solo se infiere con enum BINARIO (exacta por construccion), y un
booleano llegando a 3+ LANZA nombrando la salida declarativa (`v.mapRef`,
que YA existia en el vocabulario — ni campo nuevo ni adivinanza), doctrina
S-09. En mi re-analisis cai en dos: el «guard de contratos» de mi primera
propuesta NO existia, y mi censo de bindings verifico 2 de 12 — la suite
entera de navegador (1278✓) es la que probo que ninguno bindea booleano.
Tests nacidos en rojo 2/3 (el tercero paso porque ES la conducta binaria de
hoy) · ~~`M5`~~ **EJECUTADO 2026-08-13, y la ficha era INCOMPLETA**: acertaba en que
feat(morfo,sema): M5 — el matcher `state` habla el contrato de datos del morfo El matcher aceptaba pares de strings sueltos con la promesa escrita de que «una iteracion futura los tipara contra el contrato». La iteracion es esta, y no es UNA puerta sino DOS — el precedente de dos ejes del framework (allowedTargets/targetFallback, intentRequirement/intentGuidance): - `state` se tipa con `DataPairOf<M>`, la union discriminada derivada de `parts[].data`: attr declarado, y donde hay enum, valor del enum. Un typo en `data-last-action` deja de compilar — la clase de deriva que semaSelector existe para matar. La union atraviesa intacta el idioma `Parameters<typeof semaSelector<M>>[2]` de los 67 packs: cero migraciones. - `undeclaredState` es la puerta ABIERTA con nombre: los attrs que el morfo no declara A PROPOSITO (data-size/data-sheet del dialog, eidos-only por docblock). La regla que la usa dice lo que hace, en vez de colarse por un string abierto. Un solo slot no podia imponer enum Y quedar abierto — la rama abierta se traga a la estricta (la clase S-11). El gate de disyuncion vive en el BUILDER, no en el censo (desviacion declarada del punto 4 firmado, a mas fuerte): attr declarado por la puerta abierta lanza, attr no declarado por `state` lanza, valor fuera de enum lanza — y como los packs son modulo, revienta al IMPORTAR: ninguna suite queda verde encima. `aria` queda abierto con la razon real escrita: su unico uso en el framework casa el `role` del dialog, que el morfo deliberadamente no declara (variant-dependent, lo pone el provider). Nacidos en rojo: 4 tests runtime (enum, attr no declarado via state, attr declarado via puerta abierta, puerta abierta funcionando) + probes `@ts-expect-error`. Migrados los 2 usos de dialog y los 3 tests de escaping que usaban attrs no declarados. Una arista de implementacion documentada: dentro del cuerpo generico `DataPairOf<M>` es condicional diferido y TS no deja leer `.attr` — una lectura estructural local, como ya hace el resto del builder. ⚠️ Nota de proceso: mi primera propuesta fue un guard de censo + corregir el comentario — el mismo error que S-33 (lint donde el tipo puede hablar), y cai en el en el mismo dia. El re-analisis contra las decisiones del framework lo invirtio. La ficha M5 queda anotada como INCOMPLETA, no refutada. Verificado: selectors 15/15 · sema+morfo+contracts 547 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · docs 0/624 · prettier: dialog.ts era mio y queda limpio; selectors.ts/test/index ya fallaban en HEAD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
se puede tipar ya, pero no vio que 2 de los 5 usos casan attrs que el morfo
NO declara A PROPOSITO (`data-size`/`data-sheet`, eidos-only por docblock) ni
que un solo slot no puede imponer enum Y quedar abierto (la clase S-11). La
solucion es el precedente de DOS EJES del framework: `state` tipado con
`DataPairOf<M>` (attr declarado + valor del enum; el typo no compila) +
`undeclaredState` abierto y auto-documentado para los eidos-only, con el
builder lanzando en runtime si un attr declarado se cuela por la puerta
abierta (y viceversa) — gate en el builder y no en el censo porque lanza al
IMPORTAR el pack. `aria` queda abierto con la razon escrita (su unico uso
casa un role deliberadamente no declarado). Mi primera propuesta era un
guard de censo — el MISMO error de S-33, corregido en re-analisis. Probes
`@ts-expect-error` + 5 tests runtime nacidos en rojo (4/4) ·
~~`M6`~~ **EJECUTADO
refactor(sema,morfo)!: M6 — los alias deprecados mueren; un nombre por concepto `IntentExpectedFamily` y `SemaEventLabel` eran alias de compatibilidad de `IntentRequiredFamily` y `SemaEventKey`, y el codigo nuevo seguia importando el nombre viejo — la mitad de los tipos hablaba el vocabulario de antes del renombrado. La regla dura del repo (no backward-compat shims: actualiza los consumidores y borra el camino viejo) decide el destino: 13 usos migrados a los canonicos y los DOS alias borrados del tipo y del barrel. La unica duda doctrinal se resolvio antes de tocar: D.3 (book-deviations:489) nombra `IntentExpectedFamily`, pero DESCRIBE la implementacion con el nombre que existia entonces — registro historico, no prescripcion. El precedente es la casa misma: se ha renombrado vocabulario entero sin reescribir los registros de decisiones. Ademas, y declarado en la exposicion: `semaIntentExpectedFamilySchema` (const local de morfo/schema.ts, mismo vocabulario viejo) pasa a `semaIntentRequiredFamilySchema` — y el guard S-34 de contracts.test.ts, que lo busca por nombre LITERAL, se actualiza en el mismo commit; separarlos habria dejado el guard ciego un commit entero. Los 2 `as never` de compile.ts se retiran: eran vestigio de antes de que existiera `DepSink` (un `() => void` encaja en `(value: string) => void`), y el compilador lo confirma. Y `_resetCompileCache`, tercera pata de la ficha, resulto YA borrado — cero apariciones en src/; se anota para que no vuelva a la cola. Censo previo al borrado: cero consumidores de los alias en src/, web/ y scripts/ fuera de los 13 migrados. Las funciones `is/parse/toSemaEventLabel` y el tipo `ParsedSemaEventLabel` conservan su nombre: son API propia, no el alias, y su renombrado quedo explicitamente FUERA de lo firmado. Verificado: sema+morfo+contracts 541 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · prettier: types.ts era mio y queda limpio; compile.ts y contracts.test.ts ya fallaban en HEAD y no se reformatean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
2026-08-13**: los alias `IntentExpectedFamily`/`SemaEventLabel` BORRADOS
(no-shims; D.3 los nombra como HISTORIA, no como prescripción), 13 usos
migrados a los canónicos, `semaIntentExpectedFamilySchema` renombrado
`…Required…` CON el guard S-34 actualizado en el mismo commit, los 2
`as never` de `compile.ts` retirados (vestigio pre-`DepSink`), y
`_resetCompileCache` resultó YA borrado — fuera de la cola ·
fix(eidos): E2+E3 — el modo de sistema pasa por ActiveDom y un re-derive fallido ya no desviste la pagina E2. `createSystemColorSchemeSource` usaba `globalThis.matchMedia` + `addEventListener` crudos: la regla de la casa (listeners de window/document pasan por ActiveDom) y la ventana EQUIVOCADA en iframe/popup — globalThis es la global, no la del dom. Ahora, con dom inyectado, el media query sale de `dom.getWindow().matchMedia` y la suscripcion va por `dom.listen` (ciclo de vida gestionado); sin dom, el camino crudo queda de fallback (serializacion CSS / SSR). Descartada la opcion preferida de la ficha (tracker en adom, espejo de prefers-reduced-motion) por la regla de ≥2 consumidores: leidos los 6 boots reales de web/, TODOS pasan su propio modeSource — el camino de sistema tiene hoy cero consumidores vivos; si algun dia gana un segundo, se promociona, y queda dicho en el comentario. E3. `#renderSchemeCss` atrapaba el error del re-derive y devolvia '' — y `apply()` lee '' como «sin esquema» y BORRA el <style> anterior: un cambio de modo con semilla que no deriva no solo fallaba sin log, desvestia la pagina del bloque que ya estaba bien puesto. Ahora `#lastSchemeCss` conserva el ultimo bloque bueno, el catch avisa por `#uix?.logger.warn('eidos.scheme',…)` (la superficie que ya usa sema; eidos no tiene logger propio), y retirar el spec sigue limpiando. `applyColorScheme` no cambia: construye EAGER y una semilla invalida sigue reventando en la cara del llamador — el silencio era solo del re-derive. Dos hallazgos del proceso, anotados: el validador de config es FAIL-CLOSED y rechaza escalas donantes rotas en la puerta (por eso el rojo de E3 no puede fabricarse via config: la inyeccion va sobre `buildScheme` mismo, passthrough real hasta que el flag del test lo revienta — el sitio exacto del throw que la ficha nombraba); y el stub de matchMedia del test de E2 debe dar un mql POR QUERY, porque el tracker de reduced-motion del propio dom consulta la misma ventana. Ambos tests vistos MORDER el codigo viejo (stash de la implementacion, no del test): E2 porque el query de color iba a la ventana global (jsdom: sin matchMedia), E3 porque el bloque desaparecia. Verificado: eidos 28/28 · sema+morfo+eidos+contracts 943 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · prettier: active-eidos.svelte.ts estaba limpio en HEAD y queda limpio; el test ya fallaba en HEAD. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
~~`E2`~~ · ~~`E3`~~ **EJECUTADOS 2026-08-13** — E2: el mode-source de sistema
consulta `dom.getWindow().matchMedia` y se suscribe por `dom.listen`
(ventana CORRECTA en iframe/popup; fallback crudo sin dom); descartado el
tracker en adom por la regla de ≥2 consumidores (los 6 boots reales de web/
pasan todos su `modeSource` — el camino de sistema tiene hoy CERO
consumidores vivos). E3: `#lastSchemeCss` — un re-derive que falla conserva
el ultimo bloque bueno y avisa por `#uix?.logger` en vez de DESVESTIR la
pagina (`apply()` leia '' como «sin esquema» y borraba el `<style>`);
`applyColorScheme` sigue lanzando eager en la cara del llamador. Hallazgo
del rojo: el validador de config es FAIL-CLOSED y rechaza escalas rotas en
la puerta — la inyeccion del test va sobre `buildScheme` mismo, passthrough
real hasta que el flag lo revienta. Ambos tests vistos morder el codigo
viejo via stash.
revert(eidos): A-47 — el rescope del anillo de foco se deshace, y queda el registro de los errores Por orden del autor. Mi arreglo del 2026-08-13 (acda70f1e) no era lo que su mensaje afirmaba: - No llegaba a cinco recetas — accordion, tabs, toast (anillo) y chat-composer, field (borde de foco) pasan por alias declarados en `:root`, que se congelan. Es LITERALMENTE el bug que `docs/canon/tsc.md` documenta como «un bug arquitectonico que ningun linter caza», y no abri ese canon antes de proponer. - No era «la clase»: Card y Banner pintan sus propios lienzos solidos y quedaban fuera. - Y EMPEORABA lo que tocaba de mas: el rescope se hereda al subarbol entero, asi que un elemento que repinta lienzo claro dentro del panel (Surface soft anidada, filas de tabla) recibia un anillo blanco sobre casi blanco. surface.css vuelve a su estado previo; la fila del ledger vuelve a CONFIRMADO (fichero de la otra sesion, no commiteado). El handoff registra la reversion y conserva lo unico util del analisis, marcado como NO verificado: que el anillo pertenece al contexto de tinta y no al variant de un componente, y que el anillo de error no puede aplanarse a la tinta de contraste — el rojo ES el mensaje; su problema real es que es translucido. Y se anade `docs/process/errores-toxico-opus.md` con el registro completo, por orden del autor: los 15 veredictos de la revision adversarial de mi propio trabajo (1 limpio, 6 refutados, 8 con reservas), los 44 hallazgos que sobrevivieron al segundo esceptico, los 22 que ese paso tumbo, y el coste —~10,25 M de tokens de subagentes, 3 204 llamadas— con su veredicto: que todo ello es erroneo en bloque y no debe usarse como cola de trabajo sin re-verificar desde cero. Verificado: eidos 397 ✓ · check 69 = base aislada, diff VACIO · docs 0/625. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
5. **§3.6 blocks** — **`A-47` SIGUE ABIERTO — el arreglo del 2026-08-13 fue
REVERTIDO el 2026-08-14** (`acda70f1e` deshecho). No llegaba a 5 recetas
(alias congelados en `:root`, la violacion que `canon/tsc.md` nombra y que
no se leyo antes de proponer), no cubria los lienzos solidos de Card ni
Banner, y EMPEORABA el anillo en subarboles que repintan lienzo claro. Lo
que el analisis dejo en claro y sirve para quien lo retome: el anillo
pertenece al CONTEXTO DE TINTA (`[data-on]`), no al variant de un
componente; y el eje evaluativo (`--focus-ring-color-error`) NO puede
aplanarse a la tinta de contraste — el rojo es el mensaje; su problema real
es que es translucido (`color-mix(risk-8 52%, transparent)`) y se lo come
cualquier lienzo saturado. NADA de esto esta verificado por el autor.
[texto anterior, ahora invalido:] ~~ARREGLADO en la CLASE, no en la
fix(eidos): A-47 — el anillo de foco sostiene el contraste sobre CUALQUIER superficie solida El anillo global es primario translucido — suave a proposito — y sobre un `Surface variant="solid"` de color primario compone primario-sobre-primario: medido en la demo real del cta, 1,02:1 (peor aun que el 1,43:1 del ledger; misma pareja, panel rgb(142,78,198)). Los DOS unicos elementos interactivos del block quedaban sin indicador, contra el minimo 3:1 de WCAG 1.4.11. Arreglada la CLASE, no la instancia: cualquier hijo enfocable de cualquier superficie solida, en cualquier color y modo. Una declaracion en la regla solida que ya existia en surface.css rescopa `--focus-ring-color` a la misma tinta que el texto de ese lienzo — el slot `contrast(on-solid)`, elegido por APCA con suelo WCAG2 (rfc-color-engine §8). Como §32 canonizo UN solo modelo de foco (outline, todos los consumidores beben del var: button, calendar, breadcrumb, anchor-nav…), la variable rescopada repara el catalogo entero por cascada; forced-colors conserva su camino `Highlight`, ajeno a los tokens. A plena fuerza y sin `color-mix` — aritmetica sobre un token garantizado re-pierde la garantia (la clase «el silencio es un valor»). Medido antes/despues con sonda de composicion (canvas getImageData — el panel pinta en oklch wide-gamut y el ring en color(srgb …/0.52); un parser rgb da null) y sobre el ELEMENTO real: dentro del panel 1,02:1 → 5,18:1 (outlineColor del boton enfocado) fuera del panel 2,17:1 → 2,17:1 (el token global INTACTO) Dos hallazgos anotados sin decidir: `--focus-ring-color-error` sobre solido (volcarlo a la tinta de contraste borraria la semantica de error) y el 2,17:1 del anillo global contra la PAGINA (el indicador puede satisfacer 1.4.11 contra el boton adyacente; abrirlo es decision de diseño). La fila del ledger pasa a ARREGLADO pero el fichero NO viaja en este commit: es WIP de la otra sesion (protocolo A-85). Verificado: eidos 393 ✓ · check 69 = base aislada, diff VACIO · prettier: surface.css estaba limpio en HEAD y queda limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
instancia**: el anillo global es primario translucido (suave a proposito) y
sobre un `Surface` solido primario media 1,02:1 (peor aun que el 1,43 del
ledger; misma pareja rgb(142,78,198)). Una declaracion en la regla solida de
`surface.css` rescopa `--focus-ring-color` a la tinta del slot
`contrast(on-solid)` (pick APCA + suelo WCAG, rfc-color-engine §8; §32 = un
solo modelo outline, todos los consumidores beben del var). Medido en la
demo real: 1,02 → **5,18:1\*\* en el `outlineColor` del boton enfocado; el
fix(eidos): A-47 — el anillo de foco sostiene el contraste sobre CUALQUIER superficie solida El anillo global es primario translucido — suave a proposito — y sobre un `Surface variant="solid"` de color primario compone primario-sobre-primario: medido en la demo real del cta, 1,02:1 (peor aun que el 1,43:1 del ledger; misma pareja, panel rgb(142,78,198)). Los DOS unicos elementos interactivos del block quedaban sin indicador, contra el minimo 3:1 de WCAG 1.4.11. Arreglada la CLASE, no la instancia: cualquier hijo enfocable de cualquier superficie solida, en cualquier color y modo. Una declaracion en la regla solida que ya existia en surface.css rescopa `--focus-ring-color` a la misma tinta que el texto de ese lienzo — el slot `contrast(on-solid)`, elegido por APCA con suelo WCAG2 (rfc-color-engine §8). Como §32 canonizo UN solo modelo de foco (outline, todos los consumidores beben del var: button, calendar, breadcrumb, anchor-nav…), la variable rescopada repara el catalogo entero por cascada; forced-colors conserva su camino `Highlight`, ajeno a los tokens. A plena fuerza y sin `color-mix` — aritmetica sobre un token garantizado re-pierde la garantia (la clase «el silencio es un valor»). Medido antes/despues con sonda de composicion (canvas getImageData — el panel pinta en oklch wide-gamut y el ring en color(srgb …/0.52); un parser rgb da null) y sobre el ELEMENTO real: dentro del panel 1,02:1 → 5,18:1 (outlineColor del boton enfocado) fuera del panel 2,17:1 → 2,17:1 (el token global INTACTO) Dos hallazgos anotados sin decidir: `--focus-ring-color-error` sobre solido (volcarlo a la tinta de contraste borraria la semantica de error) y el 2,17:1 del anillo global contra la PAGINA (el indicador puede satisfacer 1.4.11 contra el boton adyacente; abrirlo es decision de diseño). La fila del ledger pasa a ARREGLADO pero el fichero NO viaja en este commit: es WIP de la otra sesion (protocolo A-85). Verificado: eidos 393 ✓ · check 69 = base aislada, diff VACIO · prettier: surface.css estaba limpio en HEAD y queda limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
token global fuera del panel INTACTO (2,17, sin cambios). A plena fuerza,
sin mix — aritmetica sobre token garantizado re-pierde la garantia.
Hallazgos anotados sin decidir: `--focus-ring-color-error` sobre solido
(semantica de error, caso aparte) y el 2,17:1 del anillo global contra la
PAGINA (el ledger no lo señalo; el contraste puede satisfacerse contra el
boton adyacente — decision de diseño si se abre). Fila del ledger →
ARREGLADO, SIN commitear (viaja con la otra sesion, protocolo A-85) ·
fix(eidos): A-69 — `duration` de count-up dice la verdad, y las cifras aterrizan juntas `duration` se documentaba como «approximate count duration in seconds» y alimentaba los parametros del muelle con λ FIJA: el asentamiento real crecia con el logaritmo de la magnitud. Medido por el ledger y reproducido: 12.500 tardaba 7,88 s (3,9x lo prometido), 340 → 5,09 s, 48 → 3,57 s, 4,31 s de dispersion — y la razon 2,21 entre cifras era INVARIANTE con duration, asi que ningun valor global las igualaba. `onEnd` disparaba desde un timer ciego a `delay + duration`, 5,9 s antes de que la cifra grande parase. La ejecucion es la disposicion del ledger, tras el re-analisis que pidio el autor — mi primera propuesta sustituia el muelle por un ease-out temporal, que era cambiar el DISEÑO de tapadillo: la caida exponencial (arranque rapido, aterrizaje suave) es la semilla que el componente porta, y mi cita de motion.md §drivers para descartarla estaba fuera de jurisdiccion (gobierna presets de UI sobre propiedades CSS; esto es numero→formatter→textContent). La forma se queda; el RELOJ se recalibra: - λ derivada de (recorrido, umbral, duration): el asentamiento es analitico (|y(t)| ≈ AMP·|d0|·e^(−λt)), asi que λ = ln(AMP·|d0|/umbral)/duration hace aterrizar la ULTIMA cifra mostrable exactamente en `duration`, para cualquier magnitud — y una banda de contadores aterriza JUNTA por construccion. ζ fija en 2√2 (el caracter del default de la semilla). - `onEnd` desde la parada REAL del muelle (el callback de settle), no del timer; el tope de 30 s se queda; recorrido menor que el umbral aterriza instantaneo; reduced-motion intacto (su rama es previa y no se toca). - types.ts y README dejan de mentir: «asienta en ≈ duration sea cual sea la magnitud». MEDIDO con Playwright headless (sonda espejo de probe-A-69b del ledger: muestreo DIRECTO de textContent cada 40 ms — su propia ficha documenta el sesgo del MutationObserver), sobre la demo real de stats-band: ANTES 12.500 → 7867 ms · 340 → 5083 ms · 48 → 3567 ms (4310 de dispersion) DESPUES 12.500 → 1977 ms · 340 → 1977 ms · 48 → 1977 ms (dispersion 0) con los valores finales exactos y su agrupacion de locale. La fila del ledger pasa a ARREGLADO sin commitear el fichero (WIP de la otra sesion, protocolo A-85). NO tocados, declarado: A-68 (arranques vs entrada escalonada — item propio) y el driver `spring()` de $motion (sustrato distinto; la nota queda: si un contador necesita fisica interactiva algun dia, el driver existe). Con esto §3.6 (blocks) queda COMPLETO: A-47 y A-69 cerrados. Verificado: eidos 393 ✓ · check 69 = base aislada, diff VACIO · prettier: types.ts limpio en HEAD queda limpio; count-up.svelte y README ya fallaban. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
~~`A-69`~~ **ARREGLADO 2026-08-13 via la disposicion del ledger, tras
re-analisis del autor** (mi primera propuesta sustituia el muelle por un
ease-out — cambio de DISEÑO de tapadillo: la caida exponencial es la
semilla portada; y mi cita de motion.md §drivers estaba fuera de
jurisdiccion — gobierna presets CSS, no numero→formatter). Ejecutado:
λ derivada de (recorrido, umbral, duration) con ζ fija en 2√2 — misma
FORMA, reloj recalibrado; `onEnd` desde la parada REAL del muelle (antes:
timer ciego que disparaba 5,9 s antes); guard de recorrido no-mostrable.
MEDIDO con Playwright (sonda espejo de la del ledger, muestreo directo de
textContent): 12.500/340/48 asientan TODOS en 1977 ms (duration 2000,
±23 ms) con dispersion 0 ms — antes 7867/5083/3567 y 4310 de dispersion.
Fila del ledger → ARREGLADO sin commitear (protocolo A-85). NO tocados:
A-68 (arranques vs entrada — item propio) ni el driver spring de $motion
(sustrato distinto). **§3.6 blocks COMPLETO.**
docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
6. **§3.3 eje B** — 4 continuos sin throttle (gradient-builder, color-picker
área, number-field, css-field) · `sequence: 'coincident'` sin implementar
con 21 eventos declarándolo (**decisión**: implementar o marcar reservado) ·
las 68 demos emitiendo sin `verb` · medir `knob.handle-drop` (`pre` con
handler) y el retraso de 120 ms de `button.contact-activate`.
7. **Firmadas que quedan** — el chip bloqueado vía `DragDrop.Draggable`
(transversal, con su letra pequeña ya medida en
`CONTINUE-perceptual-surface.md` §4) · retirar `mode`/`scope` (+ la hermana
`SemaCause`, que sigue ABIERTA).
8. **§3.2 restitución de persistentes** — eje nuevo del engine; afecta form,
file-upload, textarea, media-player, announce, aura.
9. **C5 rebanadas 3-4 de chronos** — subir a soma el comportamiento del chip
(teclado, captura de puntero, drag-to-create) y sólo después extraer
`MonthView`/`WeekView`/`AgendaView`. Con D14 dentro. El grande.
---
docs(sema): los dos hallazgos abiertos quedan DECIDIDOS y escritos donde viven El autor resolvio los dos hallazgos que salieron al cerrar §3.4/§3.5, y su decision se documenta EN EL CODIGO, no en un handoff que nadie relee: 1. La rama de politica de `validateSemaEvent` es inalcanzable — SE QUEDA. `isSemaEvent` es policy-aware (rechaza required-sin-intent y, desde S-33, forbidden-con-intent), asi que una violacion sale por el throw generico y el mensaje especifico nunca dispara. MEDIDO, no deducido: `validateSemaEvent({family:'commit'})` lanza «is not a valid canonical semantic event». Se conserva porque los dos guards responden preguntas distintas —predicado de tipo vs validador de politica— y solo su ORDEN hace redundante a uno; reordenar para hacerla alcanzable cambiaria el mensaje lanzado sin que nadie lo pida, y borrarla sacaria el enunciado de la politica de la funcion que lo posee. Si el predicado deja de imponer politica, la rama ya esta aqui y correcta. 2. `SemaActionEvent` y sus tres funciones no tienen consumidor en produccion — SE GANAN EL SITIO. No es falta de consumidor: es AUDIENCIA. Es la superficie publica que usa una app conduciendo `EngineSemantic` SIN soma para nombrar una ocurrencia — el mismo publico que sirve el canal announce (dos emisores, dos publicos, §announce de sema.md). Soma nunca toma ese camino porque baja la declaracion estructurada del morfo hasta abajo; la asimetria es el diseño. Y por eso se mantiene ATADA al contrato: S-35 estrecho la clave para que la forma etiqueta no regale lo que la estructurada rechaza, con los tests de politica vigilandolo. Verificado ademas, a peticion del autor: el cableado del canal announce en las raices NO estaba hecho — `define-engine-semantic.ts` no menciona `announce` y `active-uix` solo tiene su propio sumidero. Lo que S-19 cerro fue el desajuste de firmas que hacia IMPOSIBLE escribirlo; encenderlo sigue abierto como decision, y queda anotado en el handoff con esa distincion. Verificado: sema+morfo+contracts 552 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · docs 0/624 · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 4.1 · Hallazgos del 2026-08-13 — RESUELTOS por el autor
Los tres que salieron mientras se cerraban §3.4 y §3.5, decididos el mismo día:
- **La rama `required` de `validation.ts` es código muerto** → **SE QUEDA, y
documentada** en el docblock de `validateSemaEvent`: `isSemaEvent` es
policy-aware, así que una violación sale por el throw genérico y el mensaje
específico nunca dispara (medido). Se conserva porque los dos guards
responden preguntas distintas y sólo su ORDEN hace redundante a uno;
reordenar sería cambiar el mensaje lanzado sin que nadie lo pida.
- **El vocabulario de etiquetas sin consumidor interno** (`SemaActionEvent`,
`normalizeSemaEvent`, `toSemaEventLabel`, `validateSemaEvent`) → **SE GANA
EL SITIO, y documentado** en el docblock de `SemaActionEvent`: no es falta
de consumidor, es AUDIENCIA — la app que conduce `EngineSemantic` sin soma,
la misma que sirve el canal announce (dos emisores, dos públicos). Soma
nunca toma ese camino porque baja la declaración estructurada del morfo.
feat(active-uix,sema)!: S-19(ii) — announce encendido por defecto: un sumidero, materializado por la raiz ¿Debia encenderse? El analisis dijo que era LA RAIZ O NADIE: la opcion `announce` se pasa al construir el engine dentro de `createActiveUix(options)`, y en ese momento `uix.announce` no existe todavia — huevo y gallina. El unico cableado que un app podia escribir (`announce: { dom }`) caia en las regiones propias del canal: un SEGUNDO par de regiones vivas, exactamente lo que la doctrina «ONE sink» (AUX-1) prohibe. La unica puerta alcanzable violaba la doctrina; la raiz es el unico sitio que sostiene las dos puntas. El impl registra el canal POST-construccion con cierre tardio — `events.register(new AnnounceChannel({ announce: (m, p) => this.announce(m, p) }))` — la misma forma para standalone y attach. Default ON, y la asimetria con sound/haptic (apagados) es doctrina, no inconsistencia: esos son ORNAMENTO (opt-in); announce es SUSTITUCION (sema.md §channels — «the one that substitutes»), la categoria que el framework ya enciende solo (las regiones de uix.announce se crean solas, el camino de soma esta siempre activo). La norma del campo hace lo mismo: Angular CDK LiveAnnouncer es singleton por defecto, React Aria usa una region ambiental de modulo. Un framework de referencia no hace opt-in la accesibilidad. Opt-outs estandar: `events: { announce: false }` — el engine gana el flag legible `announceOptedOut` (las opciones de construccion no eran observables) — y un `announce` explicito del app gana: la raiz jamas pisa un canal existente. Cero doble anuncio, por diseño ya verificado: el runtime de soma no mete `message` en la señal perceptual (su a11y viaja por sources.announce), asi que el canal no-opea para todos los componentes de soma. Visto en ROJO via stash del impl (canal sin registrar) y en verde con el: una señal con `message` por `uix.events` aterriza en la region COMPARTIDA (`uix-announce-assertive`, prioridad derivada del intent threat) y el documento tiene UN solo [role='alert'] — el par fantasma nunca nace. El opt-out respetado. `sema.md` §announce reescrito: el ⚠️ de «ninguna raiz lo cablea» pasa a documentar el default y sus salidas. Verificado: sema+morfo+eidos+contracts 947 ✓ / 6 ajenos · soma navegador 1278✓/1 (el timeout ajeno — TODOS los tests de navegador arrancan la raiz tocada) · check 69 = base aislada, diff VACIO · docs 0/624 · prettier: engine.ts limpio en HEAD queda limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- ~~**Encender el canal announce en las raíces (S-19 parte ii)**~~ →
**EJECUTADO 2026-08-13, encendido POR DEFECTO en las dos raíces.** El
análisis destapó que era la raíz o nadie: la opción `announce` se pasa al
construir el engine DENTRO de `createActiveUix`, cuando `uix.announce` aún
no existe — el único cableado escribible por un app caía en las regiones
propias del canal (el segundo par que AUX-1 prohíbe). El impl registra el
canal post-construcción con cierre tardío, igual en standalone y attach.
Default ON porque announce es SUSTITUCIÓN, no ornamento (la norma del campo:
Angular CDK LiveAnnouncer, React Aria — el anunciador a11y es ambiente).
Opt-outs: `events: { announce: false }` (el engine gana el flag legible
`announceOptedOut`) y un `announce` explícito del app gana. Cero doble
anuncio: soma no mete `message` en la señal. Visto en rojo vía stash del
impl; probado que el mensaje aterriza en la región COMPARTIDA (ids de uix,
UN solo `[role='alert']` en el documento). `sema.md` §announce actualizado.
docs(sema): los dos hallazgos abiertos quedan DECIDIDOS y escritos donde viven El autor resolvio los dos hallazgos que salieron al cerrar §3.4/§3.5, y su decision se documenta EN EL CODIGO, no en un handoff que nadie relee: 1. La rama de politica de `validateSemaEvent` es inalcanzable — SE QUEDA. `isSemaEvent` es policy-aware (rechaza required-sin-intent y, desde S-33, forbidden-con-intent), asi que una violacion sale por el throw generico y el mensaje especifico nunca dispara. MEDIDO, no deducido: `validateSemaEvent({family:'commit'})` lanza «is not a valid canonical semantic event». Se conserva porque los dos guards responden preguntas distintas —predicado de tipo vs validador de politica— y solo su ORDEN hace redundante a uno; reordenar para hacerla alcanzable cambiaria el mensaje lanzado sin que nadie lo pida, y borrarla sacaria el enunciado de la politica de la funcion que lo posee. Si el predicado deja de imponer politica, la rama ya esta aqui y correcta. 2. `SemaActionEvent` y sus tres funciones no tienen consumidor en produccion — SE GANAN EL SITIO. No es falta de consumidor: es AUDIENCIA. Es la superficie publica que usa una app conduciendo `EngineSemantic` SIN soma para nombrar una ocurrencia — el mismo publico que sirve el canal announce (dos emisores, dos publicos, §announce de sema.md). Soma nunca toma ese camino porque baja la declaracion estructurada del morfo hasta abajo; la asimetria es el diseño. Y por eso se mantiene ATADA al contrato: S-35 estrecho la clave para que la forma etiqueta no regale lo que la estructurada rechaza, con los tests de politica vigilandolo. Verificado ademas, a peticion del autor: el cableado del canal announce en las raices NO estaba hecho — `define-engine-semantic.ts` no menciona `announce` y `active-uix` solo tiene su propio sumidero. Lo que S-19 cerro fue el desajuste de firmas que hacia IMPOSIBLE escribirlo; encenderlo sigue abierto como decision, y queda anotado en el handoff con esa distincion. Verificado: sema+morfo+contracts 552 ✓ / 6 ajenos · check 69 = base aislada, diff VACIO · docs 0/624 · prettier limpio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
docs(process): handoff nuevo para la cola de auditorias, con el protocolo de trabajo delante El eje perceptual quedo completo hoy (F1-F6), asi que lo que sigue ya no es «su» cola: es la cola de auditorias — sema, fable, blocks, eje B, chronos C5. Este handoff la lleva, y pone PRIMERO lo que cambio hoy de verdad: el protocolo. §0 · las cinco preguntas del autor antes de CADA accion, un item por mensaje, y parar a esperar firma. Sin puerta que permita auto-autorizarse lo mecanico: lo propuse y se corrigio en el acto. Con las dos precondiciones que hoy costaron dos rectificaciones completas — el SPEC del COMPONENTE es doctrina y se busca ANTES de diagnosticar (chronos), y se barre `docs/` antes de proponer retirar nada (S-33 esta en CANON.md y en la D.3 firmada). La regla que las resume: el codigo nunca corrige al canon; se le pone al autor la contradiccion delante. §1 · S-33 FIRMADO y sin ejecutar, con la receta y la evidencia del probe de `tsc`: el defecto es el `Exclude`, no «falta una rama». Derivar los tres cubos positivamente cierra la puerta sola al flipar el const, y la tercera rama es inerte hoy porque se teclea sobre `never` — medido, delta de conducta CERO. §2 los 6 commits de hoy · §3 las tres decisiones abiertas con su evidencia ya medida (S-35 · S-30/S-38 · S-19, esta ultima partida en dos porque (ii) cambia conducta) · §4 la cola en orden · §5 la base de verificacion, con los 6 fallos ajenos de contracts nombrados uno a uno · §6 las trampas medidas hoy, para no re-descubrirlas: la laxitud DELIBERADA del guard D9 (endurecerlo convierte 27 emisiones reales en violaciones), el panel oculto congelando la linea de tiempo, y los ficheros ya sucios en HEAD para prettier. docs:check 0/624. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 5 · Base de verificación (mídete contra esto, no contra memoria)
```
npx vitest run src/uix/sema src/uix/morfo src/uix/contracts.test.ts --project=server
npx vitest run src/uix/soma # proyecto navegador
npm run check # 69 errores = base
npm run docs:check # 0 / 623
```
- `contracts.test.ts` tiene **6 fallos AJENOS y preexistentes** (barrel de
waveform · nombres de fichero de provider · MOR-4 · escrituras DOM de menubar ·
data-attrs · namespaces de aura). Hoy llegó a haber 7: el séptimo era **mío y
de otro commit**, y se cerró (`14a169357`).
- soma navegador: **1278 ✓ / 1**, el timeout ajeno preexistente de
`soma-attr-audit`.
- sema+morfo: **532 ✓**.
---
## 6 · Trampas medidas hoy — no re-descubrirlas
- **El guard D9 es laxo A PROPÓSITO.** Compara `includes("'nombre'")` y no
`trigger('nombre')` porque el nombre llega a la llamada por VARIABLE en media
docena de patrones. Censado: endurecerlo convierte **27 emisiones reales** en
violaciones. Su precio —un nombre entrecomillado en un comentario lo
satisface— queda escrito en el propio colector.
- **El panel del navegador oculto**: `rAF` suspendido (los ticks continuos no
estampan nunca) y la línea de tiempo de animaciones congelada, así que el
desestampado cae SIEMPRE en el tope de 1500 ms de la espera de expresión.
`getAnimations()` dice QUÉ corre sobre QUÉ, nunca cuánto dura.
- **Prettier**: hay ficheros sucios en HEAD (`errors.ts`, `test-fixtures.ts`,
`runtime.svelte.ts`, `runtime.svelte.test.ts`, `contracts.test.ts`). Probar
contra HEAD antes de `--write`, y no reformatear lo que ya estaba sucio.
- **`git stash push <ruta>` + `stash pop`** es la forma limpia de comprobar si un
fallo es tuyo o de HEAD. Hoy demostró que el D9 fallaba sin mis cambios.

Powered by TurnKey Linux.