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>
alpha-0.1-dir-prefs
dev 2 months ago
parent f8dd8ff810
commit 3ee17f333c

@ -331,6 +331,29 @@ por los dos caminos.
---
## 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.
- **Encender el canal announce en las raíces (S-19 parte ii)** → **NO estaba
resuelto, verificado 2026-08-13**: `define-engine-semantic.ts` no menciona
`announce` y `active-uix` sólo tiene su propio sumidero. Lo que S-19 cerró
fue el desajuste de firmas, que hacía IMPOSIBLE escribir el cableado; hoy es
una línea. Sigue ABIERTO como decisión — encenderlo sin inyectar el
anunciador añade un segundo par de regiones vivas.
## 5 · Base de verificación (mídete contra esto, no contra memoria)
```

@ -338,6 +338,26 @@ export type SemaEvent = (
) &
SemaEventExtensions;
/**
* An event in either of its two legal forms: the structured `SemaEvent` or its
* serialised `SemaEventKey`. The pair is what `normalizeSemaEvent` /
* `toSemaEventLabel` / `validateSemaEvent` operate on.
*
* ⚠️ **No consumer inside this repo, and it stays** (author's call,
* 2026-08-13). Measured while closing S-35: neither this type nor those three
* functions are called anywhere in production — their only call sites are
* tests. That is not evidence of death, it is evidence of AUDIENCE: this is
* the public surface an app driving `EngineSemantic` WITHOUT soma uses to name
* an occurrence, the same audience the announce channel serves (see
* `architecture/sema.md` §announce — two emitters, two publics). Soma's
* components never take this path because they carry the structured morfo
* declaration all the way down; that asymmetry is the design, not a gap.
*
* So it is kept AND held to the contract: S-35 narrowed the key so the label
* form can no longer hand out what the structured one refuses. A public
* surface with no internal consumer is exactly the kind that rots unwatched —
* this one is guarded by the policy tests in `intent-policy.test.ts`.
*/
export type SemaActionEvent = SemaEvent | SemaEventKey;
// ── DOM-oriented writes used by Morfo events ──────────────────────────────

@ -25,6 +25,26 @@ export function validateIntentBinding(binding: IntentBinding, ctx = 'sema.intent
}
}
/**
* Structural + policy validation of a `SemaActionEvent`.
*
* ⚠️ The policy branch below is UNREACHABLE, deliberately kept (author's call,
* 2026-08-13). `isSemaEvent` is itself policy-aware — it returns `false` for a
* `required` family with no intent, and (since S-33) for a `'forbidden'` one
* that carries it — so a policy violation exits through the generic throw
* above and the specific message never fires. MEASURED, not deduced:
* `validateSemaEvent({ family: 'commit' })` throws «is not a valid canonical
* semantic event», not «requires intent».
*
* It stays because the two guards answer different questions and only their
* ORDER makes one redundant: `isSemaEvent` is a type predicate (is this a
* SemaEvent at all?), this is the validator (does it honour the policy?).
* Deleting the branch would move the policy statement out of the function that
* owns it, and reordering to make it reachable — checking policy before the
* shape — is a behaviour change (the thrown message) that nobody asked for.
* If the predicate ever stops enforcing policy, this branch is already here
* and correct.
*/
export function validateSemaEvent(event: SemaActionEvent, ctx = 'sema.event'): void {
if (isSemaEventLabel(event)) return;
@ -35,7 +55,7 @@ export function validateSemaEvent(event: SemaActionEvent, ctx = 'sema.event'): v
const intent = 'intent' in event ? event.intent : undefined;
// Policy enforcement: families marked `intentRequirement: 'required'` MUST
// declare intent.
// declare intent. Unreachable today — see the docblock.
if (intent === undefined && SEMA_FAMILY_POLICY[event.family].intentRequirement === 'required') {
throw new SemaInvariantError(
`${ctx}: family "${event.family}" requires intent (intentRequirement: 'required')`

Loading…
Cancel
Save

Powered by TurnKey Linux.