feat(episode): TRAZA_lo-que-qued-arreglado_S20260726.R4_XX.ops.2.hot_inf.in.cc.es.000.MGQ_J.PMCFK_E.SGNFD

Skill: NONE | Type: troubleshooting
Summary: EPISODIO 4 — MNEMO_PRE_DIGEST: Lo que quedó arreglado
This commit is contained in:
Ember 2026-07-27 03:10:08 +00:00
parent 020a468c88
commit 4de4868308

View file

@ -0,0 +1,41 @@
---
episode_id: "3e37dd13-605e-4bd3-b20b-9d83e9c26152"
puente_flat: "TRAZA_lo-que-qued-arreglado_S20260726.R4_XX.ops.2.hot_inf.in.cc.es.000.MGQ_J.PMCFK_E.SGNFD"
session_code: "S20260726.ARCO_CICLO4_AUTOMEJORA"
acto_dialogico: "informar"
actor_flujo: "multi_actor"
criticidad_aegis: "low"
skill_dominante: "NONE"
fase_proyecto: "operations"
tipo_semantico: "troubleshooting"
summary_one_line: "EPISODIO 4 — MNEMO_PRE_DIGEST: Lo que quedó arreglado"
source_type: "claude_code"
trust_boundary: "internal"
created_at: "2026-07-27T03:09:47.430880+00:00"
relectura_tagged: false
forgejo_commit_sha: "pending"
---
Cerrado y verde, con el control positivo que lo hace creíble.
## Lo que quedó arreglado
`check_override_flag()` ahora valida **de quién es** el permiso antes de concederlo. La comprobación va deliberadamente **antes** del reuso-por-ráfaga y antes de gastar el slot, así que un flag ajeno ni se reusa ni se consume — y tampoco se borra, porque pertenece a otra instancia y borrarlo sería sabotearle un trabajo que tú autorizaste.
La política, que es lo que decide el comportamiento real:
- Flag **sin** `created_by` → se acepta. Retrocompatibilidad: los flags legacy siguen funcionando, nadie se queda a medias.
- Flag **del dueño** → override normal. Compara tanto el session_code completo (`S20260726.MI_TAG`) como el tag suelto, sin distinguir mayúsculas.
- Flag **de otra instancia** → rechazado con `not owner`, sin tocar el archivo.
- Identidad no resoluble o vacía → **se acepta** (fail-open). Un fix de seguridad no debe volver el guardián más frágil de lo que era: si `session_state` se cae, el guard se comporta como antes en vez de bloquearte a ciegas.
## La evidencia
- **[test_ley_zero_override_owner.py](.claude/hooks/test_ley_zero_override_owner.py)** — 13 tests, **30 PASS / 0 FAIL**. Aísla `DATA_DIR` en un tmpdir, así que nunca toca el flag real de nadie.
- **Suite existente del guard: 36 PASS / 0 FAIL** — cero regresión.
- **Control positivo:** revertí `_flag_owner_matches` en memoria a la versión pre-fix y volví a correr el test del flag ajeno → **3 FAIL** (`override ACTIVE (1/5 edits used)`, `edits_done=1`). El test caza la regresión; no es decorativo. Esto era obligatorio: un test que no puede fallar no es evidencia de nada.
- **E2E del hook:** `docs/CUALQUIERA.md` → `allow` · `CLAUDE.md` → `deny` · `rm -rf .claude/` → `deny`. Sigue haciendo su trabajo.
## Corrijo una cosa que te dije
Te dije que otra instancia se comió mi slot, marcándolo como inferencia. Leyendo la función completa encontré una causa más probable para **mi** bloqueo concreto: existe un debounce de ráfaga de 8 segundos (añadido en S20260704, porque el harness invoca el hook varias veces por una sola tool call). Mi `ALLOW` y mi `BLOCK` estaban a **14 segundos** — fuera de esa ventana. Con 113 registros de hooks, la ráfaga se estira más de 8s y la segunda invocación encuentra el slot ya gastado. Es decir: probablemente me lo comí yo mismo, no un tercero.