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» |
**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
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é |
| --- | --- |
| `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 |
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
`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
**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
**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).
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
4. ** §3.5 fable** — `M1 emission` (aceptada y nunca implementada; cerraría los
waivers de tooltip y gradient-picker) · `M4` (`enumPair` frágil con 3+
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
valores) · ~~`M5`~~ **EJECUTADO 2026-08-13, y la ficha era INCOMPLETA** : acertaba en que
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 ·
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
`E2` (`globalThis.matchMedia` crudo en eidos) · `E3` (catch mudo en
`#renderSchemeCss` ).
5. ** §3.6 blocks** — `A-47` (anillo de foco 1,43:1 sobre panel sólido) ·
`A-69` (`duration` de count-up no significa segundos).
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.
---
## 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.