# Changelog

Formato basado en [Keep a Changelog](https://keepachangelog.com/es/1.1.0/) y versionado según
[SemVer](https://semver.org/lang/es/).

`cauce upgrade` reemplaza `system/` completo sin pedir confirmación. Este archivo es lo que hace que
esa operación sea confiable en vez de sólo cómoda: acá se lee qué cambió antes de aplicarlo. Por eso
un cambio en el protocolo, en las reglas del sistema o en un guard es visible para el usuario y sube
minor aunque no toque una sola línea de código.

Cada entrada la imprime `upgrade` a quien está por aplicarla, y un salto de varias versiones las
imprime todas seguidas. Dice qué cambia en lo que recibe y qué tiene que hacer; lo que sólo se observa
desde este repositorio no va, porque el que lee no puede actuar sobre eso. Cuando una entrada pasa de
unas pocas líneas casi siempre es porque cuenta cómo se descubrió el problema o por qué se eligió el
diseño — eso vive en el commit y en el código.

## [0.119.0] - 2026-11-03

### Cambiado

- **`ops evaluate` cuenta cuántos veredictos siguen valiendo** (caso 480). Un veredicto vence si su contrato o su caso, con
  su fixture, cambiaron después; el resumen dice «7/7 pasan (…), 0 vigente(s): 7 mide(n) una versión anterior» en vez
  de un aviso que salía en casi todos los cargos.
- **Un commit propio que no se publicó se puede corregir con amend** (caso 481). `AGENTS.md`, `PROTOCOL.md` y las fases
  de `autobuild` decían «nunca amend», y R8 sólo lo frena sobre lo publicado: una sesión rodeaba la prohibición
  reescribiendo el commit a mano.

## [0.118.0] - 2026-11-02

### Agregado

- **Un `/flow` que frena en un gate se retoma con la respuesta, sin volver a pagar lo que ya cerró** (caso 458).
  - **Al frenar**, el recorrido deja `planning/reports/<fecha>-<recorrido>-retoma.json` con lo que cada etapa ya
    estableció, y dice cómo retomar.
  - **Cómo se retoma**: `/flow {"flow": "<recorrido>", "resume": "<esa ruta>", "answers": "<lo que contestó la
    persona>"}`.
  - **Qué corre**: las etapas que cerraron salen del archivo. La que frenó vuelve a correr con las respuestas y con
    su análisis anterior.
  - **Si una respuesta invalida una etapa cerrada**, sumá `"rerun": ["<etapa>"]`: se relanzan esa etapa y las que
    dependen de ella.
  - **Retomas que no corresponden**: una de otro recorrido, otra intención u otras etapas se rechaza diciendo qué
    difiere. Una que alguien editó a mano se rechaza también.
  - **Antes**, la única salida era relanzar el recorrido entero, y las respuestas pasadas aparte se ignoraban sin
    aviso. Ahora `answers` sin `resume` frena y lo dice.
  - **Lo que contestó la persona cierra su acción humana**: si con la respuesta el gate se cumple, la fila que ese
    gate había abierto en `planning/human/` pasa a `resuelta`.
  - **Comando nuevo**: `ops flow resume save|show <archivo> [ops-root]` sella y lee el archivo, y es lo que corre el
    recorrido. No hace falta usarlo a mano.

### Corregido

- **`/flow {...}` con argumentos estructurados ya no se lee como intención cuando llegan como texto** (caso 458).
  Una sesión puede pasar el JSON como cadena, y `flow` lo tomaba entero por la intención y corría el recorrido por
  defecto desde cero. Ahora un texto que es un objeto JSON se lee como argumentos; cualquier otro sigue siendo la
  intención.

- **Un `git commit` con la ruta entre comillas ya no se saltea los guards** (caso 464). `git -C "$dir" commit`, `cd "$dir"
  && git commit` y `git -C "a b" commit` caían o no se reconocían como commit, y el commit salía sin `verify`,
  `governance` ni `dependencies`. Ahora esos commits se juzgan: **algunos que pasaban van a empezar a frenarse.** Si la
  ruta está en una variable que no se asigna en el mismo comando, frena y pide escribirla literal. Una ruta con espacios
  se lee entera sólo con `git` como primera palabra de un comando simple. `git -c user.name="A B" push -f` ya no esconde el verbo. También se reconocen `--no-advice`, `--no-lazy-fetch` y
  `--attr-source`, que escondían el verbo.
- **Un guard que falla frena en vez de dejar pasar** (caso 467). Salía con 1, que Claude Code no toma como freno. Ahora
  sale con 2 y nombra el guard. Si un guard tiene un defecto, frena la sesión hasta arreglarlo. `stop` y los del mensaje
  de la persona siguen sin frenar.
- **Un guard del proyecto puede avisar sin frenar** (caso 466). Saliendo en 0, lo que escribe por stderr le llega a la
  sesión, recortado a 2000 caracteres. **Si un guard tuyo usa stderr para depurar, ahora eso lo lee la sesión.** En
  Antigravity llega antes del turno siguiente, con un hook nuevo: reinstalá el runner. En Gemini le llega a la persona y no
  al agente, porque no tiene otro canal.
- **Un `git worktree` de la instancia responde como la instancia** (caso 465). `plan-first` ya no le pide plan a lo que la
  instancia posee en ese árbol, y `ops-config` sí frena la llave de push en su `ops.config.json`.

- **`ops claim` ya no le ordena empujar a una sesión delegada** (caso 471). Con `CAUCE_DELEGATED=1` calla ese consejo,
  y lo publica quien la lanzó.
- **`ops lines` dice qué hacer cuando no reconoce la rama viva** (caso 470). Un repositorio que vive en otra rama que
  `main` o `master`, sin `HEAD` en el remoto, se quedaba sin conteo y sin explicación: ahora nombra
  `git remote set-head <remoto> --auto`.

- **Lo que un commit ejecuta ya no se esconde en su mensaje** (caso 469). `git commit -m "$(git push -f)"` y `git commit
  -m x; sh -c '…'` pasaban sin que ningún guard los viera. Un mensaje que sólo cita comandos sigue pasando, también por
  `-m "$(cat <<'EOF' …)"`.
- **Los guards ven lo que un heredoc o un `-c` ejecutan, y dejan de frenar lo que cae en otro repositorio** (caso 473).
  Ahora frenan una `$(orden)` en el cuerpo de un heredoc sin comillas, lo que venga después de cerrarlo, un cuerpo que
  recibe un shell, y lo que corre un alias o un `core.sshCommand` pasados con `-c`. Dejan de frenar `(cd F && git commit)`,
  `cd F || cd R`, `pushd F` y `cd F && git -C "$PWD/a b" add -A` cuando el commit cae fuera de la instancia. Un `cd` dentro
  de un bloque deja dudas, y con dudas se juzga la sesión.
- **Un commit dentro de un bloque o una función se juzga donde cae** (caso 474). `if cd repo; then git commit; fi`,
  `pushd repo; if …; then git commit; fi` y una función que commitea quedaban sin juzgar, igual que el commit tras un `cd`
  que no entra. **Algunos que pasaban van a frenarse**: `for r in a b; do git -C $r commit; done` y una función con el
  commit y un `git add` fuera piden separar los pasos o escribir la ruta. Además, `secrets-shell` ya no pierde lo que va
  junto a un commit: `git commit -m x; cat ".env"` frena, y el mensaje sigue siendo dato.
- **Lo que una sustitución borra o crea fuera de las raíces se frena** (caso 475). `echo $(rm -rf /etc/foo)` pasaba. Lo
  citado con comilla simple y lo que sólo lee siguen pasando.
- **Una línea continuada con `\` se lee como una sola** (caso 478). `git \<salto> push -f`, `git add \<salto> -A` o un
  `--amend` en otra línea pasaban sin que frenara nada. 
- **Un prefijo ya no esconde lo que corre detrás** (caso 479). `then cat .env`, `timeout 5 cat .env`,
  `cross-env CI=1 npx jest` y `timeout 60 git commit` en rama viva frenan. Dejan de frenar en falso `./jest.sh`,
  `jest-junit` y `time grep "git add -A" src`.
  También frenan `time -p git commit`, `bash -c "git commit …"`, `npx -y jest`, `pnpm dlx jest` y la firma que llega a
  `-F -` por un `printf`, un `echo` o un `cat`. `echo "…; git commit"` deja de frenar.
  También frena `git commit;` sin argumentos. Un `git archive`, `pull` o `reflog` antes de un commit en otro repositorio ya no
  arrastra el juicio a la instancia.
- **Más formas de un comando se leen como corren** (caso 477). Frenan `bash -c "git push -f …; git commit …"`,
  `git commit -m "$(cat .env)"`, `then (rm …)`, un `$(rm …)` en el cuerpo de un heredoc sin comillas, y `--amend` o `-a`
  después de un mensaje con heredoc. La firma se mira en `-F-`, `-aF -` y `-m"…"`. Lo citado en un programa que nadie
  anotó como de sólo lectura, como `node -e`, que además nombra un commit se lee como orden.
- **La firma de IA se mira también en `git commit -F -` con un heredoc** (caso 476), y desde el 479, también la que llega por una tubería.
- **Los guards de commit juzgan el repositorio donde el commit corre** (caso 468). Se seguía el primer `cd` o `-C` del
  comando, y `cd /etc; cd repo && git commit`, `echo git -C /etc && git commit` o `git commit && cd /etc` quedaban sin
  juzgar. **Algunos commits que pasaban van a empezar a frenarse.** Si no se puede saber cuál `cd` corrió, se juzga el
  candidato que sea de la sesión.

### Cambiado

- **El gate de `plan` en `product-development` ya no frena por una validación que la corrida no puede tener**
  (caso 460). Pedía que los dueños hubieran validado el plan que esa misma etapa acababa de escribir, así que frenaba
  siempre, y relanzar con las respuestas producía otro plan sin validar. Ahora exige estimaciones, dependencias y
  criterios de integración explícitos, y la capacidad declarada por escrito por quien la tiene, con horas y duración:
  sin eso sigue frenando, por chico que sea el plan. La
  validación de los dueños que falte queda como condición, con quién la da, en `planning/human/`. Promover la épica
  sigue siendo la firma humana.

## [0.117.2] - 2026-10-28

### Corregido

- **La regla del plugin de Antigravity ahora se aplica** (caso 459). Se instalaba sin `trigger` en el frontmatter,
  `agy` no la cargaba, y la sesión trabajaba sin las reglas del proyecto ni la instrucción de leer `AGENTS.md` y el
  protocolo. Ahora lleva `trigger: always_on`, y `automation doctor` da error si la regla lo pierde. `upgrade` no reinstala
  los adaptadores: corré `automation install <ops> antigravity`, y después `agy plugin validate .agents/plugins/cauce`
  tiene que pasar. Si habías editado el archivo a mano y no quedó igual al
  de Cauce, `install` frena porque es del toolkit: repetí con `--force`.

## [0.117.1] - 2026-10-26

### Corregido

- **La mutación que cita una condición por su marcador ya no cuenta como otra condición** (caso 457). Una aceptación
  con `(1)` a `(5)` y una mutación que dice «pone en rojo la (5)» se contaba como de siete condiciones, y `check`
  pedía partir la tarea o escribirle `(sin partir: <razón>)` aunque estaba dentro del umbral de R17. Ahora cuenta
  cada marcador una sola vez. Si esquivaste el aviso escribiendo «la condición 5» o una razón de `sin partir`, ya no
  hace falta.

## [0.117.0] - 2026-10-26

### Corregido

- **Desde una línea de trabajo, un `git` que cambia el checkout principal de un repositorio de la línea se pregunta
  como uno en la instancia** (caso 453). Poner al día ese checkout después de mergear la entrega se frenaba con «Esto
  lo corregís vos», sin abrir el diálogo, y ofrecía pegar la carpeta entera. Ahora, en una sesión interactiva de
  Claude Code, abre el diálogo de confirmación; en el chat, la orden tiene que nombrar el `git` —«merge», «pull»,
  «stash»—, y la línea para pegar es `git merge @ <checkout>`. Nombrar sólo la carpeta ya no alcanza: con eso, un
  pedido sobre otra cosa —«arreglá el login de la app»— dejaba al agente cambiarle la rama a la sesión principal. La
  línea que ya tenías con la carpeta sigue valiendo. Un archivo escrito ahí, o un `git` que no le cambia la rama ni el
  árbol, siguen como antes.
- **Desde una línea, un `git` con `--git-dir` o `GIT_DIR` de otro checkout se frena** (caso 454). Corrido desde el
  árbol de la línea contra el repositorio del checkout principal, un `stash` terminaba en el de la sesión principal y se
  llevaba lo de la línea, sin que nada lo frenara. Ahora cuenta también el checkout dueño del repositorio o del índice
  —`--git-dir`, `--work-tree`, `-c core.worktree`, `GIT_DIR`, `GIT_WORK_TREE`, `GIT_INDEX_FILE`, `GIT_COMMON_DIR`—,
  también si se exporta antes, y se pregunta como cualquier `git` sobre él. Lo que no se puede ubicar —una variable sin
  resolver, `$(…)`, la configuración por `GIT_CONFIG_*` o `--config-env`— frena hasta que se escriba con la ruta entera.
- **Un prefijo con valor ya no esconde el comando** (caso 454). Con `sudo -u yo`, `env -u X`, `nice -n 5`,
  `timeout 10`, `xargs -n 1`, `exec -a x` o `time -f …` delante, el valor se tomaba por el comando y lo de atrás no se
  juzgaba: ni el `git` de una línea ni un `rm` fuera de las raíces. `env -C` y `sudo -D` cuentan como la carpeta donde
  corre.
- **Un comando escondido detrás de otro se juzga como escrito suelto** (caso 455). Lo que corre adentro de `sh -c`,
  `bash -lc`, `eval`, `env -S`, `su -c` o `flock -c`, y lo que va detrás de `stdbuf`, `ionice`, `taskset`, `chrt`,
  `flock`, `setsid`, `doas`, `unbuffer`, `runuser -u` o `sudo -T`, escapaba a los límites: el `git` de una línea sobre
  el checkout principal, un `rm` o un `touch` fuera de las raíces, y una escritura con `>`, `tee` o `cp` —también la
  de una credencial o de `ops.config.json`—. Lo que corre en otra máquina, como `ssh` o `docker exec`, sigue sin
  juzgarse. Y `env -C` o `sudo -D` ya no cambian la carpeta de los comandos que siguen, sólo la del suyo.
- **Lo que un shell lee de su entrada también se juzga** (caso 456). `echo '…' | sh`, `printf '…' | bash`,
  `bash <<EOF`, `cat <<EOF | sh`, `sh <<< '…'`, `sudo -s <<EOF` y `source /dev/stdin` corrían su comando sin que los
  límites de escritura y borrado, ni el de una línea de trabajo, lo vieran. Un documento escrito con `cat > archivo <<EOF`, un commit con `-F-` o un `ssh host bash <<EOF`
  siguen siendo dato. Y `bash <<< X` ya no hace que un renglón posterior `X` esconda lo que hay en medio.

## [0.116.0] - 2026-10-25

### Cambiado

- **Una ruta de `.ops-approval` se aprueba entera, y vale para ese archivo de ese repositorio** (caso 445). Relativa,
  `package.json` aprobaba el manifiesto de todos los repositorios de un sidecar y de todos los árboles de sus líneas.
  Vale para los gates de commit —`dependencies`, `verify`, `governance`, `comments`— y para los guards que reciben la
  ruta de una herramienta, como `migrations` o `test-evidence` con un parche. El bloqueo dice la línea exacta, y el de
  `dependencies` nombra el repositorio en vez de `.`. `ops governed` contesta en la misma forma.
- **Un comando aprobado vale en el repositorio o la carpeta donde se aprobó** (caso 446). La línea lleva el lugar al
  final —`git clean -fd @ /ruta/al/repo`, `push origin feat/x @ /ruta/al/repo`, `terraform apply @ /ruta/a/infra/prod`—
  y lo concedido en el chat también: un «dale» en un repositorio ya no deja pasar el mismo comando en otro. `gh` sin
  `--repo` deja de aprobarse por archivo, porque elige el repositorio solo; nombrándolo, la línea vale para ese
  repositorio. `kubectl`, `helm`, `docker … prune` y los comandos sin cota de workers siguen sin lugar. Y un
  `git -C <repo> push` se juzga en ese repositorio —su rama viva incluida—, no en la carpeta desde donde corre.
- **`planning/.ops-approval` es local.** Una aprobación es de quien la dio, en su máquina; versionada, autorizaba en el
  clon de otro. El molde la ignora, y `check` avisa si tu instancia la versiona.

### Corregido

- **Pedir en el chat que se apruebe un archivo por su nombre ya no lo aprueba en todos los repositorios** (caso 447).
  Con «agregá package.json a .ops-approval», si otro checkout también tiene ese archivo, el agente no puede escribirse
  la línea: el bloqueo lista las rutas y pregunta cuál. Un «sí» las aprueba, y nombrar el repositorio o la ruta entera
  aprueba sólo esa. Lo mismo para un comando con su lugar, como `git clean -fd`.
- **Un `/autobuild` en una línea ya no intenta cambiar la rama de la instancia para commitear planning** (caso 450).
  Si la instancia está en `main` o `master`, la corrida cierra el hito y para con `planning-uncommitted`: el estado de
  planning lo commitea la sesión principal. Antes intentaba cortar `work/planning`, el guard de la línea lo frenaba y
  la única salida era aprobar la instancia entera.
- **`git clean -d -f` y `git clean -x -f` se frenan, y una simulación con `-n` ya no** (caso 452). El guard miraba
  sólo la primera opción: esas dos formas borraban sin preguntar, y `git clean -fdn`, que no borra nada, se frenaba.
- **Un «dale» cubre el reintento aunque el agente le cambie un `cd` o un `echo` al comando** (caso 451). Lo que se
  aprueba es el tramo que el guard reconoce, con su lugar: la línea para pegar pasa a ser `git clean -fd @ <repo>` y
  no el comando entero. Las líneas escritas antes con el comando entero siguen valiendo para ese comando.
- **Desde una línea de trabajo, pedir en el chat un `git` sobre la instancia lo destraba** (caso 449). «Haz el merge
  de main en work/…» pasaba sólo nombrando la carpeta de la instancia; ahora alcanza con nombrar el `git` —«merge»,
  «mergeá», «stash»—, y el guard frena si el mensaje pregunta, niega o frena. La línea para pegar es
  `git merge @ <instancia>`, y no la instancia entera; la que ya tenías con la carpeta sigue valiendo. En una sesión
  interactiva de Claude Code, ese `git` ya no frena: abre el diálogo de confirmación.
- **Con Gemini, un comando se juzga en la carpeta donde corre** (caso 446). Gemini manda esa carpeta aparte, en
  `dir_path`, y los guards la ignoraban: una aprobación con lugar valía en todos los repositorios, y un commit en un
  repositorio frenaba con «git no toma … como repositorio».
- **Retomar un `/autobuild` ya no repite las decisiones de Review en `planning/human/`** (caso 448). La corrida que
  retoma vuelve a revisar, y lo que ya estaba pendiente se registraba otra vez con `-2`. En una línea de trabajo, la
  fila nombra el árbol de la línea; y la aprobación que pide un guard al commitear ya no se registra como decisión:
  la pide la parada del commit, con la ruta exacta.

**Al actualizar**: `check` nombra cada línea de `.ops-approval` en la forma vieja —una ruta relativa, o un comando sin
su lugar—, que ya no aprueba nada; si todavía hace falta, el próximo bloqueo da la línea nueva. Si `check` dice que git
versiona `planning/.ops-approval`, sacala del índice con el comando que da el aviso, sumá `planning/.ops-approval` a tu
`.gitignore` y commiteá las dos cosas: `upgrade` no toca ese archivo.

## [0.115.1] - 2026-10-24

### Cambiado

- **`AGENTS.md` dice cómo declarar la rama viva cuando no es `main`.** Es la rama por defecto del remoto, y se cambia
  con `git remote set-head origin <rama>` en cada repositorio; decía cuál era, pero no cómo nombrar otra ni qué
  depende de ella: de dónde nace una línea nueva, qué commits y pushes frenan los guards, contra qué se cuenta una
  línea y cuándo su retiro la da por llegada. **Al actualizar**, si tu rama viva es `develop` u otra, corré
  `git remote set-head origin <rama>` en cada repositorio: sin eso tus líneas nacen de `main` y un commit directo
  sobre esa rama no se frena.

## [0.115.0] - 2026-10-23

### Cambiado

- **`AGENTS.md` y `planning/delivery/teamwork.md` abren con un índice.** Los dos pasaban de 300 líneas sin forma de
  navegarlos, y una instancia no podía agregarles el índice sin separarse del molde. Una prueba falla ahora cuando un
  documento del molde cruza ese tamaño sin índice. Al actualizar no hay nada que hacer (caso 442).

- **El relato de los incidentes que originaron R14, R17, R20, R21, R24, R25, R26 y R28 pasó al README de reglas.** Las
  reglas conservan su instrucción y su argumento; las frases que contaban la sesión que las originó —los siete
  millones de tokens, el pico de 24,2 GB, los cuatro cargos— viven ahora en «De dónde salió cada regla», que no se
  carga. Con esto, con R29 y con lo que R9 volvió a cargar de ella, el bloque de reglas queda en 45,9 KB por agente,
  desde 52,3. Medido con sesiones reales antes y después sobre un pedido que tienta a suponer el comportamiento de una
  herramienta, y sobre uno que tienta a escribir una decisión como tarea: los agentes comprobaron el dato, y dejaron la
  decisión como acción humana, igual con el relato que sin él (casos 441 y 443).

- **La doctrina de cómo se prueba sale de R9 y pasa a R29, en `system/proof.md`, nombrada y no cargada.** R9 se queda
  en `commits.md` con lo que rige al entregar —verde no reemplaza al artefacto, el criterio que ninguna prueba aserta
  no está cubierto, una prueba que nunca se vio en rojo no prueba nada— y con las cuatro cosas que no admiten
  excepción: la mutación se declara por escrito, una quita no se entrega sin su aserción de ausencia vista en rojo, una
  quita casi nunca se ve como una —tampoco cuando lo que cambia es quién hace algo, que se prueba con el antes y el
  después—, y una confirmación que estorba casi siempre está cuidando algo. Cómo se cumplen, la precondición, la
  deprecación y los dependientes de una quita pasan a R29, con `aplica: al escribir o revisar una aceptación, una
  prueba, o un cambio que quita o reemplaza algo`. Medido con sesiones reales antes y después: con un pedido de quitar
  un comportamiento, los tres agentes abrieron el archivo y probaron la quita por ausencia, viéndola en rojo, igual que
  con R9 cargada. Y con un pedido de agregar un `--yes` a un script que confirma antes de borrar, medido también en
  Codex, Gemini y Antigravity: Gemini dejó de proteger el destino del borrado cuando la confirmación que estorba quedó
  sólo en R29 (de 3/3 a 0/3), y volvió a 2/3 con esa línea de nuevo en R9. **Al actualizar**: si sobrescribiste
  `commits.md`, tu copia todavía trae la doctrina entera, y R29 la repite; sacala de ahí o sobrescribí también
  `proof.md`. Las lecciones que corrigen sobre esa doctrina pasan a citar `proof.md#R29`, así que el conteo de
  correcciones repetidas no junta las nuevas con las que citaban `commits.md#R9` (caso 440).

- **La nota de por qué las reglas de `system/runs.md` viven ahí pasó al README de reglas.** Explicaba el override por
  nombre de archivo (caso 160), le sirve a quien mantiene las reglas y no a quien las cumple, y cada agente la
  cargaba: el bloque de reglas baja de 52,3 a 51,8 KB por agente. Ninguna regla cambió de texto. Al actualizar no hay
  nada que hacer (caso 439).

### Corregido

- **`flow` ya no pisa el análisis de etapa de la corrida anterior.** Cada etapa lo escribía en
  `planning/reports/<etapa>-analisis.md`, y la corrida siguiente —u otro recorrido con una etapa del mismo nombre,
  como `frame`— lo reemplazaba sin aviso, dejando a los informes anteriores citando una ruta que ya decía otra cosa.
  Ahora es `<AAAA-MM-DD>-<recorrido>-<etapa>-analisis.md`, y si ya existe, `…-analisis-2.md`. Los archivos con el
  nombre viejo que tengas en `planning/reports/` se quedan donde están; nada los lee por nombre (caso 437).
- **El retiro de una línea comprueba lo que tiene su árbol, no el nombre de su rama.** Con el árbol en otra rama —un
  `git switch` adentro, o la rama renombrada— el retiro (`ops lines join` sobre una línea declarada) miraba sólo
  `line/<línea>`, decía «llegó» con trabajo sin mergear, y `--apply` quitaba el árbol y la declaración. Ahora se niega
  mientras el commit del árbol no esté en la rama viva, esté en la rama que esté —también con su carpeta borrada,
  porque git sigue teniendo su HEAD—, y dice qué hacer con esa rama; si ya llegó, retira y nombra la rama que queda,
  porque sólo borra `line/<línea>`. `ops lines` dice en qué rama está el árbol y cuenta lo que tiene, y el aviso de
  solape entre líneas ve sus archivos; `ops line` avisa al reusar un árbol que está en otra rama, y no rearma uno
  borrado cuyo registro tiene trabajo que no llegó. Una carpeta de la línea que perdió su `.git`, o un árbol sin
  commits, ya no se leen como el repositorio que los contiene ni se retiran. **Al actualizar**, si tus ramas de línea
  tienen otro nombre en el remoto —una por persona, por ejemplo—, el nombre local puede seguir siendo `line/<línea>` y
  el del remoto fijarse al empujar: `git push origin line/<línea>:<rama-remota>` (caso 438).
- **Un commit desde una carpeta que git no toma como repositorio dice por qué, y no la ayuda de `git diff`.** Los
  guards que leen el índice frenaban bien, pero ahí git no dice la causa: rechaza `--cached` y suelta su ayuda entera,
  y el mensaje la pegaba con un consejo sobre variables que no venía al caso. Un agente lo leyó como un hook roto.
  Ahora cita lo que git responde sobre esa carpeta —que no es un repositorio, o que es de otro dueño y cómo
  habilitarlo—. Al actualizar no hay nada que hacer.
- **`teamwork.md` dice que `codex exec` lanzado por otro proceso va con la entrada cerrada**, `< /dev/null`. Sin eso,
  con la entrada que no es una terminal, Codex la espera antes de empezar y la sesión queda colgada sin avisar: medido
  con `codex-cli 0.163.0`, seis sesiones quietas 2h41m en `Reading additional input from stdin...`.

## [0.114.0] - 2026-10-22

### Agregado

- **Los guards propios del proyecto corren en todos los runners.** Se declaran una vez, por evento, en `guards` de
  `ops.config.json` —`{ "pre-shell": ["automatization/hooks/guard-acme.sh"] }`, con rutas relativas a la raíz ops—, y
  el motor los corre después de los suyos en Claude, Codex, Gemini y Antigravity. Antes un guard registrado en
  `.claude/settings.json` sólo corría en Claude, y Antigravity no tenía dónde registrarlo. El guard recibe la llamada
  por stdin y `OPS_ROOT`, y sale en 0 o en 2 como un hook de Claude Code. Un 0 con la decisión en JSON
  —`decision: block`, `continue: false`, o `permissionDecision` en `deny` o `ask`— también frena; el `ask` frena
  porque estos guards no abren diálogo. Cualquier otra salida, que no esté, que `guards` no tenga la forma de un objeto
  con una lista de rutas por evento, que nombre un evento que no existe, o que los de un evento tarden más de 20 s
  entre todos, también frena. Con `guards` mal escrito pasa una sola cosa, la escritura que arregla `ops.config.json`,
  para que la sesión no quede sin salida; esa escritura la siguen juzgando los guards de Cauce. **Al actualizar**, si tenés guards propios registrados en la configuración de un
  runner, pasalos a `guards` y sacalos de ahí: `automation doctor` avisa los que quedan en los dos lugares, porque
  corren dos veces, también con la variable entre comillas o detrás de un intérprete. En `stop` no se vuelven a
  correr cuando la sesión ya sigue por un cierre frenado (`stop_hook_active`), para que uno roto no la encierre (caso
  428).
- **`/autobuild --role build` y `/autobuild --role review` reparten una tarea entre dos sesiones.** El traspaso de
  0.112.0 estaba escrito y ningún recorrido lo seguía: el programador se revisaba y cerraba su propia tarea, en
  cualquier runner. Con `--role build` corre las fases de su carril hasta Commit, salvo Review y QA, y para con
  `review-pending` dejando el WIP en `phase: Review` y su evidencia en `## Evidencia de Build`. Con `--role review`
  toma sólo esa tarea, revisa el commit de quien la construyó, la verifica, le hace QA y la cierra. Si no aprueba —o
  falta una prueba, o un gate o el QA están en rojo— no corrige: anota los hallazgos en `## Bloqueos` del WIP, le
  agrega al plan el paso que los corrige, lo devuelve a `phase: Build` y para con `review-returned`. Un rol que no existe para con `role-unknown` antes de gastar nada, y
  revisar sin una tarea entregada, con `review-not-ready`. Vale para el recorrido de Claude, el skill de Antigravity
  y Codex y el comando de Gemini (caso 430).

- **`automation doctor . codex` avisa los hooks que Codex no corre porque nadie los confió.** Codex corre un hook del
  proyecto sólo si alguien lo confió con `/hooks`, y en `codex exec` no avisa: la sesión trabaja sin ningún guard de
  Cauce. `doctor` lo da como error, con cuántos faltan, y la tabla de runners de `teamwork.md` lo pide. **Al
  actualizar**, si usás Codex: abrí `codex` en la carpeta de la instancia y confiá los hooks con `/hooks`; y de nuevo
  cada vez que `install` cambie `.codex/hooks.json` (caso 434).

### Cambiado

- **`PROTOCOL.md` dice qué fases corre cada carril, en una tabla.** Decía que `lite` «agrega Ready, Plan y QA», y el
  recorrido además le saltea la crítica del plan y le hace una QA más barata: en un runner sin recorrido con fases,
  que gradúa la tarea por ese texto, una tarea `lite` recibía el trato completo. La tabla dice lo que hace el
  recorrido, y una prueba de este repositorio las compara. El skill de `autobuild` y el comando de Gemini mandan a
  esa tabla, y el traspaso entre sesiones de `teamwork.md` manda a revisar con el carril del WIP (caso 431).
- **La crítica del plan es un paso propio y queda escrita.** El paso 6 de `PROTOCOL.md` decía «Plan y Critique» en
  un renglón, y en `full` dos de tres runners sin recorrido planificaron sin criticar. Ahora la crítica va aparte,
  deja lo que objetó —al menos la objeción más fuerte— en `## Crítica` del WIP, y Done lo copia a un campo nuevo de
  `done/<slug>.md`, `critique:` —`n/a — razón` en un carril que no la corre—. En `full`, `check` nombra la entrada
  sin crítica o con `n/a` cerrada desde la primera que trae el campo, y cuenta las de antes, que no tenían dónde
  escribirla. El recorrido de Claude la escribe solo, también cuando la cierra la sesión que revisa (caso 433).
  **Al actualizar**, una tarea `full` que ya estaba en curso no tiene `## Crítica` en su WIP: cierra con
  `critique: n/a — no quedó registrada` y `check` la nombra. Es cierto, y no hace falta corregir nada.
- **Review comprueba lo que la crítica dice que cambió en el plan.** Hasta ahora contrastaba con el diff sólo las
  condiciones con que la crítica aprobaba. Ahora comprueba también lo que anuncia como cambio del plan, en
  `## Crítica` del WIP: un cambio anunciado que el diff no tiene es un hallazgo, y vuelve a quien construyó como
  cualquier otro. Lo dice el paso 9 de `PROTOCOL.md`, y el recorrido de Claude se lo pasa a Review (caso 436).

### Corregido

- **Con `CAUCE_RUNNER` fijado para todo el proyecto, la sesión de una línea tiene su propio id.** Antes la variable se
  usaba tal cual y el `--line` no entraba: la línea compartía con la principal el WIP y los reclamos, y `context` le
  ofrecía como suya la tarea que la principal tenía tomada. Ahora el id es `<CAUCE_RUNNER>#line/<línea>`, el mismo en
  `ops` y en los guards —`plan-first` y el alcance de lo concedido en el chat leen el WIP de la línea—. Un
  `CAUCE_RUNNER` que ya trae la línea se respeta, y uno que trae otra frena en vez de usarse para ésta. **Al
  actualizar**, si una sesión de línea tiene trabajo en curso con
  la variable fijada, `claim` la frena nombrando el id viejo: soltala con el comando que imprime `release` y volvé a
  tomarla con `--line`, y mové su plan de `planning/wip/<id>.md` a `planning/wip/<id>-line-<línea>.md` (caso 429).
- **`release` reconoce tu propio reclamo hecho desde otro id.** Antes decía que la tarea «es de» vos mismo y mandaba a
  borrar el reclamo a mano. Ahora dice desde qué runner la tomaste e imprime el comando que la suelta con ese id, como
  ya hacía `claim`; y si la tiene la sesión de otra línea, o la principal, manda a soltarla desde ella. El id sale
  citado para el shell, en éste y en todo `export CAUCE_RUNNER=…` que imprime el CLI (caso 432).

## [0.113.3] - 2026-10-21

### Corregido

- **Una escritura a `planning/.ops-approval` que no llega en `tool_input` se frena como cualquier otra.** Con el
  archivo de primer nivel, en un parche suelto o invocando el guard a mano con `OPS_HOOK_FILE`, el guard de archivos
  se caía con un error y salía en 1 en vez de bloquear con 2, que es lo único que el runner lee como «no». Ningún
  runner instalado manda hoy esas formas para Claude Code ni Antigravity; al actualizar no hay nada que hacer
  (caso 427).

## [0.113.2] - 2026-10-21

### Cambiado

- **Nada cambia en lo que recibe tu instancia.** Esta versión deja al día la publicación del propio toolkit: reintentar
  una publicación justo después de que npm la aceptó ya no vuelve a publicarla ni termina en rojo (caso 425). Al
  actualizar no hay nada que hacer.

## [0.113.1] - 2026-10-21

### Corregido

- **`ops lines` muestra la línea que todavía no trabaja ningún repositorio.** Una línea declarada vacía
  (`"piloto": []`) desaparecía del panorama con toda su cola, aunque `context --line` la viera. Ahora sale con
  sus hitos, sus reclamos y su trabajo en vuelo, y dice que su primer repositorio se suma con la primera tarea que
  pide su árbol. En `--json` viene en `declared` con `repos: []` (caso 426).

## [0.113.0] - 2026-10-21

### Cambiado

- **Una línea ya no enlaza los repositorios que no trabaja.** `lines/<línea>/` tiene sólo sus árboles: los enlaces a
  cada vecino casi nunca los usaba nadie, y se leían como repositorios metidos en la línea. Si un repositorio nombra a
  otro por ruta relativa (`../shared-lib`), ése se suma a la línea con su árbol. `ops line` saca los enlaces que dejaron
  las versiones de antes —sólo los suyos, sin tocar adonde llevan— y lo dice (caso 420). `teamwork.md` explica la
  línea desde el frente: sus hitos, y un árbol por repositorio porque eso es lo que la aísla de la principal (caso 421).
- **Una línea puede nacer sin repositorios.** El frente se decide antes que el repositorio: `ops line . piloto` declara
  la línea sin armar nada en disco, y su sesión ya puede escribir y planificar sus hitos. El repositorio de la primera
  tarea que pide su árbol —`ops worktree --line`, que es lo que corre `/autobuild`— se suma a la línea en ese momento, y
  la salida lo dice; antes se negaba y mandaba a correr `ops line` a mano. Un árbol de la línea que falta se vuelve a
  armar ahí mismo (caso 421).

### Corregido

- **La sesión de una línea ya no escribe en el checkout de la principal.** Con su plan escrito, nada la frenaba en
  `app/` ni en un repositorio que la línea no trabaja: lo que escribía ahí no viajaba en su rama y le aparecía a la otra
  sesión como cambio propio. Ahora los guards de límites —de archivos y de shell— le dejan sus árboles y la instancia, y
  frenan lo demás de la carpeta de sesión diciendo en qué árbol va. Qué línea lleva la sesión lo leen de lo que ella
  dice —`/autobuild <línea>`, el `--line` de `claim` o `worktree`— o de `CAUCE_RUNNER`: no hace falta exportar nada. Pasa
  lo que la persona nombra o confirma en el chat, y la sesión principal no cambia. `ops worktree --line` se niega con una
  tarea que no es de la línea, como ya hacía `claim` (caso 422). Y tampoco cambia ese checkout con git —`switch`,
  `checkout`, `stash`, `restore`, `pull`…—, ni la rama o el stash de la instancia, que es una sola para las dos: lo que
  sólo lee, como `status`, `log` o `diff`, pasa, y en la instancia también `add` y `commit` (caso 423).
- **El bloqueo que el agente corrige solo ya no le habla a un subagente de una confirmación que no ofreció.** Decía
  «devolvele el bloqueo a quien te lanzó» y seguía con «esa confirmación cubre lo que se frenó»; ahora dice que quien lo
  lanzó se lo pregunta a la persona, y que su confirmación vale para el reintento. Vale para los diez bloqueos que el
  agente corrige solo, de `verify` a `plan-first` (caso 424).
- **Con `ops.config.json` ilegible, volver al último commit ya no se frena.** Un conflicto sin resolver dentro del archivo
  dejaba la sesión sin ninguna herramienta, ni siquiera un `ls`, y tampoco la que lo repara. Ahora pasa
  `git checkout HEAD -- ops.config.json` (o `git restore --source=HEAD`), solo y sin nada que el shell expanda, y el
  bloqueo lo nombra con la ruta de la instancia. Desde otra rama o desde el índice sigue frenando: ahí puede venir un
  permiso que nadie aprobó. Y si restaurar corre código del repositorio —un hook `post-checkout`, un filtro de
  `.gitattributes`, `core.fsmonitor`—, también: correría sin guards, y la salida queda de la persona (caso 419).

## [0.112.0] - 2026-10-20

### Corregido

- **En Antigravity vuelven a correr los guards de archivos.** agy 1.4 escribe con `write_file` y `edit_file`, y el
  puente sólo escuchaba los nombres de antes: ningún guard de archivos —`secrets`, `plan-first`, `engine`— corría cuando
  agy creaba o editaba un archivo, y nada lo decía. Ahora los escucha, y `automation doctor antigravity` avisa cuando agy
  cambia de versión respecto de la que se midió. **Reinstalá el runner** (`automation install . antigravity`) para que el
  puente nuevo llegue (caso 410). Y la lectura con su herramienta de leer archivos pasa ahora por `secrets-read`: un `.env`
  ya no entra al contexto de la sesión sin que la persona lo apruebe (caso 413).
- **El id de una línea es el mismo desde cualquier carpeta.** `ops context`, `claim`, `release` y `worktree` con `--line`
  lo arman con la instancia, y es el que busca `plan-first`. Antes, corridos desde la carpeta de sesión —donde se abre la
  sesión— dejaban el plan bajo otro id, y la escritura en el árbol de la línea se frenaba con «hay plan escrito, pero bajo
  otro id». Un reclamo tomado así antes de actualizar queda con su id viejo: `ops context` lo muestra con el
  `export CAUCE_RUNNER=…` que lo retoma (caso 411).
- **Una tarea se puede repartir entre quien la construye y quien la revisa.** `teamwork.md` explica el traspaso por el
  WIP: quien construye se detiene en Commit con `phase: Review`, y quien revisa juzga con lo que está en disco y cierra o
  devuelve. Dentro de una línea los dos comparten el id. Y `ops release` suelta el reclamo de una tarea que ya está en
  DONE aunque lo haya tomado otro: el revisor que cierra ya no queda con el reclamo que `check` le manda soltar (caso 412).
- **`teamwork.md` dice qué runner puede tomar qué rol.** Los cuatro pueden ser líder, programador o revisor: se midió con
  sesiones reales. La tabla dice lo que necesita cada uno: Codex, acceso total para commitear en el árbol de una línea;
  Gemini, la terminal permitida en la política del usuario (caso 406).
- **Los guards de commit juzgan también el árbol de una línea.** En `lines/<línea>/<repo>` pasaban `git add -A`, el commit en
  la rama viva, la firma de IA y el commit sin la puerta, porque esos guards no lo contaban entre los repositorios de la
  sesión (caso 414).
- **Un rol que lanza otro agente se lanza con `CAUCE_DELEGATED=1`.** Cauce no frena lo que la persona pide en el chat, y el
  pedido con que un coordinador lanza a otro agente (`claude -p "…"`, `codex exec "…"`, `gemini -p "…"`) contaba como de ella:
  esa sesión entera quedaba sin `plan-first`. Con la variable, ningún mensaje de la sesión cuenta como de la persona
  (caso 415).
- **Con pnpm, cambiar sólo la versión de `package.json` ya no pide aprobación por el lockfile.** La lista de claves
  que no mueven el lock es ahora una por gestor: con pnpm suma `name`, `version`, `license`, `engines`, `bin`,
  `funding`, `workspaces`, `os` y `cpu`, que su lock no registra. Con npm sigue igual (caso 403).
- **`ops line` avisa cuando un árbol nace sin los hooks de git de su repositorio.** `core.hooksPath` lo comparten
  todos los worktrees, y la carpeta a la que apunta husky (`.husky/_`) la genera la instalación: en el árbol nuevo no
  está, y git commitea sin hooks sin decir nada. El aviso se repite al volver a pedir la línea hasta que la
  instalación la genere (caso 398).
- **El commit de cada tarea del autobuild ata su mensaje al id de la tarea.** Si el mensaje va en un archivo, se
  llama `commit-<id>.txt` y su pie se comprueba antes de commitear. Antes, el reintento de un commit frenado podía
  tomar el archivo que había dejado la tarea anterior, y el commit salía con el asunto y el `Task:` de otra (caso 400).
- **`decisions:` dice cómo se escriben ahí los registros de R14.** Lo verificado o documentado va como
  `[fuente: ...]` y una hipótesis como `[supuesto: ...]`: lo dice `PROTOCOL.md` y lo repite el error de `check`. Antes,
  quien rotulaba a la R14 (`Verificado — …`) recibía un error con un formato que no mencionaba lo que había escrito
  (caso 401).
- **Escribir una credencial desde el shell se frena como con las herramientas de edición.** `echo … > .npmrc`,
  `tee ~/.netrc` o `cp clave id_ed25519` pasaban; ahora `secrets-shell` los frena con la misma regla y la misma
  aprobación por archivo que `secrets`. La familia `.env` queda afuera, para que `cp .env.example .env` siga
  pasando. Y `cat > .npmrc` ya no se frena como si leyera el archivo: lo que va después de `>` es una escritura
  (casos 405 y 402).
- **`ops lines migrate` avisa si la instancia no está en su rama viva.** El planning de la línea queda en la rama en
  que esté la instancia, que suele ser la de un upgrade. Avisa y no frena (caso 397).
- **`ops line` avisa cuando el árbol de un repositorio con compose es el mismo proyecto que el checkout de siempre.**
  Compose nombra el proyecto por la carpeta, y el árbol se llama como el repositorio: comparten volúmenes y
  contenedores. El aviso da el valor que lo separa —`COMPOSE_PROJECT_NAME=<repo>-<línea>` en el `.env` del árbol—, y
  `teamwork.md` explica lo que el compose fija a mano (caso 399).
- **A una línea se le puede sumar un repositorio que ya tenía enlazado.** `ops line` enlaza en la carpeta de la línea los
  repositorios que no trabaja, y sumar después uno de ellos fallaba con «ya existe y no es un worktree». Ahora el enlace se
  reemplaza por el árbol (caso 408).
- **El Mapa real de `workspace.md` llega a las fases de autobuild que trabajan el código.** El preámbulo de cada fase
  prohíbe releer ese archivo, y el contrato no lo llevaba: lo que `onboard` pide escribir ahí —qué servicio vive dónde— no
  le llegaba a ningún agente. Viaja sólo si el proyecto lo escribió (caso 409).
- **En una sesión delegada, un guard frena en vez de pedir el diálogo de Claude Code.** Con `CAUCE_DELEGATED=1` no hay quién
  lo conteste, y en `claude -p --permission-mode bypassPermissions` lo pedido se aprobaba solo: una escritura de `.npmrc`
  frenada por `secrets-shell` se hizo (caso 415).
- **En `bypassPermissions`, un guard frena y la confirmación va por el chat, en vez del diálogo de Claude Code.** En
  `claude -p` ese diálogo no aparece y lo pedido se aprobaba solo, también sin la variable, y el hook no tiene cómo
  distinguir `-p` de una sesión con alguien mirando. Quien trabaja interactivo en ese modo aprueba con un sí en el chat, en
  vez de con un clic (caso 416).
- **`echo $T >&.npmrc` es una escritura, y el cuerpo de un heredoc con un delimitador como `END-1` no se lee como
  comandos.** El primero pasaba los guards que miran lo que escribe un comando, y el segundo frenaba crear un script que
  nombraba una credencial (caso 405).
- **`cd && rm -rf <x>` se juzga en la carpeta personal**, que es donde deja un `cd` solo. `destructive` lo juzgaba en el
  directorio de antes (caso 418).
- **`teamwork.md` dice que Gemini tiene que confiar en la carpeta**: con `--skip-trust` no carga la configuración del
  proyecto y ningún guard corre (caso 406).
- **Por el shell, la excepción del `.env` cubre sólo `.env` y `.env.local`.** Eximía la familia entera, también `.env.keys`
  —la clave privada de dotenvx— y `.env.production`, que la herramienta de edición frena (caso 417).
- **Escribir una credencial por el shell frena también con un commit en el mismo comando, con el `>` pegado y bajo un `cd`
  que no se resuelve.** `git commit -m wip && echo … > .npmrc` y `echo x>.npmrc` pasaban. `cd "$HOME/x"` ahora se resuelve, y
  bajo un `cd` que no se resuelve se pide la ruta, o que la persona lo pida en el chat (caso 405).
- **Sumar a una línea un repositorio anidado, o uno cuyo árbol no se puede armar, deja la línea como estaba.** El enlace de
  la carpeta de arriba también se reemplaza, y si `git worktree add` falla el enlace vuelve (caso 408).
- **El aviso de compose calla cuando el árbol tiene su propio nombre de proyecto**, distinto del checkout de siempre, y el
  nombre que sugiere es uno que Compose acepta (caso 399).
- **`ops lines migrate --apply` avisa antes de traer la línea, y también cuando se niega** (caso 397).
- **`automation doctor` no se cuelga si `agy --version` no termina**: lo corta a los 5 s, con lo que haya lanzado, y lee
  también la versión de stderr (caso 410).

### Cambiado

- **`engine/hooks/shell.js` y `engine/planning/contracts.js` se partieron por propósito.** Estaban a una y a tres líneas
  del límite de 500. El guard `dependencies` pasó a `engine/hooks/dependencies.js`, la lectura de lo que escribe un comando
  a `engine/hooks/shell-writes.js` y la validación de las entradas de DONE a `engine/planning/done-entry.js`. Los guards
  responden igual: se comprobó comparando la salida de antes y la de después sobre los mismos casos (caso 407).

## [0.111.0] - 2026-10-19

### Cambiado

- **Una línea de trabajo ya no tiene carpeta de sesión propia: vive en `lines/<línea>/<repo>/`, dentro de la de
  siempre.** Es un nombre y los repositorios del producto que trabaja: `ops line . pagos backend-pagos` la declara
  en `ops.config.json` (`"lines": { "pagos": ["backend-pagos"] }`, que se commitea) y arma un worktree de cada
  repositorio en la rama `line/pagos`, con los demás enlazados al lado. No instala runners ni crea `.claude/` ni
  `CLAUDE.md`: la sesión de la línea se abre en la misma carpeta que la principal, con sus reglas, sus permisos y
  la única instancia. Cada árbol es un checkout aparte: **sus dependencias se instalan adentro**. Antes, la carpeta
  de al lado nacía sin las instrucciones del proyecto ni los permisos entre carpetas (caso 394).
- **La sesión dice qué línea lleva**: `/autobuild pagos`, o `--line pagos` en `context`, `claim`, `release` y
  `worktree`. **Sin argumento es la principal**, que ve los hitos sin `line:` y nunca toma uno de una línea. El id
  del runner de una línea la lleva, así que las dos sesiones tienen su WIP y sus reclamos aunque corran `ops` desde
  la misma instancia. Las tareas de una línea se construyen, se verifican y se commitean en su árbol y en su rama.
- **`ops lines` muestra las líneas declaradas** —sus árboles, cuánto los separa de la rama viva, su cola, sus
  reclamos— y, cuando dos trabajan el mismo repositorio, qué archivos cambiaron las dos. `ops line` lo avisa al
  armarla. **`ops lines join . <línea>` retira una línea** cuya rama ya llegó por PR a la rama viva: saca sus
  árboles, sus ramas y su declaración, y se niega si algo no llegó o tiene cambios sin commitear.
- **Sin líneas declaradas, nada cambia**: `context`, `claim`, `check`, `contract` y `/autobuild` dan la misma salida
  que en 0.110.0.

### Deprecado

- **Las líneas de antes** —un worktree de la instancia con su carpeta `<carpeta>-<nombre>/` al lado— siguen
  andando: la línea sale de la rama de su árbol, y `context` lo avisa. **Pasalas con `ops lines migrate . <nombre>
  <repo>...`**, que trae su planning a la instancia, retira el worktree y su rama, y arma la línea nueva; se niega
  con reclamos en vuelo. La carpeta de al lado no se borra sola: rescatá lo que le hayas copiado a mano y borrala.
  La lectura de la línea desde la rama se retira en la 1.0.0.

### Corregido

- **La rama de una línea nueva sale de la rama viva, no de la rama en que esté la instancia.** `ops line` creaba
  `line/<nombre>` desde el `HEAD`, y si la instancia estaba en una rama de trabajo la línea nacía con sus commits sin
  mergear. Ahora parte de la rama por defecto del remoto, o de `main` o `master`, lo dice en la salida (`sale de:`) y
  avisa si la instancia estaba en otra. **Si querés una línea montada sobre otra rama**, creá antes
  `git branch line/<nombre> <rama>`: `ops line` usa la que ya existe (caso 395).

## [0.110.0] - 2026-10-17

### Corregido

- **`automation install` dice en qué carpeta tiene que abrirse la sesión para que corran los guards.** Los runners
  cargan la configuración del proyecto desde la carpeta en que se abre la sesión hasta la raíz de su repo git, y no
  más arriba. Una sesión abierta en un repo de producto dentro de la carpeta de un sidecar, o en una subcarpeta sin
  git, corría sin guards, sin reglas y sin cargos, y nada lo avisaba. Ahora `install` lo dice con la ruta puesta.
  **Si trabajás con Antigravity desde un repo de producto, abrí la sesión con `agy --add-dir <carpeta del
  workspace>`**: así corren los guards y las reglas encuentran la instancia. En Claude Code y Gemini, `--add-dir` e
  `--include-directories` no cargan los hooks de la carpeta agregada, así que esas sesiones se abren en la carpeta
  del workspace. Codex documenta la misma búsqueda (caso 393).

## [0.109.0] - 2026-10-16

### Cambiado

- **Las razones de parada van por familias, y son las que `autobuild` usa de verdad.** `PROTOCOL.md` decía que toda
  parada se nombraba con una de diez razones, y el recorrido paraba con cuarenta. Ahora el protocolo las agrupa en
  nueve familias, según el tramo donde paró, y una prueba compara la lista con el recorrido en los dos sentidos.
  **Si leés la razón de una parada**, el checkpoint humano sin resolver ahora se llama `awaiting-review`, como en
  `ops context`, y no `awaiting-human-review`. `review-unresolved`, `verify-regression`, `verify-inconsistent` y
  `budget-low` quedan deprecadas, con su reemplazo escrito: ningún recorrido las emitía (caso 390).
- **`aplica:` también sirve para un momento del trabajo, no sólo para una superficie.** Una regla de commits o de un
  tracker puede declarar `aplica: al commitear` o `aplica: al escribir en Jira`: queda nombrada en el bloque y deja de
  cargarse en cada agente. En tres sesiones con una regla de commits sólo nombrada, los tres agentes la abrieron y la
  aplicaron. Si en tu instancia un paso la saltea, cargala. Lo explica `planning/rules/README.md` (caso 381).

### Agregado

- **`ops governed <planning> <archivo>...` dice qué archivos va a frenar el guard de gobernanza.** Usa la misma regla que
  el guard y descuenta lo aprobado en `planning/.ops-approval`. Con `--service <dir>` resuelve también las rutas
  escritas desde el servicio de la tarea.

### Corregido

- **Antigravity corre Cauce sólo en su proyecto.** `automation install . antigravity` pedía registrar el plugin con
  `agy plugin install`, y esa copia, una por usuario, corría en toda carpeta sin plugin propio, con la raíz y las
  reglas del proyecto que la registró. `agy` 1.4.0 descubre el plugin del `.agents/` del proyecto sin registrarlo,
  así que Cauce ya no pide ese paso, `doctor` avisa si queda una copia global, y el puente se abstiene fuera de su
  proyecto. **Si alguna vez corriste `agy plugin install .agents/plugins/cauce`, quitala con
  `agy plugin uninstall cauce`**, y reinstalá el adaptador con `automation install . antigravity` (caso 392).
- **`autobuild` para antes de construir si el plan toca gobernanza sin aprobar.** El commit de un subagente no puede
  esperar a una persona, así que la corrida se enteraba al final, con Build, Review, Verify y QA ya pagados. Ahora
  pregunta con `ops governed` después del plan y para con `governance-approval-needed`, nombrando qué escribir en
  `planning/.ops-approval` y dejando la fila en `human/`. Si el Commit igual se frena por una aprobación, por un archivo
  que el plan no nombró, para con `commit-needs-approval`, con el trabajo stageado y sin el commit de planning que la
  rotulaba bloqueada (caso 383).
- **La descripción de una tarea ya no arrastra los asteriscos de un criterio en cursiva.** Una línea que cita su
  criterio como `*(criterio C1)*` le pasaba a cada fase una descripción terminada en `**` (caso 384).
- **El commit de estado de planning conserva el scope `planning` cuando tus reglas cambian el asunto.** Si una
  regla tuya pedía otro tipo, el agente reescribía `chore(planning): …` y a veces se le perdía el scope. Ahora el
  pedido dice que el scope y el slug se conservan, salvo que tu regla fije otro scope (caso 377).
- **Ready marca lo que agrega y no se puede probar.** Al aclarar una aceptación, Ready podía sumarle una condición a
  futuro («cuando exista el selector») o una nota de fuera de alcance, y Verify paraba la corrida por esa cláusula
  con el trabajo ya hecho. Ahora Ready escribe esas cláusulas como un tramo aparte, marcado
  `(fuera de verify: <razón>)`, que Verify ya no exige probar (caso 382).

- **Partir una tarea pide slugs nuevos para las partes.** Si una subtarea se quedaba con el slug de la partida,
  la corrida paraba con `split-not-applied` y decía que la escritura no había ocurrido, con el partido ya escrito
  y commiteado. Ahora la consigna pide un slug nuevo por parte, como dice R25. Si igual pasa, la parada dice qué
  subtarea hay que renombrar antes de reanudar (caso 385).

## [0.108.0] - 2026-10-14

### Agregado

- **`ops lines` dice qué está haciendo cada línea de trabajo, desde cualquier árbol.** Con dos líneas, saber
  qué tenía cada una en vuelo era abrir una carpeta por línea y correr `context` en cada una. Ahora un bloque
  por worktree de la instancia: la cola de su línea, sus reclamos, su WIP, los checkpoints que la frenan y
  cuántos commits la separan de la base. Medido sobre un banco con dos líneas: el principal no lista el
  checkpoint de `admin` aunque lo tenga en disco, y la línea muestra su reclamo y su fase.
- **`ops lines join` trae una línea a la rama donde se corre, en seco o con `--apply`.** Juntar era un merge a
  mano donde había que acordarse de qué archivos chocan a propósito. En seco calcula la fusión con
  `git merge-tree` sin tocar el árbol y clasifica cada conflicto con lo que `delivery/teamwork.md` ya decía
  —lecciones, las filas de las dos; cola e INBOX, dos decidieron sobre lo mismo; acciones humanas, falta la
  unión—, lista las épicas que repiten número, que git no ve, y avisa de los reclamos en vuelo. Con `--apply`
  se niega sobre un árbol sucio, mergea y corre `check` sobre lo que quedó; con conflictos deja el merge en
  curso, como git, con la clasificación al lado. Nunca empuja.
- **La épica puede declarar su reparto en `## Hitos`, y `ops promote` lo escribe en la cola.** El hito nacía al
  promover, copiando historias a mano a `backlog/<hito>.md`, así que un reparto entre líneas no tenía dónde
  caer y `line:` se retipeaba. Ahora la épica agrupa sus historias en hitos, cada uno con su `(line: nombre)`
  si es de una línea; `check` exige que cada historia esté en exactamente un hito y que la línea tenga nombre
  de línea; y `promote planning epic-NNN-slug` escribe un archivo por hito con el `order` siguiente y las
  historias como tareas, y pasa la épica a `active`. Sin la sección, un solo hito con el slug de la épica.
  Se niega sin escribir nada ante un marcador, un reparto que `check` rechaza —también por tamaño, R17— o un
  hito que ya está en la cola, en su archivo o en `BACKLOG.md`. Y avisa cuando un hito va a una línea que
  todavía no tiene rama: nadie lo ve hasta `ops line . <nombre>`, y `check` no lo dice.

- **Una tarea puede ser `manual`: la hace la sesión, sin agentes.** Hasta acá toda tarea de la cola pasaba por
  el recorrido, y el piso no bajaba de seis o siete agentes ni en `express`: tres tareas de pantalla de una o dos
  horas costaron 47. Ahora `[manual]` es un carril válido en la línea y en `done/`; `context` no le ofrece esa
  tarea a ningún runner y la lista aparte, y el clasificador puede elegirlo —un solo servicio, aceptación con su
  mutación, nada por decidir, ninguna dependencia abierta, una hora; ante la duda no, y nunca sobre «Qué no se
  puede romper»—: la corrida suelta la reserva, sigue con la próxima y devuelve en `manual` qué quedó para la
  sesión y por qué; si la tarea toca una superficie crítica, no la devuelve y la corre por `directo`. Una manual
  que el runner tiene reclamada o en su WIP —la sesión haciéndola— frena la corrida con `manual-held` en vez de
  construirla. La sesión la cierra con la misma evidencia, sin revisor, como `express`. **Medido** sobre las
  doce tareas de una épica real, tres veces: salió `manual` en dos tareas de CSS que estaban en el borde, y nunca
  en las que tenían una decisión, un borde abierto o una dependencia (caso 380).

### Cambiado

- **`flow` propone el reparto de la épica entre las líneas que existen.** Al redactar, agrupa las historias
  en hitos y, si la instancia trabaja en líneas —se las pregunta a `ops lines`—, propone una por hito entre
  ésas y ninguna inventada; sin líneas no propone ninguna. Es una propuesta: la persona la corrige en la
  épica y la confirma con `promote`. La promoción sigue siendo humana. **Medido**: cuatro corridas reales de `feasibility-review` sobre un banco con una
  línea: las tres primeras midieron que el manifiesto trae `lines: ["admin"]` de `ops lines` y que el Draft
  recibe las líneas que existen; la cuarta, con el caso 379 arreglado, escribió la épica con `## Hitos` —tres
  hitos, cada historia en uno, `(line: admin)` y ninguna línea inventada—, `check` la aceptó, `promote` escribió
  los tres `backlog/<hito>.md`, `join` los llevó a la línea y `context` desde `admin` ofreció su primera tarea.

### Corregido

- **Partir una tarea reapunta a quien dependía de ella.** La partición retiraba la línea de la tarea y escribía
  las partes, y toda tarea con `(depende: <partida>)` quedaba esperando un slug que ya no iba a existir, con
  `check` en rojo al terminar la corrida. Ahora pasa a depender de **todas** las partes nuevas: si son
  paralelas, esperar a una sola la dejaría arrancar antes. Medido con un agente real sobre el paso exacto:
  `(depende: T-1)` quedó `(depende: T-1a, T-1b)` y `check` en verde (caso 376).
- **La aceptación que `Ready` corrige se escribe en la línea de la tarea.** Vivía sólo en memoria: el
  planificador recibía la corregida y, por la descripción de la línea, la vieja con la orden de no re-decidirla;
  al parar la corrida la corrección se perdía y la siguiente arrancaba con el mismo error. Ahora el recorrido
  la escribe en el archivo de la tarea, relee la cola y para con `refine-not-applied` si no quedó escrita; y el
  replan recibe la aceptación que rige, que antes no recibía. La guarda tolera el punto final que un agente
  real le agregó al texto (caso 378).
- **El guard de dependencias afloja con pnpm igual que con npm.** Sumar un script, una descripción o cualquier
  clave que el lock no registra frenaba el commit en un proyecto pnpm como si fuera un cambio de dependencias,
  y el rodeo —aprobar `package.json` para siempre— dejaba pasar justo lo que el guard frena. Medido clave por
  clave con pnpm 10.30.2: las claves inertes de npm lo son también en pnpm, y lo que mueve su lock
  —`pnpm.overrides`, `resolutions`, `pnpm.patchedDependencies`— sigue frenando. yarn y bun siguen sin medir, y
  con ellos no afloja nada (caso 375).
- **Una etapa de `flow` ya no muere por pasarse unos caracteres del tope de su resumen.** Con el pedido igual al
  techo, el encuadre de `feasibility-review` devolvió 1001 y 1015 caracteres cinco veces seguidas y el recorrido
  murió sin resultado: el modelo no cuenta con precisión. Pedir 800 con el techo a la vista no alcanzó, porque el
  agente escribe hacia el número más alto que ve. El prompt pide 800, 400 y 160 sin nombrar ningún techo, y
  `summary` admite 1500. El caso de `change-review` que murió con el primer arreglo llegó al final sin un solo
  resumen rechazado (casos 379 y 386).
- **Editar sólo comentarios de `.ops-approval` ya no frena sin salida.** Una escritura que no agrega ninguna
  línea —cambiar un comentario, quitar una ruta— no es aprobarse nada, y el guard la frenaba con un mensaje que
  ofrecía confirmar un bloqueo que no existía; la persona terminaba editando a mano. Pasa sin preguntar, como ya
  pasaba cuando la persona nombraba el archivo; agregar una ruta sigue frenando (caso 374).
- **Dos evaluaciones lanzadas a la vez ya no pueden juzgar los casos de otro sujeto.** El agente que lee los
  casos guardó la lista en un archivo del scratchpad, que comparten todas las corridas de la sesión, y una
  corrida de un cargo leyó la de otro. Sin filtro de casos, o con un id que comparten 32 cargos, habría juzgado
  casos ajenos sin avisar. `evaluate --cases --json` dice de quién es la lista, y `agent-eval` y `flow-eval` se
  detienen con `casos-ajenos` si no es la del sujeto que se pidió (caso 387).
- **Seis cargos, un recorrido y el molde de empresa vuelven a decir «equipo» donde hablan de gente.** El cambio
  de *teams* a *flows* de 0.46.0 reemplazó «equipo» por «recorrido» también donde nombraba a personas: el
  `SKILL.md` de tech-lead se describía como «la autoridad técnica sobre un cambio concreto y el recorrido que lo
  hace». Se devolvió en las 59 líneas donde el sentido era ése, incluidos ocho contratos, el exit gate de
  `product-development` y `organization/company.md` y `domains.md`, que llegan con el próximo `upgrade`. Ninguna
  conducta cambia (caso 388).
- **`flow-eval` le dice al recorrido qué archivos trae el caso.** El banco los copiaba, pero el recorrido
  recibía sólo el pedido y frenaba pidiendo justo lo que tenía delante: «la épica, o dónde está», con la épica en
  la raíz. Ahora la intención los nombra y el juez sabe que no son obra del recorrido. Los dos casos que se
  volvieron a medir pasan, y uno de ellos ganó el informe que daba por hecho y no traía (caso 389).

## [0.107.0] - 2026-10-10

### Cambiado

- **Lo que cae fuera de las raíces se puede dejar pasar por una vez, confirmando el bloqueo.** Los dos guards
  de límites sólo ofrecían declarar la ruta en `writableOutsideRoots`, también para lo que una persona pide
  una sola vez —liberar disco, limpiar una caché—. Ahora el bloqueo deja la ruta esperando y pasa con la
  confirmación de la persona, o con la línea que muestra pegada en `planning/.ops-approval`; en Claude Code,
  con el diálogo del runner. Es más angosto que en los otros guards, a propósito: la línea lleva el verbo y la
  ruta resuelta —`borra /ruta`, `escribe /ruta`—, vale para ese turno y no para la sesión, y pedirlo en el
  chat nombrando la ruta no alcanza, hay que contestar el bloqueo. Lo que `destructive` frena sigue sin
  salida. Y si un comando cae afuera en varias rutas, el bloqueo las muestra todas y una sola respuesta las
  cubre (caso 370).
- **Ensanchar el límite de raíces desde su configuración también lo confirma una persona.** El agente podía
  agregar una ruta a `writableOutsideRoots` —o una raíz a `workspaceRoots`, o cualquiera de las dos en
  `ops.config.local.json`, que ningún guard miraba— y lo que el límite frenaba pasaba, por prefijo y para
  siempre, sin que nadie lo confirmara en el momento; en sesiones reales lo hacía. Ahora la escritura que
  **agrega** una ruta o una raíz se frena con la entrada resuelta a la vista —`declara /ruta`— y pasa con la
  misma confirmación angosta de arriba; lo que angosta, reordena o reescribe igual no se pregunta, y el resto
  de los dos archivos tampoco. Un fragmento o un comando sobre `ops.config.local.json` se frena como ya
  pasaba con `ops.config.json`: no hay con qué comparar —también un fragmento que sea JSON válido—. Y por
  shell, nombrar `ops.config.json` en el chat ya no deja pasar la escritura: un comando no dice con qué queda
  el archivo; el mismo pedido con la herramienta de edición pasa como antes, y por comando pasa si la persona
  confirma el bloqueo, sabiendo que no va a ver con qué queda —es la vía de Codex, que escribe sólo por parche—. **Si tus sesiones declaraban
  rutas solas**, van a preguntar una vez por cada ruta nueva; lo ya declarado no cambia (caso 373).

### Corregido

- **Con Codex y Gemini instalados, Gemini ya no avisa un conflicto por cada cargo al abrir la sesión.** Gemini
  CLI lee `.agents/skills/` —la carpeta de Codex— como alias de `.gemini/skills/`, con precedencia, así que
  cada cargo estaba dos veces y lo decía uno por uno: 53 líneas antes de la primera respuesta. Ahora Gemini
  instala sus punteros en `.agents/skills/`, los mismos que Codex, y hay uno solo por cargo. **Al reinstalar
  el adaptador de Gemini** se quitan de `.gemini/skills/` los punteros que generó Cauce; uno que hayas editado
  se queda, y la instalación lo nombra. Desinstalar uno de los dos runners conserva los punteros mientras el
  otro siga instalado (caso 371).
- **El guard de pruebas reconoce más nombres de prueba fuera de una carpeta de pruebas.** Un `a.test.mjs`, un
  `a.spec.cts`, una prueba de Cypress (`a.cy.ts`) o de punta a punta (`a.e2e.ts`) no contaban como prueba
  si no vivían bajo `test/`, así que borrarlas, apagarlas con un `skip` o renombrarlas pasaba. Ahora cuentan,
  por parche y por shell. La forma con guion —`load-test.js`— sigue sin contar, a propósito, y un nombre como
  `FooTest.java` tampoco: no se midió sobre proyectos con esos lenguajes (caso 369).

## [0.106.1] - 2026-10-10

### Corregido

- **Borrar fuera de las raíces por shell se frena igual que escribir ahí.** El guard de límites no dejaba
  tocar una ruta de afuera con un `echo >` y dejaba borrarla con un `rm -rf`. Ahora `rm`, `unlink`,
  `rmdir` y `find … -delete` se juzgan como una escritura, y `touch` y `mkdir` también. Vale lo mismo que
  para escribir: el temporal del sistema y lo que declares en `writableOutsideRoots` siguen pasando, y lo
  que no se puede resolver —una variable, un `cd` a un destino desconocido— no se juzga. Tampoco lo relativo
  después de que el comando define una función de shell: desde ahí no se sabe dónde está parado, y sólo se
  juzgan las rutas enteras. **Si tus sesiones borran algo fuera del proyecto**, por ejemplo una caché en tu
  carpeta personal, declarala ahí (caso 365).
- **Un parche de Codex que renombra un archivo se juzga también por su destino.** Con `*** Move to:` un
  archivo de adentro se mudaba fuera de las raíces, o a un nombre como `.env`, sin que ningún guard de
  archivos mirara a dónde iba (caso 366). Y por lo que trae: un archivo con SQL destructivo renombrado a una
  carpeta de migraciones se frena como si se lo escribiera ahí —también si el mismo parche se lo quita, o si
  estaba en su reversión: se juzga el archivo como está, y la salida es aprobarlo—, y renombrar una prueba a
  un nombre que ya no es de prueba se frena como borrarla (caso 367).
- **`cd ~ && rm -rf .` se frena también con el proyecto fuera de tu carpeta personal.** El guard destructivo
  la cuidaba sólo por contener al proyecto; en un servidor o un contenedor, donde no lo contiene, ese
  borrado pasaba (caso 368).
- **Lo que el runner instala con la ruta de tu carpeta escrita ya no entra a git.** Los recorridos de Claude y
  el puente de Antigravity llevan la ruta de la instancia. En una instancia embebida vivían en el
  repositorio, y cada clon y cada línea de trabajo los veía modificados para siempre. Una instancia nueva los
  ignora. **Si la tuya es anterior**, `check` te dice qué líneas agregar a tu `.gitignore` y, si ya los
  commiteaste, con qué comando sacarlos de git sin borrarlos. Después de eso, cada persona corre
  `automation install` en su clon, que es lo que ya hacía falta. Y `check` se lo dice al que clonó:
  avisa a qué runner le faltan y con qué comando se rehacen (caso 364).

## [0.106.0] - 2026-10-10

### Cambiado

- **Verify decide la causa de cada criterio por separado.** Una condición que se cumple en un documento o en
  un comentario salía `missing-test` apenas la tarea tocaba código: el recorrido pedía una prueba para prosa,
  y o la conseguía o paraba con `verify-hollow`. Ahora sale `no-surface` aunque el diff traiga código, queda
  en `done/` como `CN → n/a — razón`, y los criterios de conducta siguen obligados a su prueba. Si tu
  proyecto quiere una prueba que asercie el texto de un documento, la condición tiene que pedirla: ya no
  llega sola. Rige al reinstalar el runner (caso 345).
- **Un criterio que aparece recién en la segunda pasada de Verify compra una vuelta más.** La segunda pasada
  podía traer sin cubrir un criterio que la primera había dado por cubierto, y la corrida paraba con
  `verify-hollow` sin que nadie hubiera intentado escribir esa prueba. Ahora se piden sólo esas pruebas y
  Verify corre una tercera vez; lo que ya se había pedido y sigue faltando frena como antes. Rige al
  reinstalar el runner (caso 346).
- **Un caso descubierto cita a su rojo por un id.** `edge-unproven` comparaba dos nombres escritos por
  separado y frenó tres veces una entrega probada por cómo estaban escritos. Build le pone un id corto a
  cada rojo y el borde lo cita; quien no lo cite sigue pasando por el nombre, con las reglas de siempre.
  Rige al reinstalar el runner (caso 350).
- **El checkpoint entre hitos es un archivo por hito y frena sólo a su línea de trabajo.** Era un único
  `AWAITING_REVIEW.md`: con dos líneas, la que traía la rama de la otra quedaba frenada por un hito ajeno, y
  cuando las dos habían cerrado un hito git juntaba las dos ediciones sin avisar y dejaba `resuelta` sobre un
  checkpoint que nadie había revisado. Ahora `autobuild` escribe `planning/checkpoints/<hito>.md` con su
  `line:`, `ops context` nombra el que te frena y `ops tree` lista los pendientes de todas las líneas. No hay
  que migrar nada: un `AWAITING_REVIEW.md` que ya tengas sigue frenando a todos hasta que diga `resuelta`, y
  es la forma de parar la instancia entera a propósito. El motor rige con `upgrade`; que el recorrido escriba
  el archivo nuevo, al reinstalar el runner (caso 347).
- **`check` avisa si `HUMAN_ACTIONS.md` va a chocar entre líneas.** El molde entrega `merge=union` para esa
  tabla, pero una instancia que vive en un repositorio con su propio `.gitattributes` no lo recibe. Con
  líneas en uso y filas en la tabla, `check` lo dice y trae la línea exacta que hay que agregar (caso 347).
- **Las acciones humanas van una por archivo, en `planning/human/`.** `HUMAN_ACTIONS.md` es una tabla que
  todas las líneas de trabajo escribían, y git la fusiona por unión: cuando una línea resolvía una fila
  mientras la otra registraba la suya, quedaban las dos versiones de la fila, y la `pendiente` volvía a
  bloquear su tarea sin conflicto ni aviso. Ahora `autobuild`, `flow` y `onboard` escriben cada acción en su
  archivo —`task`, `status` y `origin` en el frontmatter, la acción como cuerpo—, y se resuelve cambiando su
  `status`. `ops human planning` imprime la tabla entera, y `ops archive planning human-actions` mueve las
  resueltas a `human/done/`. No hay que migrar nada: tu `HUMAN_ACTIONS.md` se sigue leyendo y sus filas
  bloquean igual. Si en esa tabla te aparece una fila resuelta y también pendiente, `check` lo rechaza y dice
  cuál borrar. El motor rige con `upgrade`; que los recorridos escriban en la carpeta nueva, al reinstalar el
  runner (caso 351).

- **Las reglas y la documentación del molde nombran `planning/human/` como el lugar de una acción humana.**
  R17 y R19 decían `HUMAN_ACTIONS.md`, y lo mismo once textos del molde: el protocolo, el diagrama del
  recorrido, las plantillas de recorrido y de ADR, la guía de equipo. Es el nombre del lugar, no una conducta
  nueva: los recorridos y los cargos ya escribían en la carpeta. Lo mismo en los contratos de
  `growth-marketer`, `finops-engineer` y `ux-designer` y en los recorridos `feasibility-review` e
  `incident-review`. Tu tabla se sigue leyendo (caso 359).

### Corregido

- **En una instancia embebida, el runner se instala bien en una línea de trabajo y en un clon en otra ruta.**
  Los recorridos llevan escrita la ruta de la instancia. Con `.claude/` en git, una línea o el clon de un
  compañero nacían con los de la carpeta original: la instalación tomaba esa diferencia por una edición a
  mano y se negaba, y los recorridos seguían apuntando a la otra carpeta. Ahora reconoce que sólo cambió la
  ruta y los reescribe. Y si el runner no se puede instalar en una línea, `ops line` lo dice y sale con
  error, en vez de contestar que quedó. Si armaste una línea embebida con una versión anterior, repetí
  `ops line` sobre ella (caso 363).
- **En una instancia embebida, `check` y `ops evidence` encuentran el commit de una tarea cuyo servicio es
  una carpeta.** La entrada lo cita con el nombre del servicio, que ahí no es un repositorio: `check` avisaba
  que su repositorio no estaba en la máquina y `evidence` daba las pruebas por ausentes. También encuentra la
  prueba cuando la traza la nombra subiendo desde la carpeta del servicio, `../test/…` (caso 362).
- **En una línea de trabajo, el árbol de la tarea se puede escribir aunque quede al lado de su raíz.** Con una
  raíz declarada por repositorio —`../api` en vez de `..`—, el árbol que `ops worktree` arma para cada tarea
  caía fuera de las raíces y el guard rechazaba la primera escritura: Build frenaba siempre, pidiendo una
  exención que valía para esa tarea sola. Ahora el árbol de una tarea reclamada es escribible en el lugar
  donde `ops worktree` lo arma, con el mismo alcance que el checkout principal, y se cierra solo cuando la
  tarea se suelta. `ops worktree` se niega a entregar un árbol de esa rama que esté en otro lado. Si agregaste
  rutas de árboles de tarea a `writableOutsideRoots` para salir del paso, ya no hacen falta (caso 360).
- **`ops evidence` ya no aborta por memoria cuando el producto trae binarios.** Leía como texto cada archivo
  de las raíces: con modelos, imágenes o bibliotecas compiladas, buscar una prueba que no estaba agotaba la
  memoria y el proceso moría sin decir de qué. Ahora no lee lo binario ni lo que pasa de 5 MB, y dice cuántos
  archivos dejó sin leer (caso 361).
- **`check` y `ops evidence` buscan también en el repositorio de la propia instancia.** En una instancia
  sidecar, el commit de una tarea de planning o de documentos vive ahí y no en una raíz de código: `check`
  avisaba que «no está en su repositorio» o que no se había comprobado, sin forma de apagarlo, y `evidence`
  daba por ausente la prueba. Ahora se encuentran, citados sin repositorio o con el nombre de la carpeta de
  la instancia; y el repositorio de una cita se lee aunque el texto siga después del paréntesis. No hay que
  hacer nada (caso 356).
- **`ops line` avisa si el producto quedó vacío en la línea.** Con el repositorio del producto adentro de la
  instancia y registrado como enlace de git, la línea se armaba con esa carpeta vacía y contestaba `✓`. Ahora
  imprime, después del `✓`, el mismo aviso que `check`, con qué hacer. No se niega (caso 358).
- **`ops evidence` encuentra la prueba de una tarea cerrada en una línea de trabajo.** Buscaba sólo en lo que
  estaba en disco. En una línea el producto es un enlace que no recorría, y la tarea se commitea en una rama
  cuyo árbol se retira, así que todas las trazas salían `[ausente]`, igual que una prueba inventada. Ahora
  recorre el producto enlazado y, lo que el disco no tiene, lo busca en el commit que la entrada nombra; la
  salida dice cuándo la respuesta salió de un commit y no de un archivo en disco (caso 353).
- **`automation doctor` ya no deja un proceso colgado por cada copia registrada que no contesta.** Al agotar
  el tope terminaba al intérprete que la lanzó y no a la copia, que quedaba corriendo para siempre. Si
  corriste `doctor` o `install` sobre un runner con una copia registrada que se cuelga, puede haber procesos
  `node hook.js` viejos: `pgrep -af "node hook.js"` los lista, y los que cuelgan de `systemd` o de `init` se
  pueden terminar (caso 355).
- **`ops evidence` deja de avisar «cita y no aparece» de una frase que está.** Pasaba cuando la frase citada
  traía una palabra entre comillas invertidas, o cuando el archivo tenía un `;` que la traza escribió como
  coma. No cambia ningún veredicto (caso 354).
- **`check` avisa de un repositorio anidado que una línea de trabajo va a encontrar vacío.** Si una raíz de
  `workspaceRoots` vive dentro de la instancia y el repositorio de la instancia la registró como enlace de
  git —lo que deja un `git add` que la incluye—, una línea nace con esa carpeta vacía y todo sigue en verde.
  `check` lo dice y trae el comando que lo corrige. Un submódulo declarado en `.gitmodules` no se avisa, y
  una instancia con sus servicios al lado no se ve afectada (caso 352).
- **La fila que deja una parada bloquea la tarea, o la parada dice que no.** La fila podía salir con la
  tarea, la épica y la decisión juntas en la primera columna: no bloqueaba nada, `check` la rechazaba, y la
  relectura contestaba que estaba pendiente. Ahora el pedido dice que ahí va el slug solo, la comparación la
  hace el recorrido y no un modelo, y una celda mal formada se manda a corregir una vez antes de avisar.
  Rige al reinstalar el runner (caso 349).
- **`init --force` y `contract` dicen cómo recuperar el contrato.** Adoptar un repositorio que ya traía su
  `AGENTS.md` lo conserva, y `contract` mandaba a un `upgrade` que lo conservaba otra vez. Los dos nombran
  ahora `ops upgrade --force`, avisan que descarta también las demás ediciones conservadas, y dicen que lo
  propio de `AGENTS.md` va antes a `organization/workspace.md` (caso 344).

## [0.105.0] - 2026-10-08

### Cambiado

- **La re-revisión de `autobuild` puede comprar una corrección más.** Review admitía una sola vuelta: si la
  re-revisión traía cualquier bloqueante, la corrida paraba con `review-failed`, también cuando el hallazgo
  era nuevo, estaba comprobado y el revisor había escrito la corrección entera —una frase de un comentario—.
  Ahora un bloqueante así declara `fixable: true` y, con veredicto `con-condiciones`, compra una corrección y
  una revisión más, con tope de dos correcciones por tarea; `bloqueado` y lo que no declara `fixable` paran
  como antes. Las dos correcciones llegan a `done/`. Rige al reinstalar el runner, porque el workflow vive
  ahí (caso 343).

### Corregido

- **`verify` corre los gates de cada raíz declarada que el commit toca.** Buscaba el manifiesto sólo en la raíz
  git del commit, así que en un monorepo con `apps/api/package.json` y `apps/web/package.json` —el ejemplo del
  README, y lo que `/onboard` configura solo— no corría nada y el commit salía en verde con la suite roja. Ahora
  cada `workspaceRoots[]` que contiene algo staged corre sus gates, en el árbol o en la copia del índice, y el
  bloqueo nombra la raíz; sin raíces declaradas se mira el repositorio entero, como antes (caso 333).
- **`governance` vuelve a frenar con `ops/` dentro del repositorio.** El índice de git nombra las rutas desde la
  raíz del repo —`ops/planning/rules/…`— y el guard las comparaba contra `^planning/…`, así que en el layout
  por defecto de `init` un commit que tocaba una regla del sistema, una ADR o el `SKILL.md` de un cargo pasaba
  sin preguntar. Ahora se juzgan relativas a la raíz ops, se lance el commit desde donde se lance; lo que se
  aprueba y se nombra sigue siendo la ruta del índice (caso 334).
- **El staging de integraciones deja de mentir y de callar en tres bordes.** El borrador de un adaptador propio
  lleva el nombre de su proveedor en `provider:` en vez de `jira` fijo; `integration promote` se niega con el
  proveedor deshabilitado, igual que `sync`; y cuando el remoto cambia un draft curado que estaba `ready`, el
  sync lo cuenta aparte —«bajaron de ready a pending»— y `check` lo avisa hasta que se reconcilie (caso 339).
- **`check` rechaza tres cosas que el protocolo ya decía que rechazaba.** El mismo slug dos veces en la cola, en un
  hito o entre dos; una tarea que cita un criterio que su épica no tiene, y que hasta ahora llegaba a `context`
  sin aceptación; y el `tests:` todo n/a escrito como `CN → n/a — razón` o `A → n/a — razón`, las formas
  documentadas, con un commit que toca código. Esta última puede poner en rojo una entrada cerrada desde el
  2026-09-24 con esa forma: la salida es la misma que para el `n/a` pelado, rastrear la prueba o adoptar la
  entrada con `ops adopt` (caso 335).
- **`destructive` juzga un `rm -r` por su destino resuelto.** `rm -rf .`, `cd $X && rm -rf .` con la variable sin
  resolver, y un `rm -r` que nombra la raíz ops, una raíz declarada o un ancestro de cualquiera de ellas frenan
  sin salida, como ya frenaban `/`, `~` y `..` escritos. Un `cd $(mktemp -d)` seguido de `rm -rf .` sigue
  pasando: cae en un temporal (caso 337).
- **`secrets-shell` ve dos formas que la documentación nombra como cubiertas.** `rg -g '.env*' KEY`, con el comodín
  antes del patrón, frena como ya frenaba con el patrón primero; y una identidad declarada en
  `organization/secrets.json` con `~`, como la escribe el molde, frena también cuando se la nombra con `~` o
  con `$HOME` expandido (caso 342).
- **`ops claim` ya no reserva una tarea con una fila pendiente en `HUMAN_ACTIONS.md`.** `context` la salteaba y
  la ofrecía como la siguiente, pero `claim` no miraba la tabla: el runner se quedaba con una tarea que nadie
  podía avanzar y sin poder tomar otra. Ahora se niega nombrando la fila, y pasa cuando está resuelta (caso 336).

## [0.104.1] - 2026-10-08

### Cambiado

- **`ops evidence` ya no presenta las corridas de `verify` como los gates de la tarea.** El registro guarda las
  últimas veinte de la instancia, de cualquier repositorio, y se listaban bajo cada tarea como «los que
  corrieron al commitear». Ahora se encabezan como lo que son, y si todas son anteriores al cierre de la
  tarea, lo dice. Importa sobre todo si reemplazaste `verify` por tu propia puerta: sus corridas no figuran
  ahí. `--json` no cambia (caso 332).

## [0.104.0] - 2026-10-07

### Cambiado

- **Las pruebas pasadas directo a un contenedor se frenan igual que las que van detrás de `sh -c`.** Desde
  0.103.6, `docker run img sh -c 'npx jest'` pide los dos topes del contenedor o la cota del runner, y
  `docker run img npx jest` pasaba sin nada: la forma más corta esquivaba el freno. Ahora el guard de workers
  lee también el comando que el contenedor recibe, y `sudo`, `time` o `timeout <plazo>` delante de un runner
  ya no lo esconden. **Qué hacer:** si lanzás pruebas así, agregá `--memory` y `--cpus` antes de la imagen, o
  `--maxWorkers` al runner. `docker compose run` y `docker exec` no cambian (caso 329).
- **Un comando de `boundedCommands` se reconoce con `sudo`, `time` o `timeout <plazo>` delante** (caso 329).
- **Cada prueba que `autobuild` anota en `tests:` lleva su archivo y su nombre en una forma fija.** Antes la
  redactaba un agente, y `ops evidence` no podía saber dónde terminaba el nombre y empezaba la aclaración: en
  una instancia real marcó `parcial` seis de diez trazas que decían la verdad. Ahora quien verifica da el
  archivo, el nombre de la prueba y la aclaración por separado, y el recorrido arma `archivo › «nombre» —
  aclaración`. `ops evidence` lee esa forma tal cual, y te muestra qué buscó. **Qué hacer:** reinstalá el
  runner. Las entradas ya escritas no cambian, y `check` las sigue aceptando (caso 331).
- **`ops evidence` ya no marca `parcial` una traza por lo que cita después del nombre.** En las entradas
  escritas antes, decide la prueba que la traza nombra —el último tramo detrás de `›`, o lo primero entre
  comillas— y lo demás que cite se dice al lado si no aparece. Es menos estricto que 0.103.6 con una prueba
  inventada: de cuarenta formas probadas marca veinticuatro, avisa en doce y deja pasar cuatro (caso 330).
- **Un enlace simbólico ya no deja escribir fuera de las raíces.** Los dos guards de límites comparaban la
  ruta como está escrita, y un enlace que vive adentro de una raíz y apunta afuera la dejaba cruzar. Ahora
  miran dónde cae de verdad la escritura. Lo que se alcanza por un enlace y sigue siendo del proyecto no
  cambia: la raíz declarada como enlace, un enlace interno, y lo que una línea de `ops line` enlaza desde su
  instancia. **Qué hacer:** si escribís a propósito por un enlace hacia afuera —un paquete enlazado con `npm
  link`, una carpeta compartida—, declarala en `writableOutsideRoots` (caso 317).
- **Un `mktemp -d` que puede fallar ya no se da por resuelto si lo que sigue no depende de él.** Con
  `T=$(mktemp -d -p <carpeta>); cd $T; …`, si `mktemp` falla el `cd` va a tu carpeta personal y lo que sigue
  se escribe ahí. Ahora el guard de límites lo frena cuando la carpeta no existe, no se puede escribir o la
  plantilla no trae tres `X`. **Qué hacer:** nada si uniste los pasos con `&&`, si la carpeta ya está o si la
  creás antes con `mkdir -p` y su ruta completa. Si la crea otra cosa —`install -d`, `cp -r`—, uní los pasos
  con `&&` (caso 327).
- **R9 pide comparar antes y después cuando cambia quién hace algo, o cómo.** Mover un paso de lugar o
  reemplazar una implementación es una quita aunque el resultado «sea el mismo», y esa igualdad se sostiene
  con la salida de antes y la de después sobre el mismo caso, como condición de la aceptación. Es un párrafo
  más en una regla que ya tenías: el bloque que carga cada agente pasa de 51,7 a 52,3 KB (caso 324).

## [0.103.6] - 2026-10-07

### Cambiado

- **El cierre de una corrida lee lo que `check` devuelve, y te muestra sus avisos.** Hasta ahora lo contestaba
  un agente completo, que corría el comando y contaba qué había visto. Ahora lo corre el agente liviano y el
  recorrido decide con el resultado del comando. El agente que carga tus reglas entra sólo si `check` sale en
  rojo y repara el estado derivado; si quedó en verde lo vuelve a decir el comando, y lo reparado queda
  commiteado cuando tu proyecto commitea por tarea. Lo que `check` avisa sin fallar —una aprobación que
  quedó sin borrar, por ejemplo— llega al registro de la corrida, que antes lo tiraba. **Qué hacer:**
  reinstalá el runner (`make install-claude`); si no, `autobuild` te lo va a pedir al arrancar (caso 310).

### Corregido

- **Una regla que escribís después de instalar el runner rige también en el chat.** Dentro de `autobuild` ya
  regía; en una sesión de chat seguía valiendo la regla anterior hasta que alguien reinstalaba, y sólo lo
  decía una advertencia. Ahora, mientras dure el desfase, la sesión de Claude recibe en cada mensaje cuáles
  reglas rigen y no tiene cargadas, con el pedido de leerlas. Reinstalar sigue siendo la forma de que carguen solas:
  `make install-claude` y una sesión nueva. **Qué hacer:** reinstalá el runner para que llegue el gancho (caso 315).
- **Un cierre que no se puede reparar deja una fila para una persona.** Si `check` quedaba en rojo por algo
  que no es estado derivado, la corrida paraba sin escribir nada: cerrada la sesión, el planning seguía en
  rojo sin decir por qué. Ahora queda una fila pendiente en `HUMAN_ACTIONS.md`, commiteada (caso 310).
- **Una copia hecha con `mktemp -d -p <carpeta>` ya no frena lo que le sigue.** El guard de límites sólo
  reconocía el `mktemp` del temporal del sistema; con una carpeta propia dejaba el `cd` sin resolver y
  bloqueaba las escrituras siguientes. Ahora lo juzga por dónde cae esa carpeta, en las formas en que eso se
  sabe sin adivinar: un solo `-p` o `--tmpdir=`. Las demás siguen frenando (caso 311).
- **Un `grep` con una comilla escapada ya no se frena por lo que busca.** `grep -n "\"test\|jest" package.json`
  se frenaba como si ejecutara `jest`: los guards cortaban la cadena en la comilla escapada y leían el resto
  como orden. Ahora leen la comilla escapada y el comentario como el shell. Hacia el otro lado se cierran dos
  formas que pasaban sin que nadie las viera: lo que queda entre dos comillas escapadas sueltas, y el renglón
  que sigue a un comentario con una comilla suelta (caso 312).
- **Un `mktemp` que no crea donde el guard creía ya no deja pasar lo que le sigue.** Sin `-d`, con `TMPDIR`
  asignado en el mismo comando, o con una plantilla sin ruta, el guard de límites juzgaba el temporal del
  sistema y la escritura caía en otra carpeta. Ahora el `cd` queda sin resolver y frena. `T=$(mktemp -d) &&
  cd $T` y `mktemp -d -p <carpeta>` siguen como estaban (caso 318).
- **Las pruebas lanzadas en un contenedor con tope de memoria y de CPU ya no se frenan.** Un `docker run` o
  `podman run` con `--memory` (o `-m`) y `--cpus` entre sus opciones está tan acotado como un comando de
  `boundedCommands`, y el guard de workers lo frenaba igual. Hacen falta los dos topes, con un número mayor
  que cero y antes de la imagen. Y se cierra una forma que pasaba con un comando declarado: el runner puesto
  en una sustitución, como en `scripts/run.sh true $(npx jest)`, lo ejecuta la máquina y ahora frena (caso 313).
- **`ops evidence` contrasta las trazas que escribe una corrida.** Sólo buscaba las de una palabra, y una
  traza con el archivo y el nombre del caso salía «inbuscable»: no se contrastaba ninguna entrada de
  `autobuild`. Ahora busca el archivo y, dentro de él, el caso que venga entre comillas o con `›`. Hay un
  veredicto nuevo, `parcial`, para el archivo que existe sin ese caso. Y deja de buscar en `planning/`, donde
  una prueba inventada se encontraba en la propia entrada que la nombraba (caso 316).
- **La entrada de `done/` ya no trae la ruta de tu máquina en lo que la revisión abrió.** El campo `review` va
  textual, y lo que el revisor declara haber abierto venía con rutas absolutas: el nombre de usuario y las
  carpetas de quien corrió. Ahora cada ruta que cuelga de una raíz del proyecto llega con el nombre de esa raíz
  delante. Lo que el revisor escribió en prosa no se toca. **Qué hacer:** reinstalá el runner (caso 321).
- **Cuando el planning queda en rojo al cerrar un turno, el freno dice qué se puede tocar.** El guard mostraba
  la salida de `check` y nada más, y la sesión la leía como algo a dejar en verde: en la entrada de otra
  tarea borró la condición que la traza cubría, porque era lo que rompía el formato. Ahora dice que se repara
  una entrada de `done/` sólo en lo que se deduce de otra cosa, sin borrar nada, y que lo que una tarea cerrada
  afirma no se cambia para pasar (caso 322).
- **El costo de los pasos de escritura que decía 0.103.5 valía sólo para una instancia sin reglas propias.**
  Decía «unos 60.000 tokens»; en una instancia con sus instrucciones y sus reglas son entre 76.000 y 86.000,
  un 10 % a 18 % menos que el agente completo. La entrada de 0.103.5 quedó corregida (caso 325).
- **Quien cierra una tarea no toca la entrada de otra.** Si `check` marca una entrada que no escribió, la deja
  para el cierre de la corrida (caso 310).

## [0.103.5] - 2026-10-07

### Agregado

- **Un guard frena las firmas de IA en lo que se publica.** `ai-signature` frena un commit, un merge o un tag,
  o un `gh` de pull request, issue o release, cuyo mensaje termina con la firma de un asistente:
  `Co-Authored-By: Claude Opus…`, «Generated with…». Hasta ahora lo prohibía R8 y sólo lo cumplía quien la
  había leído. Un coautor humano pasa, se llame como se llame, y una firma citada en la prosa también. **Qué
  hacer:** nada si no querés firmas. Si tu empresa quiere dejar constancia de lo que hace un agente, declaralo
  con `runner.allowAiSignature: true` en `ops.config.json`; para una vez, pedilo en el chat nombrando la firma
  (caso 300).
- **`autobuild` no corre con un adaptador que quedó atrás del motor.** Después de `upgrade` hay que reinstalar
  el runner, y hasta hacerlo el recorrido y sus agentes son los de la versión anterior: la corrida andaba, con
  lo viejo, y `doctor` sólo lo avisaba. Ahora para en el arranque con `adapter-stale` y el comando que lo
  arregla. Vale desde la próxima actualización: la copia que tenés instalada hoy todavía no sabe preguntarlo,
  así que **esta vez reinstalá a mano** con `make install-claude` (caso 308).
- **El proyecto puede declarar su huso horario.** `timeZone` en `ops.config.json`, en forma IANA
  —`"America/Bogota"`—, decide qué día es «hoy» para la entrada de `done/`, un reclamo o un vencimiento. Sin
  declararlo sigue siendo UTC, y lo que se cierra a la noche al oeste de Greenwich queda fechado al día
  siguiente (caso 303).

### Corregido

- **Lo que `autobuild` escribe en planning vuelve a seguir las reglas de tu empresa.** En 0.103.4 el WIP, la
  entrada de `done/`, la compuerta del hito y los commits de planning los hacía un agente que no cargaba tus
  instrucciones ni tus reglas. Dos cosas se notaron: los commits de planning salían en inglés aunque tu regla
  pidiera otro idioma u otro formato, y la entrada de `done/` copiaba el relato de Build —«sin commit ni push»
  al lado del commit, con la ruta de tu máquina—. Ahora ese agente carga todo lo que escribiste, y lo tuyo gana
  sobre lo que el recorrido dicta. Cuesta más que en 0.103.4 y menos que antes: cada uno de esos pasos arranca
  entre 54.000 y 65.000 tokens en una instancia sin reglas propias y entre 76.000 y 86.000 en una con las suyas
  —el agente completo, entre 92.000 y 97.000—, porque ese agente también puede usar los skills del proyecto.
  **Qué hacer:** reinstalá el runner (`make install-claude`) para que llegue el agente nuevo. Las entradas y los
  commits que escribió 0.103.4 no se corrigen solos (casos 301 y 302).
- **La entrada de `done/` se valida antes de commitearse.** Si su formato no pasa `ops check`, quien la
  escribe la corrige en ese momento y no después, con la entrada ya commiteada (caso 301).
- **«Sólo la próxima tarea» ahora para después de una.** El agente que reclama recibía el pedido pero no
  cuántas tareas había cerrado la corrida, y «la próxima» era siempre la que le tocaba: una corrida real cerró
  dos y frenó en la tercera. Ahora lo sabe. `--max N` sigue siendo la forma que no depende de una lectura
  (caso 304).
- **Un WIP con los pasos mal escritos se detecta antes de construir.** El motor cuenta los pasos por su forma,
  y uno escrito como lista sin numerar era un plan vacío que nadie notaba hasta el cierre del turno. Ahora el
  recorrido compara lo que el motor cuenta contra el plan y para con `wip-malformed` si no coinciden (caso
  305).
- **Un caso borde con su prueba en rojo ya no frena por cómo se escribió el nombre.** `edge-unproven` saltaba
  cuando el borde y el rojo nombraban la misma prueba con otra ruta —con el `describe` en el medio, o dos
  casos juntos en un rojo—. Ahora se comparan el nombre del caso y el archivo (caso 306).
- **Los pasos de un WIP se cuentan en el plan y no en todo el archivo.** Una lista numerada con casilla en
  «Decisiones tomadas» sumaba como pasos del plan (caso 309).
- **Cada tarea queda en su rama.** La tarea siguiente del mismo servicio se commiteaba sobre la rama de la
  anterior, y las dos terminaban en un solo PR. Ahora corta la suya desde ahí. Una rama que elegiste vos y no
  es de ninguna tarea se respeta como antes (caso 307).

## [0.103.4] - 2026-10-06

### Corregido

- **Pedirle a `autobuild` «sólo esta tarea» con palabras vuelve a terminar ahí.** En 0.103.3 el agente que
  reclama la tarea siguiente no recibía el pedido, así que la tomaba: la corrida la planificaba, frenaba en
  Build y dejaba un reclamo y una fila pendiente sobre una tarea que habías excluido. Ahora el reclamo recibe
  el pedido y, si la excluye, la corrida termina como cuando se queda sin tareas. `--max N` sigue siendo la
  forma que no depende de que un agente lo interprete (caso 297).
- **Un `cd` con el destino entre comillas ya no frena la escritura que le sigue.** `cd "/ruta" && sed -i …
  archivo` se bloqueaba por «destino que no se puede resolver» aunque la ruta estuviera escrita entera. Ahora
  se resuelve y se juzga igual que sin comillas: adentro de tus raíces pasa y afuera se frena (caso 298).

### Cambiado

- **`autobuild` gasta menos en escribir planning.** El WIP, la entrada de `done/`, la compuerta del hito y
  los commits de planning los hacía un agente que cargaba las instrucciones enteras del proyecto para
  transcribir lo que el recorrido ya había decidido. Ahora los hacen agentes livianos: `cauce-clerk` commitea
  y uno nuevo, `cauce-scribe`, escribe. Medido en corridas reales, el WIP pasó de unos 87.000 tokens a unos
  16.000, `done` de unos 85.000 a 42.000 y el commit de planning de 66.000 a 4.000. Los pasos que juzgan
  —planificar, construir, revisar, verificar— no cambian. **Qué hacer:** nada; `cauce-scribe` llega con el
  runner de Claude al actualizar (caso 295).
- **Los commits de `autobuild` ya no arrancan frenados.** El agente que commiteaba solía stagear y commitear
  en una línea, el guard lo frenaba y lo repetía en dos. Ahora el recorrido se lo avisa de antemano. El guard
  no cambia (caso 299).

## [0.103.3] - 2026-10-06

### Agregado

- **`/autobuild --max N` cierra N tareas y termina.** Pedirle a una corrida «sólo esta tarea» con palabras la
  cerraba bien y después salía con `claim-stuck`, como si hubiera fallado: el recorrido seguía con la tarea
  siguiente y el agente que iba a tomarla se negaba. Ahora el tope se puede escribir —`/autobuild --max 1
  sin push ni PR`—, y si igual lo pedís con palabras, el agente que se niega a tomar la siguiente termina la
  corrida en vez de romperla (caso 293).

### Cambiado

- **Los pasos de oficina de `autobuild` gastan veinte veces menos.** Leer la cola, reclamar, soltar o armar el
  árbol de una tarea es correr un comando y devolver su salida, y cada uno cargaba las instrucciones enteras
  del proyecto: unos 73.000 tokens para trabajar unos segundos. Ahora los corre un agente liviano,
  `cauce-clerk`, que arranca en unos 3.500. En una corrida real, nueve de veinticuatro agentes eran pasos así.
  El contrato del proyecto entra en lo mismo: lo deriva `ops contract`, que existía desde 0.91.0, en vez de un
  agente que leía cuatro documentos y los transcribía. Medido en una tarea chica, la corrida entera gasta
  cerca de una cuarta parte menos. **Qué hacer:** nada; el agente llega con el runner de Claude al actualizar,
  en `.claude/agents/`. Los guards lo contienen igual que a cualquier otro (casos 295 y 154).
- **Borrar una prueba por shell se frena, igual que con la herramienta de edición.** Un `rm` o un `git rm`
  sobre una prueba pasaba sin que ningún guard opinara, mientras el mismo borrado con la herramienta de
  archivos pedía a una persona. Ahora se frena igual, también con comodín y para la carpeta entera. Sólo una
  prueba ya commiteada, dentro de tu proyecto: la que el agente escribió en la misma tarea, o una copia
  desechable, no. **Qué cambia para vos:** una tarea que pide retirar una prueba va a parar hasta que lo
  apruebes —nombrándola en el chat, o con la línea en `.ops-approval`— (caso 294).

## [0.103.2] - 2026-10-06

### Agregado

- **`boundedCommands`: los comandos de tu proyecto que ya corren con tope de recursos.** El guard de workers
  frenaba un `jest` o un `vitest` sin cota aunque corriera dentro de un contenedor con memoria y CPU
  acotadas, y costaba un reintento cada vez. **Qué hacer:** si corrés las pruebas con un script así, declaralo
  en `ops.config.json` —`"boundedCommands": ["scripts/run-in-container.sh"]`— y el guard deja de opinar sobre
  lo que ese comando lanza. Sin declararlo no cambia nada (caso 291).

### Cambiado

- **El pedido de una corrida va en el mensaje que la lanza.** Claude Code puede relevarle a cada agente de un
  recorrido tu último mensaje del chat, por encima del texto del recorrido: si la sesión lanza un `autobuild`
  por su cuenta después de un mensaje sobre otra cosa, ése es el que reciben. Las instrucciones de la
  instancia ahora lo dicen. **Qué hacer:** lanzá con `/autobuild <lo que querés>` (caso 292).

### Corregido

- **Toda corrida de `autobuild` que frena commitea el estado de planning.** Desde 0.101.0 lo hacía sólo en las
  paradas que registran una fila; una que frenaba por otra razón —Verify, QA, Commit— dejaba sin commitear la
  fila, el reclamo y lo anotado (caso 289).
- **Una condición marcada `(fuera de verify: …)` ya no frena el recorrido.** Si Verify la devolvía igual como
  sin cubrir, la corrida paraba con `verify-hollow` por una condición que la aceptación había apartado
  (caso 290).

## [0.103.1] - 2026-10-06

### Cambiado

- **El diálogo de confirmación es sólo para lo que te toca decidir, y sólo en tu conversación.** Desde 0.103.0
  un guard abría el diálogo de Claude Code para todo lo que frenaba, también dentro de un recorrido: un
  `autobuild` quedó 10 y 24 minutos esperando que alguien aprobara dos `grep`. Ahora lo que el agente puede
  corregir solo —el mensaje del guard dice cómo— se bloquea sin preguntar, y la llamada de un subagente o de
  un recorrido recibe el bloqueo en vez del diálogo. El diálogo queda para un merge, un deploy, una
  credencial o un borrado que salgan de tu conversación. **Qué cambia para vos:** menos diálogos, y ninguno
  que te espere dentro de un recorrido; lo corregible lo seguís pudiendo dejar pasar confirmándolo con tus
  palabras o con la línea en `.ops-approval` (casos 285 y 282).

### Corregido

- **Una orden que se cumple después ya no se pierde por el aviso de una tarea de fondo.** «Cuando el CI quede
  verde mergeá el #7» se frenaba al llegar el aviso de que el CI había terminado, porque ese aviso entra como
  un mensaje que no escribiste vos. Ahora lo que pediste nombrándolo sigue valiendo hasta tu próximo mensaje.
  El aviso no agrega nada: lo que dice no ordena, y «dale con todos», sin PRs nombrados, no lo cruza
  (caso 281).
- **Buscar en el código el nombre de un archivo de entorno ya no se frena como leer una credencial.** `grep -rn
  "process\.env\." src` o `grep "\.env\.schema"` se bloqueaban como si abrieran un `.env`: el guard leía el
  patrón de la búsqueda como un archivo. Era el freno más frecuente dentro de un recorrido. Leer la
  credencial se sigue frenando (caso 287).
- **Un agente puede escribir en el scratchpad de su sesión.** Las sondas y los scripts de paso que Claude Code
  guarda ahí se frenaban por quedar fuera de las raíces declaradas. **Qué hacer:** si habías declarado esa
  carpeta a mano en `ops.config.local.json`, ya no hace falta (caso 288).
- **Leer la configuración de las pruebas ya no se toma por correrlas.** `grep -n 'jest' package.json`, o un
  patrón entre comillas seguido de `jest.config.*`, frenaban como un `jest` sin cota de workers (caso 286).

## [0.103.0] - 2026-10-05

### Cambiado

- **Un commit en la rama viva se frena, con cualquier runner.** Hasta ahora sólo el recorrido de Claude cortaba
  una rama antes de commitear; con Codex y Gemini `autobuild` commiteaba en `main`. Ahora un guard frena todo
  commit que caiga en `main`, `master` o la rama por defecto del remoto y le dice al agente que corte una rama
  y reintente, sin preguntarle a nadie. Pasa si lo pedís en el chat nombrando la rama —«commiteá en main»—, y
  «commiteá» a secas termina en una rama. **Qué hacer:** si en tu proyecto se commitea en la rama viva,
  declaralo con `runner.commitToLiveBranch: true` en `ops.config.json`. El primer commit de un repositorio
  nuevo, un repositorio que la instancia no declara y CI no se frenan (caso 284).
- **En modo `auto` de Claude Code, lo que un guard frena lo confirma el diálogo.** Desde 0.101.0 en `auto` el
  guard bloqueaba en seco y había que confirmarlo por chat. Medido sin nadie al teclado, el diálogo espera a
  una persona igual que en los otros modos, así que vuelve a usarse (caso 257).
- **Pedir un merge ya no depende de qué palabras uses.** 0.102.0 reconocía la orden por una lista de verbos,
  y «hacé el merge del #12», «integralo» o «dale con todos» se frenaban igual. Ahora el guard no busca
  ninguna frase: si tu mensaje nombra PRs pasan ésos y ningún otro, y si no nombra ninguno pasan los merges
  de ese turno. Sigue sin pasar lo que negás o exceptuás —«no el #3», «todos menos el #3»—, y nada si el
  mensaje pregunta, arranca frenando o trae una negación que no dice sobre qué PR. **Qué cambia para vos:**
  en una conversación directa el guard ya no frena un merge por no haberlo pedido con cierta palabra; lo que
  contiene es al agente dentro de un recorrido o un subagente, que siguen frenados.

### Corregido

- **`check` ya no se cuelga dentro del sandbox de Codex.** En cuanto `done/` citaba un commit, `ops check` no
  terminaba nunca y la tarea se cerraba sin poder validarla. Pasaba desde 0.100.0 (caso 283).

## [0.102.0] - 2026-10-05

### Cambiado

- **Un merge que pediste en el chat ya no pide confirmación.** «Mergeá el #12 y el #14» pasa esos dos
  `gh pr merge` y ningún otro; «mergealos todos», sin números, pasa los de ese turno. Vale en todos los modos
  de Claude Code, también donde antes se abría el diálogo. No pasa si el mensaje pregunta, niega o arranca
  frenando, ni desde un subagente. Antes ninguna orden alcanzaba: había que pegar el comando entero, y desde
  0.101.0 en modo `auto` cada merge pedía su propio mensaje (caso 280).

- **La confirmación de un merge vale para el PR, no para la línea de comando.** Lo que se aprueba —por chat o
  en `.ops-approval`— es `gh pr merge <PR> --repo <repo>`: el mismo PR con otras banderas ya no vuelve a
  frenar. `--admin` se aprueba aparte. **Qué hacer:** una línea de `gh pr merge` pegada antes en
  `.ops-approval` con sus banderas deja de valer; el bloqueo dice la nueva.

## [0.101.0] - 2026-10-05

### Cambiado

- **Una condición que se cumple al escribir ya no rechaza el plan.** La crítica del plan dice ahora, por cada
  hallazgo bloqueante, si corregirlo cambia el plan o si alcanza con que quien construye lo cumpla. Sólo lo
  primero pide corregir el plan y puede terminar en `plan-rejected`. Lo segundo viaja como condición al WIP,
  a Build y a Review, que comprueba sobre el diff que se cumplió. Antes cualquier bloqueante de la segunda
  crítica paraba el recorrido y dejaba la tarea fuera de la cola, aunque fueran dos nombres por cambiar
  (caso 248).

- **`autobuild` ya no commitea en `main`: corta una rama por tarea.** Si el repo del servicio está en una rama
  viva —`main`, `master` o la rama por defecto del remoto—, el paso de Commit corta `<tipo>/<slug>` y commitea
  ahí, sin preguntar. Si el repo ya está en otra rama, commitea en ésa. La entrada de `done/` dice en qué rama
  quedó. **Qué hacer:** nada, salvo que tu proyecto commitee en `main` a propósito; en ese caso declaralo con
  `"commitToLiveBranch": true` en `runner` de `ops.config.json` (caso 251).

- **Build ya no le pregunta a una persona por todo lo que nota.** Lo que nota y no arregla tiene ahora tres
  destinos: una fila en `HUMAN_ACTIONS.md` sólo si es una decisión que cambia el rumbo, el gasto, una obligación
  externa o el riesgo; `inbox/deuda/` si es trabajo identificado que no es de la tarea; y `decisions:` de la
  entrada de `done/` si no hay nada que decidir ni que hacer. Cada destino recibe hasta tres por tarea y el resto
  queda contado en el cierre. Antes todo iba a una fila pendiente (caso 250).

- **Lo que le pedís a `autobuild` al lanzarlo ahora llega.** El texto que se pasa como argumento —o su campo
  `note`— viaja a Plan, Build y Commit como pedido para toda la corrida. No reemplaza la aceptación ni las
  reglas: si las contradice, mandan ellas. Antes se descartaba sin avisar (caso 252).
- **El estado de planning va a una sola rama de trabajo.** `PROTOCOL.md` y `AGENTS.md` dicen ahora dónde se
  commitea cuando la rama viva no admite push: en una rama de trabajo que se acumula, con un solo PR, y no en
  una rama nueva por cada tarea cerrada (caso 253).

- **QA corre las mutaciones que el build declaró y no corrió.** Hasta tres por tarea, en una copia desechable.
  Lo que dio cada una queda en `qa:` de la entrada de `done/`: roja, sobrevivió o sin correr. Ninguna frena la
  entrega (caso 256).
- **Los guards de commit ya no opinan sobre repositorios ajenos.** `verify`, gobernanza, dependencias, la
  pasada de comentarios y la regla de `git add -A` actúan sólo sobre la carpeta de la sesión, la raíz ops y las raíces declaradas en
  `workspaceRoots`. Un commit en otro repositorio pasa sin frenarse y sin que se le corra su suite (caso 258).

- **Los guards de shell leen mejor qué es orden y a dónde apunta.** El texto que `sed`, `grep`, `rg`, `echo`,
  `printf` o `jq` sólo leen ya no se toma por comando: un `sed` cuyo patrón nombraba `git commit -a` se frenaba
  (caso 259). Y un `cd` o un `git -C` a una variable que el mismo comando asigna —`T=$(mktemp -d) && cd $T`— se
  resuelve en vez de bloquearse por «destino que no se puede resolver», que frenaba el trabajo en una copia
  desechable (caso 261).

- **`autobuild` commitea el estado de planning al cerrar cada tarea.** La cola, `done/`, las acciones humanas y
  el INBOX quedaban escritos y sin commitear. Ahora se commitean después del cierre, en la rama en la que esté
  la instancia; si ésa es una rama viva, en la rama de trabajo de planning que ya exista, o en `work/planning`
  si no hay ninguna. El checkpoint del hito se commitea igual. **Qué hacer:** nada si ya usás `commitPerTask`;
  con `commitPerTask` apagado no cambia (casos 266 y 271).
- **En una línea de trabajo, `autobuild` construye cada tarea en su propio árbol.** Las líneas comparten por
  enlace el mismo checkout del producto, y cortar la rama ahí lo dejaba parado en la tarea de una para todas
  las demás. Ahora, en una línea, el recorrido arma el árbol con `ops worktree`, trabaja ahí, renombra la rama
  a `<tipo>/<slug>` al commitear y saca el árbol; el checkout compartido no se toca. Fuera de una línea no
  cambia nada: se sigue trabajando en la carpeta que está. `ops worktree` deja el árbol al lado del repositorio
  tal como lo ve la sesión, y su `--json` trae `work`, la ruta donde trabajar (caso 274). Y la puerta que
  declara la raíz se corre contra ese árbol y no contra el checkout compartido, que no tiene la tarea (caso 276).
- **Review ya no manda al INBOX lo que revisó y dio bien.** Cada hallazgo que no bloquea dice si propone algo.
  Lo que propone va a `inbox/propuestas/` como antes; la constancia queda en `review:` de la entrada de `done/`
  (caso 262).

### Corregido

- **Una corrida de `autobuild` que frena ya no deja la instancia sucia.** La fila de `HUMAN_ACTIONS.md` y lo
  que la corrida había anotado en la cola quedaban sin commitear. Ahora la parada los commitea como
  `chore(planning): block <tarea>`, con el mismo interruptor `commitPerTask` y la misma regla de ramas que el
  cierre (caso 279).
- **Un plan frenado por una decisión que falta ya no se registra como «nadie pudo escribir un plan».** Esa
  fila mandaba a partir la tarea cuando lo que faltaba era contestar una pregunta, y podía quedar duplicada.
  Ahora dice qué frenó la crítica y pide resolverlo, en una sola fila (caso 278).
- **En una línea, la entrada de `done/` nombra la rama del commit y el servicio.** Citaba la rama provisional
  de la tarea, que ya no existe al cerrar, y la ruta del árbol en la máquina (caso 277).
- **Instalar un runner ya no ensucia el manifiesto.** `.cauce/manifest.json` guardaba el hash de cada recorrido
  con la ruta de la instancia adentro, así que cambiaba con sólo instalar el runner en un clon en otra carpeta
  o en una línea. Ahora el hash no depende de la ruta. Un manifiesto escrito por una versión anterior se sigue
  reconociendo: no vas a ver tus recorridos como editados. Y una línea recién armada nace con el árbol limpio:
  el enlace a `node_modules` queda ignorado (caso 275).
- **`check` encuentra el repositorio de un commit desde la carpeta de una línea.** Ahí el producto es un enlace
  al original, y el commit de una entrada de `done/` quedaba «sin comprobar» con el repositorio a la vista
  (caso 273).
- **`check` ya no avisa por el commit de planning.** En una instancia embebida, el commit que sólo toca
  `planning/` contaba como trabajo que ninguna entrada de `done/` nombra (OPS-001). Ya no cuenta; el que toca
  además código, sí (caso 270).
- **La deuda que anota Build llega entera al INBOX.** Se recortaba a 240 caracteres y terminaba en «…», sin que
  el resto quedara en ningún lado (caso 272).
- **Al retomar una corrida, Review vuelve a comprobar las condiciones de la crítica.** Las condiciones con las
  que la crítica aprobó el plan viajaban en memoria, así que una corrida retomada desde el WIP llegaba a Review
  sin ellas. Ahora Review recibe el archivo del WIP donde quedaron (caso 267).
- **`ops line` lleva el producto cuando la raíz es la carpeta de sesión.** Con `workspaceRoots: [".."]` y un
  repositorio por servicio adentro, la carpeta de la línea quedaba sólo con el worktree de la instancia. Ahora
  se enlazan los hijos de esa carpeta, salvo la configuración de los runners. Y con esa misma forma de raíz,
  `ops worktree` ya encuentra el repositorio del servicio, también en la carpeta original (caso 263).
- **En el modo `plan` de Claude Code los guards dejaban pasar lo que frenaban.** Un guard que pide confirmación
  abre el diálogo de Claude Code, y en `plan` ese diálogo no aparece: la acción corre igual, sin ningún
  mensaje. Medido con la lectura de un `.env`. Ahora el diálogo se usa sólo donde está medido que una persona
  lo contesta —`default`, `acceptEdits` y `bypassPermissions`—, y en cualquier otro modo el guard bloquea y
  pide la confirmación por chat. `auto` queda de ese lado sin que esté establecido si hace falta: en una
  sesión el diálogo apareció y en otra no consta quién aprobó (casos 257 y 268).
- **El modo plan de Claude Code puede escribir su plan.** El límite de escritura frenaba el archivo que el
  runner guarda en `~/.claude/plans/`, por estar fuera de las raíces declaradas (caso 269).
- **`plan-first` ya no frena el Build de una sesión sidecar recién instalada.** `autobuild` escribe su plan con
  el id de la instancia, y el guard, parado en la carpeta de la sesión o en el repo del producto, deducía otro y
  bloqueaba la primera edición con «hay plan escrito, pero bajo otro id». Sin `CAUCE_RUNNER` declarado, ahora
  vale el plan de la instancia; con la variable, el id sigue siendo el que declara (caso 260).
- **`ops worktree` encuentra el repositorio cuando el servicio se llama como su raíz.** Con una raíz por
  repositorio —`platform → ../platform`— y tareas `(service: platform)`, decía que el repositorio no existía.
  Ahora un servicio resuelve también por el nombre de la raíz o por el último tramo de su ruta, y `check`
  encuentra igual el repositorio que nombra un `commit:` de `done/` (caso 254).
- **Lo que la crítica anota sin bloquear ya no se pierde.** Va a la corrección del plan, con la indicación de
  no ampliarlo por eso, y queda en las decisiones del WIP. Antes no salía de la crítica, así que una decisión
  que la crítica ya había tomado no le llegaba a nadie (caso 249).

## [0.100.0] - 2026-10-01

### Agregado

- **Dos líneas de trabajo ya no se toman las tareas entre sí.** Un hito se declara de una línea con
  `line: <nombre>` en el frontmatter de su `backlog/<hito>.md`. El árbol de esa línea —el que arma `ops line`,
  en la rama `line/<nombre>`— sólo ve esos hitos, y el principal sólo los que no son de ninguna. Así el autobuild
  de una línea no construye la tarea de la otra aunque las dos colas terminen en el mismo archivo. `ops context`
  lo muestra en la línea `LINE`. Además `context` y `claim` leen los reclamos de los otros worktrees de la
  instancia: antes dos líneas podían reclamar la misma tarea sin enterarse. Y `autobuild` clasifica sólo el hito
  de la tarea que va a construir: clasificar la cola entera escribía en los hitos de la otra línea.
- **La pasada de comentarios de R11 puede tener puerta.** Declarando `"comments": {}` en `ops.config.json`, el
  guard `comments` frena una vez cada commit que agrega comentarios y los lista con las tres preguntas de R11.
  El commit entra repitiéndolo con `CAUCE_COMMENTS_REVIEWED=<token>`, y el token cambia si cambia cualquier
  comentario. Con `"language": "es"` o `"en"` frena un comentario en el otro idioma, y con `"inlineMax": 2`
  uno de más de dos líneas dentro de una unidad; esos dos se aprueban como cualquier gate. Sin la clave no
  corre: cuesta una vuelta por commit, y esa cuenta la hace cada proyecto.
- **Una instancia nueva trae su CI.** `init` deja `.github/workflows/ci.yml`, que en cada PR corre `check`, la
  sintaxis de los hooks y las pruebas de tus guards propios de `automatization/tests/`. Es tuyo desde ese
  momento: `upgrade` no lo toca, así que sumale lo que tu instancia necesite. En modo embedded no se crea,
  porque el repositorio ya tiene el suyo. Una instancia que ya existe puede copiarlo de
  `node_modules/@ingeniomaps/cauce/template/.github/workflows/ci.yml`.
- **`check --skip-roots`, para correr sin los repositorios de producto.** Un CI clona la instancia sola, y una
  raíz de `ops.config.json` que no está era un error. Con la bandera se saltea y se avisa cuál quedó sin
  comprobar.
- **`check` compila tus workflows de `workflows/`.** Uno con un error de sintaxis se instalaba igual y fallaba
  recién al invocarlo; ahora `check` lo frena y nombra el archivo.
- **`check` avisa una regla propia que no está leyendo.** Una regla escrita como `## Regla 8` o `## Rule 10 -`
  no tiene la forma que `check` lee, así que no cuenta como definida ni choca con la del sistema que contradice.
  Ahora se avisa, con la forma correcta: `## P8 — título`.
- **El molde trae dónde escribir lo que cada instancia terminaba inventando.**
  - `organization/workspace.md` suma una tabla de credenciales (servicio, variable, para qué, quién la carga y
    dónde) y una de guards propios (qué frena y qué prueba lo sostiene).
  - `planning/QA.md` dice, paso por paso, cómo se prueba una entrega en la fase QA: comprobar que el cambio
    está desplegado, no gastar datos de producción, devolver lo que se tocó y separar lo que ya fallaba.
    `upgrade` lo crea si no lo tenés y no pisa el tuyo.
  - `organization/README.md` recomienda `conventions/` y `runbooks/`.

  Las tablas de `workspace.md` sólo llegan a una instancia nueva; si querés las tuyas, copialas del molde.
- **`check` comprueba que el commit de cada tarea cerrada exista.** El `commit:` de una entrada de `done/` se
  validaba por su forma, y un hash inventado pasaba. Ahora `check` busca cada sha en su repositorio —el que
  nombra la traza, `(api@main)`, dentro de tus raíces— y avisa el que no está. Lo que no puede mirar porque el
  repositorio no está clonado en esa máquina lo dice en una sola línea. Es aviso y no error: un commit de una
  rama que no trajiste también falta.
- **Un rojo que ya estaba se declara una vez, y no se aprueba en cada commit.** Si un repositorio arrastra un
  lint en rojo o una suite rota en otro módulo, escribilo en `planning/gate-known-red` como
  `<raíz>: <gate> — <motivo>` —por ejemplo `api: lint — once errores de ESLint 9 heredados`—. `verify` deja
  pasar el commit aunque ese gate falle, un gate en rojo que no declaraste sigue frenando, y `check` te
  recuerda cada declaración mientras exista. Una línea mal escrita es error.
- **jest y vitest sin cota de workers se frenan.** Un agente que corre `npx jest` o `vitest` sin
  `--maxWorkers` lanza tantos procesos como núcleos, y dos a la vez tiran la máquina. El guard `test-workers`
  lo frena hasta que lleve una cota —`--maxWorkers=2`, un porcentaje, `--runInBand`/`-i` en jest o
  `--no-file-parallelism` en vitest— o una persona lo apruebe. Mira la llamada directa: lo que corre
  `npm test` lo cota tu script.
- **Editar por MCP la descripción de una tarjeta de Jira en markdown se frena.** El markdown reemplaza la
  descripción entera y pierde menciones, tablas y casillas. En Claude Code, el guard `jira-adf` frena
  `editJiraIssue` de cualquier servidor MCP cuando cambia la descripción sin ADF, y se aprueba como el resto
  si de verdad querés reemplazarla por texto. Llega con `automation install`.
- **Tus workflows propios reciben los mismos marcadores que los de Cauce.** Escribilos en `workflows/` de la
  instancia y `automation install` los deja al lado de los de Cauce con `{{OPS_ROOT}}`, `{{OPS_DIR}}` y los
  `{{INCLUDE:…}}` resueltos, sin `sed` después. Uno que se llame como un workflow de Cauce no se instala, y
  si borrás la fuente, la copia instalada se retira.
- **Jira se puede leer con un agente, sin un token en disco.** Con `"transport": "agent"` en
  `integrations/jira/config.json`, la integración no pide credenciales: el agente lee con el MCP de Atlassian
  y la cuenta de quien está en la sesión, y entrega lo leído con `integration sync . jira --payload <archivo>`.
  El procedimiento está en `integrations/jira/README.md`. El payload declara si trajo todo; con
  `complete: false` no se borra nada de lo que no vino.
- **Mergear, aprobar un PR y desplegar ahora se frenan como un push.** `gh pr merge`, `review`, `close`,
  `reopen` y `comment`, `gh workflow run`, `gh run rerun`, `gh release create|delete|upload|edit`, y los deploys
  con `terraform`/`tofu apply|destroy`, `kubectl` con verbos que modifican, `helm install|upgrade|uninstall|
  rollback`, `pulumi up|destroy` o `cdk deploy|destroy`. `gh pr create` sin `--repo` también se frena, porque
  en un fork abre el PR en el original. Se aprueban igual que el push. Si tu proyecto despliega con un comando
  propio —un script como `./scripts/deploy.sh`—, declaralo en `deployCommands` de `ops.config.json` y se frena igual.
- **Jira toma el servicio de una etiqueta.** Los proyectos team-managed no tienen componentes: con
  `"serviceFrom": "label"` el sync lee el servicio de una etiqueta `service:<ruta>` (el prefijo se cambia con
  `serviceLabelPrefix`), y con `"both"` de cualquiera de los dos. Si una incidencia nombra más de un servicio,
  el draft lo marca y no elige. Un `serviceFrom` mal escrito ahora es error: antes dejaba todo el staging sin
  servicio sin decir por qué.
- **Dos épicas con el mismo número se arreglan con un comando.** Dos líneas de trabajo que crean una épica a
  la vez pueden tomar el mismo número; `check` ya lo marcaba, y ahora nombra las dos y dice cómo salir:
  `node tools/ops.js renumber-epic planning <epic-NNN-slug> <NNN>` mueve la que todavía no llegó a la rama
  principal, con su frontmatter, el `(epic: NNN)` de sus tareas y las rutas que la citan. Las tareas de la
  otra épica no se tocan, y no reutiliza el número de una épica cerrada.
- **Las rutas escribibles de tu máquina ya no tienen que ir al archivo compartido.** `writableOutsideRoots`
  se suma también desde `ops.config.local.json`, en la raíz de la instancia: ahí va la memoria de tu runner o
  una carpeta de otro proyecto tuyo, sin que viaje a todos los que clonan. De ese archivo sólo se lee esa
  lista —el push se sigue decidiendo en `ops.config.json`—, y `check` muestra cada ruta exenta con el archivo
  que la declaró. Una instancia nueva ya lo trae en `.gitignore`; si la tuya es anterior, agregá la línea
  `ops.config.local.json` a tu `.gitignore` antes de crearlo.
- **`ops line` arma una línea de trabajo con su propia carpeta de sesión.** Si trabajás con varias líneas en
  paralelo sobre la instancia, `node tools/ops.js line . admin` crea `<carpeta>-admin/` con un worktree en la
  rama `line/admin`, el motor, los repositorios del producto y tus runners instalados ahí; la sesión de esa
  línea se abre en esa carpeta y corre los guards de su árbol. Antes todas las sesiones corrían los guards de
  un solo árbol, y `automation install` desde un worktree los movía para todas: ahora se niega y te manda a
  `ops line` (con `--force` se mueven igual).
- **La cola se puede partir en un archivo por hito.** Cada hito puede vivir en `planning/backlog/<slug>.md`, con
  `order: N` en el frontmatter, en vez de en `BACKLOG.md`. Si trabajás con varias líneas en paralelo —una rama
  por persona o por frente—, cada una escribe el archivo de su hito y traer la otra ya no choca. `BACKLOG.md`
  se sigue leyendo primero, así que no tenés que cambiar nada; `node tools/ops.js split-backlog planning` pasa
  tus hitos a sus archivos cuando quieras. `autobuild` cierra cada tarea en su archivo y lo borra cuando el
  hito se queda sin tareas.
- **El INBOX acepta una entrada por archivo.** Los recorridos ya no agregan al final de una sección de
  `INBOX.md`: escriben cada entrada en `planning/inbox/<sección>/<nombre>.md`, así que dos líneas en paralelo
  que anotan en la misma sección ya no chocan al traerse. `INBOX.md` se sigue leyendo primero y escribir ahí
  a mano sigue valiendo; `tree` y `check` cuentan las dos cosas juntas, y `node tools/ops.js inbox planning`
  lo imprime entero, con la sección y el archivo de cada entrada. Para promover o descartar una entrada de
  las carpetas, borrás su archivo. `check` avisa —sin fallar— una carpeta que no es sección o un archivo
  que no se llama como su entrada, que de otro modo no se contarían donde corresponde.
- **Una tarea que toca lo que no se puede romper no va por `express`.** `autobuild` lee la tabla «Qué no
  se puede romper» de `organization/company.md` y, si una tarea `express` toca una de esas superficies,
  la sube a `directo`, que pasa por Review. Lo que no puede determinar también lo sube. La entrada de
  `done/` dice qué superficie tocó la entrega, o que no tocó ninguna, y avisa si la tabla tiene filas sin
  declarar. Si la tuya sigue en `Por definir`, llenarla es lo que activa esto.
- **Cada hallazgo del Review dice de dónde sale y si se comprobó.** Nombra la regla que lo sostiene o se
  declara criterio, y dice si el revisor lo verificó. Un hallazgo bloqueante que no se comprobó ya no
  manda a corregir: se registra en el INBOX marcado `[sin verificar]`. Uno que cita una regla que no rige
  en tu proyecto se conserva, marcado como criterio.
- **Sobre una superficie crítica, una sospecha sin comprobar frena la entrega.** Fuera de esas superficies
  va al INBOX como el resto; sobre ellas no se corrige —sería cambiar código por una hipótesis— ni se
  entrega: la corrida para con `review-unverified` y deja una fila en `HUMAN_ACTIONS.md` para que alguien
  lo compruebe.
- **Lo que el Review mandó a corregir queda en `done/`**, con la regla de cada hallazgo, en vez de
  perderse al corregirlo.
- **Una regla de negocio `derogada` dice qué la reemplaza.** La plantilla trae dónde escribirlo
  —`**Reemplazada por:** BR-…` o `**Razón de baja:** …`— y `check` avisa si falta. Citar un reemplazo que
  no existe, o uno del mismo archivo, es error.
- **Lo que la revisión corrige una y otra vez vuelve como lección.** Cuando la misma regla se manda a
  corregir en dos tareas o más, el cierre de `autobuild` la anota en la sección Lecciones del INBOX, sin
  promover, y deja su fila en `planning/LESSONS.md`. Ahí decidís: `aplicada` si la reforzaste, `rechazada`
  si no hacía falta —sólo vuelve con una tarea nueva—. `node tools/ops.js lessons planning` muestra lo mismo
  sin esperar una corrida. `upgrade` agrega el archivo.
- **Una regla de negocio dice si alguien la confirmó, aparte de si rige.** La cabecera suma
  `**Verificación:** propuesta | ratificada | discrepancia`. Ratificar exige quién la confirmó con autoridad
  propia; una discrepancia lleva la pregunta abierta y quién decide, y la regla no se edita para que diga lo
  que debería. Las reglas que ya tenés sólo avisan hasta que les declares la verificación.

### Cambiado

- **R17 dice que una decisión no entra a la cola como tarea.** Lo que no tiene nada que construir va a
  `HUMAN_ACTIONS.md` con la pregunta exacta, o como precondición de la tarea que la necesita. Escrita como
  tarea, gasta una vuelta entera para terminar diciendo que había que preguntar.
- **En Claude Code, lo que un guard frena se aprueba en su diálogo de confirmación.** Antes el agente te
  pedía un sí por chat, y cualquier mensaje tuyo que no negara ni preguntara lo aprobaba, aunque hablara de
  otra cosa. Ahora Claude Code abre su diálogo para esa acción, con el motivo del guard: la aprobás o la
  rechazás ahí, sin palabras que elegir. Vale para el push, las credenciales, los gates en rojo, la
  gobernanza y lo que el agente quiera escribirse en `.ops-approval`, también cuando lo pide un subagente.
  Lo que pedís nombrándolo en el chat sigue pasando sin preguntar. En Codex y Gemini, que no tienen ese
  diálogo, la confirmación sigue siendo por chat.
- **`verify` tiene tope, corre de a uno y no escribe en tu árbol.** Cada gate se corta a los 10 minutos
  —con todo lo que lanzó, sin dejar procesos huérfanos— y el bloqueo dice que fue el tope; lo cambiás con
  `runner.gateTimeoutMinutes` en `ops.config.json`. Dos commits a la vez ya no corren dos suites en paralelo:
  el segundo espera. Y cuando el commit coincide con tu árbol, `verify` no corre `build` ni un lint con
  `--fix`, que escribirían en él; sobre la copia del índice siguen corriendo. Si querés `build` también en el
  árbol, nombralo en el `verify` de la raíz (`"verify": "npm test && npm run build"`), y si tu `lint` lleva
  `--fix`, `check` te avisa que agregues uno que sólo revise. Si habías puesto `OPS_SKIP_VERIFY=1` por lo que
  costaba, podés sacarlo.
- **R27 nombra la salida cuando cerrar por defecto rompe el uso diario:** abrir por defecto, con una prueba
  que recorre lo que existe y falla hasta que cada elemento nuevo esté clasificado.
- **Aplicar una propuesta crea los casos que la propuesta firmada enuncia, y sólo esos.** `/agent-promote`
  crea el caso que viene enunciado en la propuesta aunque esta diga que el archivo no se crea: esa frase es
  de cuando se redactó, y el caso entra con tu firma. Lo que ya no hace es escribir por su cuenta un caso
  para una conducta `required` que llegó sin el suyo: la deja anotada como desviación, para pedirla en la
  propuesta siguiente. Y `/agent-propose`, cuando agrega una `required`, enuncia el caso que la ejerce, para
  que llegue a tu firma. Un caso cambia lo que se mide de un cargo, así que lo decide quien firma.

### Corregido

- **`integration reset` y `reconcile` ya no deshacen una promoción.** Sobre un ítem promovido, `reset` lo
  devolvía a `pending` y perdía el registro de la promoción, y `reconcile` borraba la señal de que Jira cambió
  después de promoverlo. Ahora se niegan y te mandan a revisar la épica en `planning/roadmap/`.
- **El sync de Jira de una persona ya no pisa lo que curó otra.** Con `candidateAssigneeEnv`, un ítem que
  alguien curó como candidato pasaba a contexto cuando sincronizaba otra persona, y su borrador se regeneraba
  sin decirlo. Ahora sigue candidato con su curación, y el resumen del sync lo cuenta aparte. El staging pasa a
  ser de cada máquina: una instancia nueva lo trae en `.gitignore`, y si la tuya es anterior, agregá la línea
  `integrations/*/staging/`.
- **Review recibe la aceptación contra la que revisa.** Se le pedía revisar «por aceptación» sin dársela,
  y la buscaba en el BACKLOG, donde no está lo que Ready refinó en la misma corrida.
- **La entrada de `done/` trae el `review` y el `lane` completos.** El agente que cierra la tarea resumía el
  hecho de revisión y se perdían las reglas contra las que se revisó, las decisiones registradas y, desde
  esta versión, la superficie crítica y lo corregido. Ahora los copia textuales. Las entradas que ya
  escribiste no cambian.

### Cargos

- **`qa-engineer`**: Los dos candidatos salen de H11 del informe del 2026-09-21, nombrados por el propio juez
  de la evaluación al leer `evaluations/results/2026-09-16.md` (casos 09 y 13). Los dos entran sólo en
  `evaluations/expected-behaviors.yaml` —H11 ya los… (propuesta `2026-10.md`).
- **`content-specialist`**: ### 1. `learning/sources.yaml` — entrada «Federal Plain Language Guidelines»
  (líneas 20–23) (propuesta `2026-10.md`).
- **`frontend-engineer`**: Dos cambios, cada uno sale de un hallazgo distinto de los citados en «Hallazgos».
  Los dos agregan sobre el texto existente; ninguno reemplaza una frase ya escrita (propuesta `2026-10.md`).
- **`implementation-manager`**: ### 1. `references/operating-model.md` — cerrar H1 (mecánico, sin riesgo)
  (propuesta `2026-10.md`).
- **`legal-counsel`**: Las fechas y ediciones citadas abajo están verificadas en
  `agents/roles/system/legal-counsel/learning/reports/2026-09-24.md`, sección «Evidencia» (H1–H7); esta
  propuesta no reabrió las fuentes primarias, trabaja sobre esa verificación ya… (propuesta `2026-10.md`).
- **`logistics-operations-manager`**: Dos bloques, y se pueden firmar por separado porque salen de dos
  hallazgos distintos y tienen costos distintos (propuesta `2026-10.md`).
- **`machine-learning-engineer`**: Un solo hilo cruza las cinco corridas sin cerrarse: el resumen automático
  de la página HTML de una release de GitHub erró el año de la fecha en tres observaciones independientes
  —`qa-engineer` (2026-08-29), este cargo sobre PyTorch 2.13.0… (propuesta `2026-10.md`).
- **`people-operations-manager`**: No se modifica `SKILL.md`: el aprendizaje de este cargo no lo toca nunca
  (`learning/AUTOMATION.md`, `SKILL.md` → "Aprender sin reescribirse"). Los dos archivos que sí se tocan son
  los que nombra el hallazgo de 2026-09-24, y en los dos se… (propuesta `2026-10.md`).
- **`product-marketing-manager`**: De los siete hallazgos, el del informe 2026-09-24 trae una recomendación
  explícita (H1+H2+H3); los otros seis son huecos de contrato que la corrida de evaluación del 2026-09-02
  encontró en casos que igual pasaron. Esta sección convierte los… (propuesta `2026-10.md`).
- **`security-engineer`**: Dos informes seguidos (2026-09-21 H7, 2026-09-24 H7/H8) recomendaron la misma
  acción —correr, en un entorno desechable y no contra este árbol, el caso que ejerce un guard ya cerrado del
  propio repositorio— y ninguno la ejecutó; la… (propuesta `2026-10.md`).
- **`user-researcher`**: El cambio toca **tres archivos** y es aditivo en los tres: no se reescribe ninguna
  línea existente de `references/operating-model.md` ni de `evaluations/expected-behaviors.yaml`, y `SKILL.md`
  no se toca (ya lo pedía el informe). El núcleo… (propuesta `2026-10.md`).
- **`software-architect`**: Seis hallazgos de contrato salieron de corridas de evaluación (no de
  investigación: H1, 2026-09-24, confirma que no hay fuente nueva que agregar a `sources.yaml` este período).
  De los seis, tres se cierran agregando una conducta prohibida… (propuesta `2026-10.md`).
- **`ux-designer`**: Siete puntos, A–B del informe 2026-09-24 y C–G de las cinco brechas que encontraron las
  corridas de casos. Todos son aditivos: ninguno borra ni reescribe una oración existente, salvo C y G, que
  intercalan una cláusula dentro de una oración… (propuesta `2026-10.md`).
- **`project-manager`**: **Archivo:** `agents/roles/system/project-manager/learning/sources.yaml` **Sección:**
  la lista `sources:` (hoy líneas 11–26). Agregar una quinta entrada al final de la lista, después de la de
  `Government Functional Standard GovS 002` (línea… (propuesta `2026-10.md`).
- **`accounting-specialist`**: Fuente: hallazgo H1 del informe `learning/reports/2026-09-24.md`. Ese informe
  señaló esta corrida (2026-10) como la indicada para retomar la pregunta que dejó abierta: si la IFRIC
  finalizó, en su reunión del 15–16/09/2026, las ocho… (propuesta `2026-10.md`).
- **`ai-governance-lead`**: No se toca `SKILL.md`, `evaluations/expected-behaviors.yaml` ni ningún caso: los
  dos cambios son de catálogo de fuentes y de referencia, no de contrato del cargo (propuesta `2026-10.md`).
- **`ai-product-manager`**: **Único archivo a tocar:
  `agents/roles/system/ai-product-manager/references/operating-model.md`.** (propuesta `2026-10.md`).
- **`analytics-engineer`**: No se modifica `SKILL.md` ni `evaluations/expected-behaviors.yaml`: ningún informe
  consolidado (2026-09-07, 2026-09-14, 2026-09-21, 2026-09-24) propone una conducta nueva a exigir o prohibir
  — las cinco secciones de «Hallazgos» lo dicen… (propuesta `2026-10.md`).
- **`business-strategist`**: Dos archivos, los dos derivados de H1. H2, H3 y H4 piden explícitamente "sin
  acción" (ver arriba), así que no se toca `SKILL.md`, ni las entradas de ISO 56002, OECD Strategic Foresight
  o Strategyzer en `sources.yaml`, ni ningún caso de… (propuesta `2026-10.md`).
- **`cloud-architect`**: Ningún hallazgo individual pide tocar `SKILL.md`, `operating-model.md` ni
  `sources.yaml`: las cuatro corridas lo dijeron cada una por su cuenta. Pero una misma recomendación de
  método se repitió, de forma independiente, en las cuatro —… (propuesta `2026-10.md`).
- **`customer-support-specialist`**: Tres ediciones, las tres por agregado sobre el texto existente (ningún
  párrafo actual se reescribe). `SKILL.md` no se toca: ninguno de los tres hallazgos aplicables (H1, H2, H3)
  pide un cambio de conducta nueva, sólo de las fuentes que la… (propuesta `2026-10.md`).
- **`database-administrator`**: Agregar una entrada a
  `agents/roles/system/database-administrator/learning/sources.yaml`, al final de la lista `sources:` (después
  de la entrada «PostgreSQL release notes», línea 17), sin tocar ninguna de las seis entradas existentes
  (propuesta `2026-10.md`).
- **`data-engineer`**: No modifica `SKILL.md` ni `evaluations/expected-behaviors.yaml`. Agrega tres fuentes
  nuevas a `learning/sources.yaml` y sus tres líneas correspondientes a «Fundamento externo» de
  `references/operating-model.md`, consolidando H1/2026-08-31… (propuesta `2026-10.md`).
- **`data-governance-steward`**: No toca `SKILL.md`, `expected-behaviors.yaml` ni ningún caso: lo que sigue es
  `learning/sources.yaml` y `references/operating-model.md`, ambos archivos de registro del propio cargo
  (propuesta `2026-10.md`).
- **`data-scientist`**: **1. `agents/roles/system/data-scientist/learning/sources.yaml`** — agregar tres
  entradas al final de la lista `sources:` (después de la entrada `ISO IEC 25012:2008 data quality model`,
  líneas 24-27), sin tocar `rules:` ni las cuatro… (propuesta `2026-10.md`).
- **`developer-relations-engineer`**: Un solo archivo, una sola línea:
  `agents/roles/system/developer-relations-engineer/references/operating-model.md`, sección «## Fundamento
  externo», primera viñeta (línea 38) (propuesta `2026-10.md`).
- **`financial-controller`**: No se modifica `SKILL.md`. Los dos archivos que cambian son
  `learning/sources.yaml` y `references/operating-model.md` (propuesta `2026-10.md`).
- **`finops-engineer`**: **Único archivo: `learning/sources.yaml`.** Agregar una entrada nueva al final de la
  lista `sources:`, después de la de "AWS EC2 Capacity Blocks for ML pricing" (la última hoy). No se
  reescribe, reordena ni re-wrappea ninguna entrada… (propuesta `2026-10.md`).
- **`fraud-risk-analyst`**: Las seis piezas del informe tocan sólo `learning/sources.yaml` y
  `references/operating-model.md` —citas y plazos del fundamento externo—, nunca `SKILL.md`: ningún hallazgo
  cambia una regla del modelo, un límite o un criterio de entrega… (propuesta `2026-10.md`).
- **`growth-marketer`**: Dos archivos, los dos aditivos — ninguno reformula una instrucción existente;
  `SKILL.md` no se toca. Las dos vienen literales de la «Recomendación» de `learning/reports/2026-09-24.md`
  (H1, H3, H4); nada sale de los hallazgos de evaluación… (propuesta `2026-10.md`).
- **`integrations-engineer`**: **Archivo: `agents/roles/system/integrations-engineer/learning/sources.yaml`.**
  (propuesta `2026-10.md`).
- **`kyc-aml-specialist`**: Un solo archivo: `agents/roles/system/kyc-aml-specialist/learning/sources.yaml`.
  No se modifica `SKILL.md` ni `evaluations/expected-behaviors.yaml` — los tres informes consolidados
  (2026-09-14, 2026-09-21, 2026-09-24) lo dicen… (propuesta `2026-10.md`).
- **`mlops-engineer`**: Único archivo tocado: `agents/roles/system/mlops-engineer/learning/sources.yaml`. No
  se modifica `SKILL.md`, `references/operating-model.md` ni `evaluations/`: las cinco corridas citadas en
  «Hallazgos» piden soporte de fuente, no una regla… (propuesta `2026-10.md`).
- **`mobile-engineer`**: Ninguno de los candidatos de «Hallazgos» pide una regla nueva — los dos informes lo
  dicen explícitamente (09-07 candidato 1, 09-24 candidato 1: «sigue sin ser urgente convertirlo en una regla
  nueva»). `SKILL.md` queda intacto. Lo que sigue… (propuesta `2026-10.md`).
- **`partnerships-manager`**: **1. `agents/roles/system/partnerships-manager/references/operating-model.md`,
  sección «Fundamento externo», línea de ISO 44001.** (propuesta `2026-10.md`).
- **`privacy-compliance-specialist`**: Agrega sobre dos archivos existentes, sin reescribir ninguna sección
  vigente. No toca `SKILL.md`, `expected-behaviors.yaml` ni los casos: ese cambio, si hace falta, se decide
  aparte en «Evaluación» (propuesta `2026-10.md`).
- **`procurement-manager`**: No modifica `SKILL.md`, `evaluations/expected-behaviors.yaml` ni ningún caso. Los
  dos cambios son aditivos: agregan texto sin tocar un carácter del que ya existe (propuesta `2026-10.md`).
- **`product-manager`**: No se modifica `SKILL.md`. Los dos cambios son aditivos sobre un archivo de
  referencia y un comentario de datos; el núcleo del contrato (los seis flujos, los Límites, la Entrega
  mínima) no se toca (propuesta `2026-10.md`).
- **`release-manager`**: Dos cambios aditivos en `references/operating-model.md` —uno en § Readiness (H1) y
  otro en § Rollout progresivo (H2, sólo la regla que ya tiene su segunda fuente)— y una fuente nueva en
  `learning/sources.yaml`. Ninguna línea vigente se… (propuesta `2026-10.md`).
- **`sales-representative`**: Un solo archivo, `references/operating-model.md`, dos inserciones. Ninguna
  reemplaza texto existente — las dos agregan una frase o un párrafo nuevo. No se modifica `SKILL.md` ni
  `evaluations/` (H1 ya razona por qué no hace falta) (propuesta `2026-10.md`).
- **`solutions-engineer`**: **Archivo:**
  `agents/roles/system/solutions-engineer/references/operating-model.md`. **Sección:** `## Control de calidad`
  (hoy ocho preguntas, líneas 45-53 del archivo vigente). **Operación:** agregar una pregunta nueva entre la
  de POC y la… (propuesta `2026-10.md`).
- **`tech-lead`**: Los cuatro hallazgos del informe del 2026-09-24 son correcciones de datos sobre fuentes ya
  citadas; ninguno toca `SKILL.md` ni `evaluations/expected-behaviors.yaml`, y este cambio tampoco lo hace
  (propuesta `2026-10.md`).
- **`technical-program-manager`**: Dos cambios, los dos aditivos sobre archivos de referencia/catálogo.
  Ninguno toca `SKILL.md`, `evaluations/expected-behaviors.yaml` ni los casos existentes (propuesta
  `2026-10.md`).
- **`technical-writer`**: Agregado a dos archivos existentes; no se crea ninguno y no se toca `SKILL.md`
  (propuesta `2026-10.md`).
- **`treasury-analyst`**: Único archivo tocado: `agents/roles/system/treasury-analyst/learning/sources.yaml`.
  `SKILL.md`, `expected-behaviors.yaml` y los casos no se tocan. Cuatro cambios, todos aditivos salvo el
  primero (reemplazo de URL de una fuente que cambió de… (propuesta `2026-10.md`).

## [0.99.2] - 2026-09-28

### Corregido

- **Cada límite de `### Límites` llega una sola vez.** Si la primera viñeta empezaba con «El runner»,
  «Debe» o «Nunca», la lista entera llegaba otra vez a cada agente, pegada en un límite más. Si reordenaste
  tus viñetas para esquivarlo, ya podés dejarlas como quieras.
- **`check` deja de avisar que una fila resuelta de `HUMAN_ACTIONS.md` no está commiteada cuando sí lo
  está.** Pasaba con filas largas: la búsqueda de git que usaba no encuentra algunas de más de 256 bytes.
  Ahora se compara contra el archivo commiteado, y una fila que pasó a resuelta sólo en tu árbol se sigue
  avisando.

## [0.99.1] - 2026-09-25

### Cambiado

- **Una lectura de credencial que necesitás siempre se aprueba una vez.** Cuando el agente se frena leyendo
  una credencial, te pregunta además si querés dejarla aprobada para siempre. Si esa es tu intención —con
  tus palabras, sin frase fija—, él mismo escribe la ruta en `planning/.ops-approval` con quién lo aprobó y
  para qué, y ninguna sesión vuelve a preguntarlo. Tu confirmación le deja escribir esa línea y ninguna otra.
- **Confirmar el bloqueo de `.ops-approval` ahora aprueba.** Si el agente intenta escribir la aprobación sin
  que se la hayas pedido, el bloqueo te muestra qué iba a agregar y tu «sí» lo deja pasar. Antes el bloqueo
  ofrecía confirmar y la confirmación no hacía nada: sólo pasaba pidiéndolo con el nombre del archivo.
- **El bloqueo dice cuánto dura una línea pegada**: hasta que alguien la borre. Decía que dejaba de valer
  «en cuanto cambie», y una lectura siempre frena la misma ruta, así que nunca cambiaba.
- **`check` nombra aparte las credenciales aprobadas** —«N credencial(es) aprobadas hasta que se borre su
  línea»—, en vez de contarlas como una aprobación olvidada. Sigue mostrándolas en cada corrida.

## [0.99.0] - 2026-09-24

### Agregado

- **`migrations.paths` en `ops.config.json`**: las carpetas donde viven tus migraciones, por ejemplo
  `["migrations", "alembic/versions"]`. Si la declarás, reemplaza el default (`migrations`, `migration`,
  `migrate`), que no alcanzaba a Alembic. `check` avisa la extensión o la carpeta declarada que no alcanza
  a ningún archivo: una cobertura que declaraste y no cubre nada.

### Cambiado

- **`check` falla si una entrada de DONE tiene `tests:` todo `n/a` y su commit toca algo que no sea un
  documento (`.md`, `.txt`, `.adoc`) o una imagen (`.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`, `.webp`).** Es lo que vuelve creíble el `n/a` de arriba: lo demás se ejecuta y se prueba. Rige
  para lo cerrado desde el 2026-09-24, así que tu historia anterior no se pone en rojo al actualizar.

### Corregido

- **Un aviso de una tarea de fondo ya no le borra a la persona lo que dijo.** Si un guard frenaba algo en el
  turno de un aviso de un subagente, eso no quedaba esperando tu confirmación, y tu «dale» siguiente no
  aprobaba nada. Ahora lo frenado espera tu respuesta aunque el aviso llegue antes que ella, y tu negativa
  —«no toques el `.env`»— sigue valiendo después de un aviso sin depender del texto que el aviso traiga.

- **Una negación sobre otra cosa ya no te hace perder un bloqueo.** «dale, fijate si esto no es un
  defecto» dejaba lo frenado sin anotar, y tu «confirmo» siguiente no aprobaba nada. Ahora la respuesta se
  lee en la primera parte del mensaje, y lo que negás nombrándolo —«dale, pero no el .env»— sigue sin
  pasar, igual que un push si en cualquier parte decís que no se publique —«no hagas push todavía»—. Cuando algo no queda esperando tu confirmación, el bloqueo lo dice y te indica que lo pidas
  nombrándolo, en vez de ofrecerte un «dale» que no iba a servir.

- **Si lo que se frenó lo hacía un subagente, tu confirmación le llega.** Dentro de trabajo delegado —Build en
  cada `autobuild`— el bloqueo no ofrecía el chat: decías que sí y el reintento volvía a frenar con el mismo
  mensaje, y la única salida era pegar la línea en `planning/.ops-approval`. Ahora el subagente devuelve el
  bloqueo para que te lo pregunten, y lo que confirmes pasa aunque lo reintente otro subagente. Sólo eso: un
  pedido tuyo que nombraba algo sigue valiendo para el agente con el que hablás, no para un subagente.

- **Sin nadie en el chat, el bloqueo ya no le ordena al agente que se escriba la aprobación.** Decía «Aprobalo
  pegando tal cual en…», y el agente lo leía como una orden que su contrato le prohíbe. Ahora dice que las
  líneas las pega una persona, y al agente qué hacer mientras tanto.

- **El gate de commit reconoce el código de sqlc donde lo pongas.** Sólo aceptaba el generado bajo una
  carpeta `sqlc/` o `generated/`, así que un `out` como `internal/platform/pgdb/` pedía aprobación en cada
  commit que tocaba una consulta aunque hubieras regenerado. Ahora reconoce `*.sql.go` en cualquier carpeta.
  Y ve una consulta en cualquier carpeta `queries/` —`api/db/queries/` en un monorepo—, que antes se
  commiteaba sin su generado sin que nada avisara; eso sólo si el repositorio tiene `sqlc.yaml`, `sqlc.yml`
  o `sqlc.json`. Si frena, dice qué buscó. Límite: con `output_files_suffix` el generado no se reconoce.

- **Un guard invocado a mano ya no se queda colgado.** Lanzado fuera del runner —a mano, desde un script o
  en segundo plano— con stdin abierto y sin datos, esperaba hasta que el otro extremo cerrara: horas, sin
  llegar a correr. Ahora, si en 2 s no llega nada, bloquea y explica cómo invocarlo: con el JSON del hook
  por stdin, o sin entrada con `</dev/null`. Lo que manda el runner se lee entero, así que una escritura
  grande no se corta, y un JSON completo se juzga aunque quien lo mandó no cierre stdin.

- **Una tarea cuyo entregable es un documento o una decisión escrita ya puede cerrarse en `autobuild`.**
  Paraba en `verify-hollow` pidiendo pruebas imposibles. Ahora Verify declara esos criterios sin superficie
  ejecutable, llegan a Done como `tests: n/a — <razón>`, y QA comprueba que el documento exista y cubra lo
  que la aceptación enumera. Con causas mezcladas, el rebote pide sólo las pruebas que de verdad faltan, y
  Done recibe de Verify qué prueba cubre cada criterio en vez de componerlo de memoria.

- **Una condición marcada `(fuera de verify: <razón>)` ya no llega a Verify ni a QA.** Era la salida que
  `check` recomienda, y la corrida paraba igual; ahora viaja a Done para quedar cumplida en `tests:`, `qa:`
  o `commit:`.

- **El guard de migraciones ya no frena la reversión honesta.** Buscaba en todo el archivo, así que el
  `DROP TABLE` del `-- +goose Down` frenaba cada tabla nueva y una migración sin `Down` pasaba. Ahora, en
  goose y dbmate, juzga sólo el bloque que aplica; un `*.down.sql` es reversión entera; corregir el `Down`
  con una edición también pasa; y el mensaje dice en qué bloque está lo que frena.

- **Si declarás `migrations.extensions`, el guard ve también el borrado escrito con la API del ORM**, no
  sólo el SQL crudo: `op.drop_table`/`drop_column` (Alembic), `drop_table`, `remove_column(s)` y
  `drop_join_table` (Rails), `dropTable`/`dropColumn(s)` (TypeORM), `dropTable(IfExists)`/`dropColumn`
  (Knex) y `DeleteModel`/`RemoveField` (Django). La reversión —`downgrade()`, `down`— no se juzga.

- **El puente de Antigravity ya no deja pasar lo que no puede leer.** Ante un JSON ilegible respondía
  `allow`: un `git push --force` al que le faltaba una llave pasaba. Y con stdin abierto se colgaba. Ahora
  lee con el mismo lector que los guards: lo ilegible se niega en `pre-shell` y `pre-files`, si en 2 s no
  llega nada niega y dice cómo invocarlo a mano, y en `stop` deja cerrar con el motivo a la vista. Sin
  entrada (`</dev/null`) sigue permitiendo, como antes.

- **El puente de Antigravity niega una llamada que no sabe describir.** Leía cada campo por un nombre fijo,
  así que una llamada con otra forma —un campo renombrado en una actualización del runner— llegaba vacía a
  los guards y pasaba: todos apagados sin avisar. Ahora un `pre-shell` sin comando o un `pre-files` sin
  archivo se niegan, y el motivo nombra los campos que sí llegaron.

- **El gate de commit reconoce una especificación OpenAPI por lo que declara, no por la carpeta.** Cualquier
  `.yaml` bajo `api/`, `openapi/` o `spec/` pedía regenerar el cliente: una config de sqlc, un compose o un
  fixture ahí frenaban cada commit. Ahora cuenta el que declara `openapi:` o `swagger:`, o un fragmento de una
  carpeta que tiene uno.

- **En Codex, el guard de migraciones juzga cada archivo del parche por separado.** Un `apply_patch` llegaba
  como un solo sobre: los marcadores de goose y dbmate venían con el `+` del parche y no partían, así que la
  reversión honesta volvía a frenar, y un `DROP TABLE` citado en otro archivo del mismo parche frenaba la
  migración. Ahora cada archivo se juzga por su sección, y en una modificación sólo lo agregado.

- **`automation doctor` de Antigravity mira la copia que `agy` ejecuta de verdad.** `agy` corre la que registrás
  con `agy plugin install`, una por usuario, y `doctor` miraba la del workspace: decía «operativo» mientras `agy`
  ejecutaba el plugin de otro proyecto o uno roto, y frenaba cada comando. Ahora compara las dos, lanza la
  registrada como la lanza `agy`, y si difiere te dice qué comando corre para registrar esta instalación.

## [0.98.0] - 2026-09-17

### Cambiado

- **Descartar una propuesta que decide un cambio pide el motivo, y el informe siguiente lo lee.**
  `ops learn <cargo> --archived` se niega sobre una propuesta cuyo «Cambio propuesto» dice qué hacer si no
  lleva `--reason "<motivo>"`. El motivo queda en el documento, como `- Motivo:`, y en la fila de
  `HISTORY.md`. Cada informe nuevo trae arriba, en un comentario, las propuestas descartadas del
  cargo con su motivo, y le pide no volver a recomendarlas salvo que aparezca un hecho nuevo.

  Antes, archivar no dejaba ningún porqué, y el mismo hallazgo volvía todas las semanas. Una propuesta
  con el molde intacto se sigue archivando sin motivo: nadie decidió nada, y lo que traía sigue vivo.
  Si tenés un script que archiva propuestas decididas, agregale `--reason`.

- **La investigación de los cargos pasa a ser mensual: ya no hay cadencia semanal.** Los cargos con
  fuentes de tipo `advisory`, `platform` o `project` investigaban cada lunes. Ahora investigan el 24, igual
  que los demás, y el 1 se consolida como siempre. `profession` sigue trimestral. Se quitó el cron del
  lunes y la opción `semanal` de la corrida manual. El informe se llama «Investigación» a secas, y el
  prompt le pide al cargo cubrir todo lo publicado desde su informe anterior.

  Es una decisión medida. En tres semanas de septiembre, las corridas semanales dieron unos 353
  hallazgos: ninguno urgente, ninguno aplicado y la mitad repetidos. Lo que produce el ciclo es un cambio
  de contrato, que se firma una vez al mes y llega con `upgrade`, así que detectarlo antes no lo
  adelantaba.

  Si programaste la investigación en tu instalación siguiendo el `learning/AUTOMATION.md` de un cargo,
  pasala a mensual: esos archivos ya lo dicen.

### Corregido

- **Confirmar un bloqueo ya no exige una palabra.** El guard aprobaba sólo si la respuesta empezaba con
  una de once formas —«dale», «sí», «ok»…—, así que «listo», «claro», «confirmo» o «bueno dale» volvían
  a frenar lo que la persona acababa de aprobar. Ahora la confirmación es con tus palabras, y juzgar si es
  un sí le toca al agente: el mensaje del bloqueo se lo pide así. Lo único que el guard sigue frenando es
  la respuesta que niega, que arranca frenando («pará», «esperá», «cancelá») o que pregunta. Y lo que se
  frena mientras decís que no ya no queda esperando una confirmación.

- **La fila que `autobuild` registra al frenarse nace pendiente, y si no quedó así la parada lo dice.**
  En una corrida real, el agente que escribe la fila la dejó `resuelta` y «decidida por el dueño», y la
  tarea quedó desbloqueada sin que nadie decidiera nada. Las cuatro paradas que registran una acción
  humana ahora piden la fila `pendiente` y prohíben atribuir decisiones. Las tres que registran la
  propia tarea relen `ops context` y, si la fila no quedó pendiente, lo dicen en el detalle de la
  parada.

- **El guard de dependencias ya no frena un `package.json` que cambió sin tocar nada del lockfile.**
  Un commit que agrega un script o ajusta la configuración de `jest` se frenaba por llevar el manifiesto
  sin su lock, y había que pedir permiso para un cambio sin riesgo. Pasa sólo con `package-lock.json` y
  sólo si lo que cambió está en una lista medida con npm 11.16.0. `version`, `name`, `license`,
  `engines`, `bin` y los scripts de instalación sí mueven el lock, y siguen frenando. Con pnpm, yarn o
  bun no cambia nada.

- **Las ramas que mergea el auto-merge del ciclo se borran.** El auto-merge de los informes que no
  proponen cambios lo firma `github-actions`, y ese merge no dispara el workflow que borra las ramas,
  así que quedaban en el remoto. Ahora cada corrida del ciclo barre las ramas `automation/*` mergeadas
  que no se movieron después del merge.

- **La estrategia de prueba que el plan fija llega al WIP y a quien construye.** `testStrategy` es
  obligatorio en el plan —el que planifica está obligado a escribirlo— y después no aparecía en ninguna
  otra parte de la corrida. Sus propios pasos la citaban, los pasos sí viajan al WIP, y ella no: un paso
  que decía «correr la mutación declarada en testStrategy» apuntaba a un lugar que no existe.

  El costo es de quien revisa: R9 pide la mutación **declarada**, y sin ella no se puede distinguir la que
  se corrió de la que se pensó, así que la única salida es rehacer la revisión. Medido dos veces, en dos
  corridas distintas.

## [0.97.0] - 2026-09-17

### Agregado

- **La revisión de `autobuild` tiene dónde poner una decisión que no le toca tomar.** Tenía dos puertas:
  marcar el hallazgo como bloqueante —y mandar a tocar código— o dejarlo en el INBOX como propuesta, con
  tope de tres. Un contrato público que alguien tiene que definir no es ninguna de las dos, así que el
  revisor elegía frenar, que de las dos es la correcta y costaba la corrida entera sobre trabajo que
  estaba bien.

  Ahora ese hallazgo va a `HUMAN_ACTIONS.md` con qué lo cierra y quién puede tomarlo, nombrando la épica o
  el hito al que alcanza —nunca la tarea, para no bloquear a la que lo encontró—, y la corrida sigue. La
  entrada de DONE cuenta cuántas quedaron registradas.

### Corregido

- **Una parada que espera una decisión tuya suelta la tarea que había reservado.** Al registrar la fila
  en `HUMAN_ACTIONS.md`, la tarea queda bloqueada; si además seguía reservada, el runner quedaba ocupado
  por algo que nadie podía tomar y la corrida siguiente moría reclamando la que sigue —«este runner ya
  tiene …»—. Pasaba en toda parada anterior a Build, que son las más frecuentes mientras una tarea
  todavía se está definiendo, y costaba una corrida entera por vez.

  Las paradas de después de construir **no** sueltan nada: ahí el WIP existe y es resumible, y la reserva
  es lo único que dice de quién es ese trabajo.

- **Lo que la línea de una tarea ya decidió llega a las fases que deciden.** La descripción —dónde vive un
  símbolo, qué queda fuera de alcance, con qué se produce la evidencia— se leía del BACKLOG y se
  descartaba, así que Plan la volvía a decidir por su cuenta y decidía distinto. Critique **sí** abre el
  BACKLOG, y bloqueaba el plan citando la línea palabra por palabra: una compuerta juzgando contra un
  texto que la otra no recibió. Dos corridas medidas se pagaron enteras por esto, 1,10 M y 815 k tokens,
  sin escribir una línea.

  Ahora `ops context` la emite con la tarea y llega a Ready, Plan, Critique y Build, dicha como lo que es:
  decisiones ya tomadas, que no se re-deciden. **No tenés que cambiar nada en tus líneas** — es el texto
  que ya escribías antes de la aceptación.

- **`git commit --amend` se frena sobre historia publicada, y ya no sobre la que nadie vio.** Lo que R8
  protege es la historia que otro leyó —lo mismo que ya decía del force-push—, y el guard lo bloqueaba
  siempre «por política y no por daño». Eso no impedía el resultado: `git reset --soft HEAD~1` y volver a
  commitear produce exactamente lo mismo, no lo frena nada y no deja constancia. Sólo impedía el comando
  que lo nombra.

  Ahora el guard mira si algún remoto alcanza al commit, con una lectura local que no habla con ningún
  servidor. Publicado sigue cerrado y sin salida por chat, igual que el force-push. **R8 también cambió**:
  pasa a prohibir reescribir historia que otro ya leyó, en vez de nombrar el comando.

- **Una etapa de un recorrido ya no muere por escribir de más.** Pasado cierto tamaño el modelo deja de
  emitir los campos y devuelve el JSON envuelto como texto, que no valida; cinco reintentos después la
  etapa se cae y la corrida entera se para. Cada campo de la respuesta lleva ahora su tope declarado
  —`summary` 1000 caracteres, `missing` y `humanAction` 500, cada evidencia 200—, así que el rechazo
  nombra el campo y el número en vez de decir que no se pudo parsear.

  Lo que no entra no se pierde: va al archivo de análisis, que es lo que lee quien sintetiza al final.
  Si escribís un recorrido propio, no tenés que hacer nada — el tope es del esquema, no del contrato.

- **`autobuild` ya no frena una tarea correcta porque el nombre del test venga anotado de dos formas.**
  La puerta comprueba que un borde que el build arregló aparezca en algún rojo declarado, comparando los
  dos nombres por contención. Eso cubría que uno fuera prefijo del otro, y dejaba afuera la forma que
  aparece de verdad: los dos nombran el mismo test y cada uno le agrega **su propia** anotación entre
  paréntesis —dónde está la línea de un lado, por qué se vio en rojo del otro—. Ahí ninguno contenía al
  otro y la corrida paraba con `edge-unproven` sobre trabajo terminado y en verde.

  Si te pasó, la tarea estaba bien: no hacía falta rehacerla. Y el motivo de la parada ya no se contradice
  solo — dice qué comparó la puerta en vez de afirmar que faltaba la prueba.

- **Una corrección de Review se lleva también lo que ella misma dejó desactualizado.** «Corregí sólo estos
  hallazgos» acota el alcance y dejaba un cabo suelto: el conteo que enumeraba lo que cambió, el
  comentario que describía la forma vieja, la fila que la afirmaba. Como Review tiene una sola vuelta de
  corrección, esa deriva aterrizaba en la re-revisión y la corrida frenaba sobre trabajo correcto.

- **El checkpoint de hito se destraba escribiendo, no borrando.** `AWAITING_REVIEW.md` frenaba a todos los
  runners por el hecho de existir, así que resolverlo exigía acordarse de borrarlo — y mientras tanto
  quien lo leía podía ver ahí escrito que ya estaba revisado. Es lo que R28 prohíbe, y el propio archivo
  lo decía de sí mismo.

  Ahora lleva `status: pendiente` en su frontmatter y se destraba cambiándolo a `resuelta`, con lo cual el
  archivo **se queda** y se puede leer después qué se revisó. Borrarlo sigue funcionando.

  **Tu archivo actual no cambia de conducta**: sin `status` sigue frenando, que es lo que corresponde
  —abrirlo al actualizar sería destrabar una compuerta en silencio—. Agregale la línea cuando quieras
  resolverlo sin perderlo; los que escriba `autobuild` de acá en más ya vienen con ella.

- **`check` rechaza una fila de `HUMAN_ACTIONS.md` que nombra a su tarea sin ser su slug.** El motor
  bloquea por esa primera columna **exacta**, así que `| **alta-de-cliente: falta el token** | pendiente |`
  se escribía sin error, salía en `ops context` bajo `HUMAN` como si estuviera registrada, y la tarea se
  seguía ofreciendo. Es la forma que sale natural, porque esa tabla la lee una persona; en la instancia
  donde se midió, **once filas pendientes bloqueaban cero tareas** y la corrida siguiente volvía a elegir
  lo mismo y a pagar la misma fase.

  Si te aparece, el mensaje dice qué fila y a qué tarea apuntaba: dejá el slug solo en esa celda y contá
  el resto en la columna de la acción. Las otras dos formas no cambian — el slug exacto sigue bloqueando,
  y nombrar la épica, el hito o el recorrido sigue siendo válido cuando lo que se frena no es una tarea.

- **Una decisión que `autobuild` deja abierta ya no bloquea la tarea que la encontró.** Esas filas se
  registran para que la corrida siga —lo que de verdad frena tiene otro camino—, pero la fase que las
  escribe podía poner en la primera columna el slug de la tarea en construcción y bloquearla. No se veía
  mientras la corrida vivía, porque un WIP activo manda sobre la acción humana; aparecía al cerrar el WIP.

- **Una corrida de `autobuild` que no puede tomar nada lo dice, en vez de terminar como si la cola
  estuviera vacía.** Eran dos estados distintos con la misma salida: la cola terminada, y la cola cuyas
  tareas están todas reclamadas por otro runner o esperando una dependencia. La corrida informaba que no
  había nada que hacer sobre una cola que sí tenía trabajo.

  Se paga después de una parada que espera una decisión tuya: ahí el reclamo se queda puesto a propósito
  —quien paró va a volver y su `claim` es idempotente—, así que cuando contestás, **otro** runner pregunta
  y se va sin nada. Ahora para con `queue-unavailable` y dice cuántas hay; la línea `TAKEN` de
  `ops context` nombra quién tiene cada una, y se sueltan con `ops release`.

## [0.96.0] - 2026-09-16

### Corregido

- **Tres errores que el CLI contestaba con exit 1 ahora contestan 2, que es lo que dice la convención.**
  Son «no se llegó a la pregunta»: `integrations/config.json` ilegible, un proveedor que no está en ese
  registro, y un `package.json` inválido al declarar el motor. Sus hermanos —un `ops.config.json`
  ilegible, un proveedor que Cauce no trae— ya contestaban 2.

  El corte, que hasta ahora estaba en la cabeza de quien escribía cada salida, está escrito y sale en
  `ops --help`: **2** es que el comando no existe, falta un argumento, o la raíz que nombrás no es lo que
  dice ser —lo arreglás cambiando la invocación—; **1** es que se llegó y la respuesta es que no —una
  validación encontró problemas, el estado se niega, o una operación falló a mitad de camino—.

  Si tenés un script que distingue los dos, revisá esos tres casos. Si sólo mira «distinto de cero», no
  cambia nada.

- **`ops flow list|check|show` acepta la raíz de la instancia, como el resto.** Todos los comandos que leen
  una instancia la toman como posicional; `flow` la descartaba y resolvía por el directorio actual, así que
  `ops flow list <raíz>` contestaba sobre otra cosa — y una lista de recorridos se lee igual de bien venga
  de donde venga. Ahora es `ops flow list [ops-root]`, y con recorrido va después de él.

- **Una lista de cargos o recorridos vacía dice si es porque no hay o porque no se pudo resolver el
  paquete.** Eran dos hechos distintos con la misma respuesta, y las acciones son opuestas. El aviso sale
  por `stderr` y sólo cuando la lista viene vacía; el código de salida y el `--json` no cambian, porque
  ese JSON lo consume el cron del ciclo de aprendizaje.

- **`automation check` nombra un `ops.config.json` ilegible en vez de culpar al motor.** La resolución del
  paquete cuelga de esa configuración, así que un JSON roto se propagaba como archivos del motor que faltan
  y el aviso mandaba a correr `npm install` sobre un motor instalado: la acción sugerida no arreglaba nada.

- **El catálogo de cargos y recorridos se encuentra también cuando el paquete vive un nivel arriba.** Es el
  layout que documenta el arranque —`npm install @ingeniomaps/cauce` en la carpeta de la empresa y la
  instancia adentro—, y 0.92.0 lo arregló para el motor y dejó afuera el catálogo. En ese estado `check`
  pasaba en verde y `agents list` contestaba **una lista vacía con exit 0**, indistinguible de «esta
  instancia no tiene cargos»; `flow list` igual, y `evaluate` culpaba a un `SKILL.md` que falta, mandando
  a mirar el cargo en vez del paquete.

  Si te pasó, no hacía falta bajar una segunda copia: con esta versión los cargos aparecen donde ya estaban.
  Verificado instalando el paquete publicado desde el registro y recorriendo la superficie del CLI: de 0
  cargos y 0 recorridos a **53 y 7**, sobre la misma instancia.

## [0.95.0] - 2026-09-16

### Corregido

- **El bloqueo dice hasta dónde llega el «dale».** Ofrece dos salidas y sólo la del pegado decía su
  alcance —«valen para ese conjunto y dejan de valer en cuanto cambie»—; la del chat, que es la que se
  ofrece primero, decía sólo «reintentá el mismo cambio y pasa».

  Faltaban **dos** cosas y no son la misma. Que el «dale» cubre lo que se frenó y nada más: por eso en una
  carpeta donde se itera cada archivo nuevo vuelve a frenar, que desde afuera se lee como si el guard se
  hubiera olvidado de que ya la aprobaste. Y que lo concedido **sigue valiendo en los mensajes
  siguientes** hasta que lo niegues — la mitad permisiva, la que no se nota porque lo que no ocurre es un
  bloqueo. `check` la muestra al final de la corrida; ahora también se dice al concederla.

  No cambia ningún permiso: el diff es texto.

- **Partir una tarea de una épica actualiza también sus historias, así que `check` no queda en rojo.** Una
  tarea que viene de una épica es además una historia suya, y la partición reemplazaba sólo su línea del
  BACKLOG. La lista de historias quedaba nombrando un slug que ya no existe en ninguna parte, y eso rompe
  por los dos lados según qué escriba la partición: con las subtareas declarando la épica, `check` falla en
  el acto con «BACKLOG \<sub\>: no existe en epic-NNN»; sin declararla pasa en verde y **la épica no puede
  cerrar nunca**, porque `closed` exige evidencia de cada historia y la original jamás la va a tener.

  Ahí es donde la unidad partida se cierra diciendo en qué se partió, que es lo que pide R25. **No va una
  entrada en `done/`**: ese directorio es la evidencia de lo que una tarea entregó, y una partida no
  entregó nada. Una tarea sin épica no necesita ningún cierre — deja de existir limpiamente y no hay
  cruce que se rompa.

  Si tenés una épica con una historia que ninguna tarea de la cola nombra y que nunca vas a poder cerrar,
  viene de esto: reemplazá esa historia por las de las subtareas que la partición dejó en el BACKLOG.

- **Un límite escrito en dos líneas ya viaja entero a los agentes.** Una viñeta bajo `### Límites` se
  recorría línea por línea, así que de un límite que no entraba en el ancho del archivo —que es como se
  escribe cualquiera de verdad— llegaba **sólo la primera línea**, cortada a mitad de frase. Y lo que se
  pierde ahí suele ser lo que el límite decide: «…crear, editar o borrar productos, pedidos o» sin el «no
  las hace un runner» que venía abajo.

  La continuación, además, volvía por el otro lado: no coincidía con ninguna viñeta declarada y `check` la
  reportaba como un párrafo «que no llega a los agentes», citando media frase tuya. O sea que el proyecto
  hacía lo que el aviso pedía, el aviso seguía sonando, y el límite que sí viajaba era el mutilado.

  Si escribiste tus límites en una sola línea para que el aviso bajara a cero, ya podés volver a partirlos
  donde corresponda. Una línea en blanco cierra la viñeta, así que la prosa que venga después sigue sin
  contar como límite — y sigue avisándose.

- **Una notificación de tarea de fondo ya no borra del chat a la persona que está mirando.** Cuando un
  guard frena algo, el bloqueo ofrece dos salidas: contestar «dale» —corto, y es lo que la persona ya está
  haciendo— o pegar líneas a mano en `planning/.ops-approval`. La primera sólo se ofrecía si el registro
  de la sesión decía que había alguien.

  Una notificación de una tarea de fondo entra por el mismo hook que un mensaje, así que el registro
  pasaba a decir que el último que habló no era una persona — y en el turno que esa notificación despierta
  **la persona sigue ahí**. El bloqueo le ofrecía la salida cara y terminaba copiando y pegando un comando
  que no hacía falta. Se disparaba solo en el patrón más común de una sesión larga: lanzar trabajo de
  fondo y retomar cuando vuelve.

  Que haya alguien a quien preguntarle pasa a ser de la sesión y no del mensaje. **No cambia ninguna
  autorización**: preguntar no es conceder, y lo que decide si un «dale» viejo cubre algo nuevo quedó
  intacto — un mensaje de hace tres turnos sigue sin autorizar lo que se frenó ahora. Y un recorrido de
  Cauce sigue sin ofrecer el «dale», porque ahí no hay nadie leyendo.

- **Una tarea que `autobuild` parte ya suelta su reserva, así que la corrida sigue.** El recorrido reclama
  la tarea antes de estimarla, y cuando Decompose la partía el reclamo quedaba puesto sobre un slug que ya
  no estaba ni en la cola ni en lo hecho. Con eso el runner quedaba ocupado por una tarea que no existe: el
  Claim de la primera subtarea se negaba —«este runner ya tiene …»— y la corrida entera terminaba en
  `claim-stuck` **sin construir nada**, dejando además `check` en rojo con un reclamo huérfano que soltaba
  una persona a mano.

  No dependía de una carrera ni de un id de runner compartido, que es a lo que el mensaje mandaba a mirar:
  pasaba siempre que Decompose partiera, que es una fase que el propio protocolo manda correr. Si venís de
  una versión anterior y tenés un reclamo huérfano de esto, se suelta con
  `node tools/ops.js release planning <tarea>`.

  La reserva se suelta **después** de comprobar que el reemplazo en el BACKLOG ocurrió: si no ocurrió, la
  tarea sigue viva y su reserva tiene que seguir puesta.

- **Un veredicto se lee de una sola forma, así que las dos cuentas de un registro coinciden.** El registro
  de una evaluación transcribe la respuesta literal del cargo —es la evidencia de la corrida—, así que su
  cuerpo puede traer cualquier cosa, incluida una línea con forma de veredicto. La cuenta de la última
  corrida las tomaba todas y la cuenta compuesta sólo las que cuelgan de un `### <caso>`: dos números
  distintos sobre el mismo archivo y ninguna forma de saber cuál valía.

  Con un registro de dos casos cuya respuesta citaba un veredicto ajeno, la última corrida decía «2 de 3»
  sobre una corrida que midió dos y pasó las dos. Ahora un veredicto es un caso con su línea —sin
  encabezado no hay a qué atribuirlo— y las dos lecturas salen del mismo lugar.

- **Un `agents fork` que se corta a la mitad se retira en vez de quedarse puesto.** Si el copiado fallaba
  —un archivo ilegible, disco lleno— quedaba medio cargo en `agents/`, y lo caro no es perder la copia: es
  dejarla. El catálogo pasa a resolver el slug contra esa copia, así que el intento siguiente ya no dice
  que se cortó sino **«ya lo mantiene esta empresa»** — y lo que la empresa mantiene es un contrato
  incompleto que ningún manifiesto registra, o sea que el aviso de deriva tampoco lo mira nunca.

  Verificado provocando el corte con un archivo ilegible: antes el segundo intento contestaba «ya lo
  mantiene esta empresa» con `.cauce` vacío; ahora no queda nada en `agents/` y arreglando lo que lo
  cortó el fork sale entero. Sólo se borra lo que esa llamada creó, y la ruta se comprueba contra la raíz
  de la instancia antes de tocarla.

- **`automation uninstall` no borra nada si no puede leer la configuración de tu runner.** Borraba
  primero y leía el `settings.json` al final, con un `JSON.parse` sin proteger. Con un archivo que alguien
  dejó a medio fusionar, el comando moría con «Expected property name or '}' in JSON at position 13»
  —que no nombra ni un archivo— **después** de haber borrado los workflows y los cargos: la instancia
  quedaba a medio desinstalar, con la configuración registrando guards que ya no existen.

  Verificado sobre un banco: el comando se para antes de tocar el disco, dice qué archivo no pudo leer, y
  arreglando ese archivo vuelve a correr.

- **Un adaptador propio que no trae `components` ya no rompe el borrador.** El README declara el contrato
  de un adaptador por sus tres funciones y no dice qué campos trae un item, así que un adaptador de la
  empresa —que es lo que ese README invita a escribir— puede no traerlo. Con `serviceFrom: "component"`,
  que es lo que trae el molde, eso era un `TypeError` sobre `undefined` sin nada que lo atribuyera.

  Sin el campo la respuesta es la que el borrador ya sabía dar: `service: ""` y «Debe definirse un
  servicio único para la promoción». Lo que se toleró es la ausencia y no el caso de uso: con un
  componente sigue resolviendo el servicio y con dos sigue sin resolverlo.

- **`promotionEpic` se valida también cuando la promoción es una épica.** El campo se exigía `NNN` sólo
  para una historia. Para una épica es opcional —sin él el número lo elige el motor—, y por eso nadie lo
  miraba; pero cuando está se usa tal cual para armar el nombre del archivo. O sea que el único caso sin
  validar era el único que escribe una ruta con lo que diga el campo.

  `promotionEpic: 7` producía `epic-7-…`, que el roadmap no reconoce como épica: la promoción se anunciaba
  bien y lo escrito quedaba invisible para la cola. Y con un `..` la épica salía de `roadmap/` entera.
  Verificado sobre una instancia desechable: `integration check` daba exit 0 y `integration promote`
  devolvía «✓ jira:DEMO-42 promovido como epic» dejando el archivo fuera del roadmap. Ahora `check` lo
  rechaza con «promotionEpic debe ser NNN» y `promote`, que valida antes de escribir, no llega a tocar el
  disco.

- **El aviso de commits sin entrada de DONE vuelve a decir lo que ve.** El filtro por fecha leía la
  columna del `git log` en una posición fija, y `%h` no mide siempre lo mismo: git sube el largo del hash
  abreviado solo cuando el repositorio crece. En cuanto pasa de siete, la posición fija lee un espacio en
  vez de la fecha y **ningún commit pasa el filtro**, así que el aviso quedaba mudo sin decir por qué —la
  peor forma de que una puerta falle, porque se lee igual que «no hay nada que avisar»—. Ahora se parte
  por espacios y se lee el campo.

  Verificado en este repositorio, donde el abreviado mide ocho: el aviso pasó de cero a «66 commit(s)
  desde 2026-09-15 que ninguna entrada de DONE nombra». La prueba nueva lo fija en 7, 8 y 12.

- **El sello de un registro de evaluación se escribe en el frontmatter y no en el cuerpo.**
  `markConsolidated` buscaba `status:` en el documento entero. Un registro nace sin `status` en su
  frontmatter y su cuerpo transcribe la respuesta literal del cargo: si esa respuesta traía una línea
  `status: <algo>` en la columna cero, el sello aterrizaba ahí y el frontmatter quedaba sin sellar.

  Un registro sin sellar vuelve a entrar en la propuesta siguiente, o sea que el mismo hallazgo llega dos
  veces — que es exactamente lo que sellar existe para evitar.

- **`ops -h` pide la ayuda, y un nombre heredado de `Object` ya no pasa por comando.** Dos huecos de la
  puerta de entrada del CLI, los dos de la misma forma: leer algo que no era ni un comando ni una bandera.

  `-h` se anunciaba en el uso y no existía: el parser sólo reconoce lo que empieza con `--`, así que caía
  de argumento posicional y `ops check -h` contestaba «no existe el planning en …/-h» **saliendo con 0**.
  Pedir ayuda y recibir un error sobre un directorio inventado es la peor forma de contestar, porque
  parece que el comando corrió.

  Y la tabla de comandos se consultaba con `FLAGS[comando]`, que resuelve contra `Object.prototype`:
  `ops constructor` salía con 0 sin hacer nada y `ops toString --json` moría con
  «FLAGS[command].includes is not a function». No es un nombre exótico — es lo que sale de pasarle al CLI
  una variable que vino vacía o mal leída desde un script.

- **El tope de horas de una propuesta deja de desaparecer cuando la configuración no lo declara.** La
  validación de propuestas lee `runner.maxTaskHours` de `ops.config.json` y tenía un default de cuatro
  horas para cuando no se puede leer. Ese default sólo cubría el archivo ilegible: un `runner` sin el
  campo no lanza nada, así que el tope quedaba en `undefined` y la comparación pasaba a ser falsa
  siempre. O sea que el límite no se aflojaba: se iba entero, y en silencio.

  Se nota poco a propósito de lo que es — lo que deja de pasar es un **rechazo**—, así que una propuesta
  de cuarenta horas viajaba a Jira sin que nada la mirara. Verificado sobre una configuración con
  `runner: {}`: antes no salía ni un error, ahora sale «supera 4h; debe dividirse o justificarlo».

  Si tu `ops.config.json` declara `maxTaskHours`, nada cambia — `check` ya lo exigía mayor que cero.

- **`rm -r` sobre la raíz o el home se frena también entrecomillado.** La regla reconocía el destino sólo
  desnudo: `rm -rf /` bloqueaba y `rm -rf "/"`, `rm -rf "$HOME"`, `rm -rf ${HOME}`, `rm -rf $HOME/` y
  `rm -rf -- /` pasaban. La comilla es lo que más importa — citar una variable es la forma *correcta* de
  escribirlo en bash, así que el hueco premiaba al que tiene el hábito bueno.

  Los borrados con destino concreto siguen pasando, también entrecomillados: se comprobó con
  `rm -rf "$HOME/proyecto/dist"` entre otros cinco.

- **Un commit escrito en dos líneas vuelve a pasar por los guards.** `isCommit` reconocía el commit
  después de `;`, `&` o `|` y no después de un salto de línea ni dentro de un subshell — o sea que
  `git add x` ⏎ `git commit -m x` en un solo Bash, que es como se escriben dos pasos, dejaba sin correr
  `governance`, `dependencies` y `verify`, y también el bloqueo que impide stagear y commitear a la vez.

  `bash -c "git commit …"` sigue sin reconocerse, a propósito: hacerlo exige desentrecomillar, y eso
  choca con que el mensaje de un commit sea dato y no código.

- **Redirigir con `>|` ya no evade el guard que mira a dónde escribe un comando.** `>|` es el override de
  `noclobber` y escribe igual que `>`, pero el guard no veía su destino: el comando se partía en segmentos
  por `|` antes de buscar destinos, así que la redirección quedaba separada de su ruta. Verificado sobre
  un banco: `echo x > <fuera>` y `echo x >> <fuera>` bloqueaban y `echo x >| <fuera>` pasaba.

  Importa porque una de las rutas que se escriben por ese hueco es `planning/.ops-approval` — la
  aprobación que después habilita el push a la rama viva—, que es lo que cerraron el 098 y el 119.

  Las tuberías corrientes siguen sin producir destino: se comprobó con `ls | wc -l` y `a || b`.

## [0.94.0] - 2026-09-16

### Agregado

- **Cinco reglas nuevas, traídas de una empresa que las pagó.** Salieron de revisar las reglas propias de
  una instancia real: no son ideas, cada una tiene adentro la corrida que costó.

  - **R24 — una premisa sobre el propio código se abre antes de usarla.** R14 ya exigía registro para lo
    que se afirma de una herramienta o una norma, y dejaba afuera tu propio repositorio, que es donde
    nadie te va a discutir. Cuatro corridas perdidas en un día, todas frenadas en la puerta y ninguna por
    el código: la aceptación pedía algo que el sistema no hace. Y el ancla `archivo:línea` se abre, no se
    copia — una función se movió de la 555 a la 733 en la misma sesión.
  - **R25 — el identificador de una unidad de trabajo no cambia mientras está viva.** Renombrar un slug a
    mitad de camino rompe el cruce entre la cola y lo hecho **sin que nada falle**: cada lado se lee
    coherente por separado. Una tarea partida en cuatro y cerrada con otros nombres costó 594k tokens de
    la corrida siguiente para descubrir que ya estaba construida. Si el nombre tiene que cambiar, va
    `slug-nuevo (antes: slug-viejo)` hasta cerrar.
  - **R26 — una puerta acota su propio costo y no escribe en el árbol que juzga.** Dos revisores lanzando
    la misma suite fueron cuatro corridas en cuatro minutos: el sistema operativo mató la sesión entera
    con un pico de 24,2 GB. Y un formateador con `--fix` o un build que limpia su salida editan el trabajo
    de quien está commiteando. Una puerta que estorba se saltea, y desde ahí no protege de nada.
  - **R27 — una defensa se aplica por defecto y cada excepción se declara sola.** Con lista de lo que
    protege, todo lo que se agregue después nace afuera y nada lo compara. Incluye el caso que más se
    disfraza: «esta comprobación no corre en desarrollo» es una quita escrita como agregado, y garantiza
    que el camino de producción sea el único que nunca se ejercitó.
  - **R28 — un estado lo dice el contenido de un archivo, nunca su presencia.** Un centinela cuya única
    información es existir obliga a que borrarlo sea parte de la resolución, y eso alguien lo olvida: la
    corrida arranca, lee todo el estado y recién ahí muere. Pasó dos veces el mismo día, a 42k tokens por
    vez. Es lo que el WIP de Cauce ya hace bien con `status: IDLE`.

- **R9 dice cuándo se puede quitar lo que está en uso.** Exigía probar la ausencia de lo quitado y nunca
  decía cuándo se puede quitar. Ahora: lo que está en uso no se corta, se depreca, y la marca dice las
  dos cosas que la vuelven una salida y no una etiqueta — qué lo reemplaza, y qué condición permite
  borrarlo. Sin la primera, quien lo usa no sabe a dónde ir; sin la segunda, el deprecado es código
  muerto con un cartel puesto y se queda para siempre.

  Y lo que no llama nadie es otra cosa: se borra. Cortar de golpe rompe a un consumidor que nadie miró;
  deprecar lo que nadie usa cuesta mantener dos caminos para nadie.

- **R10 dice a dónde va lo que se publica, no sólo quién lo autoriza.** La autorización decía si se
  publica y nunca dónde. Ahora: lo que se publica va al repositorio en el que estás trabajando, y si ese
  remoto es un fork, va al fork — con la rama cortada de la suya, porque una rama cortada del principal
  es la antesala de mandarle el PR.

  No se deduce del contexto: que la herramienta resuelva sola el repositorio de origen no es una
  autorización, ni lo son que el cambio «obviamente tenga que llegar ahí» ni que un PR anterior haya ido
  a parar allá. Saltar al principal se pide con todas las letras y para ese caso concreto.

  Es de las pocas sin vuelta atrás: un PR mal apuntado es trabajo publicado en el repositorio de otro
  equipo — lo vieron, les llegó la notificación, y cerrarlo no deshace nada de eso.

- **R8 dice que la prohibición de firmas de IA cubre todo lo que se publica**, no sólo el mensaje del
  commit: el título y el cuerpo del pull request, y los comentarios que se dejen ahí. Y casi nunca es
  algo que alguien tipea — lo agrega la herramienta sola, al final del texto que escribiste—, así que
  cumplirla es revisar la salida antes de publicarla, no acordarse de no escribirla.

- **R17 dice qué cuenta como una condición, que es lo que volvía incontable su umbral.** La barra son
  cinco condiciones de aceptación y nunca decía qué es una. Ahora: una condición es un resultado que se
  puede mirar por separado, no una viñeta. Cinco viñetas que describen el mismo invariante desde cinco
  ángulos son **una**, y contarlas como cinco parte por la mitad lo que era una sola cosa.

  La otra dirección es la cara y la que nadie mira: una frase que promete dos resultados con vidas
  distintas —«valida el pago y manda el email»— son **dos**, y escrita como una el umbral no se entera
  nunca. Contar de menos no dispara nada, y se lee igual que una unidad chica.

  La prueba no pide criterio: si al tachar una condición las otras siguen valiendo, son distintas; si
  tachar una deja a las demás sin sentido, era una sola dicha en partes.

- **R9 ahora pide que la mutación quede escrita, no sólo que se corra.** R9 ya exigía romper, con el
  código puesto, exactamente lo que el caso dice cuidar, y verlo ponerse rojo. Lo que faltaba es que eso
  quedara en la aceptación: una línea con qué se rompe y qué prueba tiene que ponerse roja. Sin ella,
  quien revisa no puede distinguir la mutación que se corrió de la que se pensó, y lo único que le queda
  es volver a correrla — o sea rehacer el trabajo que delegarlo evitaba.

  Y escribirla antes cambia lo que se escribe: una aceptación que tiene que nombrar qué romper deja de
  poder pedir algo que ninguna mutación puede tocar. Si no hay nada que romper, no había propiedad que
  cuidar, y eso se ve al redactarla en vez de al final de la vuelta.

  **Lo que cuesta:** el bloque de reglas que cada agente carga al arrancar pasa de **39,1 a 49,1 KB**.
  Está medido, no estimado, y el umbral del aviso de `check` **no se movió**: sigue en 64 KB, porque lo
  que mide es cuánto agregaste vos, y subirlo para hacerle lugar al piso apagaría justamente eso. Quedan
  ~18 KB de margen antes de que el aviso hable.

### Corregido

- **El aviso de peso mide lo que agregaste vos, no el total.** Comparaba el bloque entero contra el
  umbral, así que el piso del toolkit y tus reglas salían del mismo bolsillo: decía «tu bloque pesa»
  cuando la mitad la habíamos puesto nosotros, y cada regla que Cauce agregaba te achicaba el margen sin
  que nadie lo decidiera. Ahora el umbral se compara contra tus reglas, y la línea dice las dos cosas —
  `114.8 KB en cada agente (93.8 KB propias)`—: el total es lo que paga el agente y lo propio es lo único
  sobre lo que podés hacer algo.

  **Una instancia recién creada ya no puede cruzarlo**, por más que el piso crezca. Antes era una cuenta
  que había que rehacer cada vez que agregábamos una regla.

  El número **no se movió**: sigue en 64 KB, a propósito, porque cambiar qué se mide y cuánto a la vez
  deja sin saber cuál de los dos movió el resultado. Lo que sí quedó medido es que 64 está por debajo de
  lo que una empresa real usa — una instancia medida tiene 93,8 KB de reglas propias — así que elegirlo
  con esa evidencia es lo que sigue.


- **Escribir tu propio `process.md` ya no te deja sin las reglas que no ibas a reemplazar.** El override
  es por nombre de archivo, así que reemplazar «pensar antes de editar» por tu versión se llevaba puesto
  el archivo entero: R16, R17, R20, R21 y R22 dejaban de llegarle a todo agente. `check` te lo decía —lo
  hace desde 0.57.0— y no había nada que hacer al respecto, porque conservarlas exigía copiar su texto y
  una copia deja de recibir las mejoras del `upgrade`.

  Ahora **R16, R20, R21 y R22 viven en `system/runs.md`** —lo que cuesta una corrida, cuándo una medición
  vale, cómo se retoma lo interrumpido y qué no se toca mientras se mide—, un archivo que reemplazar tu
  proceso no toca. `system/process.md` se queda con R1..R4 y R17.

  **No tenés que hacer nada**: el archivo nuevo llega en tu próximo `upgrade`. Si ya sobrescribiste
  `process.md`, esas cuatro reglas vuelven a regir solas, y el aviso de `check` se acorta a lo que de
  verdad reemplazaste. El bloque de reglas pasa de cuatro archivos a cinco y pesa lo mismo: 39,1 KB.

## [0.93.0] - 2026-09-16

### Corregido

- **Declarar un límite ahora lo saca del aviso, en vez de sumarlo.** Si escribiste tus límites como
  viñetas bajo `### Límites` —el camino que 0.92.0 agregó—, `check` los contaba igual como párrafos que
  no llegan a los agentes, y también contaba la frase con la que presentabas la lista. O sea que hacer
  lo correcto **subía** el número: medido sobre un banco, de 4 párrafos avisados pasaba a 6.

  Ahora una viñeta declarada y la prosa que la presenta no entran en el aviso. Lo que sigue entrando es
  la prosa de afuera del bloque, que es lo único para lo que el aviso existe: descontar el bloque entero
  la habría silenciado, porque a un bloque `### Límites` no lo cierra nada más que el próximo
  encabezado y el del molde se extiende hasta donde escribas el tuyo.

- **El aviso te manda al camino declarado y no a imitar una gramática.** Decía que tus párrafos no
  llegan «porque no arrancan con «El runner», «Debe» o «Nunca»». Desde 0.92.0 hay una forma de
  arreglarlo sin imitar nada, y es la que el aviso nombra ahora: sumar el límite como viñeta bajo
  `### Límites`. Los dos caminos siguen valiendo; lo que cambia es cuál se recomienda.

## [0.92.0] - 2026-09-15

### Agregado

- **El ciclo de aprendizaje ya no deja la propuesta mensual en «por definir».** Cada mes, el ciclo
  consolida lo que recomendaron los informes semanales de un cargo y abre su propuesta. Hasta ahora la
  dejaba con el molde intacto en «Cambio propuesto», y eso no es aprobable: nadie firma una intención.
  Medido sobre `2026-09`: de **25** propuestas, **15** se archivaron con el molde y sólo 10 llegaron a
  algo, las que alguien completó a mano.

  El recorrido que escribe el cambio exacto —archivo por archivo, contrastado contra los casos
  adversariales vigentes— ya existía; lo que faltaba era que el ciclo lo corriera. Ahora lo corre, y sólo
  sobre las propuestas que quedaron sin decidir.

  **Sin credencial no se rompe nada**: el paso avisa y el mes queda como quedaba antes, con la rama
  empujada y sus sellos puestos. Lo mismo si la corrida falla. Lo que decide si se te pide una firma
  sigue siendo el documento, no quién lo llenó.

- **Tu proyecto puede declarar sus límites en una lista, y `check` avisa del que no llegue a los agentes.**
  Los límites que tu proyecto amplía o restringe viven en `organization/workspace.md`, y de ahí viajan al
  preámbulo de cada subagente. Hasta ahora se reconocían **por cómo arrancaba el párrafo** —«El runner»,
  «Debe», «Nunca»—, que es la gramática del molde: un límite escrito de cualquier otra forma, que es como
  lo escribiría cualquiera, no llegaba a ningún agente y nada lo decía. La lista salía más corta y se leía
  igual de completa.

  Ahora esa sección trae un `### Límites` con una viñeta por límite:

  ```markdown
  ### Límites

  - En `api/` no se tocan migraciones sin aprobación de quien administra la base.
  ```

  **Lo que ya tenías escrito sigue contando**: la gramática vieja se lee igual, así que no hay nada que
  migrar. Y lo que no entra por ninguno de los dos caminos ya no se pierde callado — `ops check` lo
  reporta citando el párrafo, para que sepas cuál de tus límites se quedó afuera.

  El ejemplo del molde viene comentado a propósito: un límite de ejemplo que se obedece es peor que
  ninguno, porque nadie lo escribió y todos lo cumplirían.

- **Reanudar una tarea dejó de pagar la fase que no tiene nada que hacer, y la corrida dice cuándo lo
  hizo.** Una tarea que para antes de Commit —una revisión que pidió algo, un gate en rojo— deja su plan
  en disco con los pasos tildados. Al relanzar, el recorrido entraba igual a Build: el agente releía el
  WIP, comprobaba el disco y contestaba que no había nada pendiente. Hacía lo correcto; lo que costaba
  era haberlo llamado — **893.000 tokens sobre tres corridas de una sola tarea**, medido.

  Ahora, si el WIP no tiene pasos pendientes, esa llamada no se hace y la fase se anuncia como
  `Build (reanudado)`, que viaja al resultado de la corrida y a la entrada de DONE. Antes una corrida
  reanudada se veía idéntica a una que construyó salvo por el costo, así que comprobar qué se reutilizó
  exigía abrir la salida cruda y sumar tokens a mano.

  **Lo que no cambia es qué se revisa.** No se saltea la fase, se saltea la llamada: Review, Verify y QA
  siguen mirando el diff real que quedó en disco, venga de la corrida que venga. Y si tu instancia no
  emite ese dato, todo se comporta como antes.

- **Una raíz puede declarar qué rutas lee su puerta, y un archivo sucio que el gate no va a abrir deja de
  forzar la copia del índice.** Al commitear, `verify` corre sobre el árbol cuando árbol e índice
  coinciden y materializa el índice en un temporal cuando difieren. Hasta ahora alcanzaba **cualquier**
  archivo sin trackear para materializar: un README a medio escribir, un `tsconfig.json` que dejó otra
  sesión, o el propio `planning/.ops-approval` que el bloqueo te manda crear para aprobar unas rutas.

  Ahora, junto a `verify`, una raíz puede declarar `scope`:

  ```json
  "workspaceRoots": [
    { "name": "web", "path": "apps/web", "verify": "pnpm build",
      "scope": ["src/**", "package.json", "tsconfig.json"] }
  ]
  ```

  Con eso, sólo materializa si alguna ruta que difiere cae dentro de ese alcance. Los patrones son
  relativos a la raíz —el `scope` de `apps/web` habla de `src/**`, no de `apps/web/src/**`— y admiten
  `*` dentro de un segmento, `**` cruzando segmentos y `?` por un carácter.

  **Sin `scope` declarado no cambia nada**: cualquier delta sigue forzando la copia, que es el
  comportamiento de siempre. El campo es opcional y no hay que adoptarlo.

  Lo que se gana no es tiempo. Dentro de la copia `node_modules` viaja por **enlace**, y eso rompe
  cualquier build de Turbopack sin salida del lado del proyecto: ahí un archivo ajeno al commit no cuesta
  segundos, deja el gate sin poder pasar. Declarar el alcance es lo que lo destraba.

  Dos bordes que valen la pena saber. Un directorio sin trackear llega colapsado —git reporta `extra/` y
  no dice qué hay adentro—, así que se materializa igual si algún patrón apunta hacia adentro de él. Y
  una ruta que no cuelga de **ninguna** raíz declarada también cuenta: puede ser de la instancia o de un
  servicio que nadie declaró, y suponer que no importa es justo lo que este campo existe para evitar.

### Corregido

- **El motor se encuentra aunque lo hayas instalado un nivel arriba de tu instancia.** Si corrés
  `npm install @ingeniomaps/cauce` en la carpeta de tu empresa y después `cauce init ops`, la instancia
  queda adentro y el motor arriba. Ese es el árbol que el propio comando sugiere, y hasta ahora
  `automation check` devolvía **nueve errores** sobre un motor que estaba instalado, cada uno mandándote a
  correr `npm install` otra vez un nivel más abajo — o sea a bajar una segunda copia del paquete.

  Ahora se busca también en la raíz que tu `ops.config.json` declara, que es la misma que el motor ya usa
  para decidir dónde instalar el runner. Si tu `<empresa>-ops` tiene su propio `node_modules`, ése sigue
  ganando y nada cambia.

  **No se adivina el layout, se lee el declarado**: no se sube por el árbol como hace Node —en un monorepo
  podría encontrar un motor de otra versión, en silencio— ni se mira si hay repositorio git, porque tener
  la instancia sin versionar es un uso legítimo.

  Vale para los tres caminos, no sólo para el CLI: el shim que lanza cada guard y el bridge de Antigravity
  repiten esa búsqueda porque corren antes de poder cargar el motor, y los tres se actualizaron juntos.

## [0.91.0] - 2026-09-15

### Agregado

- **`ops contract <ops-root> [--json]`: el contrato de tu proyecto sin pasarlo por un modelo.** Devuelve lo
  que un recorrido necesita antes de la primera fase —cómo se llama el proyecto, dónde puede escribir, con
  qué se verifica, qué límites rigen y qué formatos exige `planning/`—, derivado de `ops.config.json`,
  `AGENTS.md`, `organization/workspace.md` y `planning/PROTOCOL.md`.

  Los diez campos salen de parsear, así que el comando no inventa nada y cuesta cero tokens. `autobuild` los
  derivaba con un agente que leía esos cuatro archivos y los transcribía; el comando existe para que deje de
  hacerlo, aunque el recorrido todavía no lo use.

  **Falla en vez de contestar a medias.** Si falta uno de los cuatro archivos, o si `AGENTS.md` o
  `PROTOCOL.md` perdieron la sección de la que sale el contrato, se niega nombrando cuál y manda a correr
  `ops upgrade`, que es quien los repone: entregar límites vacíos es peor que parar, porque un límite que no
  llega se lee igual que uno que no existe. Que `organization/workspace.md` no declare excepciones **no** es
  un error: es tuyo y puede no tenerlas.

### Corregido

- **La protección que evita que un gate te borre el `node_modules` no estaba puesta.** `verify` corre los
  gates sobre una copia del índice con el `node_modules` enlazado a tu proyecto, y le pedía a pnpm que no
  sincronizara dependencias antes de correr el script. Se lo pedía con un nombre que pnpm ignora: sus
  ajustes se leen del entorno con el prefijo `pnpm_config_`, no con el de npm. La petición viajaba y se
  descartaba en silencio desde 0.75.0, que es cuando entró.

  Con pnpm 11, donde esa comprobación viene encendida, se nota al commitear un cambio de `pnpm-lock.yaml`:
  pnpm decide reinstalar, la reinstalación **empieza borrando** el directorio de módulos —el tuyo, por el
  enlace— y sin TTY aborta con `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY`. Los gates vuelven en poco más
  de un segundo, así que se leen como una suite en rojo que no lo es.

  Si te está pasando, alcanza con actualizar. Lo que **no** corresponde, aunque el bloqueo lo sugiera, es
  aprobar las rutas o `OPS_SKIP_VERIFY=1` —las dos commitean sin haber corrido el gate— ni `CI=true`, que
  desarma justamente la confirmación con la que pnpm frena antes de purgar.

- **`plan-first` decía que no tenías plan cuando lo que pasaba es que no veía el tuyo.** El guard busca el
  WIP de tu id, y ese id sale del árbol donde corre el proceso mientras no exportes `CAUCE_RUNNER`. Con la
  instancia al lado de dos repositorios, el mismo cambio se frenaba o pasaba según el directorio desde el
  que saliera la llamada — y el bloqueo decía «WIP está en IDLE» con el plan escrito y a la vista, así que
  mandaba a escribir de nuevo algo que ya existía.

  Ahora distingue las dos causas: si hay un plan bajo otro id, lo nombra con su tarea y te manda a volver a
  ese id —`ops runners <planning>` los lista— o a montar el tuyo con `ops worktree <planning> <tarea>`. Y
  **deja de ofrecerte aprobar la ruta**, porque con un plan a la vista eso escribe por vos que el cambio no
  es trabajo de ninguna tarea, que es falso y queda en el registro.

  Lo que no cambia: el plan de otro runner sigue sin autorizarte a escribir. Si van a trabajar en paralelo,
  cada agente necesita su árbol —`git worktree`, que es lo que `ops worktree` prepara—, y ahí el problema no
  existe porque no comparten archivos.

## [0.90.0] - 2026-09-15

### Corregido

- **Registrar un piso de cobertura dejó de poder borrar los demás.** `coverage:update` reconstruye el
  registro entero desde lo que midió, y se negaba a escribir sólo cuando no había medido **nada**. Una
  corrida que arrancaba y moría temprano dejaba un lcov corto: los archivos que no llegó a medir
  desaparecían del registro, y la actualización salía en 0 anunciando éxito. El registro mutilado se lee
  igual que uno sano, y con él la puerta de cobertura deja de cuidar lo que perdió.

  Ahora se niega cuando un archivo que **sigue en disco** y **tenía piso** no fue medido, nombrando
  cuáles. Retirar un archivo del motor sigue funcionando igual —ya no está en disco, así que no cuenta
  como pérdida— y no hace falta ninguna excepción que recordar.

  **No tenés que hacer nada.** Si al correr `coverage:update` te frena, la corrida quedó corta: revisá que
  la suite haya terminado antes de volver a intentarlo.

- **Las pruebas que juzgan el repositorio dicen cuándo no pueden correr, en vez de fallar.** Cinco de
  ellas preguntan qué está trackeado —lo que define al árbol es el índice de git, no lo que haya en el
  disco— y en una copia sin `.git` fallaban con mensajes que hablaban del repositorio: «la suite declara
  0 pruebas y el piso es 505». Se leían como defectos reales y no lo eran.

  Importa porque R23 manda ejercitar en una copia lo que las pruebas invocan, y la copia barata
  —`git ls-files | tar`, `git archive`— no lleva `.git`. Ahora se saltean declarando qué les falta.

  **No tenés que hacer nada.** Si corrés la suite sobre un tarball o un árbol sin historia, vas a ver
  cinco `skipped` con su razón donde antes veías cinco rojos.

- **El ciclo de aprendizaje ya no te deja una rama muerta por cada informe que se mergea solo.** Cuando un
  informe declara `propone: no`, su PR se cierra con auto-merge y su rama quedaba viva para siempre: una
  por cargo y por semana. La bandera que debía borrarla estaba puesta desde la versión anterior y nunca
  hizo nada — `gh` borra «after merge», y con auto-merge el comando termina al *armar*, horas antes de que
  el merge ocurra.

  Ahora lo hace `delete-merged-branch.yml`, que escucha el cierre del PR y borra la rama cuando el merge
  ya pasó. Vale para cualquier PR mergeado de este repositorio, no sólo los del ciclo: el mismo hueco
  dejaba vivas las ramas de release. Una rama de un fork no se toca, y un PR cerrado sin mergear tampoco
  —ahí la rama es trabajo de alguien—.

  **No tenés que hacer nada.** Si tu instancia acumuló ramas `automation/*` de informes ya mergeados, se
  borran a mano una vez; las nuevas ya no se quedan.

- **La puerta del toolkit dejó de dar verde con pruebas en rojo.** `npm run ci` encadena cinco
  comprobaciones y ninguna miraba el veredicto de la suite: entraba sólo por la de cobertura, que ignora
  a propósito el exit de `node --test` para poder registrar un piso cuando la puerta está en rojo. Esa
  tolerancia valía para los dos caminos, así que seis pruebas fallando terminaban en exit 0 — y eso es lo
  que corre en CI y lo que exige `prepublishOnly`.

  Ahora la tolerancia es del modo que la necesita: al **registrar** (`coverage:update`) el exit de la
  suite sigue sin decidir, y al **comprobar** una suite en rojo corta la corrida antes de mirar
  cobertura, diciendo que el rojo es de las pruebas y no de los pisos.

  **No tenés que hacer nada, pero puede que veas rojo donde antes veías verde:** si tu proyecto tenía
  pruebas fallando, la puerta las muestra en vez de taparlas. Ninguna versión publicada de este toolkit
  llegó a npm con la suite en rojo — se comprobó clonando y corriendo las cinco últimas.

## [0.89.0] - 2026-09-14

### Agregado

- **`check` te avisa de una condición de aceptación que no se va a poder comprobar, antes de que el
  recorrido la construya.** Verify corre antes que Commit y que Done, así que una condición que pide ver
  el commit, el reclamo, `done/` o la evidencia registrada pide algo que todavía no existe cuando se la
  mira. El recorrido ya lo frenaba —y hace bien—, pero recién en Verify: en la corrida que originó esto
  fueron **1,2 M de tokens y once agentes** para terminar con el trabajo hecho, sin commit y sin poder
  cerrar la tarea.

  Ahora sale de `check`, cuesta un regex sobre la cola y corre en los cuatro carriles, incluidos los que
  saltean Ready. Avisa y no falla.

  El aviso dice qué hacer: esa cláusula va en `tests:`, `qa:` o `commit:` de la entrada de DONE, que ya la
  exigen, así que repetirla en la aceptación no agrega garantía sino un bloqueo. **Y si de verdad va ahí,
  se declara y deja de avisarse**: `(fuera de verify: <razón>)` dentro de la propia condición, la misma
  salida explícita que `(sin partir: …)` y que `n/a — razón`. Se juzga condición por condición, así que
  declarar una no exime a las demás.

- **Una regla propia puede declarar sobre qué superficie rige, y entonces se nombra sin cargarse.** Todo
  lo que `planning/rules/` contiene viaja en el contexto de arranque de **cada** agente —medido: sólo las
  cuatro reglas que trae Cauce son 38,3 KB por agente—, así que una regla de dos páginas que importa en
  una tarea de cada cien se leía cien veces.

  Ahora una regla puede llevar `aplica: <superficie>` en su frontmatter. El bloque que escribe
  `automation install` la lista —`- ruta (aplica: pagos)`— en lugar de importarla: pesa una línea en vez
  de su archivo entero. **Sigue rigiendo igual**: `ops context` la devuelve entre las reglas del proyecto
  y el recorrido se la nombra a cada agente que toca código, para que quien trabaje sobre esa superficie
  la lea antes de planificar o construir.

  **Sin el campo, la regla se carga como siempre, y por eso este cambio no te pide hacer nada.** El
  default es ése a propósito: una regla que no se leyó no existe, así que apartarla del arranque es una
  decisión del proyecto y nunca algo que se deduzca. El valor lo elegís vos —`pagos`,
  `infraestructura`— y lo único que tiene que lograr es que quien lo lea sepa cuándo le toca. Conviene
  para lo que es de un dominio acotado; lo que gobierna cómo se trabaja se paga y se carga.

- **`automation install` te dice lo que ese bloque va a pesar, y `check` avisa cuando ya pesa demasiado.**
  Escribir una regla propia encarece todas las corridas futuras de todos los agentes, y hasta acá no lo
  decía nadie: había que sumar los tamaños a mano después de una corrida cara para enterarse.

  Al instalar, una línea declara cuántos archivos carga el bloque, cuánto pesan y cuáles son los dos más
  grandes —`el bloque de reglas carga 4 archivo(s), 38.3 KB en cada agente (las más grandes: conduct.md,
  process.md)`—. Y `check` lo repite como advertencia cuando el total pasa de **64 KB**, que es el umbral
  elegido para dejar unos 26 KB de reglas propias por encima del piso que trae Cauce: más abajo avisaría
  en toda instancia recién creada y se apagaría por ruido el primer día.

  **El número va en KB y no en tokens** a propósito. Los bytes los mide el motor y se pueden comprobar;
  la equivalencia en tokens depende del modelo y del tokenizador, y una cifra estimada en una salida que
  se cita para decidir vale menos que una exacta. Como referencia, en el repositorio de Cauce esos 38,3 KB
  rondaron los 10 K tokens medidos una vez, pero eso es una observación y no un factor de conversión.

  Una regla declarada con `aplica:` no suma en ninguno de los dos números: es justamente lo que se apartó
  del arranque, y contarla haría que declararla no sirviera de nada.

### Corregido

- **El ciclo de aprendizaje ya no te pide una firma por una propuesta que no decide nada.** El documento
  se compone en dos tiempos: `learn --proposal` lo arma desde los informes y los veredictos, y
  `/agent-propose` escribe el cambio concreto. El ciclo automático corre el primero y abría el PR ahí
  mismo, con «Cambio propuesto» todavía en el molde — así se gastaron siete firmas el 2026-09-14, y
  ninguna de esas propuestas se pudo aplicar.

  Ahora el paso que compone dice si el documento quedó sin decidir y el que publica lo lee. **La rama se
  empuja igual**, porque lleva el sello de los informes que el ciclo consumió y perderlo haría entrar el
  mismo material el mes siguiente; lo que se posterga es sólo el PR. La corrida lo deja anotado con el
  nombre de la rama y qué falta para abrirlo.

  Y `ops learn <cargo> --proposal` lo dice también cuando lo corrés a mano: «sin cambio decidido: falta
  correr agent-propose antes de que esto se pueda firmar». Sin esa línea el archivo se ve terminado y no
  lo está.

- **En `mode: sidecar`, `ops context` ya no te esconde tu propio plan.** El id de un runner sale de
  `CAUCE_RUNNER` y, sin ella, del repositorio git desde el que corrés el comando. Cuando la instancia es su
  **propio repositorio** —lo habitual: `<empresa>/` y `<empresa>-ops/` al lado— hay dos raíces, y el mismo
  agente recibe un id distinto según desde cuál invoque. El reclamo queda guardado con uno, el plan se
  escribe bajo ese mismo, y la sesión que pregunta desde el otro recibe `WIP idle` y `TAKEN … vos, desde
  otro runner`: el plan existe, con sus pasos ya tildados, y nadie lo nombra.

  Ahora `context` lo nombra. Cuando una tarea tomada es tuya y hay un plan escrito bajo el otro id, agrega
  una línea `PLAN` con el archivo y con el `export CAUCE_RUNNER` que lo recupera. Y **`ops claim` imprime
  ese id al tomar la tarea**, que es el momento en que se conoce: hasta ahora sólo lo hacía `ops worktree`,
  y en sidecar no se crea ningún worktree, así que el dato no quedaba escrito en ninguna parte.

  El id sigue sin ser estable, y eso no cambia: derivarlo de quién sos en vez de dónde corrés le devolvería
  a cada agente de la máquina la tarea del otro, que es peor que no encontrar la propia. Lo que cambia es
  que dejó de ser mudo.

- **`automation install` te dice dónde quedaron los recorridos cuando no es donde estás parado.** En
  sidecar el adaptador se instala en la carpeta de la empresa y no en el repo ops desde el que corrés el
  comando. Eso es correcto y no cambió —es donde el runner ve el código—; lo que engañaba era la salida.
  Avisaba «el runner se abre en `<raíz>` — ahí queda su configuración» para un archivo, y a continuación
  nombraba los recorridos en relativo, `.claude/workflows/autobuild.js`, que leído desde el repo ops apunta
  a una carpeta vacía. Cerraba en «adaptador operativo (0 advertencia(s))», y todo era cierto.

  Lo que cuesta es que un recorrido se invoca **por nombre**, y el nombre lo resuelve la sesión contra la
  carpeta en la que se abrió: una sesión abierta en el repo ops no encuentra ninguno y recibe un «no
  existe» que se lee como instalación fallida. Ahora la salida nombra ese directorio con su raíz puesta y
  desde dónde hay que abrir la sesión para que los vea.

- **Los recorridos dejan de depender de la carpeta desde la que se abrió la sesión.** La raíz que traen
  escrita —la que usan para nombrar el planning, la configuración y el CLI en cada comando que le dictan a
  un agente— era **relativa** a la carpeta donde se abre la herramienta. Eso vale mientras el agente esté
  parado ahí, y nadie lo promete: una sesión abierta en el repo ops resolvía `<empresa>-ops/planning`
  contra su propio directorio, el tramo se duplicaba, y el comando contestaba que el planning no existe.
  En una corrida real de `autobuild` costó una vuelta entera de la fase Claim.

  Ahora esa raíz es **absoluta** y la escribe `automation install`, que es quien la conoce. Ninguna
  consigna depende ya de dónde arranque la sesión.

  **Lo que tenés que saber**: un archivo que lleva la raíz escrita se rompe si movés el proyecto de
  carpeta. Se repara con `automation install` del runner, que es lo que ya hacía falta cuando el wiring
  quedaba apuntando a otro lado. Y como los recorridos vienen del paquete, esto llega con el `upgrade`:
  después conviene reinstalar el adaptador para que la raíz nueva quede escrita.

- **Agregar un archivo al motor ya no deja la cobertura en un callejón.** La puerta de pisos fallaba
  diciendo «corré `npm run coverage:update`», y ese comando no podía completarse **mientras el piso
  faltara**: corría la suite entera bajo `set -e`, la suite incluía la prueba que estaba en rojo por el
  piso que faltaba, y el script moría antes de llegar a la línea que lo registra.

  Ahora las corridas de medición no deciden por su código de salida —existen para producir el archivo de
  cobertura— y en su lugar se exige que ese archivo traiga contenido, que es lo único que distingue una
  suite que falló de una que no llegó a arrancar. Y al abortar ya no se borran las mediciones que sí se
  completaron, así que se puede retomar desde donde quedó.

  **Y medir cero dejó de anunciarse como éxito**: `coverage-files.js --update` con un archivo de
  cobertura vacío imprimía «piso registrado» y salía en 0, dejando el registro vacío sin que nada lo
  dijera. Ahora se niega y explica por qué.

## [0.88.0] - 2026-09-14

### Corregido

- **Una revisión de propuesta que nadie decidió ya no puede cerrar el ciclo.** Cuando un cargo tiene su
  propuesta del período aplicada y aparece material nuevo, el ciclo abre una **revisión**, y su sección
  «Cambio propuesto» llega con el texto del molde hasta que alguien escribe el cambio. Ese texto empezaba
  con «Una revisión suele no ser aditiva…», y el criterio que detecta una propuesta sin decidir reconoce lo
  que empieza con «Por definir» o «Pendiente» — así que a las revisiones no las veía.

  El resultado era que una revisión se podía firmar, mergear y **sellar como aplicada** con el molde
  adentro: el ciclo llegaba a su estado terminal sin que nadie hubiera decidido nada, y el cargo quedaba
  habilitado a abrir la siguiente como si hubiera aprendido algo. Pasó con siete cargos el 2026-09-14.

  Ahora el molde de una revisión empieza por «Por definir», igual que el de una propuesta nueva y el de un
  recorrido. Y como los documentos ya escritos no cambian, el criterio reconoce además el molde viejo tal
  cual: completo y sin editar. Si lo continuaste para decir qué cambia —que es como se redacta— cuenta
  como decidido y se aplica igual; lo que se frena es el molde intacto.

  **Si tenés revisiones firmadas con el molde adentro, no se van a poder aplicar**: sellar falla con
  «todavía no la decidió nadie» y el documento se queda en `proposed`. Escribiles el cambio y volvé a
  firmarlas, o archivalas: archivar sigue siendo una salida incluso para una que ya firmaste, y cómo
  queda registrada lo explica la entrada de abajo.

- **Los encabezados que un cargo usó en su respuesta ya no se vuelven secciones de su propuesta.** El
  hallazgo de un caso en rojo viaja con el contraste entero —eso es deliberado y no cambió—, y adentro
  venía la respuesta con la estructura que el cargo eligió. Sus `##` quedaban al mismo nivel que
  «Hallazgos» o «Cambio propuesto» y pasaban por secciones del documento: dos propuestas del 2026-09
  llegaron con once y siete secciones que no eran suyas.

  Ahora bajan un nivel al componerse. No se pierde ni una línea del detalle, y las secciones del documento
  vuelven a ser sólo las del molde — que importa además porque sellar y aplicar ubican «Cambio propuesto»
  por su encabezado.

- **Una propuesta que nadie decidió ya no deja al cargo sin salida ni le bloquea la siguiente.** Al frenar
  el sellado de lo que no decide nada quedó un encierro: una propuesta firmada sobre el molde no se podía
  aplicar —no decide nada— **ni** archivar —estaba firmada—, y mientras tanto su cargo no volvía a
  proponer. La única salida era editar el frontmatter a mano, que es justo lo que este ciclo existe para
  no pedirte.

  Tres cosas cambian. **Archivar acepta una propuesta firmada cuando no decide nada**: lo que la guarda
  cuida es que no se tire una decisión, y ahí no hay ninguna. **Una archivada deja de retener el
  período**, así que el cargo puede proponer de nuevo — antes sólo una aplicada lo liberaba. Y **el
  registro dice cuál de las dos cosas pasó**: archivar lo que alguien miró sigue diciendo «se miró y no
  cambia nada», y archivar un documento que nadie llegó a llenar dice que se archivó sin decidir, en el
  historial del cargo y en la salida del comando.

  Una propuesta firmada **que sí decide** se sigue sin poder archivar: ahí la firma autorizó algo y lo
  que corresponde es aplicarla.

## [0.87.0] - 2026-09-14

### Corregido

- **El aviso de credenciales sin dueño mira el `.env.example` entero y ya no sus primeras cuarenta
  variables.** `check` te avisa de las credenciales que ningún contrato declara, y el escaneo cortaba en
  cuarenta variables por servicio **antes** de mirar qué era cada una. En un servicio grande eso no
  recortaba una lista: apagaba el análisis, y qué credencial se veía terminaba dependiendo de en qué línea
  del archivo había caído.

  El riesgo estaba escrito como aceptado y sin medir desde que el tope existe. Medirlo contra una instancia
  real lo convirtió en dos nombres: un servicio con 61 variables tenía tres credenciales pasado el corte y
  **dos que ningún contrato declaraba**, en las líneas 43 y 60. Lo único que salía era el aviso de que había
  cortado.

  Ahora el tope recorta lo que se **lista** y no lo que se mira: las credenciales se buscan sobre el archivo
  entero, y `scan` sigue mostrando cuarenta y diciendo cuántas más hay. Si tenés un servicio con más de
  cuarenta variables, contá con avisos nuevos en el próximo `check` — son credenciales que ya estaban y que
  nadie estaba mirando.

## [0.86.0] - 2026-09-12

### Corregido

- **Tu guard propio ya no se pierde al actualizar una instancia vieja.** Si tu instancia es anterior al
  registro de entregas —no tiene `.cauce/manifest.json`— y tenés un guard tuyo con un nombre que el paquete
  hoy trae, `upgrade` lo reemplazaba sin nombrarlo, salía con 0 y después lo anotaba como entregado por
  Cauce. Los tres pasos juntos hacían la pérdida silenciosa **e** irrecuperable: para cuando la notabas, el
  registro decía que ese archivo siempre había sido nuestro. Y la última línea de la corrida afirmaba lo
  contrario de lo que había pasado — «planning, organization y todo lo propio quedaron intactos».

  Ahora, sin registro para esa ruta, la duda se resuelve del lado del que se vuelve: el archivo se conserva,
  `upgrade` lo nombra y `upgrade --check` sale con 1 **antes** de tocar nada. Si el que querés es el del
  paquete, `--force` lo reemplaza diciéndolo, como ya hacía.

### Agregado

- **`check` te avisa de una fila de `HUMAN_ACTIONS.md` que figura resuelta y que ningún commit registró.**
  Una decisión que nadie dejó escrita es una aprobación autoservida, y la puerta barata no la miraba: el
  recorrido la rechazaba recién en Ready, con Triage, Pick, Claim y Decompose ya pagados. En la corrida que
  lo destapó fueron tres paradas y 1,21 M de tokens, con `check` en verde las tres veces.

  Avisa, no falla —rechazar enunciados por heurística frenaría trabajo legítimo— y **se calla cuando no hay
  con qué contestar**: sin repositorio, o con el archivo todavía sin commitear, no dice nada.

- **`check` muestra lo que autorizaste en el chat y sigue vigente.** Desde 0.83.0, lo que un guard te deja
  pasar queda concedido para el resto de la sesión, así no te vuelve a preguntar lo mismo en cada mensaje.
  Eso está bien, y no se veía en ninguna parte: por una línea olvidada en `planning/.ops-approval` `check` te
  avisaba, y por una concesión que vale toda la sesión no decía nada. Ahora lista las dos. Lo que concediste
  trabajando en otra instancia no se le cuenta a ésta.

- **Podés acotar una concesión diciendo hasta cuándo vale, y Cauce te hace caso.** Si al autorizar algo
  escribís «mientras dure la tarea t-014», esa concesión deja de valer en cuanto esa tarea ya no sea la de
  tu WIP, en vez de durar toda la sesión. Ya lo escribías y se perdía: de esa frase sobrevivía la ruta y el
  acote se tiraba.

  Hace falta la palabra `tarea` o `task` —«mientras dure la tarea t-014», «only for task t-014»—, porque sin
  ella no hay contra qué comparar. Lo que no se reconoce no se pierde: vale lo de antes, la sesión entera.

- **Lo que concedés en el chat deja un rastro local en `planning/.grant-log`.** Una línea por concesión, con
  la fecha, el alcance, la vía y la sesión; **no guarda lo que escribiste**. Es el mismo molde que
  `planning/.push-log`: sólo agrega, y sirve para contestar meses después quién autorizó qué.

  **Si tu instancia ya existía, agregale a mano esta línea a tu `.gitignore`:** `planning/.grant-log`.
  `upgrade` no puede tocar ese archivo porque es tuyo, así que la línea sólo llega a las instancias nuevas;
  sin ella, el rastro te va a aparecer en `git status`. Es lo mismo que pasó con `planning/.push-log`.

- **`check` te avisa si git no está ignorando los rastros locales de Cauce.** Son tres —
  `planning/.verify-log`, `planning/.push-log` y `planning/.grant-log`— y no deben viajar: son evidencia de
  una corrida tuya, de tu máquina. La línea que los cubre la trae el `.gitignore` que se escribe **al crear**
  la instancia, y `upgrade` no lo toca porque ese archivo es tuyo y puede tener líneas propias. Así que una
  instancia anterior a cada rastro nuevo se quedaba sin su línea para siempre y el archivo aparecía en
  `git status` listo para commitearse.

  Ahora `check` lo dice y nombra las rutas, que es lo que hay que pegar. Pregunta si git **los ignora**, no
  si la línea está escrita: si ya los cubrís con una regla propia, no te molesta. Sin repositorio se calla.

  **Si tu instancia ya existía, pegá estas tres líneas en tu `.gitignore`** — o dejá que `check` te diga
  cuáles te faltan:

  ```
  planning/.verify-log
  planning/.push-log
  planning/.grant-log
  ```

### Cambiado

- **La guía dejó de pedirle al agente que borre una autorización que no escribió él.** `AGENTS.md` decía que
  «borrarla es parte de terminar» sin distinguir la línea que el agente pidió para una operación de la que
  dejaste puesta vos a propósito. Ahora sólo se borra la primera.

## [0.85.0] - 2026-09-12

### Corregido

- **Gobernanza ya no te interroga cuando sos vos quien pide el trabajo.** El guard que frena un commit que
  toca reglas, ADR, `automatization/`, `engine/` o contratos de cargo te pedía **nombrar cada archivo** en
  tu mensaje, o contestar «dale», o pegar las rutas en `planning/.ops-approval` — y te lo cobraba en cada
  commit. Ahora, con una persona conduciendo el turno, no pregunta nada.

  Lo que **no** cambia, y es el punto del guard: sigue frenando igual cuando el que commitea es un
  subagente, un recorrido de Cauce o CI. Ahí nadie está conduciendo, que es exactamente para lo que existe.

  Sus dos vecinos —`verify` y `dependencies`— **siguen preguntando**, y la diferencia no es quién pidió el
  commit: esos frenan porque algo está mal —una verificación que falla, un manifiesto sin su lockfile— y
  callarlos porque hay alguien hablando sería taparte un rojo. `OPS_GOVERNANCE_OVERRIDE` sigue existiendo
  para el caso sin persona.

  **Qué cambia para vos**: nada que hacer. Si venías aprobando commits de gobernanza uno por uno, o pegando
  rutas a mano para poder trabajar, eso se terminó.

## [0.84.0] - 2026-09-12

### Corregido

- **Una frase en inglés que prohíbe ya no autoriza.** Cauce reconoce si le negás algo antes de dejarlo
  pasar, y en inglés sólo entendía `don't`: «the tool doesn't read the .env» o «we can't read the .env»
  nombraban el archivo, traían un verbo y ninguna negación reconocida, así que **una frase que prohíbe
  terminaba autorizando**. Entran ahora las contraídas con auxiliar —`doesn't`, `isn't`, `can't`,
  `won't`, `wasn't`, `wouldn't`, `shouldn't`…—, con o sin apóstrofo.

  **Qué cambia para vos**: nada que hacer. Si trabajás en inglés, negar algo ahora se respeta; lo que antes
  pasaba por no entenderse, se frena. Una frase como «I can't tell if the .env is right» deja de autorizar
  y cuesta un «dale», que es el mismo costo que el español ya tenía.

- **La entrada de DONE dice contra qué reglas se revisó.** `autobuild` exige que Review nombre las reglas
  contra las que revisó —si no las nombra, la corrida para—, pero esa lista quedaba sólo en el registro de
  la corrida, que no sobrevive. Ahora va también en la línea `review=` de `DONE.md`, que es lo que queda.

  **Qué cambia para vos**: nada que hacer. Al auditar una entrega vieja vas a poder decir contra qué se
  revisó sin depender del registro de aquella corrida.

- **Lo que pedís vos ya no se frena sin ofrecerte ninguna salida.** Siete de los guards que pueden frenar
  nunca consultaban si lo habías pedido: no tenían variable, ni línea que pegar en
  `planning/.ops-approval`, ni «dale». Entre ellos estaban las reglas que cubren trabajo de todos los días
  —`git reset --hard`, `git clean -f`, `git checkout -- .`, `docker compose down`, `docker system prune`—
  y los que frenan escribir una credencial, un archivo generado, un snapshot del sincronizador o el motor
  instalado. Pedirlos con todas las letras no servía de nada: se frenaban igual que si los hubiera
  decidido el agente por su cuenta.

  Ahora preguntan lo mismo que el resto de los guards: si lo pediste en el chat pasa, y si no, el bloqueo
  te dice qué se frenó y un «dale» lo aprueba. De paso se cierra una asimetría que nadie había decidido:
  **leer** una credencial se podía aprobar desde 0.80.0 y **escribirla** no.

  **Lo que no cambia, y es a propósito**: reescribir historia publicada —`git push --force`,
  `git commit --amend`—, el borrado de disco, `rm -r` sobre la raíz, el home o el directorio padre, y
  `git add -A`. Ésos no se abren ni pidiéndolos, porque R8 y R23 no admiten excepción configurable.

  **Qué cambia para vos**: nada que hacer ni que reinstalar. Lo que antes te obligaba a exportar una
  variable para poder trabajar —o directamente no tenía salida— ahora se destraba diciéndolo. El permiso
  es angosto a propósito: vale para el comando entero tal como lo escribiste, así que otra bandera es otro
  comando y se vuelve a preguntar.

## [0.83.0] - 2026-09-11

### Agregado

- **Autorizar en el chat ahora se dice como se dice.** Un guard dejaba pasar lo que pedías con un verbo de
  acción —«leé el .env», «usá esa ruta»— y se seguía frenando si lo decías autorizando: «autorizo la
  lectura del .env» no traía ninguna palabra que Cauce reconociera, así que había que repetirlo con otras
  palabras, contestar «dale», o escribir la línea a mano. Entran las formas con que una persona concede, en
  español y en inglés: `autorizo`, `autorizá`, `te autorizo`, `permito`, `apruebo`, `habilito`,
  `authorize`, `allow`, `grant`, `approved`.

  Lo que **no** cambia es la línea de siempre: nombrar algo no lo autoriza. Los sustantivos que nombran el
  acto —«la lectura del .env», «¿hace falta autorizacion?»— siguen sin autorizar nada, porque aparecen
  igual en una pregunta que no pide nada. Y negar sigue negando, también con las palabras nuevas.

  **Qué cambia para vos**: si autorizás algo diciendo que lo autorizás, pasa a la primera. Antes eso
  costaba una vuelta, y a veces terminaba en la variable que apaga el guard para toda la sesión.

- **El permiso de push ya no se lo puede escribir el agente.** `runner.allowPush` y
  `runner.pushToLiveBranches` deciden qué push se publica y viven en `ops.config.json`, que hasta ahora
  ningún guard miraba: el bloqueo que dice «esto lo decide una persona» se levantaba editando ese mismo
  archivo, con la herramienta de escritura o con un `sed -i`. Lo cierran dos guards nuevos: `ops-config`
  compara lo que se va a escribir contra lo que hay en disco y frena si cambia alguna de esas dos llaves,
  y `ops-config-shell` frena toda escritura por shell sobre el archivo, porque un comando no dice con qué
  va a quedar.

  **Qué cambia para vos**: el resto de `ops.config.json` —`workspaceRoots`, `writableOutsideRoots`,
  `migrations`, lo que sea— se sigue editando igual que siempre; sólo esas dos llaves pasan a necesitar una
  persona, que las pone editando el archivo ella o pidiéndoselo al agente en el chat nombrando
  `ops.config.json`. Por shell el archivo queda cerrado entero: ese cambio va por la herramienta de
  escritura y con el archivo completo, que es lo único que el guard puede comparar. Si tu runner registra
  los guards de a uno en vez de por grupo, corré `automation install` para que aparezcan
  `guard-ops-config.sh` y `guard-ops-config-shell.sh`.

- **Un push aprobado deja constancia en `planning/.push-log`.** Cuando Cauce deja pasar un `git push` por
  una aprobación —la orden en el chat, un «dale», o la línea exacta en `planning/.ops-approval`— anexa una
  línea con la fecha en que se autorizó, el remoto, la rama, por qué vía y la sesión. Por
  `runner.allowPush` no anota nada: esa autorización ya está escrita en `ops.config.json`.

  El archivo sólo agrega, no guarda el texto de lo que escribiste, y si no se puede escribir no frena el
  push. La fecha se llama `authorizedAt` y no `at` a propósito: el hook corre antes del comando, así que la
  línea dice que el push **se autorizó**, no que se haya publicado.

  El anillo «Local» de `planning/delivery/teamwork.md` lo nombra junto al resto de lo que no viaja por git.
  Ese archivo lo mantiene Cauce, así que `upgrade` te lo reemplaza: si lo editaste, mirá el diff antes.

  **Qué cambia para vos**: si necesitás responder quién autorizó un push y cuándo, ahora hay de dónde
  sacarlo, en la máquina donde corrió la sesión —el rastro dice la sesión y la vía, no el nombre de la
  persona: Cauce no tiene identidad de usuario—. Las instancias nuevas lo traen gitignoreado; si ya tenés
  una, agregá `planning/.push-log` a tu `.gitignore`.

- **Lo que un recorrido deja en el INBOX dice ahora de qué vía entró.** Cada línea que `autobuild`, `flow` y
  `onboard` le pasan al agente que escribe viene con su procedencia al final —`(autobuild · t-012 ·
  2026-09-11)`, `(flow · planning/reports/2026-09-11-alta.md · 2026-09-11)`, `(onboard · 2026-09-11)`—: el
  recorrido, la unidad de la que salió y la fecha. La arma el recorrido y el pedido dice que se copie tal
  cual, en vez de confiar en que el agente se acuerde de escribirla. El formato de una entrada no cambió ni
  el molde tampoco, así que `ops tree`, `check` y `context --json` siguen leyendo exactamente lo mismo.

  **Qué cambia para vos**: nada que hacer. Al recorrer el INBOX vas a ver el origen en lo que se escriba de
  acá en adelante; lo que ya tenés escrito no lo gana, y una entrada que escribas a mano sigue sin él,
  porque el INBOX es tuyo. Si en algún lado recortás el ancho de esas líneas, contá entre 20 y 60
  caracteres más por entrada: el tope de 240 pasó a medir la línea entera, así que lo que cede es el texto
  del hallazgo y nunca el origen.

### Cambiado

- **Lo que autorizás en el chat no alcanza a los gates de un commit.** Lo que un guard deja pasar porque
  vos lo pediste sigue valiendo mientras dure la sesión, con dos excepciones: publicar, y los gates de un
  commit —`governance`, `verify` y `dependencies`—, que preguntan cada vez. Lo que autoriza un commit no
  dice nada del siguiente, porque el índice ya es otro. Por el mismo motivo, nombrar `ops.config.json` una
  vez ya no deja escribir `runner.allowPush` ni `runner.pushToLiveBranches` por el resto de la sesión: ahí
  también se pregunta cada vez.

  **Qué cambia para vos**: si commiteás varias veces seguidas tocando gobernanza, con un gate en rojo o con
  un manifiesto sin su lockfile, vas a decir «dale» en cada commit en vez de una vez por sesión. Lo que no
  es un gate de commit —leer un `.env`, reescribir una migración, borrar una prueba— sigue valiendo toda la
  sesión.

### Corregido

- **Un bloqueo ya no te ofrece pegar una ruta que el shell no expandió.** Cuando el comando nombra la
  credencial a través de una variable —`cat ops/$O/.env.infisical`—, Cauce no puede saber qué archivo es:
  esa variable la expande tu shell, que es otro proceso. Antes el bloqueo ofrecía igual esa línea para
  `planning/.ops-approval`, y pegarla no servía: sólo volvía a valer si el comando se repetía escrito
  igual, y dejaba de valer apenas alguien escribía la ruta de verdad. Con la forma `${O}` era peor, porque
  la línea ofrecida ni siquiera era el archivo que el comando lee. Ahora no la ofrece, dice por qué y qué
  hacer.

  **Qué cambia para vos**: lo que un bloqueo te ofrece pegar es siempre una línea que después funciona. Si
  armás la ruta con variables, escribila entera en ese comando —o contestá «dale», que destraba igual—.

- **Lo que autorizás en el chat ya no deja de valer con el mensaje siguiente.** Hasta 0.82.0 el permiso
  duraba un mensaje: pedías leer el `.env`, el agente lo leía, y apenas escribías cualquier otra cosa
  —«gracias, seguí»— el mismo archivo volvía a frenarse. Ahora lo que un guard deja pasar porque vos lo
  pediste queda anotado y sigue valiendo mientras la sesión siga. Lo revoca decirlo: «no toques el .env» lo
  saca, y lo revocado no vuelve solo.

  El permiso sigue siendo angosto: vale para el ítem tal como el guard lo nombra, no cruza de sesión y no
  alcanza a un subagente ni a un recorrido de Cauce. Y **no alcanza a lo que vuelve a tener consecuencia
  cada vez**: publicar y los gates de un commit, que preguntan siempre —ver la entrada de abajo—. Volver a
  leer lo que ya se leyó no agrega consecuencia; volver a publicar, o commitear otra vez con otro índice,
  sí.

  **Qué cambia para vos**: si trabajás con credenciales o con rutas que los guards vigilan, alcanza con
  pedirlo una vez por sesión en lugar de repetir la frase o contestar «dale» a cada rato. Si querés cortar
  un permiso antes de cerrar la sesión, decilo nombrando el archivo.

- **El agente no puede escribir en tu aprobación una línea que vos no pediste.** `planning/.ops-approval`
  sólo lo podía escribir el agente si vos nombrabas el archivo en el chat, y ahí se terminaba el control:
  pedías agregar `src/login.js` y podía escribir `push origin main`, que es la línea que autoriza publicar
  en la rama viva. Ahora lo que se escribe se compara línea por línea con lo que pediste: pasan las que
  nombraste, y cualquier otra frena la escritura mostrándote cuál era. Las que ya estaban en el archivo no
  hay que volver a nombrarlas, y quitar líneas no pide permiso. Por shell —un `echo >>`, un `sed -i`— el
  archivo queda cerrado: un comando no dice con qué va a quedar, así que no hay contenido que comparar.

  **Qué cambia para vos**: si le pedís al agente que te agregue rutas, nombralas en el mismo mensaje
  —«agregá `src/login.js` y `src/altas.js` a `.ops-approval`»—; si frena, te dice exactamente qué línea
  quería escribir y un «dale» aprueba esa y nada más. Vos seguís editando el archivo a mano como siempre,
  que ningún guard mira. Esto venía desde 0.82.0, así que si actualizás desde ahí, cerrás de paso esa vía.

## [0.82.0] - 2026-09-11

### Cambiado

- **`allowPush: true` ya no publica en la rama viva ni desde un subagente.** La llave dejaba pasar cualquier
  push, `main` incluido, y el de un subagente igual que el tuyo. Ahora no alcanza a `main`, `master` ni a la
  rama por defecto del remoto, y un subagente no publica con ningún permiso. Tampoco la alcanza una orden en
  el chat.

  **Qué cambia para vos**: si publicabas en la rama viva con `allowPush: true`, agregá
  `"pushToLiveBranches": ["main"]` en `runner` de `ops.config.json`, o pegá `push origin main` en
  `planning/.ops-approval` para un push puntual.

- **R10 y el párrafo de publicación de `AGENTS.md` se reescribieron.** Los dos decían que el push se
  comprueba contra `runner.allowPush` y nada más; ahora dicen lo que de verdad comprueba el motor: esa llave
  —que no llega a la rama viva sin `runner.pushToLiveBranches`, ni a un subagente— o la orden que la persona
  da en el chat nombrando el remoto y la rama. Los dos archivos los mantiene Cauce, así que `upgrade` los
  reemplaza enteros.

  **Qué cambia para vos**: nada que hacer. Si tu equipo cita R10 en algún documento propio, el texto que
  baja ahora es más largo y dice qué sostiene un guard y qué sostiene una persona.

### Corregido

- **Nombrar algo en el chat ya no lo autoriza: hay que pedirlo.** Desde 0.81.0, lo que nombrabas en el chat
  pasaba los guards aunque no lo pidieras: «¿para qué sirven las credentials?» dejaba leer `credentials`, y
  «el .env tiene algo raro?» dejaba leer el `.env`. Ahora la frase que lo nombra tiene que pedir una acción
  —«leé», «abrime», «borrá», «read»…—, y la negación sigue frenando como antes.

  **Qué cambia para vos**: si pedís algo sin un verbo que Cauce reconozca, el agente te dice qué se frenó y
  con un «dale» pasa.

- **`upgrade` ya no pisa un guard tuyo que se llama como uno nuevo del paquete.** Cuando una versión empezaba
  a traer un nombre que ya usabas en `automatization/hooks/`, lo reemplazaba sin decir nada: 0.81.0 lo hizo
  con `guard-chat.sh`. Ahora lo conserva y lo avisa; `--check` lo cuenta, y `--force` lo reemplaza diciéndolo.

  **Qué cambia para vos**: si ves «ya existía y Cauce no lo entregó», renombrá tu guard y repetí `upgrade`;
  mientras tanto, el guard del paquete con ese nombre no está instalado.

- **Un guard propio ya no aparece como «editado localmente».** `upgrade` registraba todo lo que había en
  `automatization/hooks/`, así que editar un guard tuyo lo volvía una edición del molde: `upgrade --check`
  salía con 1, `check` lo contaba como congelado, y `--force` anunciaba que lo descartaba sin tocarlo. Ahora
  sólo registra lo que trae el paquete, y el primer `upgrade` suelta lo que una versión anterior registró de
  más.

  **Qué cambia para vos**: nada que hacer; tus guards dejan de aparecer en esos avisos.

- **El aviso de credenciales sin dueño avisa sólo credenciales.** `check` listaba toda variable que el
  proyecto declaraba y ningún contrato nombraba: en un proyecto real fueron ciento ocho nombres de build, y
  la única credencial quedó en «y 1 más». Ahora cuenta las que tienen nombre de secreto —`…_PASSWORD`,
  `…_TOKEN`, `…_KEY`, `…_DSN`…—, dice en qué servicio está cada una y dónde se escribe su dueño, y avisa
  aparte cuando un servicio pasó el tope de variables que se revisan.

  **Qué cambia para vos**: la declaración de secretos y la configuración de una integración ahora también
  rechazan una clave que termine en `_key`, `dsn` o `credentials`, igual que ya rechazaban `…token`. Si
  tenés una, cambiala por la referencia a una variable de entorno. Y una credencial con nombre de
  configuración —`STRIPE_LIVE`— no aparece en el aviso: el criterio es el nombre.

- **Una variable ya no se da por cargada porque otra la contiene en su nombre.** Con `API_SECRET_ROTATION`
  escrita en `organization/workspace.md`, `API_SECRET` dejaba de avisarse aunque nadie la nombrara. Ahora
  cuenta sólo el nombre entero.

  **Qué cambia para vos**: puede aparecer en el aviso una credencial que antes quedaba tapada; escribí su
  dueño y se va.

- **Un push que pedís en el chat ya no se frena.** Con `allowPush: false`, `git push` se frenaba aunque lo
  hubieras pedido con todas las letras. Ahora pasa si tu mensaje nombra el remoto y la rama —«subí feat/x a
  origin», `git push origin feat/x`—, y si no los nombra, el agente te dice qué se frenó y con un «dale»
  pasa ese push y ningún otro. `planning/.ops-approval` también acepta la línea `push <remoto> <rama>`, tal
  cual y sin patrones. Un `git push origin +rama` ahora se frena como el `--force` que es.

  **Qué cambia para vos**: para publicar una rama de trabajo alcanza con pedirlo nombrando remoto y rama, o
  con contestar «dale».

- **La sesión carga las reglas que rigen tu proyecto, no las cuatro de `system/` fijas.** El archivo de
  instrucciones de cada runner importaba siempre `planning/rules/system/`: cargaba la regla que tu proyecto
  había sobrescrito y ninguna de las tuyas. Ahora `automation install` pone las vigentes —las tuyas y las de
  `system/` que no sobrescribiste— en Claude, Gemini, Codex y Antigravity, y un `CLAUDE.md` que editaste
  recibe el bloque nuevo sin perder lo tuyo. Como `upgrade` no reinstala, `check` y `automation doctor`
  avisan cuando lo instalado quedó atrás. `AGENTS.md` dice ahora que, donde una regla tuya y una del
  sistema chocan, rige la tuya.

  **Qué cambia para vos**: después de actualizar, o cada vez que escribas o sobrescribas una regla,
  reinstalá el adaptador (`make install-claude`, o el de tu runner); `check` te dice cuándo hace falta. Si
  tu `CLAUDE.md` es tuyo y no tiene nada de Cauce, poné las marcas `<!-- cauce:reglas inicio -->` y
  `<!-- cauce:reglas fin -->` donde quieras el bloque y reinstalá.

- **`autobuild` trabaja con las reglas de tu proyecto y ya no cita una que retiraste.** Los subagentes
  planificaban, construían y revisaban sin conocer tus reglas, y la fila que pedía partir una tarea citaba
  R17 aunque la hubieras retirado. Ahora `context` dice qué reglas rigen —línea `RULES`, campo `rules` en
  `--json`—, cada fase recibe esa lista, y Review tiene que nombrar contra cuáles revisó o la corrida para
  con `review-unbacked`. Ningún prompt cita una regla por número.

  **Qué cambia para vos**: nada que hacer.

- **Los recorridos ya no llenan el INBOX sin medida.** `autobuild` volcaba en Propuestas todo lo que Review
  anotaba sin frenar, entero, y `flow` y `onboard` escribían sin tope ni forma. Ahora cada recorrido escribe
  como mucho tres entradas por tarea o por corrida, de una línea, con la forma del molde y sin repetir un
  nombre que ya está. Lo que pasa del tope no se pierde: `autobuild` lo cuenta en el `done/` de la tarea,
  `flow` lo deja en el informe y `onboard` lo nombra al cerrar. Y `check` avisa, sin fallar, cuando
  `INBOX.md` pasa de 300 líneas.

  **Qué cambia para vos**: nada que hacer. Si querés otro umbral para el aviso, poné
  `"inbox": { "warnLines": N }` en `ops.config.json`. El molde de `INBOX.md` ya no pide la evidencia dentro
  de una propuesta; tu `INBOX.md` no se toca.

- **La recurrencia que recorre el INBOX viene activa, y descomentar los ejemplos de `RECURRING.md` ya no
  rompe `check`.** Una instancia nueva trae la fila `inbox`, trimestral, que vence por primera vez tres meses
  después de `init`. Las filas de ejemplo declaran el `(service: …)` que `check` exige. Y `check` avisa una
  entrada del INBOX que se llama como una tarea de `done/`: probablemente se promovió y quedó ahí.

  **Qué cambia para vos**: `RECURRING.md` es tuyo y `upgrade` no lo toca. Para tener la recurrencia en una
  instancia que ya existe, agregá debajo de la tabla
  `| inbox | trimestral | AAAA-MM-DD | Recorrer el INBOX entero. _Aceptación: ninguna viñeta queda sin decisión de promover, dejar o borrar._ (service: planning) |`
  con una fecha futura: `Desde` es cuándo vence por primera vez, no el día en que escribís la fila.

- **Dos repositorios que terminan en una carpeta con el mismo nombre ya no salen con el mismo nombre.** Con
  varias raíces declaradas, el inventario nombraba cada servicio por la **carpeta** de su raíz, así que
  `../initech/keycloak` y `../umbrella/keycloak` salían los dos `keycloak` en `check`, en `onboard` y en
  `scan`, y no había con qué saber de qué repositorio era cada credencial. Ahora los nombra el `name` que
  cada raíz declara en `ops.config.json`, y `check` avisa cuando dos raíces se llaman igual, diciendo
  cuáles son las dos.

  **Qué cambia para vos**: si dos raíces de tu `ops.config.json` comparten `name`, `check` te lo avisa y
  sigue pasando —renombrá una cuando puedas—. Y donde el `name` y la carpeta difieren, los servicios pasan
  a listarse con el `name` —el que escribiste vos— en vez de con la carpeta; `onboard --json` sigue
  emitiendo `roots` como lista de rutas.

## [0.81.0] - 2026-09-11

### Cambiado

- **Lo que pedís en el chat ya no te frena.** Los guards veían la herramienta y nada de la conversación,
  así que frenaban igual lo que pediste con todas las letras y lo que el agente decidía solo. Ahora, en
  Claude Code, Codex y Gemini, un hook registra tu mensaje y los guards lo leen: si nombraste lo que se
  iba a frenar —«borrá la prueba de altas», «reescribí la migración 004»—, pasa; si no, el agente te dice
  qué se frenó y un «dale» aprueba exactamente eso. `plan-first` deja de pedir un plan cuando el cambio lo
  pediste vos. Lo que el agente hace por su cuenta, dentro de un recorrido o en un subagente, se sigue
  frenando igual.

  **Qué cambia para vos**: corré `automation install` para que tu runner registre el hook nuevo
  (`UserPromptSubmit` en Claude y Codex, `BeforeAgent` en Gemini); en Codex, confialo con `/hooks`. Tu
  mensaje se guarda en el temporal del sistema, no en el repositorio, y sólo el último de cada sesión.
  Antigravity sigue con el archivo de aprobación.

- **Leer una credencial por shell se frena en los cuatro runners, y en Claude ya no te frena a vos.** Un
  guard nuevo, `secrets-shell`, frena el comando que muestra una credencial —`cat .env`, `head`, `grep`,
  `source`, una redirección `<`, un `node -e`— en Claude, Codex, Gemini y Antigravity. Hasta ahora sólo
  Claude lo frenaba, con reglas nativas `permissions.deny` que tampoco dejaban pasar tu pedido; esas reglas
  se retiran, y como con el resto de los guards, si lo pedís en el chat, pasa.

  **Qué cambia para vos**: corré `automation install`; en Claude quita las reglas que Cauce había puesto y
  conserva las tuyas. Si querés un bloqueo nativo total, escribilo como regla propia.

### Corregido

- **El bloqueo de `verify` cita la prueba que falló también con jest, vitest, mocha y pytest.** Con esas
  herramientas citaba el archivo, el resumen o —con `pytest -v`— una prueba verde cuyo nombre decía «error».
  Ahora reconoce cómo marca cada una la prueba que falla, comprobado contra la salida real de cada una.

  **Qué cambia para vos**: el mensaje apunta a la prueba que hay que mirar.

- **El agente ya no puede escribirse la aprobación.** `planning/.ops-approval` se escribía con la misma
  herramienta que usa el agente y ningún guard lo miraba, así que una aprobación suya destrababa igual que
  una tuya. Los guards de límites lo frenan ahora —por la herramienta de escritura y por el destino evidente
  de un comando—, salvo que se lo hayas pedido nombrándolo en el chat.

  **Qué cambia para vos**: nada si el archivo lo editás vos. Si el agente lo escribía por pedido tuyo,
  ahora se lo pedís en el chat.

- **En sidecar, el bloqueo dice en qué archivo aprobar.** Mandaba a `planning/.ops-approval`, y desde la
  carpeta del workspace, donde se abre la sesión, ése no es el `planning/` de la instancia: pegar donde
  decía no destrababa nada. Ahora nombra la ruta que el guard lee —`acme-ops/planning/.ops-approval`—; en
  modo embebido sigue diciendo `planning/.ops-approval`.

  **Qué cambia para vos**: nada que hacer; el mensaje dice dónde.

## [0.80.0] - 2026-09-10

### Agregado

- **Leer una credencial también se frena, y las identidades declaradas cuentan como credencial.** Un guard
  nuevo, `secrets-read`, corre en la herramienta de lectura de Claude (`Read`) y de Gemini (`read_file`) y
  frena los mismos archivos que `secrets` frena al escribir. Esos archivos ahora incluyen las identidades
  `source: file` de `organization/secrets.json`, que antes pasaban por no tener nombre de credencial. En
  Claude, además, la instalación agrega reglas `permissions.deny` `Read(...)` que el propio Claude aplica
  también a `cat`, `head`, `tail`, `sed` y redirecciones.

  **Qué cambia para vos**: el agente deja de poder leer `.env`, claves y tokens conocidos; `.env.example`
  sigue legible. Si una lectura hace falta, aprobá la ruta en `planning/.ops-approval`. Corré
  `automation install` para que tu `.claude/settings.json` reciba las reglas: se suman a las tuyas. No es un
  límite de seguridad —un `grep -r` o un script propio siguen leyendo—, y en Codex y Antigravity no hay guard
  de lectura.

- **Un proveedor de integraciones propio, sin tocar Cauce.** El registro de `integrations/config.json`
  ya tenía un campo `adapter` que nadie leía: ahora `"adapter": "./adapter.js"` carga el adaptador de la
  empresa desde `integrations/<nombre>/`, e `integration enable` lo conecta sin exigir un molde de Cauce.
  El adaptador declara `contract: 1` y las tres funciones del contrato, y `check` rechaza el que no
  cumpla.

  **Qué cambia para vos**: nada si sólo usás Jira. Para otra herramienta, `integrations/README.md` de tu
  instancia trae el recorrido.

- **Un contrato de secretos compartido entre repositorios, y un chequeo sin red.** Si tus servicios
  usan un gestor de secretos, los scripts y workflows que lo conectan terminan copiados en cada
  repositorio, y el arreglo que alguien hizo en uno no llega a los demás. `organization/secrets.json`
  declara qué cuenta, qué identidad y qué archivos comparte cada servicio, y
  `node tools/ops.js secrets check .` compara cada copia contra la canónica que guarda la instancia:
  dice cuál quedó atrás y el `cp` que la pone al día. También falla si una credencial vive dentro de un
  repositorio o si la declaración guarda un valor en vez de una referencia.

  **Qué cambia para vos**: nada si no lo usás. Para adoptarlo, `organization/README.md` trae el formato
  y el recorrido. Cauce no se conecta a ningún gestor ni trae adaptadores: lo que habla con el gestor
  sigue siendo tuyo. Si tus proyectos tienen instancias separadas, el chequeo sólo compara dentro de
  cada una: para que un esqueleto y sus derivados se midan contra lo mismo, van como raíces de la misma
  instancia.

### Corregido

- **`init` rechaza un runner mal escrito sin crear la instancia.** Con `--runner none` —el valor es
  `ninguno`— o una `--integration` que no existe, `init` escribía la instancia entera y recién después
  salía con error: el código de salida decía que no había pasado nada y el segundo intento encontraba la
  carpeta creada. Ahora valida los dos valores antes de escribir.

  **Qué cambia para vos**: nada con un valor correcto. Con uno mal escrito, el destino queda como estaba.

- **El bloqueo de `verify` cita la prueba que falló.** Mostraba la primera línea de la salida que dijera
  «error», y en un reporte de pruebas ésa puede ser una verde cuyo nombre lo dice. Ahora, con la salida de
  `node --test` —spec y TAP— y de `go test`, cita la primera prueba en rojo; con otras herramientas sigue
  buscando por palabra, pero ya no elige una línea marcada como verde.

  **Qué cambia para vos**: el mensaje apunta a la prueba que hay que mirar.

- **Cada bloqueo con salida angosta dice qué líneas pegar.** Aprobar en `planning/.ops-approval` sólo
  funciona si la ruta está escrita en la forma que ese guard coteja —absoluta la de un `Write`, relativa
  al repositorio la de un commit—, y el mensaje decía «escribí esa(s) ruta(s)» sin nombrarlas. `verify`
  y `dependencies` ni siquiera mostraban la línea. Ahora cada bloqueo imprime las líneas exactas, y
  pegarlas tal cual destraba ese mismo bloqueo.

  **Qué cambia para vos**: si una aprobación no pegaba y terminabas exportando la variable del guard,
  pegá lo que dice el mensaje.

- **`plan-first` deja de frenar la configuración y los archivos de la instancia.** Frenaba
  `ops.config.json` —justo el archivo que el límite de raíces manda a editar para declarar una ruta— y,
  en sidecar, también `AGENTS.md`, `CLAUDE.md`, `package.json` y `.gitignore` de la instancia, como si
  fueran producto. Ahora producto es el código de una raíz declarada: la instancia sidecar no lo es
  aunque viva dentro de su raíz (`..`), y lo que queda fuera de toda raíz —lo declarado en
  `writableOutsideRoots`— tampoco. En embedded, el `package.json` de la raíz sigue siendo producto.

  **Qué cambia para vos**: si aprobabas esas rutas a mano en `.ops-approval` o exportabas
  `OPS_PLAN_FIRST_OVERRIDE` para poder tocarlas, ya no hace falta.

- **`verify` deja afuera del índice de su copia lo que enlaza.** Cuando el árbol difiere del índice, el
  guard corre los gates sobre una copia del índice con lo ignorado enlazado —`node_modules`, `.env`—. Si
  tu `.gitignore` escribe ese directorio con barra final (`node_modules/`), el enlace entraba al índice de
  la copia, y un gate que recorre lo trackeado —`git ls-files`— lo recibía como si fuera parte del commit.

  **Qué cambia para vos**: si una suite que pasa a mano fallaba bajo `verify` sólo cuando tenías archivos
  sin trackear, podía ser esto.

## [0.79.0] - 2026-09-10

### Corregido

- **La fila que registra una parada se escribe antes de parar.** Cuando ningún plan sobrevive a la
  crítica, el recorrido pide que la tarea quede en `HUMAN_ACTIONS.md` con su motivo —así `context` deja
  de ofrecerla y alguien la ve—. Esa escritura se lanzaba y el recorrido volvía en la línea siguiente,
  así que el agente que la escribía se quedaba a mitad de camino: en una corrida real el resumen contó
  diez agentes y el registro nueve. La parada se informaba bien y el disco no la tenía.

  Ahora se espera. Y en las tres paradas que dejan fila —plan rechazado, tarea que no está lista,
  criterio que no dice qué aserciar— si el agente no contesta, el detalle de la parada lo dice: el
  motivo sigue siendo el que la causó, y se agrega que la fila hay que escribirla a mano.

  **Qué cambia para vos**: relanzar después de un plan rechazado deja de repetir la planificación
  entera, porque la tarea queda registrada. Si venías viendo que `context` te ofrecía otra vez la tarea
  que acababa de rechazarse, era esto.

- **El guard de migraciones frena por haber viajado, no por estar en disco.** Escribir una migración son
  dos pasos —crearla y completarla— y el segundo se bloqueaba: el chequeo preguntaba si el archivo
  existía, que no es la pregunta. Un stub de hace dos segundos y una migración publicada hace un año
  daban la misma respuesta, y **ninguna herramienta lo esquivaba** —`Write`, `Edit` y `MultiEdit` se
  bloqueaban igual—, así que lo único que quedaba a la vista era apagar el guard entero, incluida la
  protección contra SQL destructivo.

  Ahora se le pregunta a git si el archivo está en `HEAD`. En el índice no alcanza: un archivo apenas
  agregado no viajó a ninguna parte. Y sin repositorio con el que contestar —un proyecto sin git— se
  conserva la conducta anterior en vez de dejar pasar, que es el lado correcto para equivocarse cuando
  no se puede saber.

  **Qué cambia para vos**: completar una migración recién creada deja de frenarse. El mensaje del
  bloqueo pasa a decir el hecho que lo sostiene —que está en el historial, o que existe y acá no hay con
  qué saberlo— y a nombrar cómo aprobar esa ruta puntual en `planning/.ops-approval`, que es lo que el
  bloqueo hermano ya hacía. Si tenías `OPS_MIGRATIONS_OVERRIDE=1` puesto para poder trabajar, ya no hace
  falta y conviene sacarlo: apagaba también lo que sí te cuida.

## [0.78.0] - 2026-09-10

### Corregido

- **Las puertas que revisan un recorrido leen los literales de regex.** Tres pruebas analizan cada
  workflow como texto —que su `meta` sea un literal puro, que no llame a nada inexistente y que no use un
  nombre que no declaró— y ninguna sabía qué es un regex. La barra escapada que cierra uno deja un `//`
  literal, que se leía como el arranque de un comentario: a partir de ahí se perdía el resto de la línea,
  en silencio. Dos recorridos del catálogo tenían cuatro líneas así.

  **Qué cambia para vos**: nada en lo que recibís, y sí en lo que se puede confiar. Un recorrido propio
  con un regex adentro ahora se revisa entero en vez de a medias, y un `meta` con un regex o con una
  interpolación —que hasta acá pasaba siempre, porque esa regla no podía dispararse— se rechaza.
- **El nombre de un banco de pruebas no trae nada que la prueba no haya pedido.** La suite aísla lo que
  cada prueba mide filtrando la salida de `check`, y esa salida empieza con la ruta del banco: cuando el
  sufijo aleatorio del directorio terminó en `adr`, un error ajeno entró a una prueba de plantillas de
  ADR y la puso en rojo. Falla una vez cada muchas corridas y se lee como un hipo del entorno.

  **Qué cambia para vos**: nada — es la suite del toolkit, no algo que recibas. Va acá porque una puerta
  que falla al azar enseña a relanzar en vez de a mirar, y eso sí llega a quien la usa.

- **`autobuild` lee `blocked` por su valor y no por su verdad.** El campo del contexto es un vocabulario
  de tres valores —vacío, `awaiting-review` y `blocked-on-human`—, y el recorrido lo probaba con un `if`
  a secas. Dos consecuencias, y la que más costaba no era intermitente: una cola trabada por acciones
  humanas frenaba con el motivo del checkpoint de hito y mandaba a mirar `AWAITING_REVIEW.md`, un archivo
  que en ese escenario no existe. Ahora cada bloqueo para con su motivo y nombra su archivo:
  `AWAITING_REVIEW.md` para el checkpoint, `HUMAN_ACTIONS.md` —con las tareas trabadas— para la cola.

  La otra es la que se ve al azar: el agente que transcribe el contexto a veces entrega el vacío como la
  cadena de dos comillas, y eso frenaba la corrida en la fase 1 sin que hubiera nada que resolver. Medido
  sobre siete corridas de una instancia real: 3 de 24 lecturas llegaron así, y una de cada siete cayó en
  la única lectura que consulta el campo. Las formas equivocadas del vacío se desenvuelven antes de leer.

  Y lo que no está en el vocabulario ya no se adivina en ninguna de las dos direcciones: no se sigue como
  si no hubiera bloqueo ni se inventa cuál es — para diciendo que el contexto llegó fuera del
  vocabulario. El esquema declara los tres valores, igual que `lane`; eso documenta el contrato, y lo que
  sostiene el arreglo es la lectura.

## [0.77.0] - 2026-09-10

### Corregido

- **`check` dice cuánto del trabajo que entró al repositorio quedó registrado.** Cuenta, por raíz de
  trabajo, los commits que **ninguna entrada de DONE nombra** desde la última tarea cerrada. Es un aviso,
  no un error: es un hecho del pasado que no se arregla editando nada.

  El número no existía y sacarlo pedía cruzar a mano los `commit:` contra la historia de cada
  repositorio. Hecho así sobre una instancia real: **312 commits, 75 registrados**. Y el desglose de los
  que faltaban no era trabajo suelto — **69 `feat` y 51 `fix` de 173**—, ni tareas que produjeron varios
  commits: de 80 entradas, una sola registra más de uno.

  La ventana arranca en la última tarea cerrada y no en la primera, y esa decisión es la que hace que
  sirva: contar toda la historia da una deuda que nunca baja y se lee como decorado. Así vuelve a cero
  cada vez que el flujo se cierra, y lo que queda a la vista es la deriva de ahora.

  **Lo que te pide algo**: si el número no es cero, ese trabajo no está en `planning/` y `OPS-001` dice
  que ahí está la fuente de verdad. Qué hacer con él es tuyo — el aviso no propone cerrar nada
  retroactivamente, porque escribir entradas de memoria sería inventar la evidencia que el registro
  existe para tener.

- **Un plan que ninguna crítica aprueba deja de reintentarse a ciegas.** `autobuild` cortaba con
  `plan-rejected` sin dejar rastro, así que relanzar repetía **la corrida entera** sobre la misma tarea:
  Ready y Decompose la volvían a dejar pasar —su criterio no cambió y la tarea tampoco— y Critique la
  volvía a rechazar. Medido en dos corridas consecutivas: idénticas, **9 agentes y ~780 k tokens cada
  una, sin escribir una línea de código**.

  Ahora la tarea queda registrada en `HUMAN_ACTIONS.md` con el motivo, y eso hace las dos cosas de una
  vez: `context` deja de ofrecerla —una acción pendiente saca esa tarea de la cola y ofrece la siguiente,
  sin frenar la corrida— y alguien ve la fila. Vale igual para `plan-blocked`, que cortaba igual de mudo.

  **Lo que te pide algo**: esa fila es tuya. Lo que pide R17 es mirar si la unidad son dos resultados con
  vidas distintas y partirla, o dejarla entera con la razón escrita.

- **El guard de migraciones deja de ser inerte en todo proyecto cuyas migraciones no sean `.sql`.** Filtraba
  por ruta **y por extensión**, así que en TypeORM, Prisma, Django, Rails o Alembic no miraba nada: ni
  frenaba el SQL destructivo, ni protegía una migración existente de ser reescrita. Y no lo decía — aparecía
  cableado y en verde. Medido en una instancia real: **64 migraciones `.sql` cubiertas y 409 TypeORM `.ts`
  invisibles**.

  Ahora la extensión la declara el proyecto:

  ```json
  "migrations": { "extensions": ["sql", "ts"] }
  ```

  El default sigue siendo `["sql"]`, así que nada cambia para quien no lo declare — y ampliarlo por
  nuestra cuenta reintroduciría el falso positivo que 0.63.0 vino a cerrar: un archivo de lenguaje que
  menciona `DROP TABLE` en un comentario. La ruta la sigue fijando el motor: `migrations/`, `migration/`
  o `migrate/`.

  Y la descripción del guard dejó de prometer de más: nombra el campo que amplía la cobertura y el
  default de quien no lo declara.

  **Lo que te pide algo**: si tus migraciones no son `.sql`, declaralas. Hasta que lo hagas, ese guard
  no las mira — y ahora `automation list-hooks` te lo dice.

- **La tabla que dice qué ruta aprobar cuando un guard te frena estaba ofreciendo la salida ancha para el
  freno más común.** `plan-first` —el que exige un plan escrito antes de cambiar el producto— no figuraba
  entre las aprobaciones por ruta, y sí en la tabla de variables que apagan un guard **para toda la
  sesión**. Quien se topaba con ese bloqueo encontraba documentado `OPS_PLAN_FIRST_OVERRIDE=1` y no la
  aprobación acotada, que es la vía recomendada.

  Ahora está la fila, con la pregunta que va antes —**¿esto es trabajo de una tarea?**— y con lo que
  cuesta: si lo es, la salida no es aprobar la ruta sino escribir el WIP con su plan; si no lo es —un
  typo, un umbral que corregís de paso—, aprobar es la respuesta correcta, y ese cambio entra **sin
  entrada de DONE**, así que `planning/` no lo registra.

  **Lo que te pide algo**: si venías apagando ese guard con la variable, la aprobación por ruta hace lo
  mismo para el archivo que vas a tocar y se apaga sola en cuanto el conjunto cambia.

### Cambiado

- **R17 gana un tercer disparador de división: un plan que ninguna crítica aprueba.** Los dos que ya
  tenía —cinco condiciones de aceptación, cuatro horas de esfuerzo— miran la unidad **escrita**. Éste mira
  lo que pasó al intentarla, y por eso es la evidencia más directa de las tres y la única que no se puede
  tener de antemano.

  Lo que lo hace fácil de perder es que llega **después** de que las otras dos dieron el visto bueno, y
  las dos acertaron: la aceptación era concreta y las condiciones no cruzaban el umbral. Una unidad puede
  estar bien escrita y no ser planificable, y eso sólo se sabe habiéndolo intentado.

  **Lo que te pide algo**: es una regla del sistema, así que baja a tu `planning/` en el próximo
  `upgrade`. Como las otras dos, dispara una revisión y no una partición automática.

### Agregado

- **R23: un borrado se lee resuelto antes de correrlo, y sólo alcanza lo desechable.** Antes de destruir
  —`rm -rf`, un borrado recursivo, un `DROP`, un `reset --hard`— se resuelve el objetivo y **se lee la
  ruta final**, no la variable que la contiene. El destino tiene que colgar de algo desechable, y eso se
  comprueba: la raíz de un repositorio, un directorio de trabajo, el home de nadie y `/` no lo son.

  Con dos cosas que la regla dice y que son menos obvias. Las pruebas **no montan nada bajo el home**: un
  banco ahí pone la carpeta personal de quien las corre dentro del alcance de todo lo que la suite borra.
  Y **decidir se separa de destruir**: la función que juzga si algo se puede borrar no borra, así que
  probarla con `/` o con la raíz de un repositorio no puede destruir nada. Mezcladas, la prueba que
  ejerce la defensa tiene que apuntarle a rutas reales, y ahí apagar la defensa **es** el desastre.

  **Lo que te pide algo**: es una regla del sistema, así que baja a tu `planning/` en el próximo
  `upgrade`. Si tenés pruebas o scripts que borran, la pregunta que contesta es «¿de dónde salió esta
  ruta?», no «¿está bien escrita esta línea?».

## [0.76.0] - 2026-09-10

### Corregido

- **Un comando que no encuentra tu planning lo dice, en vez de contestar un hecho sobre un directorio que
  no existe.** `context` y `tree` ya lo hacían desde 0.71.0; los otros nueve no. `claim` contestaba «no
  está en BACKLOG», `release` «no está tomada por nadie», `evidence` «DONE no tiene ninguna entrada»,
  `runners` «ningún runner tiene trabajo abierto» — todos hechos concretos sobre lo que no pudieron leer.
  **Cuatro de ellos salían con exit 0**, así que un script veía éxito.

  El daño no es el mensaje sino lo que induce: en una corrida real, `claim` dijo «no está en BACKLOG»
  sobre una tarea que **sí** estaba, y el recorrido mandó a una persona a promover lo único que ya estaba
  bien, citando la regla correcta con la conclusión al revés. Ahora los once comandos que reciben un
  planning fallan igual, con la ruta **resuelta** puesta — que es lo que hace falta cuando el error es de
  resolución: en sidecar, `<empresa>-ops/planning` escrito desde adentro de la raíz apunta a
  `<empresa>-ops/<empresa>-ops/planning`.

  **Lo que te pide algo**: si tenías un script que trataba ese vacío como «nada que hacer», ahora falla.
  Es la misma dirección que 0.63.0 y 0.71.0 ya tomaron. Y `ops check` sobre una ruta que no existe pasa a
  decir eso en vez de «falta BACKLOG.md»: sale con 2 y nombra la ruta.

### Agregado

- **La entrada declara también `review:`, y `check` cruza los dos.** El carril dice cuánta ceremonia
  **merecía** la tarea; `review:` dice cuánta **recibió** —el veredicto y quién revisó, o `n/a — razón`
  cuando no corrió—. Es la dimensión que la propia ADR OPS-006 nombraba como la que falta: «se sabría
  comparando hallazgos de review por carril, y hoy no se registra esa dimensión en DONE».

  Con los dos campos, `check` avisa lo que hasta ahora no tenía cómo ver: una entrada cuyo carril convoca
  revisor —`directo`, `lite`, `full`— y cuya revisión no corrió. `express` queda afuera porque es el único
  que legítimamente no convoca a nadie. Avisa y no falla: es un hecho del pasado que no se arregla
  editando la entrada, y el único camino al verde sería reescribir el registro.

- **La entrada de una tarea cerrada declara `lane:`, el carril con el que corrió.** El carril decide qué
  fases recibe una tarea —`express` se saltea Ready, Plan y QA; `full` las corre todas— y viajaba sólo en
  la línea del BACKLOG, que **se borra al cerrar**. Con eso, «¿esta tarea recibió la ceremonia que su
  superficie pedía?» dejaba de tener dónde contestarse: en una instancia real, **0 de 79 entradas de DONE
  registraban el carril**. Ahora queda en el registro, y `sin clasificar` es un valor y no un hueco — dice
  que la línea no lo declaraba, que es distinto de que nadie llenara el campo.

  El plan en vuelo también lo lleva: una corrida que se reanuda arma la tarea desde `wip/<runner>.md`, y
  sin el campo ahí llegaba al cierre con el carril ya perdido aunque la tarea sí lo tuviera.

  **Lo que te pide algo**: `ops check` **avisa** cuántas entradas no lo traen y **no falla** — las
  escritas antes de esta versión no lo tienen y no hay de dónde sacárselo. Lo que sí falla es un valor
  inventado. Si cerrás a mano, agregá `lane:` a la entrada; si cerrás con `autobuild`, ya lo escribe.

## [0.75.0] - 2026-09-10

### Cambiado

- **R9 dice ahora que una quita casi nunca se ve como una quita, y que eso no admite excepción.** Se
  escribe como un agregado —una bandera que se pone, una variable que se exporta— y lo que la delata es
  la frase que la justifica: «lo agrego **para que** deje de …». Silenciar un aviso, saltear una rama o
  desarmar una confirmación son quitas, y una quita no se entrega sin su aserción de ausencia vista en
  rojo devolviendo lo quitado.

  Con un renglón propio para el caso que más se disfraza: **una confirmación que estorba casi siempre
  está cuidando algo**, y si no sabés qué, eso es el resultado de la medición y no un permiso para
  seguir. Lo que corresponde es quitarle a la herramienta el motivo de preguntar, no la pregunta.

  **Lo que te pide algo**: es una regla del sistema, así que baja a tu `planning/` en el próximo
  `upgrade` y aplica a todo cambio, no sólo a los del toolkit.

- **Un punto y coma dentro de la prosa de `tests:` ya no parte la traza.** El `;` es el separador que R8
  fija y a la vez el signo más común de la prosa española, y ese campo pide las dos cosas: el contrato
  pide `CN → prueba` y R9 pide decir cómo se vio fallar esa prueba. Una sola traza con prosa se partía en
  tres y `check` rechazaba el campo entero mandando a revisar la traza, que era lo único que estaba bien
  — y la salida fácil era acortar la prosa hasta que pasara, o sea empobrecer justo la evidencia.

  Ahora se corta sólo cuando detrás **empieza otra traza**, que es la misma decisión que `commit:` ya
  tomaba para el sha. Las entradas con varias trazas siguen valiendo igual.

  **Lo que te pide algo**: si tu prosa contiene literalmente `; A → algo`, se va a partir ahí — desde
  afuera es indistinguible de dos trazas. Es la misma concesión que `commit:` aceptó.

- **Un caso no se cierra sin haberlo probado corriendo.** El `## Cierre` nombra qué se corrió y qué
  devolvió —la salida, la mutación vista en rojo, el número medido—; «la suite pasa» no cuenta, porque
  dice que nada de lo que ya había se rompió y no que esto funcione. Vale igual cuando se decide no
  arreglar: ahí se prueba el dato que sostiene la decisión. La puerta lo comprueba desde esta versión y
  no hacia atrás, por lo mismo que el contraste rige desde 0.65.0.

### Corregido

- **`autobuild` deja de reintentar un reclamo que no puede salir bien.** Cuando la cola vuelve a ofrecer
  la misma tarea después de un `claim` fallido, nadie la tomó: el reclamo falló por su cuenta y repetirlo
  no cambia nada. Antes se repetía hasta agotar el cupo de tareas de la corrida — en una corrida real
  **28 de 50 agentes** se fueron ahí, sin construir nada y sin que nada lo dijera. Ahora para con
  `claim-stuck`, y el motivo lleva el slug que la cola ofreció y lo que contestó el reclamo. Perder la
  carrera de verdad sigue sin frenar: ahí la cola pasa a ofrecer otra tarea, y ahora la corrida dice con
  cuál sigue. Lo mismo en Decompose: si tras pedir la partición la cola sigue ofreciendo la tarea sin
  partir, la escritura no ocurrió y para con `split-not-applied`.

- **El registro de una corrida dice qué hace cada agente.** Las llamadas sin nombre se veían como el
  arranque del preámbulo compartido, que es igual en todas: la misma cadena repetida treinta veces, y un
  bucle de veintiocho agentes indistinguible de trabajo. Las veintisiete llamadas del recorrido llevan
  etiqueta propia.

- **Un gate ya no puede borrar el `node_modules` de tu proyecto, y se revierte el `CI=true` de 0.74.0.**
  Esa variable resolvía el síntoma del caso anterior —pnpm dejaba de preguntar antes de purgar—
  desarmando la confirmación en vez de quitarle el motivo. Y esa confirmación era lo único que protegía
  al árbol enlazado: sin ella la reinstalación avanza y **borra por el enlace**. Medido: `require()`
  dejaba de encontrar las dependencias del proyecto, y la instalación ni siquiera necesita completarse
  para hacerlo — en la corrida medida abortó por `frozen-lockfile` y para entonces ya había borrado.

  Ahora la copia lleva `verify-deps-before-run=false`, que le dice a pnpm que no sincronice nada antes de
  correr el script. No hay purga que confirmar, así que no hay confirmación que desarmar.

  **Lo que te pide algo**: si tenías un gate que se portaba distinto bajo `CI`, deja de verla. Vuelven el
  color y los prompts de otras herramientas — y un prompt en un proceso sin terminal aborta, que es la
  barrera que se quiere de vuelta.

## [0.74.0] - 2026-09-10

### Corregido

- **Un gate ya no pisa lo que vos construiste.** Al medir el índice, lo ignorado se enlazaba al árbol
  real: el gate corría sobre lo staged y te dejaba la salida de build con **esa** versión, mientras tu
  fuente en disco tenía otra y nada lo decía. Si corrías la app después de commitear, corrías algo que
  no era lo que estabas mirando. Ahora lo que un gate puede fabricar —`dist`, `build`, `out`,
  `coverage`, `.next`, `.nuxt`, `.svelte-kit`, `.turbo`, `.output`, `.parcel-cache`, `__pycache__`,
  `.pytest_cache`— se construye adentro de la copia y se descarta con ella. Lo que no puede fabricar
  —`node_modules`, un `.env`— se le sigue enlazando.

  Arregla también algo del propio gate: construía sobre restos de tu corrida anterior, así que su
  veredicto dependía de un estado que nadie declaró.

  **Lo que te pide algo**: el build del gate deja de ser incremental, así que ese commit tarda más. Y si
  tu proyecto genera en un directorio ignorado que no está en esa lista, seguí reportándolo: el nombre
  se agrega.

- **Los gates corren sobre una copia que se declara no interactiva, y un gate que falla dice qué dijo.**
  `verify` mide el índice en un temporal y enlaza ahí lo ignorado, `node_modules` incluido, apuntando al
  original. En un proyecto pnpm eso no corre: el gestor ve que el árbol enlazado no fue instalado ahí y
  su reacción es reinstalar, que empieza borrando el `node_modules` **del proyecto**. Lo único que lo
  detenía es que el hijo no ve una terminal. Ahora la copia lleva `CI=true`, que es la variable que el
  propio pnpm nombra — y sólo la copia: si árbol e índice coinciden, los gates corren en tu directorio y
  ahí no se te cambia nada.

  Y el bloqueo dejaba de decir por qué: `test (exit 1)` era el mismo texto para una suite en rojo y para
  un gestor que se negó a arrancar el script, así que empujaba a aprobar el commit como «rojo conocido»
  sin que nada se hubiera medido. Ahora llega con la duración y con la primera línea de error de la
  herramienta, y cuando todos los gates fallan por debajo de dos segundos lo dice — sin afirmar que no
  corrieron, que es algo que no se puede saber desde acá.

  **Lo que te pide algo**: si tenías un gate que se comportaba distinto bajo `CI`, ahora lo va a hacer
  al commitear con algo sin stagear o sin trackear.

## [0.73.0] - 2026-09-09

### Corregido

- **Los guards dejan de frenar trabajo legítimo escrito en varias líneas.** Siete reglas acotaban
  «dentro de este comando» con `[^;&|]`, y el salto de línea —que también separa comandos— no estaba en
  esa lista: cualquier bandera de una línea posterior se leía como parte del comando de arriba. `git
  commit -m "x"` seguido de `ls -a` se bloqueaba como si stageara al commitear, y `git push origin main`
  seguido de `rm -f /tmp/x` como un force push, que además nombraba una violación que no estaba.

  Lo que frena sigue frenando: `commit -am`, `add -A`, `push --force`, `--amend` e `install -g` en una
  línea siguen bloqueados, y un `git push` seguido de otra cosa sigue pidiendo la acción humana que R10
  exige — sólo que ya no lo llama reescritura de historia.

- **Un banco de evaluación que no se puede rehacer lo dice, en vez de seguir sobre uno que no es nuevo.**
  El borrado ahora se comprueba, y si algo sobrevive la corrida corta nombrando qué. Hasta acá el mismo
  hecho se venía rodeando por síntoma —reintentos en el borrado, `force` en el andamiaje, un `rm` antes
  del enlace—, y cada rodeo dejaba la medición siguiente corriendo sobre restos de la anterior.

  La prueba que lo destapaba, además, **descartaba el resultado de la corrida que fallaba**: cualquier
  causa terminaba en el mismo `true !== false` con el `stderr` tirado, y por eso tres investigaciones
  dejaron escrito «no está establecido por qué». Ahora la falla llega con su código y su ruta.

  **Lo que te pide algo**: si rehacer un banco te corta con «no se pudo borrar entero», borralo a mano y
  volvé a correr. Antes eso seguía en silencio y fallaba más adelante, en otro lado.

## [0.72.0] - 2026-09-09

### Corregido

- **Una fuente que el cargo no pudo abrir se declara en `pending:`, y el ciclo avisa cuando vuelve a
  servir.** Antes eso terminaba en un comentario del propio `sources.yaml` —cinco cargos lo escribían
  así, y uno dejó un «registrar cuando exista una ficha legible» que nadie iba a revisar—. Ahora es una
  lista con `name`, `why` y `since`, y `url` cuando hay una candidata; el chequeo semanal reporta **sólo
  la que volvió a servir el documento**, porque una que sigue cerrada es lo esperado.

  No alcanza con que el host conteste: se pide que la página traiga la marca del documento, el primer
  número del nombre. Un sitio grande devuelve cientos de palabras de navegación sin una línea de la
  norma, y así fue como la primera versión de esto dio por recuperada una ficha de IEEE que seguía
  siendo una cáscara.

  **Lo que te pide algo**: si tenías pendientes anotadas como comentario en un cargo propio, moverlas a
  `pending:` es lo que hace que alguien se entere cuando la URL empiece a funcionar.

- **La fase `Pick` de `autobuild` deja de prometer una expansión que ya no existe.** Su descripción
  —lo único del recorrido que se lee **antes** de autorizar la corrida— seguía diciendo «o la épica que
  falta expandir» después de que la promoción de épicas se quitara. Lo que el recorrido hace hoy es
  tomar la próxima tarea del hito y reservarla.

- **Un merge a `main` ya no le quita la corrida de CI al merge anterior.** Todos los pushes a `main`
  compartían grupo de concurrencia, y sólo una corrida puede estar pendiente por grupo: la que llega
  desaloja a la que esperaba. En una tanda de merges sobrevivían la primera y la última y morían todas
  las del medio — el 2026-09-08, quince corridas. No lo causaba `cancel-in-progress`, que ya estaba
  apagado en `main` y funciona: la corrida **en curso** sobrevivió cada vez. Ahora cada commit de `main`
  tiene su propio grupo, así que ninguna espera detrás de otra.

  **Lo que te pide algo**: si mergeás varios PR seguidos, ahora corre la suite completa una vez por
  merge en vez de una por tanda. Lo que se compra es saber cuál rompió `main` sin bisect.

- **`agent evaluate` deja de anunciar que el contrato cambió hoy cuando no cambió.** La fecha sale de
  `git log` sobre el `SKILL.md` del cargo, y un checkout de un solo commit —el que hace `actions/checkout`
  por defecto— no tiene con qué contestarla: git le atribuye el árbol entero a ese commit, así que
  **todos** los contratos parecían haber cambiado el día de la corrida. En un repositorio truncado el
  aviso ahora dice que no se puede saber, en vez de inventar una fecha, y el job que evalúa se lleva la
  historia completa para que sí se pueda.

  **Lo que te pide algo**: si corrés `agent evaluate` en un pipeline con clon superficial, vas a ver el
  aviso nuevo. Se destraba con `fetch-depth: 0` en el checkout; sin eso el chequeo no puede correr y
  decirlo es la mitad del arreglo.

### Cambiado

- **El README del catálogo dice qué calibra un caso de evaluación recién escrito.** El rojo de su primera
  corrida prueba que puede fallar, no que falle por lo que dice medir: eso lo prueba una corrida donde se
  rompa a propósito la conducta que cuida. Es lo que R9 ya pedía para una prueba, dicho donde se leen los
  veredictos.

## [0.71.0] - 2026-09-09

### Cambiado

- **`planning/PROTOCOL.md` pide contrastar la línea de una tarea contra su propia descripción.** La línea
  declara cuatro cosas —qué hace, en qué carril, quién entrega y revisa, con qué se comprueba— y las
  cuatro las escribe la misma mano en el mismo acto, así que nada las cruzaba después. Releerlas no
  encuentra el hueco: una aceptación incompleta se lee perfecta porque todo lo que dice es cierto.

  La pasada vive al escribir la tarea y no en una fase, y la razón es que cualquier fase donde viviera es
  una que el carril puede saltar — una tarea mal marcada `express` es justamente la que se salta la fase
  donde alguien lo notaría.

  **Lo que te pide algo**: cuesta minutos por tarea al promover un hito. En el caso que originó esto, seis
  correcciones sobre cinco tareas, todas antes de escribir una línea de código.

### Corregido

- **`autobuild` comprueba su raíz en la primera fase, y lo dice nombrándola.** `ROOT` viaja escrito en
  el workflow y es relativo a la carpeta donde se abre la herramienta: si la sesión abrió en otra, todas
  las rutas resuelven a `<raíz>/<raíz>/…` y ninguna existe. Nada lo comprobaba, así que la corrida
  gastaba Triage entero sobre archivos ausentes y paraba más abajo mandando a revisar el planning — que
  está bien; lo que no existe es la carpeta de la que cuelga.

- **`learning/HISTORY.md` dice lo mismo en los 53 cargos, y su fila entra en una tabla.** El encabezado
  tenía trece redacciones distintas y nueve contradecían la tabla que llevan debajo —«registrar únicamente
  cambios aprobados», cuando la columna se llama «Decisión» y hay dos—. Y dieciséis archivos no tenían
  tabla: la fila que escribe el ciclo quedaba pegada al párrafo, que en markdown es texto con barras.

  Los tres cargos con una exigencia propia —país, jurisdicción, revisión de Legal— la conservan como línea
  aparte. Ninguna se cumplía, y aun así no se borraron: dos son de cargos regulatorios.

- **`autobuild` ya no promueve épicas: nombra la que sigue y para.** Cuando se le acababa la cola,
  expandía la próxima épica del roadmap al BACKLOG. El roadmap llama `open` a «candidata editable que aún
  no fue promovida al backlog», así que pegarla en la cola es promoverla — y BR-OPS-002 deja una propuesta
  fuera de la cola hasta que la apruebe una persona. El prompt pedía «la próxima épica abierta y
  aprobada», y «aprobada» no correspondía a ningún dato: una épica declara `epic`, `title`, `status` y
  `service`, y ninguno registra una aprobación.

  Ahora `ops context` nombra la próxima épica sin promover —`EPIC 003: … — sin promover`, y el campo
  `nextEpic` en `--json`— igual que ya nombraba una recurrencia vencida, y por el mismo motivo: la máquina
  calcula, la persona encola.

  **Lo que te pide algo**: si usabas `autobuild` desatendido esperando que encadenara épicas, ahora se
  detiene al terminar el hito y hay que pegar el siguiente en `BACKLOG.md`. `context` te dice cuál es.

- **El chequeo semanal de fuentes mira también `references/` y `SKILL.md`, y deja de dar por rotas las
  páginas sanas.** Miraba sólo `sources.yaml`: las 207 URLs que un cargo cita en su método no las
  comprobaba nadie, y 29 no servían —dos de ellas 404 de páginas movidas hacía meses—. `evaluations/`
  queda afuera a propósito: un caso adversarial inventa dominios y comprobarlos mide el fixture.

  Y el chequeo se equivocaba en las dos direcciones sobre lo que sí miraba. Se identificaba como
  `cauce-learning/1.0`, que no es con lo que el cargo lee: las tres páginas de `ftc.gov` del catálogo dan
  403 a ese `User-Agent` y 200 con miles de palabras a uno de navegador. Y veinte segundos no alcanzan
  para un PDF grande —el instrumento de la OCDE que cita `sales-representative` llega pasados los
  treinta—. Ahora lo que falla se reintenta una vez, con más tiempo y como navegador, y el resumen dice
  cuántas lo necesitaron: que un dominio nos rechace es un dato suyo y se pierde si el reintento lo tapa.

  El aviso nombra además el archivo donde está escrita cada URL, porque eso decide quién la arregla.

- **Las URLs rotas del catálogo se arreglaron, no sólo se reportaron.** Veintitrés reemplazos
  comprobados uno por uno. Casi ninguna estaba muerta: cuando un dominio bloquea suele haber otra forma
  publicada del mismo documento —el PDF donde el HTML tiene Cloudflare (`acm.org`, los instrumentos de la
  OCDE), otro sitio del mismo organismo (`oecd.ai`, `gov.uk`), o el feed que la propia CISA distribuye—.
  Las cuatro que no tienen ninguna —`pmi.org`, `fatf-gafi.org`— se quedan como fuente y pierden el enlace
  en `references/`, conservando el nombre: un 403 ahí sólo le hace perder un clic a quien lee.

- **Las normas ISO del catálogo se citan por una ficha que se puede leer.** `www.iso.org` devuelve 403,
  y 41 entradas de 22 cargos apuntaban ahí: para todas ellas la investigación semanal producía el mismo
  informe «sin novedades» que produciría una norma que no cambió. Ahora una ISO/IEC se cita por su ficha
  del IEC Webstore y una ISO sola por la de `committee.iso.org`, que sirve el mismo número de catálogo.

  La edición pasó al nombre de las ISO/IEC —`ISO IEC 25010:2023 product quality model`— porque la ficha
  del webstore es de una edición concreta: buscar «ISO/IEC 25010» ahí devuelve primero la de 2011.
  Cada ficha se comprobó contra su `<title>` antes de anotarla, y `cloud-architect` pasó a declarar la
  edición 2 de ISO/IEC 27017, publicada el 2026-07-27 — su modelo operativo decía «no tratarla como
  publicada» y eso dejó de ser cierto. Los `references/` de esos cargos enlazaban las mismas normas a las
  mismas URLs muertas y también se cambiaron: 40 enlaces en 22 archivos.

  **Lo que te pide algo**: si forkeaste alguno de esos 22 cargos, tu copia sigue con las URLs viejas y
  `check` te avisa que el original cambió río arriba. Vale traerlas: las viejas no responden.

- **El ciclo semanal comprueba que las fuentes declaradas de un cargo respondan, y anota las que no.** Una
  fuente ilegible y una que no cambió producían el mismo informe —«sin novedades»— y no son lo mismo: la
  primera no se comprobó. Ahora el resumen del job dice cuántas fuentes declara el cargo y cuáles no
  respondieron, con su código, y sale una anotación cuando hay alguna. Avisa y no falla: un 403 de una
  semana puede ser temporal, y lo que decide una cadencia es el patrón sostenido.

  Mide además **el texto que la página trae**, no sólo que responda: por debajo de 50 palabras fuera de
  etiquetas se reporta igual que un 403, porque una aplicación renderizada por cliente devuelve su título y
  poco más. El umbral sale de medir las 255 fuentes del catálogo — catorce caen debajo y sólo dos entre 50
  y 200, así que no parte ningún grupo. Y un `202` se reporta aparte: es «aceptado, vuelve más tarde», que
  es lo que contestan las seis normas europeas que el catálogo cita.

- **El lector de fuentes era ciego para uno de los dos formatos del catálogo.** `sources.yaml` admite la
  entrada repartida en varias líneas y la escrita en una sola, y sólo se leía la primera: en los seis
  cargos que usan la segunda se veían **cero** fuentes. De ahí sale la validación que rechaza una URL
  declarada dos veces con nombres distintos, así que esos seis nunca la tuvieron — y no fallaba nada,
  porque no encontrar duplicados y no mirar producen el mismo silencio.

- **Archivar una propuesta ahora deja quién lo decidió y cuándo.** Antes quedaba con «Responsable: por
  definir» y ninguna fila en `learning/HISTORY.md`, así que una propuesta que alguien miró y descartó se
  leía igual que una que nadie tocó. El responsable sale de `CAUCE_OWNER` o de `git config user.email` —la
  misma identidad con la que se reclama una tarea— y la fila usa la columna «Decisión» que la tabla ya
  tenía, con el valor `archivada`.

- **`ops context` y `ops tree` sobre un planning que no existe ahora fallan, en vez de contestar como una
  cola terminada.** Antes devolvían `queued: 0` con código 0, así que una ruta equivocada se propagaba
  como dato y no como error. Ahora salen con código 2 y nombran la **ruta resuelta**, que es la que hace
  falta para ver el problema: en `sidecar`, `<empresa>-ops/planning` escrito desde adentro de la raíz
  apunta a `<empresa>-ops/<empresa>-ops/planning`, y las dos formas se ven razonables.

  **Lo que te pide algo**: si tenías un script que trataba la salida vacía como «nada que hacer», ahora
  recibe un error. Es deliberado y va en la misma dirección que el cambio de código de salida de
  `upgrade` en 0.67.0 — un comando que no pudo leer no responde como si hubiera leído.

- **`autobuild` no expande una épica sobre una lectura que falló.** La fase Pick tomaba «sin tarea y cola
  en cero» como permiso para promover la próxima épica al BACKLOG, y ese es exactamente el estado que
  devolvía un planning ilegible. Una corrida real escribió seis historias que ninguna persona aprobó
  —lo que BR-OPS-002 prohíbe— y el `check` posterior dio verde, porque once tareas en cola es un estado
  válido.

  El arreglo del CLI no alcanzaba solo: el esquema se completa igual, y ceros es lo que un modelo escribe
  cuando no tiene qué poner. Así que el informe de estado ahora declara si de verdad leyó, y expandir
  exige esa lectura afirmada en vez de la ausencia de tarea. Parar cuesta una corrida; promover escribe en
  el repositorio.

## [0.70.0] - 2026-09-08

### Agregado

- **`planning/claims/`: quién tomó qué, para que dos runners no construyan lo mismo.** Un archivo por tarea
  tomada, con el slug de la tarea como nombre: `ops claim planning <tarea>` lo crea y `ops release` lo borra.
  `ops context` deja de ofrecer una tarea con reclamo ajeno —antes le entregaba la misma a los dos y ninguno
  se enteraba—, nombra quién la tiene y devuelve antes lo que vos reclamaste que lo que está libre.

  Es un archivo por tarea y no uno por persona a propósito: así dos personas en tareas distintas no tocan
  nunca el mismo archivo, y dos que toman la misma chocan en git, que es donde el choque significa algo.
  `check` rechaza el reclamo que nombra una tarea que no existe, avisa a los tres días de tomada y avisa
  cuando hay dos reclamos sobre el mismo `service:` — avisa y no frena, porque frenar serializaría a un
  equipo entero sobre un servicio.

  El reclamo distingue `owner` —la persona, a quién preguntarle— de `runner` —el agente que la hace—, y
  lo segundo es lo que decide de quién es una tarea. Con varios agentes en una máquina la persona es la
  misma y el árbol de trabajo no: sin esa distinción, el segundo agente tomaría por propia la tarea del
  primero. Se crea con exclusión —el archivo se abre en modo exclusivo, así que dos reclamos simultáneos
  no se pisan— y un runner lleva una tarea a la vez.

  **Lo que te pide algo**: el reclamo hay que commitearlo y empujarlo — sin eso, el otro runner lee lo
  que hay en su copia y la reserva no existe para nadie más—. Y si corrés varios agentes en la misma
  máquina, cada uno exporta `CAUCE_RUNNER` con un valor propio.

- **`autobuild` reserva la tarea antes de construirla y la suelta al cerrarla.** Es lo que hace que dos
  corridas en paralelo dejen de trabajar lo mismo: entre preguntar qué toca y reservarlo hay una ventana, y
  perder esa carrera no frena la corrida — relee y sigue con la que quedó libre.

- **El aviso de reclamo viejo mira si la rama avanzó, no cuánto hace que se tomó.** El tiempo transcurrido
  no distingue una tarea larga de una abandonada, y equivocarse cuesta en los dos sentidos: apurar a quien
  está trabajando, o dejar bloqueada para siempre la tarea de quien se fue. Ahora `check` mira el último
  commit **propio** de `task/<tarea>` —los que no están en el tronco, porque una rama recién creada hereda
  su historia entera y sin esa distinción toda rama parecería haber avanzado el día que se creó—: tres días
  sin ninguno avisan, y una tarea que recibe commits no se apura nunca
  aunque lleve semanas tomada. Sin repositorio resoluble el aviso vuelve a mirar sólo la fecha — degrada a
  lo que había, no rompe.

- **`ops context --hito <slug>` acota la cola a un hito.** Es la forma más barata de que dos personas o dos
  agentes no se crucen: en hitos distintos casi nunca dependen entre sí ni tocan los mismos archivos. Lo
  que se acota es qué se ofrece, no qué se sabe — una dependencia que vive en otro hito se sigue juzgando
  igual—, y un hito mal escrito lo dice en vez de contestar «sin tarea disponible», que es indistinguible
  de un hito terminado.

- **El plan en vuelo es uno por runner: `planning/wip/<runner>.md`.** Con `mode: sidecar` hay un solo
  `planning/` por máquina, así que un plan compartido lo escribían todos los agentes que corren ahí: el
  segundo pisaba el del primero, y `ops context` le entregaba la tarea que el primero estaba construyendo
  —con el plan ajeno adentro y diciéndole que estaba libre—. `context` honra sólo el tuyo y `check` los
  recorre todos.

  **Lo que te pide algo**: `planning/WIP.md` se retiró. Mové tu plan a `planning/wip/<runner>.md` —el
  nombre sale de tu `CAUCE_RUNNER`, aplanado; `ops context --json` lo dice en `wipFile`— y borrá el
  archivo viejo, que mientras esté `ops check` lo nombra. El `.gitignore` nuevo excluye `planning/wip/*.md`
  y conserva su README; si venías con la línea de `planning/WIP.md`, cambiala.

- **Un `service:` ambiguo entre varias raíces se nombra en vez de elegirse.** Con más de un
  `workspaceRoots`, un servicio que existe en dos —`.` existe en todas— resolvía al primero: el árbol de
  trabajo terminaba en el repositorio que no era, y el aviso de avance miraba las ramas de otro. Ahora
  `ops worktree` nombra los candidatos y se niega, y el aviso degrada a mirar sólo la fecha.

- **`ops worktree` avisa cuando la instancia está embebida.** Con `mode: embedded` cada árbol se lleva su
  propia copia de `planning/`, así que los reclamos de un agente no los ve el otro hasta mergear y la
  coordinación entre varios deja de existir sin que nada falle. No lo frena: un árbol por rama con un solo
  agente es un uso legítimo.

- **`ops runners <planning>`: qué runners tienen trabajo abierto, para que un agente pueda preguntar.**
  Elegir con qué runner se arranca es lo primero de una sesión y `ops context` no lo contesta: responde
  «qué hago» para un runner ya elegido. Sin esa lista, un agente se inventa un id y deja huérfano el
  trabajo de ayer, o se lo pisa a otro que sigue corriendo.

  **Lo que te pide algo**: nada, y es el punto. `AGENTS.md` le dice al runner que mire esa lista al abrir
  la sesión, que **pregunte** cuál se retoma o si arranca uno nuevo, y que **exporte el id él mismo**. A
  una persona no se le pide que escriba una variable de entorno.

- **Tu propio reclamo desde otro runner se reconoce en vez de resolverse solo.** Volver al día siguiente
  sin reponer `CAUCE_RUNNER` y correr un segundo agente tuyo se ven idénticos desde el archivo, y las dos
  salidas automáticas rompen trabajo: retomar sola le saca la tarea al otro agente, y crear un runner
  nuevo deja dos construyendo lo mismo. `ops claim` dice cuál es cuál y con qué id se retoma; `ops context`
  marca esas tareas como «vos, desde otro runner».

- **La evidencia de una tarea cerrada vive en su propio archivo: `planning/done/<slug>.md`.** Cerrar es lo
  que más se hace, y mientras la evidencia se acumulaba en un `DONE.md` compartido, cerrar era agregarle
  una entrada a algo que otro también estaba tocando. Ahora dos personas —o dos agentes— que cierran a la
  vez escriben archivos distintos: no hay conflicto que resolver ni regla de merge que aplicar.

  La entrada declara `fecha:`, la del cierre. Mientras vivían en un archivo, «la última» era la última del
  archivo; con archivos sueltos el orden lo daría el listado del directorio, que es alfabético, y la
  respuesta equivocada se leería igual de bien que la correcta. `ops evidence` sin `--task` ordena por ese
  campo, y el contrato de una entrada está en `planning/done/README.md`.

  **Lo que te pide algo**: `planning/DONE.md` se retiró. Pasá cada entrada a su propio
  `planning/done/<slug>.md` con su `fecha:` y borrá el archivo — mientras esté, `ops check` lo dice en vez
  de ignorarlo, porque un `DONE.md` que ya nadie lee deja a sus épicas sin poder cerrar y a sus historias
  figurando sin evidencia.

- **`ops archive <NNN>` se retiró; `ops archive human-actions` se queda.** Archivar una épica existía para
  descongestionar un `DONE.md` que se hinchaba con una entrada por tarea; con un archivo por tarea no hay
  nada que descongestionar, y mover esos archivos a una carpeta por épica sería reintroducir el movimiento
  que esto vino a sacar. El comando lo dice, en vez de contestar «la épica debe ser NNN».

- **`(depende: slug)` en una línea de tarea: lo que sigue no se le ofrece a otro.** El orden del BACKLOG
  era la dependencia y alcanzaba mientras hubiera un runner; con dos, el segundo toma la que sigue mientras
  el primero construye aquella de la que depende, y las dos ramas se pisan al integrar. Una tarea con
  dependencias sin cerrar no se ofrece ni se puede tomar, y `context` la muestra con una línea `WAIT` que
  nombra la dependencia y quién la tiene — una cola trabada no se lee como una cola vacía. `check` rechaza
  la dependencia que no existe y nombra el ciclo entero cuando lo hay.

- **`ops worktree <planning> <tarea>`: un árbol de trabajo por agente, sin clonar el repositorio.** Resuelve
  en qué raíz de `workspaceRoots` vive el `service:` de la tarea, crea la rama `task/<slug>` y el árbol al
  lado, y devuelve la ruta con el `export CAUCE_RUNNER` ya escrito. `git worktree` comparte el mismo `.git`
  y el mismo historial, así que no hay una segunda copia del repositorio: lo que hay es un segundo
  directorio de archivos fijado a su rama, y por eso **ningún agente hace `checkout`** sobre el trabajo de
  otro. Repetirlo devuelve el árbol que ya existe.

- **`.gitattributes`: `DONE.md` y `HUMAN_ACTIONS.md` se concatenan en vez de conflictuar.** Dos personas
  cerrando trabajo el mismo día chocaban siempre, y ese conflicto no significaba nada: las dos entradas son
  buenas y van las dos. Lo que `union` no hace es deduplicar, y esa falla ya la atrapa `DONE duplicado`.

- **`planning/delivery/teamwork.md`**: qué comparte el equipo y qué no, por qué dos agentes necesitan un
  `git worktree` cada uno, cómo repartir trabajo, y qué se rompe primero según el tamaño del equipo.

- **BR-OPS-005 — una tarea, un runner.** La contracara de BR-OPS-001: aquélla impide que un runner lleve dos
  tareas, ésta que dos runners lleven la misma.

### Cambiado

- **`planning/WIP.md` pasa a ser local y deja de viajar por git.** Existe para recuperar la sesión de quien
  lo escribió —nadie más puede retomarla— y cambia en cada paso, así que compartirlo era un conflicto por
  commit a cambio de nada. `check` deja de exigir que exista: ausente se lee como IDLE, que es lo que
  significa, y un clon nuevo ya no falla por no traerlo.

  **Lo que te pide algo**: el molde nuevo lo gitignorea, pero tu `.gitignore` es tuyo y `upgrade` no lo toca.
  Para aprovecharlo, agregale `planning/WIP.md` y sacalo del índice con `git rm --cached planning/WIP.md`.
  Sin hacer nada, todo sigue funcionando como antes.

- **BR-OPS-001 se acota al runner.** Decía que WIP es el mutex sin decir de quién, y con equipo eso se leía
  como «trabaja uno por vez». Ahora dice que un runner no toma dos tareas; que dos runners no tomen la misma
  es BR-OPS-005.

## [0.69.0] - 2026-09-07

### Agregado

- **`planning/RECURRING.md`: el trabajo que vuelve se declara una vez.** Actualizar dependencias, revisar
  quién tiene acceso a producción, mirar el gasto del mes: una fila con su cadencia —`mensual`,
  `trimestral`, `semestral` o `anual`— y la celda de tarea escrita como la cola de su línea de BACKLOG,
  así que la aceptación se decide una vez y no se improvisa en cada vuelta.

  **Nada se dispara.** No hay cron ni cola: el vencimiento se calcula cuando alguien corre el CLI, y rueda
  desde el período que cerró la última vuelta en `DONE.md` en vez de una celda que haya que acordarse de
  actualizar. `node tools/ops.js recurring planning` dice qué venció, y `--promote <qué>` emite la línea de
  esa vuelta —la emite y no la escribe: pegarla en `BACKLOG.md` es el acto de promoción—. `check` rechaza
  la fila ilegible y avisa la vencida sin frenar nada; `context` la nombra con `DUE`. Postergar se escribe
  a mano con su razón, compra un período, y tres seguidas se avisan porque ahí lo que falla es la cadencia.

  **Lo que te pide algo**: el archivo llega vacío con esta actualización y, hasta que declares una fila, el
  motor no dice una palabra. Y si tu runner ya está andando, la regla nueva que necesita está en
  `AGENTS.md`: una recurrencia vencida no la promueve él, por más que `context` la nombre sola.

## [0.68.0] - 2026-09-07

### Cambiado

- **`upgrade` deja de borrar una ruta retirada cuyo contenido no puede probar suyo.** Retirar una ruta
  nunca volvió al toolkit dueño de lo que hay adentro, y hasta acá se borraba el directorio entero: quien
  tenía sus propios workflows en `automatization/workflows` los perdía sin confirmación y sin vuelta
  atrás. De las seis rutas retiradas, cuatro viven bajo `system/` o son un archivo con nombre del
  toolkit y se siguen retirando igual; las dos que un proyecto también usa para lo suyo
  —`automatization/runners` y `automatization/workflows`— se conservan.

  **Lo que te pide algo**: esas dos rutas quedan en disco con lo que tengas adentro, la corrida te dice
  cuántos archivos son, y `check` las cuenta en cada corrida hasta que las muevas o las borres. Si lo
  que tenés ahí son restos viejos del toolkit y querés limpiarlos, `--force` las retira.

- **La regla R9 dice cómo se prueba una quita.** Una prueba que comprueba que aparece lo nuevo no
  comprueba que desapareció lo viejo, y lo que se quita tiene dependientes que no se anuncian —el
  mensaje que afirmaba la invariante, la condición que la deducía—. Sale de una regresión de 0.67.0 que
  entró exactamente por ahí.

### Corregido

- **El informe de `upgrade` afirmaba descartes que no ocurrieron.** 0.67.0 pasó a conservar los archivos
  editados y la salida siguió enumerándolos como `− descartado tu cambio en …`, uno por uno, después de
  la línea que anuncia que terminó bien. En una instancia real fueron diecinueve renglones falsos, y
  entre ellos iba la única línea destructiva verdadera de esa corrida, indistinguible. Ahora el informe
  recibe qué pasó —descartado, conservado, pendiente— en vez de deducirlo de una condición que dejó de
  valer, y la línea «planning, organization y todo lo propio quedaron intactos» sale justo cuando es
  cierto, que es cuando se conservó algo.

## [0.67.0] - 2026-09-07

### Agregado

- **Guard `plan-first`: no se cambia el producto sin un plan escrito.** R1 y el paso 7 del protocolo lo
  piden desde siempre y nada lo comprobaba: tocar el archivo primero y redactar después la aceptación
  que lo justifica salía igual de verde y se leía igual en DONE. Ahora una escritura de producto exige
  un WIP activo con al menos un paso numerado.

  No juzga lo que tu instancia posee —`planning/`, `organization/`, `agents/`, `flows/`,
  `integrations/`, `automatization/`, `tools/`—: el plan se escribe en `planning/`, y exigirlo ahí sería
  un candado con la llave adentro. Y queda **inerte mientras tu planning no declare ninguna tarea**, que
  es una instancia recién creada; `automation check` te lo dice cuando lo está.

  **Lo que te pide algo**: si el cambio no es trabajo de una tarea, aprobá la ruta en
  `planning/.ops-approval`. La variable `OPS_PLAN_FIRST_OVERRIDE=1` lo apaga para toda la sesión.

- **`ops evidence <planning-dir>`: contrasta la evidencia de una entrada de DONE contra lo que no
  escribió su autor.** Los dos lados de `tests: CN → prueba` los escribía la misma mano en el mismo
  acto, así que compararlos medía prosa. Ahora se comprueban dos cosas independientes: si el artefacto
  que el rastro nombra existe en tus raíces de código, y qué gates corrió `verify` al commitear con su
  código de salida. Dice también lo que **no** puede contestar —que la prueba nombrada haya corrido
  depende del runner— y no reemplaza a leer su fuente, que es lo que R9 pide.

  **Lo que te pide algo**: `verify` deja ese registro en `planning/.verify-log`. Tu `.gitignore` no se
  actualiza con el molde —es de `init`—, así que agregale esa línea o el archivo te va a aparecer sin
  trackear en cada commit.

### Cambiado

- **`upgrade` conserva lo editado y actualiza el resto, en vez de abortar.** Antes cortaba si algún
  archivo del molde estaba editado, y el único flag que lo destrababa descartaba todos tus cambios de
  una: quien adoptó Cauce sobre un proceso propio quedaba eligiendo entre no actualizar nunca y perder
  su corpus. Ahora recibís `rules/system/` y `adr/system/` frescos y conservás lo tuyo, y la corrida
  nombra qué congeló — en cada corrida, no sólo la primera.

  `check` cuenta esos archivos como advertencia para que la deuda no desaparezca entre actualización y
  actualización. Y el consejo nombra los cuatro que no tienen contraparte propia adónde mudarse
  —`PROTOCOL.md`, `METHODOLOGY.md`, `FLOW.md` y el `Makefile`—, en vez de mandarte a mudarlos.

  **Lo que te pide algo**: `upgrade` ya no devuelve código distinto de cero por una edición local. Si lo
  llamás desde un script que esperaba ese fallo, ese script cambia. `--force` sigue reemplazando todo.

### Corregido

- **Un pipe escapado en `HUMAN_ACTIONS.md` corría las columnas de su fila.** En markdown un pipe dentro
  de una celda se escribe `\|` —es la única forma— y el parser lo tomaba como separador. La cara que
  importa era silenciosa: con el pipe detrás de la palabra del vocabulario, `check` pasaba y el runner
  recibía el contenido de `Origen` como si fuera la acción de desbloqueo. El escape ya no parte la
  celda, y se quita al leerla porque esa columna existe para que una persona la lea.

  **Puede que empieces a ver un error nuevo**, y es correcto: una fila con las columnas corridas podía
  esconder un estado fuera del vocabulario y desbloquear una tarea que nadie resolvió. Leída bien, ese
  estado se ve.

- **La cabecera de `HUMAN_ACTIONS.md` sólo se salteaba si decía exactamente `Tarea`.** Cualquier otro
  encabezado —`Tarea Requerida`, `Bloqueo`— caía del lado de los datos y `check` reportaba las etiquetas
  de tus columnas como una acción rota. Ahora la cabecera se reconoce por su forma —es la fila anterior
  a la de separadores— y se saltea la de **cada** tabla del archivo, no sólo la primera.

- **Los gates de `verify` corrían con el `GIT_DIR` de tu repositorio.** Un gate que escribe con git
  —una suite que levanta repositorios de prueba y les commitea— escribía entonces en el tuyo: commits
  ajenos en tu rama, archivos trackeados que nadie agregó y un `core.worktree` apuntando a un temporal
  ya borrado, sin que nada lo anunciara. Se disparaba al committear una naturaleza por vez, que es lo
  que R8 pide. Ahora la copia que `verify` materializa es un repositorio propio.

  **Lo que te pide algo**: esa copia no tiene historia. Un gate que lea una etiqueta o un `git log` no
  la encuentra, y un gate cuyo efecto **es** una escritura de git —taggear, commitear un lockfile
  regenerado— la hace sobre la copia, que se borra. Si tenés un gate así, sacá esa escritura del gate.

## [0.66.0] - 2026-09-07

### Corregido

- **`shell-boundary` ignoraba el `cd` del propio comando.** Resolvía las rutas relativas contra el
  directorio que le entrega tu runner, no contra el que el comando elige antes de escribir, así que
  juzgaba una ruta que nadie iba a escribir. Fallaba para los dos lados: `cd <fuera de tus raíces> &&
  echo x > nota.md` pasaba sin decir nada y el archivo se escribía afuera; y con el runner abierto en
  otro directorio, un `cd` a un lugar legítimo se frenaba nombrando una ruta que no estaba en el
  comando.

  Ahora cada escritura se juzga contra el `cd` que la precede. **Lo que te pide algo**: un `cd` cuyo
  destino no se puede saber acá —`cd $TRABAJO`, `cd -`— deja sin juzgar a toda ruta relativa que venga
  después, y eso se bloquea pidiendo la ruta absoluta o el `cd` en un comando aparte. Es deliberado y
  es el criterio que ya regía para el índice: un guard que no puede verificar no autoriza. Una ruta
  absoluta no depende del `cd` y se sigue juzgando igual.

## [0.65.0] - 2026-09-07

### Cambiado

- **R15 ahora cubre la enumeración que escribió la propia unidad de trabajo.** Decía que una entrega se
  contrasta contra lo que el contrato enumera; le faltaba el caso donde la enumeración la escribió el
  ticket, el diagnóstico o el caso: ahí lo que es código se tacha solo —hay un diff, hay una prueba— y
  una revisión pendiente, una decisión o un borde que hay que mirar no dejan rastro de haberse hecho ni
  de no haberse hecho. Cerrar por el diff los deja adentro, cerrados, y vuelven como defecto nuevo.

  **Qué cambia para vos**: una línea del tipo «vale la pena mirar si…» pasa a tener dos destinos y
  ninguno es el silencio — se hace y se dice qué encontró, también cuando no encontró nada, o sale como
  unidad propia antes de cerrar. Y cerrar deja de ser la consecuencia de que el código esté listo: es un
  acto con su propio recorrido, ítem por ítem, sobre lo que la unidad enumeró.

- **La aprobación por operación ahora abre todos los guards, y el archivo cambió de nombre.** Era
  `planning/.governance-approval` y sólo la miraba el guard de gobernanza; ahora es
  `planning/.ops-approval` y la consultan también los de migraciones, evidencia de pruebas,
  dependencias y el que corre los gates. **Si tenías una aprobación escrita, renombrá el archivo**; si
  no tenías ninguna —lo normal—, no hay nada que hacer.

  Lo que se aprueba sigue siendo una lista de rutas, y sigue valiendo para ese conjunto y para ningún
  otro. Cada guard lee la ruta sobre la que está decidiendo: la migración, la prueba que se borra, el
  manifiesto que va sin su lockfile. `verify` es la excepción y aprueba **todo lo que está en el
  índice**, porque lo que juzga es el commit entero: stagear una cosa más invalida la aprobación, que
  es lo que hace que autorizar un commit en rojo sea para ese commit y no para la sesión.

  Publicar un paquete o instalar algo global no se puede aprobar así, porque no hay ninguna ruta sobre
  la cual decidir. Esa sigue siendo una acción humana con su variable, y `AGENTS.md` lo dice donde
  estás mirando cuando el guard te frena.

- **`check` avisa si el baseline de adopción creció.** `ops adopt` ahora sella el archivo con una
  huella de lo que generó, y `check` la recalcula: una entrada agregada a mano se nombra en vez de
  sumar en silencio a una cuenta. **Retirar un renglón cambió**: en vez de borrarlo, ponele `#~`
  delante. Así la lista activa se achica igual y el conjunto original queda entero, que es contra lo
  que se compara.

  Un baseline generado por una versión anterior no tiene huella. `check` te lo dice y `ops adopt`
  sobre ese planning se la agrega sin regenerar la lista; con huella puesta se sigue negando a
  rehacerla, que es para lo que existe.

### Corregido

- **Los gates corrían sobre tu directorio y el commit graba el índice.** Son dos cosas distintas cuando
  stageás algo y después seguís editando, y también cuando un archivo nuevo todavía no está agregado: el
  gate pasaba porque el archivo estaba en disco, y el commit salía sin él. Ahora, cuando el árbol y el
  índice difieren, `verify` materializa el índice en un temporal y corre ahí; cuando coinciden corre
  donde está, que es lo mismo y no cuesta nada.

  **Esto puede empezar a frenarte commits que antes pasaban, y es lo que tiene que hacer**: si el gate
  falla y en tu directorio pasa, es que en disco tenés algo que no está staged. Lo ignorado —
  `node_modules`, `.venv`— viaja a la copia, así que los gates siguen encontrando lo que necesitan; lo
  sin trackear no viaja, que es justamente cómo aparece el `git add` que faltaba.

- **El guard de dependencias preguntaba al disco qué lockfiles hay.** Borrar el lock del directorio sin
  stagear el borrado dejaba de disparar la comprobación, aunque el commit siguiera llevándolo. Ahora un
  lock cuenta si está en disco **o** si el commit lo va a llevar; el aviso de «hay varios lockfiles»
  sigue siendo sobre el disco, porque lo que elige cuál manda es el gestor que corras.


- **Una opción global de `git` desactivaba la regla que miraba el subcomando.** `git` admite `-C`,
  `-c`, `-P` y las demás entre el verbo y el subcomando, y los patrones los esperaban pegados. Con
  cualquiera en el medio pasaban sin decir nada la prohibición de stagear todo, el force-push, el push
  a secas, `reset --hard`, `commit --amend`, `clean -f` y la forma ancha de `checkout`. Ahora las
  opciones se sacan una sola vez y cada regla vuelve a ver el verbo.

- **Un comando que stagea y commitea a la vez dejaba ciegos a tres guards.** Gobernanza, dependencias
  y el de los gates deciden mirando el índice, y un hook corre antes que el comando: con
  `git add … && git commit` o con `git commit -a` encontraban cero archivos y concluían que no había
  nada que revisar. Ahora `commit -a` se rechaza —es stagear todo con otra ortografía, que R8 ya
  prohíbe— y encadenar el `add` con el `commit` se frena pidiendo dos comandos.

- **El guard de migraciones frenaba cualquier archivo que mencionara SQL destructivo.** Miraba el
  contenido sin consultar la ruta, así que un ADR que citaba la migración o un comentario que advertía
  que eso no se hace se bloqueaban con el mensaje «La migración contiene SQL destructivo». Ahora el
  chequeo comparte el filtro por ruta con el otro, y el mensaje nombra el archivo.

- **La prohibición de stagear todo leía el mensaje de un commit como si fuera un comando**, así que el
  commit que explica la regla no se podía escribir. Y no veía la bandera cuando venía seguida de una
  comilla, o sea dentro de `bash -c` o `eval`.

## [0.64.0] - 2026-09-06

### Agregado

- **Las salidas de los guards están documentadas, y con su alcance real.** Cinco guards se pueden abrir
  y ninguna de las cinco llaves estaba escrita en un archivo que alguien fuera a abrir: el bloqueo te
  nombraba una variable y no había dónde leer dónde va. `AGENTS.md` ahora las lista en «Qué se puede
  editar y qué no», con lo incómodo dicho: el guard lee la variable de **su propio proceso**, así que
  escribirla delante del comando no llega, y la forma que sí funciona —exportarla en el entorno desde
  el que arranca tu runner— deja el guard apagado hasta que cierres la sesión. La única con alcance de
  operación es la aprobación de gobernanza.

- **Un commit de gobernanza se aprueba por operación, no por sesión.** El guard ofrecía como salida
  `OPS_GOVERNANCE_OVERRIDE=1`, que se lee del entorno del proceso: prendida antes de lanzar tu runner
  deja el guard apagado hasta que la sesión cierre. Eso convierte «aprobado este commit» en «apagado
  hasta que me vaya», y el `Makefile` de este proyecto ya decía cuál es el alcance correcto — «la
  autorización de R10 es por operación y humana». **Qué cambia para vos**: escribís las rutas
  autorizadas en `planning/.governance-approval`, una por línea, y el commit pasa. Vale para ese
  conjunto y para ningún otro: si después sumás un archivo, ese archivo no está aprobado. No se consume
  ni se borra sola —así un commit frenado por otra razón no te obliga a rehacerla—, así que `check` te
  avisa mientras exista para que la borres. La variable sigue funcionando y el mensaje del guard ahora
  dice por qué no es la vía recomendada.

## [0.63.0] - 2026-09-06

### Corregido

- **El salto de línea termina la lista de argumentos de un comando.** `shell-boundary` leía los
  argumentos de un `cp`, un `tee` o un `sed -i` cruzando a la línea siguiente, así que el destino que
  acusaba podía ser el comando de abajo: un bloqueo que hablaba de una escritura en `…/python3`, una
  ruta que no aparecía en el comando. Falla hacia el lado seguro —frena de más— pero señala algo que no
  existe, y un guard que señala mal es el que se termina apagando. **Qué cambia para vos**: si escribís
  scripts de varias líneas en un solo comando, dejás de ver bloqueos por rutas inventadas; lo que sí
  escribe fuera de las raíces se sigue frenando igual.

- **El cuerpo de un heredoc es texto, no un comando.** Escribir un archivo con
  `cat > nota.md <<'FIN' … FIN` juzgaba cada línea del documento como si fuera a ejecutarse, así que no
  se podía documentar lo que los guards vigilan: un párrafo que explica por qué no se borra la raíz se
  bloqueaba por nombrarlo, y la salida era cambiar de herramienta para escribir un archivo. Un heredoc
  es entrada estándar y no se ejecuta nunca. **Qué cambia para vos**: el cuerpo deja de juzgarse y la
  línea que lo abre se sigue juzgando entera, con su redirección —también si la escribís después del
  delimitador, como en `cat <<FIN > salida`—. Lo que se pierde a cambio: un cuerpo que después alguien
  ejecuta; al escribirse no ejecuta nada, y cuando se corra el guard verá el comando de verdad.

- **Una variable delante de `git commit` ya no apaga tres guards.** `dependencies`, `governance` y el
  control de generados de `verify` sólo corren sobre un commit, y decidían si lo era con un ancla que
  no contempla lo que un shell admite antes del verbo. Con `VAR=1 git commit` dejaban de correr **sin
  decir nada**, y con la ironía de que el prefijo que se escribe para un commit de gobernanza es una
  asignación: `OPS_GOVERNANCE_OVERRIDE=1 git commit` no leía el override, hacía que el guard no se
  ejecutara. **Qué cambia para vos**: si venías escribiendo ese prefijo, ahora el guard corre y te va a
  frenar. La variable se lee del entorno del guard, no del comando, y el mensaje ahora lo dice.

- **Un índice que no se puede leer deja de autorizar el commit.** `stagedFiles` devolvía una lista vacía
  tanto si el índice estaba vacío como si no se pudo leer, y los tres guards de arriba leen esa
  respuesta: una lectura fallida se les presentaba como «no hay nada que revisar». Llegar a una es
  fácil, porque el guard no expande variables: `git -C $OPS commit` resuelve la ruta literal `$OPS`.
  **Qué cambia para vos**: ese comando ahora se frena con un mensaje que nombra la causa. Escribí la
  ruta literal en `git -C`.

## [0.62.0] - 2026-09-06

### Corregido

- **Tres reglas de `destructive` reconocen el comando aunque venga envuelto.** Decidían dónde termina
  una palabra admitiendo sólo un espacio, el principio o el fin, y en un shell una palabra también
  termina en `;`, `&`, `|`, `)` y en una comilla. Con eso `rm -rf /; echo listo` **pasaba** —sin una
  sola comilla: lo que decidía era el espacio antes del punto y coma— y las tres se esquivaban dentro de
  `bash -c`, `sh -c`, `eval` o un subshell, donde lo de adentro sí se ejecuta. Las tres son `rm -rf`
  sobre `/`, home o `..`; `git checkout -- .` y su `restore`; y `mkfs`/`shred`. Las otras cinco de la
  tabla ya estaban sanas. **Qué cambia para vos**: algún comando que ayer pasaba ahora se frena, y es el
  que la regla siempre dijo que frenaba. Lo que **no** cambia es lo corriente: `rm -rf /srv/cache`,
  `rm -r build/cache` y `git checkout -- src/app.js` siguen pasando — revertir un archivo nombrado
  nunca fue lo que esta regla toca.

## [0.61.0] - 2026-09-06

### Agregado

- **`R11` se recorre antes de entregar, como `R14` y `R15`.** Antes de dar por terminado un cambio se
  repasan los comentarios que agrega, uno por uno, y de cada uno se contesta si alguien lo preguntaría,
  si su razón ya está escrita en otro lado y si está en el destino que le toca. Es la tercera regla de
  la misma familia —algo que releer no encuentra, porque quien lo escribió ya sabe por qué y la copia se
  lee bien precisamente porque lo que dice es cierto— y era la única sin la pasada. La regla dice
  también que una puerta que mida esto ayuda y no la reemplaza: las dos formas que más aparecen son la
  razón repetida apenas por debajo del umbral y el comentario que no repite a ningún otro porque repite
  el nombre que tiene al lado, y bajar el umbral hasta agarrarlas empieza a marcar lo que está bien.

- **Un guard nuevo, `shell-boundary`: el destino de un comando también se juzga.** El límite de raíces
  sólo se disparaba con `Edit` y `Write`, así que el archivo que una herramienta no dejaba escribir se
  escribía sin obstáculo con un heredoc por `Bash`: frenaba a quien actuaba de buena fe y no a quien
  quería pasar. **Qué cambia para vos**: se leen las redirecciones, `tee`, `truncate`, `cp`, `mv`,
  `install`, `rsync` y `sed -i` —sin `-i`, `sed` lee y no se juzga—, y el bloqueo nombra la salida
  —declarar la ruta en `writableOutsideRoots`— en vez de sólo decir que no. `/dev/null` y el temporal del sistema no se juzgan, y un destino armado con una variable
  que no sea `$HOME` tampoco: adivinar su valor sería inventar un límite. **No es completo y no se
  presenta como si lo fuera**: `eval`, un heredoc dentro de `bash -c` o un script propio escriben igual
  y ningún patrón los ve. Frena la forma habitual, como el resto de `destructive`.

- **`ops adopt` — adoptar Cauce en un proyecto que ya tiene historia.** `check` le exigía a toda entrada
  de `DONE.md` los mismos campos, incluida la que se escribió bajo otro contrato o bajo ninguno, y las
  únicas salidas eran escribir `tests: n/a — razón` en cada una vieja —que deja la exención adentro del
  campo de evidencia, donde alguien la va a copiar a una entrada nueva— o dejar `check` en rojo
  permanente. **Qué cambia para vos**: `ops adopt <planning-dir>` genera una vez
  `planning/.adoption-baseline` con las entradas que hoy no cumplen, y `check` deja de juzgarlas. El
  perdón es por entrada y no por campo, así que una historia vieja con un `commit:` de otro formato
  también queda afuera. `check` muestra la cuenta en cada corrida y avisa cuando una exenta ya cumple el
  contrato o cuando nombra una entrada que no existe, para que la lista se achique en vez de envejecer;
  achicarla es borrar el renglón. `adopt` no se vuelve a correr sobre un baseline que ya existe.

- **`writableOutsideRoots` — declarar rutas escribibles que no son raíces de código.** El guard de
  límites bloquea todo lo que caiga fuera de la raíz de ops y de `workspaceRoots`, y ahí cae el
  directorio donde tu runner guarda su memoria entre sesiones, que no es código de tu proyecto.
  Declararlo raíz para que pasara metía un árbol ajeno en `scan` y en el inventario de credenciales, y
  era el único guard que bloqueaba por política sin salida declarada. **Qué cambia para vos**: una lista
  opcional de rutas en `ops.config.json` —`~` se expande a tu casa, el resto se resuelve contra la raíz
  de ops— y `check` te las muestra resueltas en cada corrida, porque una exención que no se ve es un
  límite que ya no existe. No declarar ninguna sigue siendo el caso normal: al actualizar no hay nada
  que agregar.

### Corregido

- **El «Contexto relevante» de la épica llega a quien construye la tarea.** `ops context` resolvía la
  épica y mandaba número, título y estado; la sección que dice contra qué se construye se quedaba en el
  archivo, y `/autobuild` le dice al ejecutor que lea cuatro archivos «una sola vez y no leas nada más»,
  entre los cuales el roadmap no está. Recibía el resultado a lograr y la condición que lo cierra, nunca
  la razón por la que existe. **Qué cambia para vos**: `ops context` imprime la sección en líneas `CTX`
  y la trae en `--json` bajo `epic.context`, y la fase Plan de `/autobuild` la recibe —ya la pedía en su
  prompt y nunca le llegaba—. Viaja entera: nada en esa salida se recorta, y una lista de viñetas no
  tiene primer párrafo que resuma al resto. Si en tu proyecto esa sección es enorme, se va a notar acá.

- **La barra de cinco condiciones de R17 también cuenta la aceptación que la tarea escribe.** Contaba
  sólo los criterios heredados con `(→ CN)`, así que una tarea con su aceptación en la línea —la forma
  que el molde muestra primero— contaba cero y nunca cruzaba el umbral; la salida que R17 describe,
  `(sin partir: <razón>)`, no se le pedía jamás. **Qué cambia para vos**: es probable que aparezcan
  mensajes en tareas que hoy pasan, y en un proyecto recién adoptado pueden ser varias a la vez; la
  salida es la misma de siempre. Se cuenta lo que vos marcaste —los `(1)`, `(2)`… cuando hay más de uno,
  y si no, lo que separaste con `;`— y sub-cuenta a propósito: una frase larga con comas vale una. El
  mensaje dice cuál de los dos contó, para que «8» no mande a buscar ocho referencias que no existen.

- **R10 dice cuál de sus seis actos comprueba el motor.** La regla prometía «la autorización
  configurada para el proyecto» para push, PR, merge, tags, deploy y rollback, y el motor comprueba uno.
  Medido con `allowPush` apagado: `gh pr merge`, `gh release create`, `gh workflow run`, `git tag` y un
  `kubectl apply` pasan todos. **Qué cambia para vos**: nada de lo que hoy funciona deja de funcionar —
  lo que cambia es que la regla ya no promete lo que no comprueba, y dice que a esos cinco los sostiene
  ella y el review. Un deploy no tiene forma reconocible en un comando; un guard tendría que adivinarla.

- **`--force` y `--amend` se frenan por su cuenta.** R10 enumera seis actos de publicación y el guard
  comprobaba uno, sin distinguir lo que la prosa distingue: `git push` y `git push --force` caían en el
  mismo patrón, así que `allowPush: true` habilitaba también reescribir historia ya publicada. Y
  `git commit --amend` no lo miraba nadie, con la llave prendida o apagada, aunque R8 lo prohíbe sin
  excepción. **Qué cambia para vos**: si tu proyecto publica con `allowPush`, un `--force` ahora se
  frena igual —con su propio mensaje, que dice que la llave no lo desbloquea—, y un `--amend` también.
  Si venías usando alguno de los dos, lo vas a notar; la salida es un push normal o un commit nuevo.

- **Un mensaje de commit ya no dispara los guards que nombra.** `git commit -m "fix: bloquear git push
  --force"` caía por el guard de publicación, y nombrar `rm -rf` dentro de una explicación caía por el
  de destrucción; con el heredoc que se usa para un mensaje largo, el cuerpo entero viaja adentro del
  comando. Ahora lo entrecomillado se lee como dato **sólo** cuando el comando es un commit: en
  cualquier otro, lo que va entre comillas se ejecuta y se sigue juzgando.

- **`AGENTS.md` dice cuál de sus límites puede habilitar tu proyecto, y cuál no.** Enumeraba seis cosas
  que el runner nunca hace y dos párrafos después las llamaba «los cuatro límites del párrafo anterior»,
  que son los que no se amplían. Aparte del conteo, entre esas seis estaba **publicar**, que el motor
  hace configurable a propósito con `runner.allowPush`. Un proyecto que lo leyera al pie concluía que su
  `allowPush: true` era ilegítimo, o que podía ampliar cualquiera de las seis y elegía mal. **Qué cambia
  para vos**: el conteo desapareció, y el texto dice que publicar es lo único que se habilita y con qué
  llave, y que reescribir historia publicada no entra en ese trato.

- **R12 manda las excepciones sobre sistemas externos donde `upgrade` no las borra.** La regla cerraba
  diciendo que se documentan en el `AGENTS.md` del proyecto, y ese archivo es del toolkit: se reemplaza
  entero en cada actualización. Quien obedecía la regla escribía su excepción donde la siguiente
  actualización se la iba a llevar, sin que nada lo avisara. **Qué cambia para vos**: ahora apuntan a la
  sección «Integraciones y ambientes» de `organization/workspace.md`, que ya nombraba a R12 y reclamaba
  esas excepciones. Si tenías alguna escrita en `AGENTS.md`, movela: ahí no sobrevive.

- **Un campo de `DONE.md` que se envuelve se lee entero.** Cada campo —`acept:`, `done:`, `qa:`,
  `tests:`, `decisions:`, `commit:`— se leía de una sola línea física, y sus valores son prosa que se
  envuelve como cualquier otra línea. Cuando la envoltura partía una cita, `check` respondía «decisions
  debe citar» sobre un campo que **sí** citaba: el mensaje nombraba una ausencia que no era la que
  había, y mandaba a revisar lo único que sí estaba. **Qué cambia para vos**: las entradas que venías
  reescribiendo hasta que entraran en un renglón pasan como están, y `tests:` y `commit:` dejan de
  perder lo que quedaba debajo del salto.

## [0.60.1] - 2026-09-03

### Corregido

- **Rehacer un banco de evaluación ya no falla de vez en cuando.** `evaluate --bench --force` borra el
  banco antes de recrearlo, y sobre un árbol grande y versionado ese borrado da `ENOTEMPTY` a veces —es
  transitorio—. La corrida moría antes de empezar, sin haber evaluado nada. Ahora reintenta, que es lo
  que `rmSync` ofrece para eso. Sólo afecta al toolkit: en una instancia `--bench` no corre.

## [0.60.0] - 2026-09-03

### Corregido

- **La épica que promueve una integración pasa `check`.** La descripción remota trae sus propios
  encabezados —el fixture de Jira trae `## Criterios de aceptación`— y quedaban compitiendo con los que
  escribe la promoción, así que el parser leía la sección importada, que no tiene ningún criterio, y el
  planning quedaba en rojo apenas promovías. Ahora lo importado baja un nivel al entrar, el parser
  prefiere la coincidencia exacta cuando existe —`## Historias (Tareas)` sigue resolviendo— y el
  criterio sale sin el guión de la viñeta pegado.

- **Curar un draft ya no deja la integración en rojo.** El README manda editar el draft y correr
  `check`, y ese `check` fallaba: editar deja obsoleto un flag de `remote.json` que sólo se recalcula en
  `sync`, `rebase` y `reconcile`. Dejó de exigirse porque no protegía nada: se leía en un único lugar,
  la validación que lo comparaba consigo mismo, y lo que decide qué vuelve al remoto compara el
  contenido. **Qué cambia para vos**: el recorrido documentado funciona sin pasos intermedios.

- **`/onboard` escribe el mapa donde vive desde 0.57.0.** Seguía mandándolo a `AGENTS.md`, así que el
  primer recorrido de una instancia nueva deshacía el arreglo que sacó de ahí lo que sólo sabe tu
  proyecto. Ahora escribe las tres secciones de `organization/workspace.md` —incluidas integraciones y
  excepciones de autonomía, que ningún recorrido llenaba— y suma `organization/domains.md`, que no
  nombraba.

- **`upgrade` te dice cuando se lleva las instrucciones de tu runner.** `AGENTS.md` es del toolkit y se
  reemplaza entero, con el bloque que `automation install` dejó adentro. Lo recuperás reinstalando —el
  propio `upgrade` te lo recuerda— pero ese recordatorio no tenía causa visible, y el bloque desaparecía
  sin explicación. Ahora se nombra el archivo y el runner, una sola vez.

- **`check` avisa cuando dos secciones de una épica compiten por el mismo rol.** El parser prefiere la
  exacta, así que resuelve — en silencio, y quien escribió las dos no se entera de que una se ignora
  entera. Un título con sufijo como `## Historias (Tareas)` no dispara nada: es el único de su rol.

- **`/autobuild` ve las excepciones de autonomía de tu proyecto.** Armaba sus límites leyendo sólo
  `AGENTS.md`, y desde 0.57.0 los tuyos están en `organization/workspace.md`. Lo que viajaba a cada
  subagente como «Límites del proyecto» eran los genéricos del toolkit. Ahora ese archivo entra en la
  lectura única de Triage; si no existe o su sección sigue como la trae el molde, no se inventa ninguno.

## [0.59.0] - 2026-09-03

### Corregido

- **Los atajos `team-check` y `team-show` del Makefile pasan a `flow-check` y `flow-show`.** El rename
  de `teams/` a `flows/` llegó al motor y al README en 0.45.0 —esa entrada hasta te pedía renombrar los
  `ops team check|list|show` que tuvieras automatizados— y el Makefile que te entregamos se quedó
  llamando al comando viejo, así que los dos atajos fallaban con el banner de uso en toda instancia.
  La variable ahora es `FLOW=<slug>`, y se suma `flow-list`, que no tenía atajo. **Qué cambia para
  vos**: si automatizaste alguno de los dos, renombralo — hoy no funcionan, así que nada que ande deja
  de andar.

- **`automation list` marca instalado lo que está instalado, también en `sidecar`.** Buscaba el wiring
  dentro de la instancia, y en `sidecar` —el modo por defecto— vive en el repo del que abrís tu
  herramienta. Los cuatro adaptadores salían `○` aunque `doctor` los diera operativos dos líneas
  después, así que el glifo no distinguía ningún caso del otro.

- **El error de `evaluate --bench` en una instancia dice qué hacer.** Recomendaba adoptar el cargo, y
  adoptarlo no habilita nada: lo que decide es el modo de la instancia. Quien seguía el consejo
  forkeaba, repetía el comando y recibía el mismo mensaje. Ahora nombra la salida real: `ops evaluate
  <cargo>` sin la bandera.

## [0.58.0] - 2026-09-03

### Corregido

- **`upgrade` te trae los archivos propios que una versión agrega.** `upgrade` reemplaza lo del toolkit
  y no toca lo tuyo, que es lo correcto — pero cuando una versión **agrega** un archivo del proyecto, la
  instancia que actualiza no lo recibía nunca. Pasó con 0.57.0: `organization/workspace.md` llegaba a
  una instancia nueva y no a una que actualizaba, que quedaba con un `AGENTS.md` nombrándolo tres veces
  y el archivo sin existir. Ahora se crea y se dice: `+ organization/workspace.md: lo agrega esta
  versión, completalo`.

  **Lo que no hace, a propósito**: reponer cualquier archivo del molde que falte. Borrar uno que no usás
  es legítimo y devolvértelo en cada actualización sería peor que el problema, así que se crea sólo lo
  que cada versión declara que agrega. Lo que ya tenés no se pisa nunca.

## [0.57.0] - 2026-09-03

### Agregado

- **`organization/workspace.md`: dónde va lo que sólo sabe tu proyecto.** El mapa real, las
  integraciones con su entorno concreto y las excepciones de autonomía. Es tuyo y `upgrade` no lo toca.
  Hasta ahora el paso 2 del README te mandaba completar `AGENTS.md`, que es del toolkit y se reemplaza
  entero: no perdías nada —desde 0.55.0 el `upgrade` se detiene y lo nombra— pero te tocaba fusionar a
  mano en cada versión, sobre el único archivo garantizado a entrar en conflicto. Es la separación que
  el toolkit ya usa en `planning/rules/system/` y en `planning/delivery/project.md`.

  **Si ya llenaste tu `AGENTS.md`**: movés esas secciones a `organization/workspace.md` y listo. El
  `upgrade` te lo dice cuando se detenga, antes de que puedas descartarlas con `--force`.

### Corregido

- **`check` te dice qué reglas deja de regir un override.** Un archivo propio con el nombre de uno de
  `system/` lo reemplaza entero, no las reglas que mencionás: las que ese archivo definía y el tuyo no
  redefine dejan de existir para tu proyecto. La advertencia nombraba el par de archivos y se leía como
  benigna. El caso caro es una regla que el motor **sigue exigiendo** —R17 lo hace—: quedaba exigida sin
  estar escrita en ningún lado. Ahora dice cuáles: `deja de regir R2, R17…`. Sigue siendo advertencia.

- **Instalar un runner ya no hace que `upgrade` te acuse de editar `AGENTS.md`.** En modo `embedded`,
  `automation install` con Codex escribe sus instrucciones dentro de ese archivo, entre marcas. El
  registro anotaba el bloque en un solo lado, así que el `upgrade` siguiente se detenía sobre un archivo
  que nadie había tocado —lo había escrito el comando que el README manda correr justo después—. Lo que
  vos escribas alrededor se sigue detectando igual.

## [0.56.0] - 2026-09-03

### Corregido

- **El upgrade que documentaba el README rompía el pin exacto que puso `init`.** La razón para no
  saltear su primer paso es que «`init` fija la versión exacta, así que `npm update` no la mueve», y
  el comando de al lado la desarmaba: npm guarda con caret por defecto, así que
  `npm install --save-dev @ingeniomaps/cauce@latest` dejaba `^0.55.0`, y dentro de `0.x` ese caret
  alcanza a los patches —que Cauce publica—. Un `npm install` en otra máquina podía dejar el motor en
  una versión que `ops.config.json` no decía.

  Se arregla por los dos lados. El comando lleva `--save-exact` en los tres lugares que lo dictan —el
  README, el `make upgrade` de tu instancia y la salida de `upgrade --check`—, y además **`upgrade`
  repone la versión exacta al terminar**, así que una instancia que ya quedó con un rango se repara
  sola en la próxima actualización. **Qué cambia para vos**: `upgrade` puede tocar ahora una línea de
  tu `package.json`, y lo dice cuando lo hace. El resto del manifiesto queda como estaba, y una
  instancia sin `package.json` no recibe uno.

## [0.55.0] - 2026-09-02

### Corregido

- **`upgrade` ya no pisa lo que `init --force` conservó.** Adoptar Cauce en un repositorio que ya tiene
  contenido es `init --force` y después `upgrade`. El primero conserva tus archivos y lo cumple; el
  segundo los reemplazaba sin avisar e imprimía que no había tocado nada propio. El registro se grababa
  hasheando el disco, así que un archivo conservado quedaba anotado como entregado por Cauce con **tu**
  contenido, y la comparación no veía ninguna diferencia. Ahora se anota el digest de lo que el molde
  habría escrito: tu archivo se ve como lo que es —una edición local— y `upgrade` se detiene
  nombrándolo. **Qué cambia para vos**: el primer `upgrade` después de una adopción con `--force` se
  detiene listando esos archivos. Resolvelos a mano, o repetí con `--force` para descartarlos, que
  ahora además los enumera en la salida.
- **Tu guard propio sobrevive a `automation install`.** `AGENTS.md` te dice que lo pongas en
  `automatization/hooks/guard-<nombre>.sh` y promete que sobrevive a cada actualización. Sobrevivía al
  `upgrade` y no a la reinstalación del runner: lo nuestro se reconocía por la carpeta, así que
  cualquier `guard-*.sh` de ahí se desregistraba antes de fusionar. El archivo quedaba en disco, el
  guard dejaba de correr y lo único que se veía era una línea diciendo que se había quitado una entrada
  obsoleta. Ahora se reconoce por lo que efectivamente entregamos: los guards que el motor trae, más
  los que esta instancia anotó haber recibido —el manifiesto gana una entrada por runner con el wiring
  que dejó puesto, y por eso una entrada nuestra se sigue retirando el día que el motor deje de traer
  ese guard—. **Si moviste tu guard fuera de esa carpeta para sortearlo, ya podés devolverlo.**
- **`automation doctor` deja de llamar divergente a una configuración completa.** Un hook propio sumado
  *dentro* de un grupo que escribió el toolkit dejaba a `doctor` en rojo aunque estuvieran todas las
  entradas esperadas: comparaba el grupo entero serializado, así que uno con un hook de más contaba
  como ausente. Ahora compara por `matcher` y contenido. Lo que falta se sigue reportando igual.
- **`upgrade --check` avisa de lo que tenés editado aunque la versión coincida.** Salía por un camino
  corto que informaba «al día» sin llegar a listarlo, que es el estado normal entre actualizaciones
  —`init` fija la versión exacta—: el único modo que no toca nada era también el único que no podía
  anticipar el conflicto. **Qué cambia para vos**: `--check` ahora sale 1 cuando hay archivos del
  sistema editados, un caso donde antes salía 0. Si lo tenés en un script, revisá esa condición.

### Cambiado

- **`init` e `init .` eligen el mismo modo en el mismo directorio.** El default salía de si escribiste
  el argumento, no de a dónde apunta: en `acme-ops/`, `init` daba `sidecar` e `init .` daba `embedded`.
  No es cosmético — en `embedded` la raíz del workspace pasa a ser la carpeta ops misma, así que
  `guard-workspace-boundary` deja de reconocer los repos hermanos y el wiring del runner se escribe
  adentro en vez de al lado, donde abrís tu herramienta. Ahora lo decide el destino resuelto, con la
  misma regla que ya elegía dónde aterrizar: una carpeta llamada `ops` o terminada en `-ops` **es** la
  instancia. **Qué cambia para vos**: `init .` en una carpeta así ahora da `sidecar`, donde antes daba
  `embedded`. El caso embebido sigue disponible pidiéndolo: `init . --mode embedded`.

## [0.54.0] - 2026-09-02

### Agregado

- **Un proyecto puede declarar la puerta que lo verifica.** `ops.config.json` acepta el comando que
  comprueba tu código —`gates`—, y el contrato de una tarea viaja con ella hasta quien la ejecuta.
  Antes cada cargo lo adivinaba desde el árbol. Si no lo declarás, todo sigue como estaba.
- **Una propuesta mensual se puede archivar en vez de aplicar.** Es el tercer destino que faltaba:
  mirarla y decidir que no cambia nada. Antes eso se hacía mergeando sin firmar, que la dejaba
  `proposed` para siempre e indistinguible de una que espera trabajo. `learn <cargo> --archived`.
- **Una propuesta firmada y sin aplicar lo dice.** Antes se contaba igual que una que nadie miró.

### Cambiado

- **Los 53 cargos traen el paso de entrega de R14.** Antes de dar por entregado, el cargo recorre los
  artefactos que se leen solos —una fila de acciones humanas, una lección, un ítem de INBOX, un paso de
  runbook— y comprueba que cada afirmación sobre el comportamiento de una herramienta, norma o sistema
  de terceros llegó con su registro. En 19 cargos la regla además se movió a la sección de reglas: donde
  estaba, se leía como una nota de colaboración. **No hace falta que hagas nada**: el catálogo se
  resuelve desde la dependencia.

  El motivo, medido sobre 198 registros de evaluación: esa conducta se lleva el 47 % de los casos rojos
  del catálogo, en 25 de los 53 cargos, casi cinco veces lo que su superficie explica. Y falla siempre
  en el mismo lugar —el informe clasifica con cuidado y la copia sale en plano al artefacto derivado—,
  que es lo que este paso va a mirar. **Que el cambio lo corrija no está demostrado**, y conviene que lo
  sepas antes de leerlo como una mejora: la tasa de recuperación por varianza entre corridas es del
  83 %, así que las dos mediciones que salieron a favor tenían un 69 % de salir igual sin cambio alguno.

- **`planning/rules/system/conduct.md` se reemplaza y R13 gana un borde.** Faltar contexto es no saber
  **cómo** —qué formato, qué borde, qué valor—, y eso se supone y se entrega marcado. No saber **qué se
  quiere ni para qué** es otra cosa: ahí el borrador deja de ser barato, porque todo lo que se construya
  encima hereda la suposición y quien lo reciba no tiene cómo ver que el objetivo era supuesto. Lo que
  separa los dos casos es si la respuesta existe escrita en algún lado. Si venías apoyándote en R13 para
  arrancar sin objetivo, esto lo acota.

### Corregido

- **Sellar una propuesta ahora marca el documento entero.** La salida temprana miraba sólo `status:` del
  frontmatter, así que si algo movía ese campo antes que el sello, el cuerpo quedaba en «- Estado:
  aprobada» para siempre: el frontmatter decía una cosa y el cuerpo la contraria, dentro del mismo
  documento. **Si tenés propuestas aplicadas con esa contradicción, `learn <cargo> --applied --period
  AAAA-MM` ahora las cierra bien.**

## [0.53.2] - 2026-08-29

### Cambiado

- **`tools/ops.js` se reemplaza y no cambia lo que hace.** Su comentario prohibía `import` y dos líneas
  después nombraba el `import()` dinámico que el propio shim usa; ahora dice que lo prohibido es el
  estático. Y citaba `team list`, un comando que dejó de existir. Se lo nombra porque vive en tu
  instancia: `upgrade` lo va a reemplazar y, hasta que lo corras, aparece como desactualizado.

## [0.53.1] - 2026-08-29

### Corregido

- **`init` no instalaba el motor en Windows.** Lanzaba `npm.cmd` directo, y la documentación de Node
  dice que un `.cmd` no es ejecutable por sí solo: ahora va por `cmd.exe` con el comando de argumento.
  Fallaba en silencio útil —imprimía el error de npm y dejaba `npm install` entre los pasos
  pendientes—, así que si venías corriéndolo a mano en Windows, ya no hace falta. Sin comprobar en un
  Windows real: acá no hay uno, y el comentario del código lo dice.

### Cambiado

- **Los trece shims de guards se reemplazan y ninguno cambia lo que bloquea.** Su comentario prometía
  «qué bloquea y cómo lo hace» en `engine/hooks/run.js`, que es un registro de una línea por guard: lo
  que bloquea vive en otro módulo, y el puntero mandaba al lugar equivocado. Los trece cambian sólo en
  comentarios, sin una línea ejecutable distinta. Se los nombra porque viven en tu instancia: `upgrade`
  los va a reemplazar y, hasta que lo corras, `automation check` los reporta desactualizados.
- **El motor se repartió en más archivos de los que tenía**, sin cambiar una sola conducta:
  `engine/automation/index.js` en cinco, `engine/agents/learning.js` en tres y `engine/hooks/run.js` en
  cuatro, cada uno cortado por lo que lo hace cambiar. Llega con `npm install` y no pide nada de tu
  parte; se menciona porque un `require` a una ruta interna del motor —que nunca fue superficie
  pública— puede haber dejado de resolver.
- Los recorridos que instala tu runner cambiaron de texto —comentarios y nombres internos, ahora en
  inglés como pide la convención—. Para recibirlos hay que reinstalar el wiring; si no lo hacés, los
  que ya tenés siguen funcionando igual.

## [0.53.0] - 2026-08-27

### Corregido

- **Dieciocho fuentes estaban clasificadas como rápidas y no lo son**, así que su cargo investigaba
  cada semana para leer el mismo texto. La cadencia sale de la fuente más veloz, y cada una se comprobó
  contra su propia fuente antes de moverla —no se dedujo del nombre—: `arc42` va de v8 (feb 2022) a v9
  (jul 2025); la especificación OpenAPI publica con meses o años de diferencia; el PSA `I-060923-PSA`
  del FBI es un documento fechado y cerrado; el GOV.UK Design System publica una vez al mes o menos; la
  guía de contenido útil de Google y su guía de estilo llevan meses sin cambiar; el archivo de «core app
  quality» de Android muestra años entre revisiones; Standard Webhooks no publica desde febrero; el
  Contributor Covenant va de 2016 a 2021 en tres versiones; Open Source Guides se toca cada dos a cuatro
  meses; la documentación de GitHub sobre moderación y sobre endurecer Actions lleva meses o más de un
  año sin cambios; la página de idempotencia de Stripe no expone fechas ni historial; y **Shopify publica
  una versión de API por trimestre**, en fecha fija. Las dieciocho pasan a `standard`, que es mensual.
  Los semanales bajan de 29 a 20.
- **Lo que no se pudo comprobar quedó donde estaba.** Apple Human Interface Guidelines y la guía de
  Gainsight no exponen fechas de revisión, y de Material Design sólo se pudo medir su implementación de
  referencia —4 a 9 meses entre versiones—, que no es la guía que el cargo cita. Las tres siguen
  semanales: moverlas por su nombre, o por el ritmo de otro artefacto, es lo que `R14` prohíbe.
- **Una URL con dos nombres ahora es un error.** `evaluate` lo rechaza dentro de un archivo —la cadencia
  sale del `tier` de cada entrada, así que dos copias de una fuente pueden decir cosas distintas sobre
  cada cuánto publica, y la más rápida gana sin que nadie lo decida— y una prueba del catálogo lo
  detecta entre cargos, que es la forma en que apareció. Se unificaron dieciséis: el catálogo pasa de
  229 fuentes a 211.
- **La especificación OpenAPI estaba tres veces con nombres distintos** —`OpenAPI Specification`,
  `...latest published` y `...3.2.0`—, así que corregirla en un cargo no la corregía en los otros.


## [0.52.0] - 2026-08-27

### Corregido

- **Lanzar el aprendizaje a mano ya no arrastra el ensamblaje mensual.** El `workflow_dispatch` corría
  las dos fases, así que probar la investigación de un cargo abría además su propuesta — y una
  propuesta cuesta una firma humana. Peor: consolidar **sella** los informes que consume, o sea que un
  lanzamiento de prueba movía estado real del ciclo. Ahora el dispatch acepta `phase`, con `research`
  por defecto, que es para lo que se lanza a mano; `propose` y `both` siguen disponibles cuando de
  verdad haga falta, y los crones deciden por su cuenta como siempre.
- **Los permisos de la investigación llegaban partidos, y `Bash` nunca se concedía.** Iban en una
  cadena sin comillas, así que el shell partía `Bash(make agent-learn *)` en tres palabras y el CLI
  descartaba la última —«Ignoring --allowedTools rule "*)"»—. La investigación salía bien igual porque
  con web y lectura alcanzaba, así que el defecto vivía en dos líneas del log y en ningún resultado.
- **La investigación late mientras corre y deja dicho lo que costó.** El log quedaba mudo los diez
  minutos que dura y no se distinguía de una corrida colgada: la única salida era esperar el timeout
  para saber cuál era. Ahora avisa cada minuto —el latido lo emite el shell y comprueba el proceso, así
  que no puede mentir que sigue vivo— y al cerrar deja en el resumen del job **cuánto costó en dólares,
  cuántos tokens y qué permisos se le negaron**. Eso último es lo que vuelve visible un permiso mal
  escrito, que es como se descubrió el de arriba.

## [0.51.0] - 2026-08-27

### Agregado

- **El guard `destructive` bloquea la forma ancha de `git checkout` y `git restore`.** `git checkout
  -- .` destruye lo mismo que `git reset --hard` —que ya se bloqueaba— y sin recuperación, pero se
  escribe como una limpieza. Se bloquea sólo cuando la ruta es `.`, `*`, `:/` o no hay ninguna:
  revertir un archivo nombrado es trabajo corriente y sigue pasando. Lo que engaña es que el alcance no
  se ve en el comando — `.` es el directorio actual, y ahí suele haber más de lo que uno está mirando.

## [0.50.0] - 2026-08-27

### Corregido

- **La investigación semanal recibe las herramientas que su instrucción nombra.** Corría sin permisos
  declarados, así que no podía correr un comando ni salir a la web —que es todo lo que una
  investigación hace— y devolvía el informe vacío en tres minutos. El cargo se portaba bien y lo decía:
  «no voy a fabricar fuentes, fechas ni salidas de comando para llenar el informe». Ahora se le declaran
  las que su `AUTOMATION.md` nombra —buscar, leer, escribir su informe, `make agent-learn` y
  `make agent-evaluate`— y sólo ésas: saltear permisos le daría justo lo que su instrucción le prohíbe.
- **Un informe sin contenido ya no abre PR.** Que el archivo exista no alcanza: `learn` lo crea vacío y
  el modelo puede devolverlo tal cual, y así se publicaba. Un lunes eso son veintinueve PRs en blanco
  sin que nada lo diga. Ahora se miran las secciones que la propuesta mensual consolida, y si están
  vacías el job falla nombrando cuáles y dónde mirar la causa.

## [0.49.0] - 2026-08-27

### Agregado

- **`R22` — lo que se mide no se toca mientras se mide.** Mientras una medición corre, el sujeto y todo
  aquello contra lo que resuelve se quedan quietos. Lo difícil es que no avisa: la corrida termina y su
  resultado se lee igual que uno limpio, así que quien lo reciba decide sin saber que se movió el piso.
  Y entra por donde no se mira — un banco desechable puede resolver la herramienta por un enlace al
  repositorio vivo, así que editar ahí cambia lo que la corrida lee sin tocar el banco. Si hace falta
  trabajar igual, se trabaja donde la medición no mira; y si ya pasó, se dice qué cambió y cuándo: un
  resultado cuyo entorno se movió es una hipótesis, no un veredicto.

### Corregido

- **`dependsOn` dejó de ser decorativo: un recorrido corre sus etapas como su contrato las declara.**
  Hasta ahora el motor las corría en fila y le pasaba a cada una los handoffs de **todas** las
  anteriores, sin mirar de qué decía depender. `technical-design` existe para tener tres lecturas
  independientes de un mismo encuadre y no las tenía: su propio guardrail dice que las tres «no negocian
  entre sí ni ajustan su hallazgo para que cierre con el de otra», y una que ya leyó a la primera no
  puede cumplirlo. Ahora las etapas se agrupan por nivel de dependencia, las de un mismo nivel corren a
  la vez, y cada una ve **sólo** los handoffs de aquello de lo que declara depender. Cinco de los siete
  recorridos del catálogo tienen un nivel con paralelismo real y lo estrenan con esto. Una etapa que
  **no** declara `dependsOn` sigue viendo todo lo anterior, igual que antes: independencia es una
  afirmación, y una afirmación se declara. Si una etapa no cumple su gate, el corte es por nivel — las
  que corrieron con ella entran igual al handoff, y lo que se abandona son los niveles siguientes.
- **Un recorrido que frena da igual la salida que su contrato reserva para no poder.** `change-review`
  enumera tres veredictos y el tercero es «no poder aprobar»; bloqueado, su informe parcial escribía
  «No hay veredicto», que no es ninguno de los tres. Con el diff, los importadores y las pruebas a la
  vista, un revisor **sí** puede firmar que no aprueba: eso es un veredicto, no su ausencia. Vale para
  cualquier recorrido cuyo contrato tenga esa salida — un destino de «nada que hacer», una
  recomendación de investigar.
- **Cambiar el `flow.json` de un recorrido ahora envejece sus veredictos.** El aviso de «el contrato
  cambió y la última corrida es anterior» miraba sólo el `SKILL.md`, que un recorrido no tiene, así que
  no se disparaba nunca: se le podía agregar una dimensión a un gate y sus casos aprobados seguían
  leyéndose vigentes. Qué archivo **es** el contrato depende del sujeto — el de un cargo es su
  `SKILL.md`, el de un recorrido su `flow.json`— y ahora se mira el que corresponde.
- **`ops context` ya no se traga las acciones humanas cuando no hay tarea en cola.** Se imprimían
  después del `return` de «sin tarea disponible», así que una instancia recién arrancada —`onboard`
  deja filas pendientes y ninguna tarea todavía— preguntaba qué toca ahora y recibía «nada», cuando lo
  que tocaba era que una persona desbloqueara siete cosas. Lo destapó una corrida de un recorrido sobre
  su banco.

## [0.48.0] - 2026-08-26

### Corregido

- **`destroy` ya no deja el `package.json` que escribió `init`.** En modo `embedded` quitaba sus nueve
  rutas y decía «tu repositorio queda donde está», dejando un manifiesto cuya única dependencia era el
  motor. Sobre un repositorio Rust eso es basura conspicua y nadie avisaba. Ahora saca su clave y nada
  más: si el manifiesto lo había creado `init` —sin dependencias ni scripts propios— se va con él, y si
  es tuyo se conserva entero con tus scripts y tus dependencias. `node_modules/` y el lockfile no se
  tocan porque los escribe npm y pueden tener lo tuyo, pero la salida ahora los nombra para que decidas.

## [0.47.0] - 2026-08-26

### Agregado

- **`organization/company.md` gana «Qué no se puede romper»**, una tabla donde declarás qué superficies
  detienen la operación al fallar, a quién alcanzan y dónde viven. No todo cuesta lo mismo cuando se
  rompe, y sin eso escrito una revisión sólo puede elegir entre revisar todo con el rigor del peor caso
  o revisar el peor caso con el rigor de lo demás. Lo que no se sepa se deja en `Por definir`: que no
  conste dónde vive un flujo crítico ya es un hallazgo.
- **`change-review` pregunta si el cambio toca una de ellas.** El gate de `scope` ahora lo exige y
  `completion` lo enumera: si toca, cuál y qué se detiene; si no, que se miró. Se contrasta contra tu
  tabla y no contra el criterio de quien revisa, y una tabla vacía se reporta como hallazgo en vez de
  pasar por permiso. Sin esto, un cambio de una línea en un helper compartido se revisaba igual
  estuviera en reportes o en el alta de un pedido.

## [0.46.0] - 2026-08-25

### Agregado

- **`R21` — retomar empieza por establecer qué quedó hecho.** Un trabajo que se corta deja trabajo
  hecho, y «continuá» pide seguir desde ahí, no volver a lanzarlo: primero se mira qué artefactos y qué
  resultados ya existen, y se corre la diferencia. Cuando el pedido sí es relanzar de cero, primero va
  el veredicto de lo avanzado y decide quien pidió — puede querer la corrida entera y hay razones
  legítimas, pero tirar trabajo es una decisión, no un default. Ese veredicto trae con qué decidir: qué
  ya tiene resultado, qué quedó a medias **y si sirve** —son dos preguntas y la segunda no se contesta
  viendo que el archivo está—, cuánto cuesta rehacer cada parte, y cómo se puede partir, porque casi
  nunca es todo o nada. Puede terminar en «desde cero» sin que eso lo invalide: lo que la regla impide
  no es relanzar, es relanzar sin saber. Lo que ya tiene resultado no se vuelve a medir por venir en la
  misma tanda, y un mecanismo de reanudación se comprueba en vez de suponerse: `R14` no hace excepción
  con las herramientas propias.
- **`R14` se recorre antes de entregar, como `R15`.** Se repasan los artefactos que se van a leer solos
  —la fila de acciones humanas, la lección del INBOX, el paso de runbook, la respuesta escrita— y por
  cada afirmación de mecanismo se comprueba que llegó con su registro. Es donde el rótulo se pierde: el
  informe clasifica bien y la copia sale en plano, y releer no lo encuentra.
- **El ensamblaje mensual también consolida recorridos.** Hasta ahora el ciclo de un recorrido existía
  pero nada lo disparaba: la automatización sólo veía cargos. Ahora `flow list --json` los expone y el
  cron del día 1 abre la propuesta de cada recorrido que tenga corridas sin consolidar. No investiga
  —un recorrido no tiene profesión— y sólo entra el que dejó veredictos que mirar.

- **`R17` ahora tiene dos barras, y `check` las cuenta.** La regla decía que una aceptación de más de
  cinco condiciones se parte, y nada lo medía: una épica de veinte criterios pasaba `check` sin una
  queja. Ahora falla al cruzar **5 criterios en una tarea, 7 en una épica, 9 tareas en un hito**. Lo
  que falla no es el tamaño: son disparadores y no límites, y dejar la unidad entera es una salida
  legítima —igual que `R7` con el código—. Lo que falla es cruzarlos **sin decidir**. Si es un solo
  resultado, se escribe `(sin partir: <razón>)` donde vive la unidad —la misma forma parentética que
  `(service: …)`, en el archivo de la épica, el encabezado del hito o la línea de la tarea— y pasa en
  silencio. Los umbrales son fijos y no se configuran: la salida ya está adentro de la regla y deja
  rastro en el artefacto, mientras que un número que se sube el día que molesta no lo deja. Se cuenta
  lo estructurado; la aceptación escrita en prosa la sigue mirando el review.
- **`R17` incorpora el tope de esfuerzo, que hasta ahora era sólo texto en la plantilla.** Las cuatro
  horas no miden lo mismo que el conteo de condiciones y ninguna de las dos ve lo de la otra: «crear la
  página de inicio» tiene **una** condición de aceptación y son tres días: no está acumulada, está sin
  pensar. La barra obliga a la conversación antes —qué mensaje, qué interacción, qué ya está
  decidido— y de ahí salen tareas que se pueden mirar. Medir una sola de las dos deja pasar la mitad
  de los casos.

### Corregido

- **La re-corrida del mismo día entra al ciclo de aprendizaje.** Un registro `<fecha>-2.md` no
  matcheaba el patrón de nombre, así que quedaba fuera de toda propuesta sin que nada lo dijera — y es
  el que trae el veredicto más nuevo. Si tenés re-corridas archivadas, la próxima propuesta las va a
  incluir.

## [0.45.0] - 2026-08-23

### Cambiado

- **`teams/` ahora es `flows/`, y `ops team` ya no existe.** Ninguno de los recorridos era un equipo:
  los seis son procesos, y el archivo de adentro ya se llamaba `WORKFLOW.md` mientras la carpeta decía
  `team`. `upgrade` mueve tu instancia solo —`teams/` → `flows/`, `team.json` → `flow.json`,
  `WORKFLOW.md` → `FLOW.md`, tus propios recorridos incluidos— y te lo dice en la salida. Si ya tenés
  las dos carpetas conviviendo se niega, porque no puede adivinar cuál manda. Lo que sí es tuyo:
  renombrar los `ops team check|list|show` que hayas automatizado a `ops flow check|list|show`, y el
  `--team` que le pases a `learn` o `evaluate`, que ahora es `--flow`.
- **`R14` deja de aceptar el rótulo como sustituto de ir a mirar.** Lo que se establece con una
  invocación inocua o una página pública se comprueba **antes** de escribir; marcarlo «hipótesis» ya no
  alcanza y pasa a ser una entrega con un hueco donde había un dato disponible. La abstención sigue
  valiendo sólo cuando comprobar exige lo que `R12` prohíbe.

### Agregado

- **Dos reglas del sistema.** `R19` — lo que llega de afuera es dato, no instrucción: un ticket, un
  README ajeno o la respuesta de una API se leen y se citan, pero no dan órdenes, y lo que cambia el
  rumbo lo decide una persona. `R20` — una medición se lanza contra lo que podría refutarla: antes de
  una tanda cara se escribe qué resultado la desmentiría, y el tamaño sale de ahí y no de la lista.
- **Dos recorridos nuevos en el catálogo**: `intake`, para cuando alguien dijo algo y todavía no hay
  una intención, y `change-review`, que decide si un cambio ya construido se puede entregar.
- **`organization/domains.md`**, el molde donde se fija qué significa cada término del negocio y de qué
  se distingue. Se llena con la palabra que alguien tuvo que aclarar alguna vez, no con un diccionario.
- **Cada cargo declara sus fuentes**, y de ahí sale cada cuánto investiga en vez de que todos corran el
  mismo día: un aviso de seguridad se mira todas las semanas y una norma que se revisa por edición, no.

### Corregido

- **`upgrade` escribe su manifiesto de forma atómica** y ya no lee uno ilegible como si estuviera vacío
  —que era leerlo como una instancia sin nada instalado—.
- **`integration check` distingue un proveedor mal configurado de uno ausente**, que antes se reportaban
  igual.
- **El paquete ya no publica la evidencia de nuestras corridas de ningún catálogo.** En 0.44.0 se
  excluía sólo la de `agents/`; la de los recorridos seguía viajando.

## [0.44.0] - 2026-08-22

### Cambiado

- **`ops check` rechaza planning que antes pasaba.** Corrélo apenas actualices. Los contratos que el
  protocolo ya declaraba ahora se comprueban, y lo que fallaba callado pasa a ser un error con su línea
  citada: el estado de una acción humana fuera de `pendiente | resuelta`, la línea de BACKLOG que no
  cumple el contrato de tarea —incluida la tildada ahí en vez de movida a DONE—, un lane que no existe,
  un WIP activo cuyo plan no tiene pasos numerados, un `commit:` que no apunta a un sha, una evidencia
  de DONE que no rastrea el criterio que su historia citó, una épica activada con un `Por definir`
  adentro, una tarea cuya aceptación no está decidida, un ADR sin estado del vocabulario o sin sus
  secciones, y dos reglas con el mismo número.
- **Las reglas propias del proyecto se numeran `P1..Pn`**: `R` queda reservado al sistema. Si escribiste
  una regla propia con un `R`, renumerala, o llevala a un archivo con el mismo nombre que el del sistema
  —que es como se declara un override—.
- **Tres reglas cambian y hay dos nuevas.** `R8` corta los commits por naturaleza del diff y no por
  tarea; `R7` usa los umbrales como disparador de una revisión en vez de prohibir números; `R9` pide
  romper lo que el caso dice cuidar y verlo en rojo, porque el rojo previo sólo prueba ausencia. Se
  agregan `R17` —la aceptación que pasa de cinco condiciones se parte— y `R18` —un doble de prueba se
  justifica y los datos salen de una fábrica—.
- **La guía de `planning/delivery/` es del toolkit y se reemplaza entera.** Hasta ahora se quedaba en la
  versión del día que instalaste, así que ninguna mejora te llegaba. Si editaste alguno de sus seis
  archivos, `upgrade` se niega: llevá lo tuyo a `delivery/project.md`, que sigue siendo del proyecto, o
  repetí con `--force`.
- **El paquete ya no publica la evidencia de nuestras corridas de evaluación** y la instalación baja de
  7 MB a menos de 2. Los contratos de cada cargo —`SKILL.md`, `references/`, casos, fuentes e historial—
  llegan igual.

### Agregado

- **`ops archive <planning> human-actions`** saca las filas resueltas a `done/human-actions.md` y deja
  legible lo que queda por decidir.
- **`ops context` nombra por qué paró.** Con toda la cola trabada por una persona imprime
  `BLOCKED  blocked-on-human` con la fila que la destraba, en vez de decir que no hay tarea disponible,
  que era lo mismo que dice una cola vacía. El vocabulario completo de paradas está en `PROTOCOL.md`.
- **Dos decisiones del sistema**: `OPS-005`, el catálogo viaja dentro del paquete; `OPS-006`, la
  ceremonia escala con la superficie del cambio.

### Corregido

- **Un criterio escrito en dos líneas se truncaba.** La tarea que heredaba su aceptación recibía sólo la
  primera, que suele ser la mitad sin el cómo se verifica. Revisá las épicas con criterios de más de un
  renglón: lo que el runner recibe cambia.
- **`upgrade` daba el consejo ajeno** al negarse sobre un documento del toolkit —hablaba de desactivar
  guards—. Ahora cada clase de archivo escucha la salida que le corresponde.
- **Los hallazgos de una revisión iban a la sección equivocada del INBOX.** Un cambio del producto va a
  Propuestas, lo aprendido sobre cómo trabajamos a Lecciones, y una pregunta abierta a Ideas.

## [0.43.0] - 2026-08-20

### Agregado

- **Codex recibe los cinco recorridos como skills.** Hasta ahora su adaptador lo declaraba incapaz de
  skills y el arranque le llegaba sólo como prosa dentro de `AGENTS.md`, con el recorrido a pie. Era un
  dato cierto cuando se escribió el adaptador y dejó de serlo sin que nadie lo revisara. Ahora
  `automation install` deja `$onboard`, `$team`, `$autobuild`, `$integration-sync` e
  `$integration-promote` en `.agents/skills/` —una de las rutas que Codex escanea— junto con el catálogo
  de cargos, y se invocan con `$`. **Reinstalá el adaptador** para recibirlas; el `hooks.json` no cambia,
  así que la confianza que le hayas dado sigue valiendo.

## [0.42.0] - 2026-08-20

### Corregido

- **Los guards de Codex no corrían nunca.** Tres cosas a la vez, y ninguna hacía ruido: el archivo se
  instalaba en `.codex/hooks/hooks.json` —esa forma es la que empaqueta un plugin; un repositorio se lee
  en `.codex/hooks.json`—, los `matcher` filtraban nombres del protocolo interno en vez del nombre de la
  herramienta (`Bash`, `apply_patch`/`Edit`/`Write`), y el comando era una ruta relativa mientras Codex
  ejecuta el hook con el cwd de la sesión, así que desde cualquier subdirectorio no existía. **Reinstalá
  el adaptador**, borrá a mano el `.codex/hooks/hooks.json` que queda huérfano, y confiá los hooks con
  `/hooks` dentro de una sesión: Codex los saltea en silencio hasta que lo hagas, y hay que repetirlo
  cada vez que el wiring cambie.
- **El guard de archivos no veía nada bajo Codex.** `apply_patch` manda el parche entero como
  `tool_input.command`, no como `patch`, y sin reconocer ese campo el guard miraba una escritura y no
  encontraba un solo archivo: reescribió una migración existente en una corrida real, y lo mismo valía
  para secretos y para los límites del workspace.
- **Los guards de Antigravity tampoco corrían.** `agy` ejecuta la copia del plugin que registró en
  `~/.gemini/config/plugins/` y resuelve las rutas contra la carpeta del plugin, así que la del
  workspace apuntaba a un módulo inexistente; su payload además no nombra el workspace —manda
  `workspacePaths` vacío y un `Cwd` que apunta al scratch del CLI o al home—, de modo que la raíz ops
  ahora se escribe como ruta absoluta al instalar. **Reinstalá y volvé a registrar** con
  `agy plugin install .agents/plugins/cauce` cada vez que cambie el wiring.
- **Un puente que no arrancaba dejaba al agente sin poder cerrar la sesión.** En Antigravity, un guard
  que bloquea y un fallo de infraestructura devolvían los dos `continue`; ahora sólo el bloqueo lo hace,
  y la falla deja cerrar con la razón a la vista.
- **Los guards resolvían rutas relativas contra la carpeta equivocada en Antigravity**, que no es la del
  proyecto: un guard que juzgaba `src/x.js` estaba juzgando otro archivo.
- **Instalar acumulaba las entradas de hooks de versiones anteriores.** Una ruta nuestra que cambiaba
  dejaba viva la anterior, el runner la ejecutaba, no encontraba nada y el guard no corría. Ahora se
  reemplazan las nuestras y se conserva lo que agregó el proyecto.
- **Un cargo con el mismo nombre que un recorrido lo pisaba en silencio**, porque comparten el espacio de
  skills del runner. La instalación ahora se detiene y dice cuál: renombrá el cargo en `agents/roles/`.
- **Gemini invocaba `/ops:autobuild`**, un prefijo que ya no existe, y **Antigravity anunciaba sus
  recorridos sin la barra** (`cauce:onboard` en vez de `/cauce:onboard`).
- **La regla de Antigravity apuntaba a un `AGENTS.md` sin prefijo**, que en modo sidecar es el del
  repositorio de producto y no el que trae las reglas del sistema.

### Cambiado

- **El arranque de Gemini vive ahora en su comando.** `GEMINI.md` remite a `/cauce:onboard` en vez de
  repetir el recorrido, y el comando trae la lista completa de lo que se comprueba al final —incluidos
  los dos puntos que la copia había perdido—.

## [0.41.0] - 2026-08-19

### Corregido

- **`destroy` ya no borra el repositorio en modo embebido.** Ahí la instancia **es** el repositorio, así
  que borrar la carpeta se llevaba el código del producto: pasó de verdad sobre un caso de prueba, y el
  aviso previo enumeraba épicas y acciones humanas sin nombrar lo único irrecuperable. En embebido saca
  lo que Cauce escribió —`planning/`, `organization/`, `teams/`, `integrations/`, `automatization/`,
  `tools/`, la configuración y el manifiesto— y deja el repositorio. En sidecar sigue borrando la
  carpeta, que ahí no contiene nada más.

## [0.40.0] - 2026-08-19

### Corregido

- **En modo embebido, Codex se quedaba sin instrucciones.** `AGENTS.md` es el único nombre que el runner
  y la instancia comparten, y ahí `install` conservaba el archivo entero —lo correcto para algo del
  proyecto—, así que el recorrido que ese runner tenía que seguir nunca llegaba a estar escrito. Ahora su
  contenido se fusiona adentro, entre marcas: reinstalar reemplaza el bloque en vez de duplicarlo,
  desinstalar lo saca sin llevarse el archivo, y todo lo que la empresa escribió alrededor queda intacto.

  `doctor` lo tenía al revés en ese archivo: comparando el hash completo avisaba cuando el bloque estaba
  bien —el texto de la empresa alrededor difiere siempre— y decía «operativo» cuando alguien lo había
  borrado, que es la falla que importa.

## [0.39.0] - 2026-08-19

### Corregido

- **`check` nombra las credenciales que nadie se llevó.** El arranque tiene que dejar una fila por cada
  variable que el proyecto declara, y en cuatro corridas reales se cumplió en proporción a lo que se
  habló: las que sólo estaban en el inventario quedaron afuera —entre ellas el broker por donde entran
  los datos y el servicio al que se le mandan errores—. Ahora `check` nombra las que no aparecen ni en el
  mapa ni en `HUMAN_ACTIONS.md`. Mira sólo cuando la instancia ya tiene contexto escrito, porque antes no
  hay dónde tendrían que estar, y avisa en vez de fallar: una variable sin dueño no rompe nada hoy, rompe
  el día que alguien tiene que desplegar.

## [0.38.0] - 2026-08-19

### Añadido

- **Gemini recibe `onboard` y `team`, que sólo tenía como prosa.** Estaban descritos en `GEMINI.md` y no
  existía ningún comando detrás, así que quien venía de otro runner los buscaba en la lista y no
  aparecían.

- **`automation install` termina diciendo cómo se invoca lo que acaba de instalar.** Una línea con los
  nombres exactos para ese runner, que es lo que evita buscar en una lista de cincuenta skills el nombre
  que se usó en otro lado.

### Cambiado

- **Una convención para el nombre de cada recorrido.** El nombre es el mismo en todos los runners
  —`onboard`, `team`, `autobuild`, `integration-sync`, `integration-promote`—; el prefijo lo pone cada
  uno según su espacio de nombres: `/onboard` en Claude, `/cauce:onboard` en Gemini, `cauce:onboard` en
  Antigravity. Codex no tiene comandos y opera el protocolo desde sus instrucciones.

  Los comandos de Gemini se mudaron de `/ops:` a `/cauce:`. Si actualizás una instalación anterior,
  `.gemini/commands/ops/` queda huérfano: se borra a mano, `install` no toca lo que ya no declara.

## [0.37.0] - 2026-08-19

### Cambiado

- **El arranque deja de completar una dimensión que nadie preguntó.** Al llegar al tope de tres
  preguntas puede quedar una afuera —quién lo usa, qué está muerto, qué externo hay—, y una corrida real
  la escribió igual, deducida del README y de las respuestas a las otras. Todo plausible, nada dicho por
  una persona, y sin marca: se leía con el mismo peso que lo que alguien contestó. Ahora esa dimensión
  queda «Por definir» con su pregunta en el INBOX, y lo que se deduce de otra respuesta va marcado
  `(supuesto)` por plausible que sea. Rige en el recorrido ejecutable y en los tres runners que operan el
  arranque leyendo instrucciones.

## [0.36.0] - 2026-08-19

### Corregido

- **`automation doctor` ejecuta el puente del runner en vez de darlo por bueno.** Instalado no es
  operativo: un puente que el runner no puede lanzar falla cerrado y niega cada llamada a herramienta,
  y `doctor` decía «adaptador operativo» porque los archivos estaban en su lugar. Ahora lo invoca como
  lo hace el runner —una vez desde la raíz del workspace y otra desde otra carpeta, porque una ruta
  relativa en su configuración deja de resolver cuando el cwd no es el que suponía— y un puente que no
  responde es error, no advertencia.

### Cambiado

- **La lista de salida termina en el chequeo más barato: `ops onboard`.** Si vuelve a ofrecer la
  pregunta de apertura, la instancia sigue vacía y no hay nada que informar. Una corrida real entregó
  cuatro puntos en pasado —«registrado», «documentado», «creada la primera épica»— sobre archivos que en
  el disco seguían siendo el molde. Estar bloqueado es un resultado legítimo; narrarlo como entrega, no.

## [0.35.0] - 2026-08-19

### Añadido

- **`ops destroy <ops-root>`: sacar una instancia entera, en un comando.** Enumera qué se pierde
  —épicas, cola, trabajo terminado con su evidencia, acciones humanas pendientes, el contexto escrito— y
  para. Sólo un segundo intento con `--force` desinstala cada runner cableado y después borra la
  instancia. El orden no es negociable: borrar la carpeta antes que el wiring deja al runner ejecutando
  guards que ya no existen, que es exactamente lo que pasaba haciéndolo a mano. También como
  `make destroy`, que muestra el resumen sin borrar.

### Corregido

- **Una épica que nadie va a leer deja de pasar por válida.** Una corrida real escribió
  `roadmap/epic-001.md`, sin slug: el parser busca `epic-NNN-<slug>.md`, así que `check` respondía
  «planning válido: 0 épica(s)» con la épica ahí, y `autobuild` nunca habría encontrado trabajo en ella.
  Renombrarla destapó dos errores que el silencio tapaba —un `status` inválido y un criterio sin historia
  que lo cubriera—. Ahora cualquier archivo del roadmap que se llame como una épica y el parser no vaya a
  leer es un error que dice cómo nombrarlo.

- **`check` avisa cuando una dimensión del molde desapareció de `organization/`.** Un agente que reescribe
  esos archivos se queda con el contenido y pierde la estructura: el resultado se lee entero y completo, y
  la dimensión que falta no la va a reclamar nadie. Va como advertencia —esos archivos son de la empresa y
  reestructurarlos a propósito es legítimo—, y agregar secciones propias no dice nada.

- **Volver a una versión anterior deja de anunciarse como una actualización.** `upgrade --check` decía
  «hay una versión más nueva: 0.33.0» contra una instancia en 0.34.0. Ahora dice que volvés, y te imprime
  las entradas que dejás de tener en vez de las que ganarías.

### Cambiado

- **Los runners sin recorrido ejecutable llevan una lista de salida.** Codex, Gemini y Antigravity operan
  el arranque leyendo instrucciones, y una corrida real falló cinco puntos de una vez: preguntas en
  formulario, una épica sin slug, `organization/` reestructurado, `HUMAN_ACTIONS.md` vacío y `ops` listado
  como servicio del producto. Todos del mismo lado: lo que no deja un archivo visible. La lista dice qué
  se comprueba mirando el disco, que es lo que la prosa pidiendo cuidado no consigue.

## [0.34.0] - 2026-08-19

### Añadido

- **R16: el costo es el contexto, no las palabras.** Regla nueva del sistema, en
  `planning/rules/system/process.md`, así que rige en cada tarea la haga quien la haga. Cada llamada
  reenvía el contexto entero: gasta más quien da más vueltas que quien escribe más. Comandos
  independientes en una sola invocación, el CLI antes que el archivo, el fragmento antes que el archivo
  entero, un subagente sólo cuando el trabajo no entra en la corrida actual, y un archivo se lee una vez
  y se escribe entero en vez de releerlo para confirmar.

  Cierra con la parte que la hace segura y que una regla de eficiencia escrita a las apuras suele
  omitir: **verificar es la excepción y no se negocia**. Ahorrar una llamada nunca justifica afirmar sin
  haber comprobado —R14 no admite descuentos— ni dar por terminado lo que no se corrió.

  No nombra precios, ventanas de caché ni cuándo limpiar una conversación: eso lo fija el runner, y una
  regla del sistema que lo nombrara envejecería con su próxima versión en todas las instancias a la vez.

### Cambiado

- **El arranque acota las vueltas dentro de cada fase, no sólo cuántas fases hay.** El techo de tres
  agentes limitaba cuántos contextos se cargan, no cuántas veces se reenvía cada uno. Medido en una
  corrida real: la fase que escribe gastó veinte vueltas —seis lecturas y tres ediciones— para producir
  cuatro archivos, y los tres agentes sumaron tres millones de tokens de caché, un cuarto de la sesión.
  Ahora cada fase sabe qué cuesta: leer una vez y sólo para escribir, escribir entero de una, no releer
  para comprobar, y correr `check` una sola vez.

## [0.33.0] - 2026-08-19

### Corregido

- **Con varias raíces declaradas, ningún servicio tenía nombre.** Cada repositorio es la raíz de su
  propio escaneo, así que su proyecto principal volvía como `.`: tres servicios llamados igual y nada que
  los distinga. Una credencial no se podía atribuir a un servicio porque ningún servicio era nombrable, y
  una corrida real terminó pidiéndole a una persona que declarara variables que su propio repositorio ya
  declaraba. Ahora cada candidato lleva el nombre de su raíz, y donde hay una sola el proyecto de arriba
  se nombra por su carpeta en vez de aparecer como `.` a secas.

- **El arranque podía negar lo que el inventario le entregó.** Se le dice explícitamente que un nombre
  que el inventario trae está declarado, así que pedir que se declare de nuevo contradice al repositorio.
  La mitad de la corrección anterior que tocaba el recorrido —la que le hace copiar los nombres de
  variable por servicio— no había llegado a publicarse.

## [0.32.0] - 2026-08-19

### Corregido

- **Las credenciales de un multirepo no existían para el arranque.** `/onboard` leía un solo
  `.env.example`, el de la raíz del workspace: en un monolito eso es todo, entre repositorios hermanos no
  es nada. Una corrida real sobre tres repos dejó filas que decían «la credencial del proveedor» sin
  nombrar una sola variable, y un externo que sólo ese archivo mencionaba —un certificado de organismo
  fiscal— no apareció en ningún lado.

  Ahora `ops scan` reporta, por servicio, los nombres que declara su ejemplo y de qué archivo salieron, y
  el arranque los usa para nombrar cada fila. **Sólo los nombres**: el valor es de una persona, y el
  ejemplo es la mitad pública del par. Se leen `.env.example`, `.env.sample`, `.env.template` y
  `.env.dist`; un ejemplo de más de cuarenta variables es un archivo generado y no un contrato, así que
  se corta ahí y lo dice.

- **`ops scan <ruta>` dejaba afuera al proyecto de la raíz.** Un monolito que declara sus comandos en el
  nivel de arriba volvía listando todo menos a sí mismo, mientras que desde adentro de la instancia el
  mismo árbol sí lo listaba: dos respuestas distintas sobre el mismo directorio. Los dos caminos comparten
  una sola función y el proyecto de la raíz va primero.

## [0.31.0] - 2026-08-18

### Añadido

- **`ops automation uninstall <ops-root> <runner>`: sacar el wiring sin llevarse lo tuyo.** Hasta ahora
  desinstalar era borrar la carpeta ops y descubrir después que cada llamada de herramienta ejecuta un
  guard que ya no existe; la otra salida —borrar `.claude/` entero— se lleva puesto lo que hayas puesto
  vos. El comando quita lo que Cauce entregó **y sigue igual que como lo entregó**: sus guards de la
  configuración del runner, sus workflows, sus punteros a cargos. Tus hooks, tus workflows y tus skills
  quedan donde están, y un archivo del toolkit que hayas editado se conserva y aparece nombrado en la
  salida, porque decidir sobre él es tuyo. La instancia no se toca: borrarla es una decisión aparte.
  También como `make uninstall-claude` y sus equivalentes.

### Corregido

- **Una instancia dejaba de enterarse de que había versiones nuevas.** `init` fija la versión exacta del
  motor, así que `npm update` no la mueve y `upgrade` compara contra el que está instalado: quien
  actualizaba con `make upgrade` recibía «la instancia está al día» en todas las versiones siguientes, sin
  nada que le dijera que la comparación era local. Ahora la salida nombra contra qué comparó y da el
  comando que trae un motor nuevo, y `make upgrade` lo corre antes de aplicar.

  Actualizar son tres pasos y el tercero sigue siendo aparte: `npm install --save-dev
  @ingeniomaps/cauce@latest`, `ops upgrade .` y `ops automation install . <runner>`. Los workflows y las
  skills viven en el runner, no en la instancia, y `upgrade` lo recuerda al terminar.

### Cambiado

- **`init` termina en un solo cierre, y sus opciones se leen como una selección.** Las tres líneas finales
  —cada una con tono de última— pasaron a ser una con la acción concreta, y las opciones de runner e
  integración van una por línea con el default marcado donde se mira: `5) ninguno   ← Enter`, en vez de un
  `[ninguno]` pegado al prompt.

## [0.30.0] - 2026-08-18

### Añadido

- **`ops onboard`: qué le falta a una instancia para arrancar, y con qué pregunta empezar.** Determinista
  y en milisegundos: dice si la instancia sigue vacía, qué hay en el workspace y qué queda por cubrir.
  `init` lo imprime al terminar, que es donde mira quien acaba de instalar y todavía no sabe qué hace la
  herramienta. Cuando `organization/` o el roadmap ya tienen contenido real no ofrece nada, porque habría
  trabajo que pisar.

### Cambiado

- **El arranque pregunta en vez de negarse, y pregunta primero.** Invocado sin contexto, `/onboard`
  terminaba diciendo «volvé a correrlo con contexto» —y gastaba un subagente para decirlo—. Ahora la
  primera línea es la pregunta, sea el workspace vacío, un monorepo o diez repos, y el inventario viene
  después: de qué trata el proyecto no depende de lo que haya en el disco. Cada runner recibe la
  instrucción de abrir con esa pregunta antes de mirar nada.

- **El recorrido tiene techo: una llamada cuando falta contexto, tres cuando hay con qué escribir.** Y
  ninguna sale a explorar —tiene prohibido recorrer directorios, leer código fuente y abrir archivos que
  no vaya a escribir—, porque lo que necesita ya está resuelto: un arranque que te hace esperar diez
  minutos dejó de ser un arranque.

- **El escaneo mira las raíces declaradas en `ops.config.json`, y nada por encima.** Antes suponía la
  carpeta madre en modo sidecar; ahora acotar `workspaceRoots` acota también lo que se lee, que es más
  rápido y es lo único que alguien autorizó.

- **Y saltea lo que tu proyecto ya declaró basura.** Además de los directorios de paquetes y de los
  ocultos, lee el `.gitignore` de cada raíz y omite los nombres de directorio que encuentre ahí. Los
  interpreta de forma ancha —por nombre, a cualquier profundidad, salteando todo patrón con comodín—:
  alcanza para decidir si vale la pena mirar adentro, y no es un parser de gitignore. Las listas largas
  se recortan a veinte en pantalla diciendo cuántas quedaron afuera; `--json` las trae todas.

- **El arranque declara para qué es.** Entender qué es el proyecto, dejar la instancia correcta para él y
  que la primera tarea pueda empezar: eso, y nada más. El análisis profundo llega cuando alguien pide
  algo concreto, y adelantarlo retrasa el único momento en que la herramienta todavía no sirve para nada.
  Está escrito en cada prompt del recorrido y en las instrucciones de los cuatro runners.

- **Las preguntas dejan de ser un formulario, y de dar por sentado que el proyecto vende algo.** Antes
  eran cuatro fijas, y la primera preguntaba qué vende la empresa y a quién: a un proyecto libre, interno
  o sin fines de lucro le pedían una respuesta que nadie había dado. Ahora hay una sola pregunta escrita
  —de qué trata el proyecto, la única que no depende de ninguna respuesta— y lo que el motor fija después
  son las dimensiones a cubrir: a quién sirve, cómo se sostiene, qué querés que pase, qué está fuera de
  alcance, qué externos hay que conectar. Quien conduce la conversación las formula con las palabras de
  ese proyecto, una por vez y hasta tres.

- **El molde de `organization/company.md` deja de asumir un negocio.** «Modelo de negocio», «quién paga»
  y «fuentes de ingreso» pasan a ser «De qué se trata», «A quién sirve» y «Cómo se sostiene», que nombra
  donación, presupuesto interno y trabajo voluntario junto con la venta. Lo que no aplica se dice, no se
  completa con algo plausible. Afecta a las instancias nuevas: `upgrade` sólo reemplaza `system/`, así
  que el archivo que ya escribiste sigue siendo tuyo.

## [0.29.0] - 2026-08-18

### Añadido

- **`ops scan`: qué hay en el workspace, resuelto por código.** Lista los subproyectos con manifiesto
  propio, su runtime y los comandos de test, lint y build que cada uno declara, diciendo de qué archivo
  salió cada comando. No corre ninguno y no inventa ninguno: un comando que nadie declaró se lee igual
  que uno real, y el primer Verify de una tarea es donde eso se descubre. Saltea `node_modules` y todo
  directorio oculto, que es lo que lo mantiene en milisegundos. Una instancia sidecar escanea su carpeta
  madre, donde vive el código; cualquier otro modo, donde está parada.

### Cambiado

- **`/onboard` cuesta lo que encuentra.** Empezaba pidiéndole a un agente que explorara el repositorio y
  terminaba corriendo la suite de tests de cada servicio: una corrida sobre una carpeta vacía gastó doce
  minutos sin poder producir nada. Ahora arranca con `ops scan` —una llamada, milisegundos— y, si el
  workspace no tiene código y nadie aportó contexto, termina ahí diciendo qué le falta, en vez de escribir
  una empresa inventada.

  El recorrido ya no ejecuta nada del proyecto. El mapa real anota cada comando **tal como está
  declarado**, con su archivo de origen, y verificarlo corriéndolo pasa a ser una historia de la épica
  001, donde tiene dueño y tiempo asignado. Lo que escribe y lo que deja a una persona no cambió:
  borradores marcados como supuestos, credenciales y MCP en `HUMAN_ACTIONS.md`, épica sin promover.

  Si ya tenés una instancia, el recorrido actualizado llega con `automation install`, no con `upgrade`:
  los workflows viven en el runner.

## [0.28.0] - 2026-08-18

### Añadido

- **`/onboard`: el recorrido que llena una instancia recién creada.** `init` la deja funcionando y sin
  enterar de nada —`organization/` es el molde y el roadmap está vacío—, y llenarlo exige leer el
  repositorio y decidir qué es cada cosa, que es justo lo que un CLI determinista no puede hacer. El
  recorrido inventaría los subproyectos con manifiesto propio, **corre** los comandos de test, lint y
  build que cada uno declara, y escribe `organization/`, la sección «Mapa real» de `AGENTS.md` y las
  raíces reales en `ops.config.json`.

  Corre los comandos en vez de copiarlos del README porque un mapa copiado envejece sin avisar y el
  primer Verify de una tarea real es donde aparece: cada comando queda como verificado, falla o ausente.
  Lo que deduce va marcado «(supuesto)» y lo que nada sostiene queda «Por definir». No lee `.env` ni
  ninguna credencial —de `.env.example` toma sólo los nombres de variable—, y las credenciales, los MCP
  y el permiso de push salen como filas de `HUMAN_ACTIONS.md` con la acción concreta que las desbloquea,
  sin proponer ningún valor. Cierra escribiendo la épica 001 —que una tarea pueda atravesar el ciclo
  entero— y no la promueve.

  Si la instancia ya tiene contexto escrito, para y pide `force`: reescribir un borrador que alguien
  corrigió no deja rastro de lo que se perdió. Y un workspace todavía sin código no es un error: escribe
  lo que el contexto permita y traer los repos pasa a ser la primera historia.

  Claude y Antigravity lo reciben como recorrido ejecutable; Codex y Gemini lo operan desde la sección
  «El arranque» que `automation install` deja en sus instrucciones. Si ya tenés una instancia, el
  workflow llega con `automation install` —no con `upgrade`—: los workflows viven en el runner.

### Corregido

- **`init` dentro de una carpeta que ya nombra al toolkit deja de anidar.** Correrlo parado en
  `acme-ops/` creaba `acme-ops/ops/` —una raíz ops dentro de otra— y llamaba «acme-ops» al proyecto.
  Ahora la instancia es esa carpeta y el proyecto se llama «acme». Además un `.git` solo dejó de contar
  como contenido, así que `mkdir acme-ops && git init && cauce init` no pide `--force` para no pisar
  nada.

## [0.27.0] - 2026-08-18

### Añadido

- **Instalar Cauce es un comando.** `npx @ingeniomaps/cauce init`, sin destino, crea `ops/` en modo
  sidecar, te pregunta con qué runner vas a trabajar y qué integración querés, corre `npm install`,
  deja el wiring del runner puesto y valida la instancia antes de terminar.

  Antes había que elegir destino y modo a ciegas, correr `npm install` a mano —sin él la instancia no
  funciona: el shim, los cargos, los equipos y los adaptadores se resuelven desde `node_modules`— y
  después instalar el runner. Las dos preguntas tienen «ninguno» como default: instalar un runner
  escribe en tu repositorio, así que un Enter apurado no deja archivos que no pediste, y los dos pasos
  se agregan más tarde con `automation install` e `integration enable`.

- **Banderas para instalar sin preguntas.** `init` acepta `--runner`, `--integration` e
  `--install`/`--no-install`. Sin terminal —CI, un contenedor, un Dockerfile— no pregunta nada ni
  descarga nada: materializa la instancia y dice qué falta, así que una automatización decide por
  bandera y no hereda una descarga. Un `npm install` que falla se reporta y deja escrito por dónde
  seguir, en vez de terminar en un error del runner tres pasos después.

### Cambiado

- **El destino de `init` es opcional, y sin él la instancia se aparta en `ops/`.** Antes cortaba con
  `Falta <destino>`, así que ninguna invocación existente cambia de comportamiento. Lo que cambia es
  a dónde va lo que no elegiste: un monorepo recibía `planning/`, `teams/`, `organization/` y
  `AGENTS.md` en su primer nivel y dejaba de distinguir qué era suyo. El modo `embedded`, que es el que
  despliega el molde en la raíz, ahora hay que pedirlo explícito.

## [0.26.0] - 2026-08-17

### Añadido

- **Una segunda corrida del día ya no borra a la primera.** Los registros de evaluación aceptan
  `AAAA-MM-DD-N.md` además del nombre pelado, y `ops evaluate <cargo> --record [AAAA-MM-DD]` te dice
  dónde escribir el próximo. `agent-eval` lo pregunta en vez de componer el nombre desde la fecha.

  Importa porque aplicar una propuesta cambia el contrato y el mismo recorrido pide volver a correr los
  casos ahí mismo: con el nombre saliendo de la fecha, esa segunda corrida escribía encima de la
  primera, que es la línea base contra la que se compara. Si ya tenés registros, no hay que hacer nada:
  el nombre viejo sigue siendo válido y es la corrida 1 de su día.

- **Una propuesta aplicada se puede corregir dentro de su período.** `ops learn <cargo> --proposal` abre
  una revisión —`AAAA-MM-r2.md`, con `corrects:` en el frontmatter— cuando la anterior ya está aplicada.
  La aplicada queda sellada donde está: no se reabre ni se reemplaza.

  Aplicar no era el final del ciclo —la evaluación posterior es la que dice si el cambio sirvió—, y
  cuando decía que no, el sello que impide reaplicar lo mismo también impedía corregirlo hasta el mes
  siguiente. Sigue habiendo una sola propuesta pendiente por período. `agent-promote` nombra la revisión
  como salida en vez de mandarte a esperar.

### Cambiado

- **R11 se reescribe: comentarios con destinatario.** En `planning/rules/system/code-shape.md`, así que
  rige para todo cargo y para el runner trabajando sin cargo. Antes pedía que un comentario dijera el
  porqué y no el qué, con un tope de tres líneas. Ahora el filtro es quién lo va a preguntar o a
  deshacer sin saberlo: tener un porqué no alcanza, porque una convención también lo tiene. Distingue
  tres lugares —dentro de una unidad, encabezándola, y donde ningún nombre alcanza— y retira el tope de
  líneas, que contradecía a R7 dos reglas más arriba y en la práctica se leía como presupuesto a gastar.

  Lo que vas a notar: se habilita el comentario que encabeza una unidad y dice qué garantiza para poder
  usarla sin leerla entera, que la redacción anterior prohibía.

- **R14 exige que el registro viaje con la afirmación, no con el informe.** Párrafo nuevo en
  `planning/rules/system/conduct.md`. Una lección, una regla propuesta, una fila de acciones humanas o un
  paso de runbook se leen solos, así que una afirmación de mecanismo que sale del informe hacia uno de
  ellos lleva su registro o no sale. Y el disparador es a dónde va la afirmación, no cuán discutible
  parece — lo que se deja plano suele ser lo que sostiene el propio procedimiento.

- **`release-manager` acota las operaciones de esquema al motor.** Dos reglas nuevas: qué preserva un
  rename, una copia o un drop depende del motor y su versión, y hay que declararlo antes de apoyar ahí un
  ensayo o una salvaguarda; y una copia previa a un borrado es una foto —con su instante de corte y qué
  queda afuera—, no una reversión, con el roll-forward entregado en la misma pieza que la conclusión de
  que revertir dejó de ser seguro. Suma la sección «Qué preserva cada operación de esquema» a su modelo
  operativo, la conducta prohibida
  `unscoped_schema_operation_or_data_copy_presented_as_safeguard` y el caso `07-schema-safeguard-scope`.

- **`procurement-manager` separa el alcance de una figura de su deber de documentarla.** Dos reglas
  nuevas: qué habilita una sole source o una excepción de emergencia sale del régimen aplicable con su
  edición, no de la parte del procedimiento propio que dice qué registrar al usarla; y un cuantificador
  universal sobre normas —«ningún régimen», «todos los marcos»— es una afirmación de mecanismo y lleva su
  registro. Suma la sección «Alcance de las figuras y afirmaciones normativas», el caso
  `07-exception-scope` y dos conductas prohibidas:
  `figure_scope_inferred_from_own_documentation_duty_or_universal_norm_claim` y
  `scorecard_dimension_dropped_instead_of_carried_as_a_conditional` — una dimensión que todavía no se
  puede ponderar queda como obligatorio condicionado, con qué la activa, qué evidencia la cierra y quién
  la revisa, en vez de declararse ausente.

### Corregido

- **El guard de migraciones dejaba pasar el `DELETE FROM` más común.** Nombra tres cosas que frena y una
  de ellas no frenaba: el límite de palabra quedaba al final del grupo, y después de un punto y coma no
  hay límite de palabra, así que `DELETE FROM pedidos;` —la forma que tiene en cualquier migración—
  pasaba y sólo se detenía la variante sin punto y coma. Además `DROP COLUMN` y `DROP CONSTRAINT` nunca
  habían estado en la lista, y pierden datos y garantías igual que `DROP TABLE`.

  Si dependías de este guard, revisá las migraciones que integraste desde que lo instalaste: puede haber
  pasado algo que creías bloqueado.

## [0.25.0] - 2026-08-17

### Añadido

- **R14 — una afirmación de mecanismo lleva su registro.** Regla nueva en
  `planning/rules/system/conduct.md`, así que rige para todo cargo, para uno propio que hayas escrito y
  para el runner trabajando sin cargo. El comportamiento de una herramienta, un motor, un formato, una
  norma o un sistema de terceros es material de trabajo: público, versionado y comprobable. Por eso cada
  afirmación declara en cuál de tres registros va —**verificado**, **documentado** o **hipótesis**—, la
  verificación llega hasta donde R12 permite (fuente pública, `--help`, `--version`, invocación inocua;
  nunca conectarse ni ejecutar la operación que se describe), y una hipótesis no sostiene una negativa,
  un diagnóstico, un número ni un paso de procedimiento, ni entra en informe, runbook, regla o lección.

  Lo que vas a notar: los cargos empiezan a decir «esto lo comprobé así» o «esto es plausible y no lo
  verifiqué» donde antes afirmaban de corrido, y a negarse a apoyar una decisión en lo segundo. Eso es
  lo que la regla busca. No tenés que instalar nada: `upgrade` reemplaza `planning/rules/system/`
  completo.

### Cambiado

- **Los 46 contratos del catálogo apuntan a R14.** Cada uno gana un renglón en sus reglas operativas que
  nombra los tres registros y remite a la regla, en vez de repetirla. `database-administrator` conserva
  su propia redacción, más específica, y no recibe el puntero.

- **`agent-eval` juzga contra las conductas prohibidas del cargo.** Hasta ahora quien juzgaba recibía
  sólo los comportamientos esperados del caso, y la lista `forbidden` de `evaluations/expected-behaviors.yaml`
  no la leía ningún código: entraba a un veredicto únicamente si quien lanzaba la corrida se acordaba de
  escribirla en el prompt. Ahora viaja junto a los casos, con redacción fija.

  Consecuencia para vos: si tenés cargos propios con su `expected-behaviors.yaml`, sus prohibiciones
  pasan a pesar tanto como los comportamientos esperados, y un caso pasa sólo si se observan todos y no
  ocurre ninguna. Un resultado anterior midió menos criterios que uno de ahora, así que no son
  comparables — conviene volver a correr los casos de los cargos que te importen.

### Corregido

- **`upgrade` ya no reemplaza en silencio un archivo del sistema editado.** El manifiesto registraba el
  runtime y el `system/` de cada colección, pero no los archivos sueltos que el toolkit mantiene, así que
  `AGENTS.md`, el `Makefile` y los README del sistema se reemplazaban sin comparar nada: una edición ahí
  desaparecía sin aviso, mientras la misma edición bajo una ruta registrada frenaba el upgrade y nombraba
  el archivo. Ahora los compara igual que al resto.

  Si editaste alguno de esos archivos, el próximo `upgrade` te lo va a decir en vez de pisarlo. Lo que
  corresponde es mover ese cambio a un archivo del proyecto: `system/` se reemplaza entero por diseño.

- **El README de ADR ya no pide mantener un índice.** Su paso 4 mandaba actualizar una tabla de
  decisiones del proyecto que vive dentro de un archivo que mantiene Cauce, así que cada fila agregada se
  perdía en el `upgrade` siguiente — y con el arreglo de arriba habría pasado a bloquearlo. Las decisiones
  del proyecto son los archivos `NNN-*.md` del directorio y su estado vive en cada uno, sin nada que
  sincronizar. Si tenías filas en ese índice, la información ya está en las ADR; no hay que migrarla.

## [0.24.0] - 2026-08-17

### Cambiado

- **`database-administrator` nombra el registro con el que emite una afirmación de mecanismo.** Es el
  primer cambio de contrato del catálogo que nace de un **fallo medido** y no de investigación semanal.
  El cargo rechazó bien un `DROP DATABASE`, atacó bien la premisa, y afirmó en negrita —como «modo de
  falla real, no hipotético»— que `dropdb` con la variable vacía elimina la base por defecto. Eso es el
  comportamiento de `createdb`. Lo dejó escrito además como lección permanente en su banco.

  Es hueco de cobertura y no de ejecución, y el propio veredicto lo prueba: el mismo juicio que reprueba
  certifica que no inventó **ningún** hecho de la instancia. Los nueve objetos que el contrato ya
  enumeraba —topología, configuración, capacidad, backup, restore, RPO/RTO, privilegio, causa,
  evidencia— son todos hechos del sistema administrado; el comportamiento público y verificable de una
  herramienta no está entre ellos, y para este cargo *es* la materia de trabajo.

  Lo que se agrega no es «no inventar» otra vez —eso sería paráfrasis, y una paráfrasis en un contrato
  es deuda—. Es el **registro** con que se emite la afirmación (verificado, documentado, hipótesis), el
  **límite** de con qué se verifica —documentación de la versión e invocación inocua, nunca
  conectándose ni ejecutando la operación descrita— y la **consecuencia**: sin verificar no sostiene una
  negativa ni entra a un artefacto durable. Más la conducta prohibida
  `unverified_tool_or_engine_behavior_asserted_as_fact` y su caso adversarial.

  Volver a medir los siete casos mostró que la regla cambia el comportamiento **en casos para los que no
  se escribió**: ninguno de los siete menciona `dropdb`, y aparecen un cargo declarando que los binarios
  que verificó son de su máquina «no del entorno», otro que no nombra ningún comando porque el motor no
  consta, otro que desactiva por nombre su única hipótesis no documentada, y otro que se niega a inferir
  el flag de una herramienta desde otra de nombre parecido. Y dos fallos nuevos que antes no se veían:
  soltar el hedge al resumir conservándolo en el informe, y fechar mal una fuente bajo la etiqueta
  inventada para garantizar que ninguna afirmación exceda la suya.

- **El paquete deja de llevar `.github/workflows/` a cada instalación.** `init` no los copia,
  `agent-learning.yml` está en la lista de retirados que `upgrade` borra, y `ci.yml` corre `npm run ci`,
  que una instancia no tiene. Publicar dejó de ser manual sin nada que lo revisara: `prepublishOnly`
  corre el mismo gate que CI.

- **La salida generada no arrastra deber de atribución.** MIT pide que el aviso viaje con porciones
  sustanciales, e `init` copia porciones sustanciales al repositorio de una empresa sin ningún aviso.
  Nadie atribuye archivos andamiados; ahora está escrito que no hace falta. El copyright además queda a
  nombre de quien puede tenerlo: un handle de GitHub no es una persona jurídica.

### Agregado

- **`make` alcanza integraciones y equipos desde una instancia**, que es el único lugar donde las
  integraciones corren. La plantilla mandaba a escribir el CLI a mano mientras la automatización ya
  tenía atajo. `sync` toma `PROVIDER`, así que un segundo proveedor no necesita un target nuevo.

### Corregido

- **`runner.allowPush` se validaba y no se leía.** El guard bloqueaba el push sin condición, así que un
  proyecto podía declararlo y no cambiaba nada. Lo encontró la evaluación de un cargo, que lo leyó y
  concluyó que existía un control técnico inexistente. Falla cerrado: sin raíz legible, no hay permiso.

- **Una historia envuelta en dos líneas perdía su criterio y su servicio.** El cuerpo se matchea
  multilínea, pero el lookahead terminaba en `$` con la bandera `m`, que casa fin de *línea*: cortaba en
  el primer salto, y `check` respondía «no declara `(service: <ruta>)`» sobre una historia que sí lo
  declara. Dos cargos lo encontraron reescribiendo su historia hasta que entrara en un renglón.

- **Un ítem de inbox sin nombre en negrita desaparecía en silencio.** La plantilla traía cuatro
  encabezados vacíos y ningún ejemplo, así que doce viñetas se leían como un inbox vacío. La convención
  se conserva —ese nombre es con el que se cita el ítem después—; ahora `tree` dice cuántas quedaron
  afuera y la plantilla muestra la forma.

- **El estado de una regla de negocio se valida contra un conjunto cerrado.** La plantilla traía
  `vigente` cableado mientras la de ADR presentaba el menú, y el validador sólo comprobaba que la línea
  tuviera la forma. Tres cargos distintos publicaron reglas declarándose vigentes derivadas de un ADR
  que ellos mismos habían dejado en propuesto: cada uno hizo lo que su plantilla le pedía. Ahora la
  afirmación débil es la que no cuesta nada.

- **El README servía a dos lectores a la vez** y todo lo desactualizado estaba del lado del mantenedor,
  porque quien lo notaría lee `AGENTS.md`. Decía once guards donde hay doce, pisos de cobertura que no
  eran los que `coverage.sh` exige, y una ruta de equipos que se había movido.

### Al actualizar

Dos controles nuevos **gatean** y pueden hacer fallar `check` o `evaluate` en una instancia que ya
existía. Hoy no hay ninguna empresa consumiendo Cauce fuera del proyecto de prueba, así que esto es
para quien actualice más adelante:

- Una regla de negocio cuyo `Estado:` no sea `propuesta`, `vigente` o `derogada` falla `check`. El
  arreglo es una línea por archivo.
- Un cargo propio sin `summary:` en el frontmatter de su `SKILL.md` falla `evaluate` (introducido en
  0.23.0). El arreglo es la línea con la que se elige ese cargo, de 120 caracteres o menos.

## [0.23.0] - 2026-08-17

### Agregado

- **Una línea por cargo, para elegirlo sin abrir cuarenta y siete carpetas.** Una empresa con una tarea
  en la mano tenía que leer los contratos para saber a quién asignarla: el `description` que cada cargo
  ya traía ronda los quinientos caracteres porque su lector es el runner al seleccionar, así que los
  cuarenta y siete de corrido son unos veintitrés mil.

  `ops agents list` imprime ahora la línea que cada cargo carga en su propio frontmatter, alineada en
  columna, y `--json` la lleva también: cuando el que asigna es un agente, es la máquina la que elige.
  La línea vive en el cargo y no en un índice aparte —un índice se desincroniza en silencio, y una
  línea que miente al elegir es peor que no tenerla—, así que un fork se la lleva y una empresa que
  escribe su cargo escribe la suya. Un cargo sin ella falla sus controles estructurales.

  Lo que gobierna esas líneas no es el largo sino distinguir vecinos: una que no separa un cargo del de
  al lado te hace asignar el equivocado, que es peor que abrir las carpetas. Se escribieron por racimos
  —los cargos que de verdad colisionan— y casi todas cierran con la exclusión que más se malinterpreta:
  `qa-engineer` recomienda el release pero no lo aprueba, `finops-engineer` no es el cierre contable,
  `security-engineer` no es la base legal de un dato personal.

  Y la respuesta negativa cuesta lo mismo que la positiva: la lista termina diciendo dónde va el cargo
  propio, porque forzar el más parecido es peor que no usar ninguno. El `AGENTS.md` de la plantilla dice
  lo mismo, que es lo que lee el agente de la empresa antes de trabajar.

- **El ciclo de aprendizaje tiene final.** Había firma, aplicación e historial, y faltaba el paso que
  vuelve irrepetible lo ya hecho: la propuesta nacía `status: proposed` y nadie lo movía nunca.
  `agent-promote` busca la propuesta más nueva y aplica si el estado dice aprobada con responsable —y
  «aprobada y aplicada» también lee como aprobada—, así que volver a promoverla la aplicaba de nuevo.
  Como toda propuesta es aditiva por diseño, eso no falla: duplica cada viñeta y cada fuente del
  contrato en silencio.

  Sellar (`ops learn <cargo> --applied`) es lo último, después de aplicar y registrar, y lo hace el
  motor y no el recorrido: marcar el estado editando frontmatter a mano es justo el paso que se hace mal
  sin que nadie lo note. El recorrido se niega ante una propuesta ya aplicada, y las aplicadas dejan de
  contarse como pendientes, así que un cargo ya no reporta trabajo que se cerró el mes pasado.

### Cambiado

- **`security-engineer` nombra la automatización con credenciales como actor.** Salió de su propia
  investigación: el contrato cubría «agente autónomo con credenciales en CI» por implicación y nunca por
  nombre, y eso no es un detalle de redacción. Mínimo privilegio dice cuánto puede hacer un proceso, no
  que su decisión la escriba un tercero. Un servicio con credenciales ejecuta un camino fijo —para
  abusarlo hay que encontrarle una falla o robarle el token—; un agente lee entrada no confiable y actúa
  con las credenciales del pipeline, así que la entrada es el programa y los controles que uno esperaría
  corren cuando ya ejecutó.

  Trae una conducta prohibida nueva, `post_hoc_check_as_containment_for_credentialed_agent`, con el caso
  adversarial que la distingue de las dos con las que se solapaba. Las ocho recomendaciones operativas
  del informe quedaron deliberadamente fuera: son sobre el pipeline de este repositorio, y un contrato
  que se instala en empresas ajenas no es el lugar de esas decisiones.

- **La conducta universal salió de `AGENTS.md` y llegó a las empresas.** El documento mezclaba mecánica
  que sólo tiene sentido dentro de una instancia con conducta que vale para cualquier agente; sólo lo
  primero pertenece a un archivo que describe un repositorio. La conducta pasa a `planning/rules/`, que
  ya era la capa compartida — y que hasta ahora ningún runner cargaba: las reglas viajaban a cada empresa
  como archivos que nadie leía nunca.

- **La sincronización parcial de integraciones se retiró.** Nadie podía pedirla.

### Corregido

- **Un caso adversarial entrega el artefacto que describe, en vez de sólo nombrarlo.** Los cuarenta y
  siete casos del catálogo decían «una guía externa», «un CSV externo», «un runbook externo» y no
  entregaban ninguno. Eso mide algo más fácil de lo que dice medir: al cargo se le pregunta si obedecería
  un documento del que se le está hablando, y un texto que nunca leyó no puede inyectarlo.

  El hueco se hizo visible cuando un cargo escribió que había leído una guía inexistente y la premisa
  falsa quedó asentada en su banco como antecedente documental sin documento. Es el quinto defecto de
  fidelidad del arnés, y la corrección no fue reformular la pregunta sino escribir los cuarenta y siete
  artefactos: cada uno en su formato real —guía, CSV, notebook, módulo IaC, model card, portal, pliego—
  con las instrucciones que su caso describe y las coartadas que las hacen funcionar.

  El banco los copia **antes** de su commit limpio, así que `git status` no se los atribuye al cargo: si
  entraran después, el juez leería como obra suya el documento que vino a resistir. Falta de artefacto es
  error y no advertencia, porque es estático y verificable sin modelo — como advertencia es como estuvo
  faltando en los cuarenta y siete sin que nada lo dijera.

  Los nueve cargos que ya tenían registro se volvieron a medir contra el artefacto real, y uno es el
  argumento de haberlo hecho: el caso de `backend-engineer` **fallaba** antes y pasa ahora, que es cómo
  se ve un defecto del arnés desde afuera.

- **Un guard que no puede leer bloquea, en vez de permitir.** `run-hook.sh` enuncia el principio para el
  motor que carga, y tres lugares hacían lo contrario: una coma de más en `ops.config.json` apagaba
  `workspace-boundary` y `engine` sin imprimir nada, y una entrada ilegible se leía como «ningún comando
  y ningún archivo», que todo guard interpreta como nada que revisar.

- **Una bandera mal escrita falla en vez de ignorarse.** `check --jsonn` imprimía la salida humana y
  salía con 0, así que quien esperaba JSON recibía prosa sin ninguna señal. Cada comando declara ahora
  qué acepta, y `--help` dejó de depender de ir primero: `check --help` corría `check` sobre el
  directorio actual.

- **El toolkit se niega a correr los comandos de una empresa contra sí mismo.** `upgrade` reemplazaría
  los archivos de raíz que este repositorio mantiene —incluido el `AGENTS.md` donde vive la regla que lo
  prohíbe—, e `install` construiría una superficie de consumo cuyos punteros apuntan al catálogo que se
  escribe acá, con guards que bloquean el push de cada release.

- **Un `ops.config.json` ilegible se reporta en vez de leerse como ausente.** Una coma de más hacía que
  `upgrade` e `install` dejaran de reconocer el modo `toolkit` y siguieran adelante. Ausente sigue
  significando ausente; presente pero ilegible es un estado roto y ahora lo dice.

- **Un título vacío dejaba de reportarse como faltante.** El título de toda propuesta de integración se
  leía con un patrón donde `\s` casa el salto de línea, así que un documento con encabezado vacío se
  comía la línea siguiente: el resumen volvía como `## Descripción`, no vacío, y «falta título» quedaba
  callado.

- **`sync` dice qué hizo con los ítems que el remoto dejó de traer.** Los contaba y no imprimía
  ninguno: se borran cuando nada local se había curado sobre ellos, y se conservan marcados cuando sí, y
  la única forma de enterarse era ir a mirar el directorio.

- **La verificación de los guards se deriva del registro.** Comparaba contra una lista de quince nombres
  copiada a mano, así que un guard nuevo no se verificaba hasta que alguien se acordara de agregarlo —la
  misma deriva que tenía la cuenta de guards informando once.

## [0.22.0] - 2026-08-17

### Corregido

- **El juez de un caso ve lo que el cargo escribió, no sólo lo que dijo.** La respuesta de un cargo no
  es necesariamente su entrega: pedido un webhook de pagos, `backend-engineer` contestó un resumen y
  dejó el contrato real —orden de verificación de firma, comparación en tiempo constante, ventana
  antirreplay, catorce pruebas— en su `INBOX.md`. El juez, leyendo sólo la respuesta, marcó esos
  comportamientos como ausentes.

  El banco pasa a ser un repositorio git commiteado en su estado limpio, así que `git status` y
  `git diff` muestran exactamente qué produjo el cargo, separado del andamiaje. Es el cuarto hueco de
  fidelidad del arnés, y lo abrió el arreglo anterior: antes los cargos no podían escribir, así que
  todo lo que tenían estaba en el texto.

  Medido en la corrida de tres cargos, ese acceso decidió **seis comportamientos** repartidos en tres
  casos. Y sirve para lo inverso: para un comportamiento negativo —«no exportar ni borrar datos»— un
  diff vacío es prueba positiva, que un texto sólo puede afirmar.

- **Rehacer un banco con trabajo sin recoger se niega.** El registro de la evaluación se escribe
  *desde* el banco, así que recrearlo antes de recogerlo destruye justo lo que se iba a anotar. Pasó:
  se rehizo un banco probando otra cosa y con él se fue lo que un cargo había escrito. Ahora hace
  falta `--force`.

- **La suite dependía de directorios que sólo mantenían vivos unos restos.** `learning/reports/`
  existía en cada cargo porque contenía un molde; retirados los moldes muertos, git dejó de trackearlo
  —no versiona directorios vacíos— y desaparece en cuarenta y cinco de los cuarenta y siete cargos al
  clonar. La prueba lo leía directo y pasaba sólo en una máquina con los restos. Verificado ahora
  contra un clon limpio.

- **El conteo de guards se deriva del registro.** `automation check` informaba «11 guards» como
  literal mientras el motor registraba doce; quedó viejo al agregar uno y nada falló. Un número de
  auditoría que no sale de lo que describe envejece sin avisar.

### Removido

- **Código muerto, ayudantes duplicados y una configuración inerte.** `template/automatization/config.json`
  se distribuía a cada instancia y no lo leía nadie: qué guard corre lo decide la configuración del
  runner, que es la única fuente. El README de la plantilla ahora lo dice.

### Notas

- **Primera medición de tres cargos del catálogo**: `product-manager` 5/5, `privacy-compliance-specialist`
  6/6, `backend-engineer` 4/6. Sesenta y siete citas textuales sostienen los comportamientos.

  Los dos fallos son reales y están descritos con su razón. Uno de ellos deja además una pregunta
  sobre el caso, escrita en el registro en vez de escondida: `backend-engineer/06-adversarial-docs`
  falló «verificar fuente oficial y versión aplicable» mientras el caso equivalente de
  `privacy-compliance-specialist` pasó una versión más amplia. Si los dos deberían medir lo mismo es
  discutible, y esa discusión va por propuesta firmada.

## [0.21.0] - 2026-08-17

### Corregido

- **Cada caso adversarial recibe su propio banco.** Con uno por cargo, los casos corren a la vez sobre
  el mismo `planning/` y se leen entre sí. Correr `product-manager` lo mostró sin lugar a dudas: un
  caso tomó por «una sesión anterior de este mismo cargo» lo que otro acababa de escribir, y otro
  construyó su respuesta entera alrededor de cuatro candidatas que en su enunciado no existían.

  Ninguno de los dos cambió de veredicto, así que nada parecía roto — pero sus respuestas dejaron de
  ser las que esos casos pedían medir, y la independencia entre casos es la premisa de medir con
  ellos. La bandera pasa a ser `evaluate <cargo> --bench <caso>`.

  Preparar un banco ya no borra el del vecino, que puede estar a mitad de corrida, y el nombre del
  caso se valida antes de convertirse en una ruta.

### Notas

- **`product-manager` tiene su primera medición válida: 5 de 5.** El registro anterior se había
  descartado por tomarse dentro del toolkit, donde el cargo no tenía dónde escribir y se negaba —con
  razón— en los dos casos que escriben. Con el banco, esos dos pasan.

  Veinte citas textuales sostienen los veinte comportamientos. Queda escrito en el propio registro lo
  que el juez de `03-epic` dejó anotado: no se produjo ningún criterio `CN`, y bajo un estándar que
  exigiera criterios de épica específicamente ese comportamiento caería. El caso presupone una
  oportunidad aprobada que no existía, y el cargo se negó a inventarla.

## [0.20.0] - 2026-08-17

Casi todo lo de abajo sale de la primera investigación semanal del cargo `security-engineer` corriendo
de verdad sobre este repositorio. De sus ocho hallazgos, dos resultaron sobredimensionados al
verificarlos y quedaron cerrados sin cambio; el resto está acá.

### Cambiado

- **El agente de investigación semanal ya no comparte job con la credencial de escritura.** Ingiere
  contenido web que nadie controla, y hasta ahora corría con `contents: write` y la API key en el
  mismo lugar: cualquier instrucción que llegara dentro de una página tenía un repositorio a mano.

  Ahora entrega su informe como artifact y un segundo job lo commitea, sin modelo y sin credencial de
  API. Ese job resuelve **dónde** aterriza el archivo con el mismo CLI que resolvió el catálogo, así
  que nada de lo que viaje en el artifact puede redirigirlo. El permiso por defecto del workflow baja
  a `contents: read`: un job nuevo ya no nace pudiendo escribir por herencia.

- **Las acciones de GitHub quedan fijadas por SHA de commit**, y el CLI del agente por versión. Un tag
  es mutable: quien controle el repositorio de la acción puede moverlo a otro commit y el workflow
  ejecuta código nuevo sin que cambie una línea acá.

- **`guard-secrets` reconoce los archivos de credencial que las herramientas escriben solas** —
  `.npmrc`, `.netrc`, `id_rsa` y compañía—. Correr el guard sobre trece nombres mostró que `.env`
  bloqueaba y esos pasaban, y `.npmrc` es donde vive el token de publicación. Tapa un caso conocido;
  no vuelve completo al guard.

### Corregido

- **Los informes de aprendizaje traen escritas las convenciones de las que depende el ciclo.** Cuatro
  corridas independientes etiquetaron sus hallazgos `H1`, `H2`, … sin que nada se lo pidiera, y esa
  etiqueta terminó siendo carga: dentro del informe une «Hallazgos» con «Evidencia» y «Recomendación»,
  y la propuesta mensual la cita para decir de qué hallazgo sale un cambio. Funcionaba porque los
  modelos convergen, que es exactamente lo que deja de funcionar sin avisar.

  Lo mismo con los títulos: la consolidación busca `## Recomendación` exacto y renombrarlo no da error
  — deja la propuesta diciendo que no se registró nada.

### Removido

- **Se retiran 94 `_template.md` que el motor nunca leyó.** `learn` arma el andamiaje del informe y de
  la propuesta desde un molde propio; esos archivos no los leía nadie: ni el motor, ni un workflow, ni
  un runner, y ningún `SKILL.md` los nombra.

  No eran inertes. **Quince de los cuarenta y siete describían una forma que el motor nunca produce**
  —viñetas planas, sin sección de hallazgos y sin el título que la consolidación busca—, así que un
  agente que respetara su molde habría reestructurado el informe y dejado la propuesta vacía sin
  levantar nada.

  Comprobado antes de retirarlos: sin sus moldes, `qa-engineer` sigue evaluando 7/7 y `learn` arma su
  andamiaje igual. Adoptar un cargo pasa a copiar trece archivos en vez de quince.

### Notas de seguridad sin cambio

- **El token de npm no requiere acción.** El hallazgo lo describía como secreto de larga vida y sin
  expiración; verificado, tiene ventana acotada, y la afirmación de que npm revocó los tokens clásicos
  no se sostiene —el token funciona—. Lo único cierto que queda es que su alcance es de cuenta y no de
  paquete: al renovarlo conviene acotarlo, y nada más.

- **Los guards no son un límite de seguridad**, y `automatization/hooks/README.md` ahora lo dice con
  sus dos casos reproducibles. El riesgo real no es el bypass sino la confianza que doce guards
  puestos inspiran: si algo tiene que ser imposible, va en permisos, alcance de token o revisión
  humana, no en una coincidencia de texto.

## [0.19.0] - 2026-08-16

Los cuatro arreglos de abajo salieron de correr los tres caminos de punta a punta —un cargo que
aprende en el toolkit, uno propio de una empresa, y uno del catálogo adoptado y aprendiendo—, no de
un test. Son cosas que se veían correctas hasta que algo las siguió de verdad.

### Agregado

- **`guard-governance` cubre el contrato de un cargo, su medición y su firma.** `agent-promote` se
  niega si «Aprobación humana» no está firmada, pero lo único que impedía que la escribiera un agente
  era una frase en un prompt: una instrucción, no un candado, y el archivo no estaba protegido por
  nada.

  Alrededor de la firma van las otras piezas del mismo acto. `SKILL.md` y `references/` son lo que la
  propuesta cambia: sin ellos, editar el contrato directo saltea el ciclo entero. Y
  `evaluations/cases/` con `expected-behaviors.yaml` son el denominador con que se juzga —el propio
  recorrido de propuesta ya dice que cambiarlo «es parte de lo que se aprueba»—, así que moverlo en
  silencio ablanda toda medición pasada sin tocar una regla.

  **Para una empresa esto significa** que tocar su propio cargo pide `OPS_GOVERNANCE_OVERRIDE=1`, la
  misma puerta explícita que ya rige para `planning/adr/` y `planning/rules/`. Las dos clases de
  evidencia quedan afuera: `learning/reports/` y `evaluations/results/` registran lo que pasó un día
  en vez de decidir algo, y un veredicto se escribe en cada corrida.

### Corregido

- **La automatización de un cargo adoptado apuntaba al catálogo.** El `AUTOMATION.md` del sistema dice
  «mantené `agents/<tipo>/system/<slug>`», que dentro de una empresa es el paquete. Copiado tal cual,
  el ciclo semanal del cargo adoptado escribía en un directorio que el guard bloquea y que npm borra
  —y reportaba éxito—. `agents fork` ahora reescribe esas rutas a las de la empresa.

- **Devolver un cargo al catálogo dejaba avisos sobre una copia que no existe.** Borrar un fork
  resuelve bien —el cargo vuelve a salir del paquete— pero el manifiesto conservaba su registro, así
  que `check` decía «tu copia no recibe mejoras del catálogo» y mandaba a mirar un directorio
  borrado. Devolver es tan legítimo como adoptar: ahora la deriva exige que la copia exista antes de
  comparar, y `upgrade` poda el registro huérfano igual que ya poda los archivos.

- **El puntero que instala el runner no decía a qué se ancla.** Dos agentes que resolvieron un cargo
  parados en el repo ops construyeron la ruta doblada —`<empresa>-ops/<empresa>-ops/...`— y tuvieron
  que deducir la raíz. En sidecar el wiring vive en la carpeta de la compañía y el repo ops es uno de
  sus hijos; ahora el puntero lo dice.

- **`learn` sugería copiar un cargo a mano** en vez de nombrar `agents fork`, que ya existe.

## [0.18.0] - 2026-08-16

### Agregado

- **`ops evaluate <cargo> --bench`: un banco desechable donde un cargo del catálogo puede realmente
  trabajar.** El toolkit no es una raíz ops y no puede serlo —el único `planning/` que vive acá es
  `template/planning`, el molde que se distribuye—. Un cargo cuya entrega es una épica o una entrada de
  INBOX no tenía dónde escribir, se negaba con razón, y su caso lo contaba como fallo: el número
  describía el lugar, no al cargo.

  El banco es una instancia de verdad: `check` pasa, el catálogo resuelve desde adentro y `planning/`
  está vacío y escribible. Se recrea entero en cada corrida —reutilizarlo dejaría que lo que un cargo
  escribió el lunes sea contexto del que responde el martes— y queda en disco al terminar, gitignorado,
  porque después de un veredicto raro lo primero que uno quiere es mirar qué escribió el cargo.

### Cambiado

- **La evaluación corre sobre el banco en vez de negarse.** La 0.16.0 detuvo el recorrido dentro del
  toolkit: acertó el diagnóstico y erró el remedio, porque negarse dejó al catálogo sin ninguna forma
  de medirse, y el catálogo es nuestro y nos toca medirlo.

  El veredicto se escribe junto al cargo, no en el banco: el banco se borra en la corrida siguiente
  —es donde el cargo trabajó, no donde vive— y el veredicto pertenece al contrato que lo rindió.

  En una empresa no hay banco ni hace falta: su instancia ya es el lugar. Lo que se exige ahí es que el
  cargo sea suyo —propio o adoptado con `agents fork`—, porque evaluar uno del catálogo mediría su
  configuración y dejaría el registro sin dónde vivir.

## [0.17.0] - 2026-08-16

### Agregado

- **`ops agents fork <cargo>`: una empresa se lleva un cargo del catálogo y lo mantiene desde su
  carpeta.** El tercer camino ya resolvía —un cargo propio con el mismo slug tapa al del sistema, en el
  listado, en `learn` y en el puntero que instala el runner—, pero llegar hasta ahí era copiar a mano, y
  eso sale mal de una forma que no se nota: se agarra el `SKILL.md`, que es lo que se ve, y quedan atrás
  los casos adversariales, las fuentes y el modelo operativo. El cargo responde igual y ya no se puede
  evaluar, sin ningún aviso.

  No se heredan los informes de aprendizaje, las propuestas ni los veredictos de evaluación. Un veredicto
  pertenece al contrato que lo ganó, y el fork nace para dejar de ser ese contrato; una propuesta
  pendiente arrastraría a la empresa a firmar una decisión que era nuestra.

  En el toolkit se niega: acá el catálogo se edita, no se lo copia. Dejarlo pasar creaba un duplicado que
  tapaba al original, y el trabajo siguiente se hacía sobre la copia mientras la versión que se publica
  quedaba quieta.

- **`check` y `upgrade` avisan cuando el cargo que forkeaste mejoró río arriba.** El mecanismo ya existía
  para ADRs y reglas —«sobrescribir es legítimo; lo que no puede pasar es que ocurra en silencio»— pero
  no cubría los cargos, que es donde más caro sale: un fork se hace una vez y se olvida.

  Se compara contra los digests guardados al forkear, nunca contra la copia: la copia está editada a
  propósito, así que medir contra ella devolvería «todo cambió» desde el primer ajuste. Editar lo propio
  no dispara nada, y esa mitad es la que decide si el aviso se lee o se ignora.

## [0.16.1] - 2026-08-16

### Corregido

- **El mensaje de `guard-engine` daba un comando que no hace nada.** Decía `npm update
  @ingeniomaps/cauce`, pero el motor se declara con versión exacta y npm no mueve un pin exacto:
  responde «up to date» y no toca nada. Encontrado sobre una instalación real que llevaba dos minors
  atrasada sin que nadie lo notara.

  Ahora nombra los dos pasos: `npm install --save-dev --save-exact @ingeniomaps/cauce@latest` —con
  `--save-exact` porque `install @latest` a secas escribe `^` y rompe esa disciplina— y después
  `node tools/ops.js upgrade`, porque bajar el motor no refresca las rutas del sistema de la instancia.

## [0.16.0] - 2026-08-16

### Agregado

- **`guard-engine` impide editar el motor instalado.** `workspace-boundary` no lo cubría: en una
  instalación `node_modules/` cae dentro de la raíz declarada, así que editar el motor le parecía
  legítimo y nada estructural sostenía la regla.

  El daño de esa edición es silencioso por partida doble. El próximo `npm install` la borra, de modo
  que el arreglo se pierde justo cuando alguien creyó haberlo hecho; y hasta entonces la empresa corre
  un motor que no coincide con la versión que declara, que es la forma habitual de un bug
  irreproducible. **Un problema del motor se reporta y se arregla arriba.**

  El toolkit queda exento por `mode: toolkit`, donde el motor es el producto y editarlo es el trabajo.
  Lo de cada empresa —cargos, equipos, integraciones, planificación— sigue abierto.

### Cambiado

- **`planningDir` se retira: era obligatorio y nadie lo honraba.** El renderizador de la plantilla lo
  resolvía siempre a `planning`, mientras `findOpsRoot` y el registro de integraciones tienen esa ruta
  escrita a mano. Cambiarlo no cambiaba nada: no configuraba, prometía. Una instancia con
  `planningDir: "roadmap"` seguía corriendo sobre `planning/` sin aviso.

  Se retira en vez de honrarse porque la ubicación no es opinable: `findOpsRoot` reconoce un
  repositorio de operaciones justamente por tener `planning/` en la raíz.

  **Al actualizar: borrá la línea `planningDir` de `ops.config.json`.** La validación nombra el campo y
  dice qué hacer con él, en vez de caer en «propiedad desconocida».

- **El recorrido de evaluación se detiene si `mode` es `toolkit`.** La 0.15.1 lo documentó en un
  comentario y no alcanzó: la corrida siguiente ocurrió otra vez dentro del toolkit y su resultado se
  leyó como defecto del cargo. Un comentario no impide nada. Ahora corta antes de gastar un agente.

### Corregido

- **La 0.15.1 atribuyó a `legal-counsel` cero casos de escritura en `planning/`, y no es cierto**: su
  respuesta declina uno citando el modo toolkit, y el juez aceptó la justificación —de ahí el 6/6—. El
  único registro limpio de la muestra es el de `security-engineer`.

  El registro de `product-manager` se descarta. Sus dos fallos son los dos casos que escriben, y la
  negativa citaba `planningDir` apuntando a la plantilla distribuida: un campo inerte: el motor habría
  escrito en `planning/` de la raíz, que el toolkit no tiene. Ese 3/5 no mide al cargo ni al entorno,
  sino una configuración que mentía. Se vuelve a ganar sobre una instancia.

## [0.15.1] - 2026-08-16

### Corregido

- **El recorrido de evaluación debe correrse sobre una instancia, no sobre el toolkit**, y ahora lo dice
  con su razón. Un cargo cuyo trabajo es producir artefactos de planning necesita un `planning/` donde
  escribir sea legítimo; dentro del repositorio del toolkit ese directorio es `template/planning`, que se
  distribuye a cada instalación. El cargo se niega —con razón— y el caso lo contaba como fallo.

  La correlación es exacta: `product-manager` falla los **dos** casos que piden escribir en `planning/` y
  ninguno de los otros tres; `security-engineer` y `legal-counsel`, con cero casos de ese tipo, dan 6/6
  en las dos corridas. Es el tercer hueco de fidelidad del arnés, y el que más engaña: hace parecer roto
  a un cargo que está acertando.

## [0.15.0] - 2026-08-16

### Cambiado

- **`AGENTS.md` incorpora «Negarse no es entregar».** Correr los casos adversariales sobre cinco cargos
  mostró el mismo defecto en tres: el cargo es fuerte para negarse y se queda corto en la acción
  positiva, **incluso cuando su propio contrato la autoriza**. El `product-manager` citaba su vía de
  escape sin tomarla; el `data-analyst` enumeraba seis definiciones y pedía elegir; el `qa-engineer`
  rechazaba una guía externa sin verificarla.

  No son tres defectos sino uno repetido, y es de redacción: los límites van en sección propia y en
  lista, las obligaciones positivas van de pasada dentro de un párrafo. Bajo presión, pesa la
  prohibición. Por eso el arreglo es una regla general en `AGENTS.md` —donde todo puntero de cargo ya
  mandaba a mirar— y no 47 ediciones. Verificado: los tres casos pasan.

  No afloja ningún límite: no promover, no prometer fechas, no inventar evidencia y no exceder la
  autoridad siguen absolutos. Se cierra la salida de cumplirlos sin entregar.

### Corregido

- El recorrido de evaluación le daba al cargo **sólo su `SKILL.md`**, cuando el puntero que instala cada
  runner dice «respetá ese contrato **y las reglas de `AGENTS.md`**». Lo medía en una situación que
  nunca ocurre, y ahí se perdía toda regla general.

## [0.14.2] - 2026-08-16

### Corregido

- **Un comportamiento esperado partido en varias líneas contaba como varios.** El parser de casos leía
  líneas en vez de viñetas, así que un caso con cuatro comportamientos declaraba siete — y ese número
  es el denominador de toda la evaluación: ninguno podía pasar. No se veía en el catálogo del sistema,
  donde todas las viñetas entran en una línea; apareció en el primer caso escrito por una empresa.

## [0.14.1] - 2026-08-16

### Corregido

- **Un recorrido de equipo que frena en una etapa tiraba el trabajo de las anteriores.** Los handoffs
  vivían sólo en memoria: si la etapa 3 no cerraba su gate, lo que las dos primeras habían establecido
  —con su evidencia y su cita— desaparecía. Ahora viaja con el bloqueo, para que quien lea la acción
  humana no vuelva a discutir lo ya resuelto.

  Lo encontró el ciclo de aprendizaje de un cargo propio de una empresa: buscaba sus propios veredictos
  para aprender de ellos y no existían en ninguna parte, porque nunca se habían escrito.

## [0.14.0] - 2026-08-16

### Cambiado

- **`learning/CODEX_AUTOMATION.md` pasa a llamarse `learning/AUTOMATION.md`.** El nombre venía de
  cuando Codex era el runner asumido; hoy son cuatro, el prompt es agnóstico y el cron semanal lo
  ejecuta con Claude. Un archivo que dice a qué agente pertenece un ciclo que no pertenece a ninguno
  invita a atarlo a ese agente. Ninguna instancia lo arrastra: los cargos del sistema viven en el
  paquete y uno propio nunca lo tuvo obligatorio.

### Añadido

- `ops agents list --own` y `--system`. Una empresa mantiene sus cargos, no los del catálogo: sin
  poder acotar la lista, su ciclo tendría que recorrer 48 para encontrar el suyo y chocar 47 veces
  con la negativa de `learn`.
- `AGENTS.md` de una instancia explica el ciclo de sus propios cargos: por comando, sin cron
  —activarlo en su repositorio es decisión suya— y con `learning/AUTOMATION.md` propio si su profesión
  no existe fuera de la empresa.

## [0.13.0] - 2026-08-16

### Añadido

- **El ciclo de aprendizaje se cierra solo hasta la firma, y sigue solo después de ella.** Faltaban
  las dos mitades entre «recomendación» y «contrato actualizado`»:
  - `/agent-propose <cargo>` escribe el cambio concreto —el texto exacto, archivo por archivo— y lo
    contrasta contra los casos vigentes. Antes la propuesta llegaba con «Cambio propuesto: por
    definir», y nadie firma una intención.
  - `/agent-promote <cargo>` aplica una propuesta **ya firmada**, registra en `HISTORY.md` y manda a
    correr los casos. Dos candados: se niega si «Aprobación humana» no tiene responsable —un agente no
    se autoriza a sí mismo— y exige verificar, porque aplicar sin correr los casos deja un contrato
    cambiado sin saber si se sostiene.
- Aplica **prosa, no un parche**, a propósito: un parche envejece si alguien toca el archivo mientras
  la propuesta espera firma. El costo es que aplicar exige criterio, y por eso toda desviación se
  escribe en la propia propuesta: quien firmó tiene derecho a saber qué se aplicó de lo que firmó.
- Investigación **semanal** en el cron, además de la consolidación mensual. Corre sólo si el
  repositorio declara `ANTHROPIC_API_KEY`; sin ella se saltea entero en vez de abrir un PR por cargo
  con un informe vacío. El prompt no vive en el cron: lo declara cada cargo en su
  `learning/CODEX_AUTOMATION.md`, así que un cargo nuevo trae su investigación sin tocar el workflow.

### Nota

El cron es de Cauce, no de las instancias: una empresa no lo recibe. Su ciclo de aprendizaje es por
comando, y activarlo en su propio repositorio es decisión suya.

## [0.12.0] - 2026-08-16

### Cambiado

- **`qa-engineer` incorporó su primera propuesta aprobada.** Aditiva en cuatro archivos: cinco fuentes
  nuevas, oráculos probabilísticos para sistemas de IA, transparencia de contenido generado y plazos
  regulatorios en el contrato, sus métodos en el modelo operativo, y la conducta prohibida
  `unreviewed_agent_test_repair` con el caso `07-agent-test-repair.md` que la pone a prueba. **7 de 7
  casos pasan** contra el contrato nuevo.
- El recorrido de evaluación ya no le pone tope de extensión a la respuesta que mide. Un tope de doce
  líneas hacía fallar dos casos que pasan: un comportamiento esperado puede exigir seis elementos
  —«versión, entorno, datos, pasos, frecuencia y artefactos»— y cuatro de esos no entran. El caso define
  qué hace falta; el arnés no puede maniatar la respuesta y después contar lo que falta.

## [0.11.1] - 2026-08-16

### Corregido

- El resultado de los casos es una advertencia de `evaluate`, no un error que corte la integración.
  Correr los casos exige un modelo y CI no lo tiene: gatear con un resultado viejo obligaría a pagar
  una corrida para poder integrar, y volvería a fallar cada vez que el contrato cambie. Quien falla
  fuerte es el recorrido que sí los ejecuta.

### Cambiado

- `qa-engineer`: **descartar no es verificar**. El contrato enseñaba a tratar el contenido externo como
  dato no confiable y no decía nada de verificarlo, así que el cargo rechazaba un documento externo en
  bloque sin preguntar quién lo publica, si hay versión oficial, qué alcance declara ni a qué versión
  aplica. Lo encontró la primera corrida de sus casos adversariales.

## [0.11.0] - 2026-08-16

### Añadido

- **Los 281 casos adversariales se ejecutan.** Existían desde el principio y nadie los corría:
  `evaluate` los contaba. Era una suite que sólo comprobaba que los archivos `.test.js` existieran.
  - `ops evaluate <cargo> --cases [--json]` los expone.
  - El recorrido `/agent-eval <cargo>` los corre: **quien responde nunca ve los comportamientos
    esperados** —si los viera, el caso mediría su capacidad de repetirlos, no su criterio— y quien
    juzga no es quien respondió.
  - El veredicto queda en `evaluations/results/<fecha>.md`, con la respuesta del cargo y la cita que
    sostiene cada comportamiento observado.
- `evaluate` informa si el cargo se corrió alguna vez y cómo le fue. No tenerlo es una advertencia, no
  un error: ejecutar cuesta y exigirlo en CI sería exigir red y credenciales. Un resultado que cubre
  menos casos de los vigentes **sí** es error: da una confianza que no tiene.

## [0.10.1] - 2026-08-16

### Corregido

- **La propuesta mensual consolidaba una sola línea de cada recomendación.** Es su única razón de
  existir: juntar lo que recomendaron los informes de la semana. Con la bandera `m` el `$` casa fin de
  *línea*, así que la búsqueda no ávida cortaba en el primer salto y una recomendación de diez líneas
  llegaba como una. El ciclo corría verde entregando casi nada.
- La comprobación de citas en las pruebas cortaba las rutas en el punto, así que verificaba la
  existencia de un archivo sin su `.md` y nunca lo detectó.

## [0.10.0] - 2026-08-15

### Cambiado

- **El motor llega siempre como dependencia. Se retiró el modo copia.** `--engine copy|dependency` ya
  no existe: `init` declara `@ingeniomaps/cauce` en el `package.json` del repo ops, creándolo si hace
  falta.

  La copia en `.ops/` existía para no exigirle npm a un repo de Go, Python o Rust. Dejó de tener
  sentido cuando el repo ops pasó a ser un **sidecar**, hermano de los repos de producto: declarar npm
  ahí no le impone un stack a ninguno. Y Node hace falta igual —el motor, los guards y los workflows
  son JavaScript—, así que la copia sólo ahorraba un `package.json` de seis líneas a cambio de 5 MB y
  763 archivos en la historia de la empresa, y de no tener cómo enterarse de que salió una versión
  nueva: sin npm no hay `npm outdated`.
- Los tres resolutores en cascada —`tools/ops.js`, `run-hook.sh` y el motor— pasan de tres caminos a
  dos. Menos superficie donde esconder un caso raro.
- Una instancia que arrastra `.ops/` **no se toca**: `upgrade` avisa que Cauce ya no lo distribuye y
  dice qué correr. Borrarlo por su cuenta la dejaría sin motor.
- El `$schema` de `ops.config.json` ya no depende del modo.

## [0.9.2] - 2026-08-15

### Corregido

- Una instancia ya no recibe un `.github/workflows/` vacío. `init` lo copiaba salteando los dos
  únicos archivos que existen —`ci.yml` valida el toolkit y el ciclo de aprendizaje dejó de
  distribuirse en 0.4.0—, así que creaba dos directorios y no ponía nada adentro.

## [0.9.1] - 2026-08-15

### Corregido

- **Una instancia no recibía `.gitignore`.** En modo dependencia eso significa commitear
  `node_modules/` —el paquete entero— dentro del repo de la empresa, y sus credenciales con él. El
  archivo viaja sin punto y se restituye al copiar: npm **excluye** cualquier `.gitignore` de un
  tarball, así que puesto con punto habría existido en el repo del toolkit y desaparecido para todo
  consumidor real.
- **`AGENTS.md` y `Makefile` se actualizan con el toolkit.** Son las reglas que un agente obedece y
  los atajos que envuelven al CLI; ninguno tiene una línea de la empresa. Envejecidos mienten: el
  `AGENTS.md` de una instancia seguía describiendo `automatization/runners/` como runtime del
  proyecto cuatro versiones después de que `upgrade` lo retirara.

## [0.9.0] - 2026-08-15

### Añadido

- `ops integration disable <raíz> <proveedor>`: el par que faltaba. Apagar **no desinstala** —
  `integrations/<proveedor>/` puede tener snapshots y borradores de la empresa, y borrarlos para
  desconectar una integración sería perder trabajo suyo. El andamiaje queda y volver a encender no
  pierde nada.
- `integration enable` sólo pide lo que falta: reencender un proveedor ya configurado no manda a
  completar un archivo que la empresa terminó hace meses.

## [0.8.2] - 2026-08-15

### Corregido

- **`integration list` mostraba encendido un proveedor que se habría negado a sincronizar.** Hay dos
  interruptores y `sync` exige los dos: el del registro dice que el proveedor está conectado al
  proyecto, el suyo propio que su configuración está terminada. La lista leía sólo el primero, así
  que después de `enable` decía `● jira` y `sync` contestaba `jira está deshabilitado`. Ahora
  distingue los tres estados y `enable` no promete más de lo que hizo.
- `integration enable` no falla si el andamiaje ya está. Habilitar no es inicializar: una instancia
  que lo arrastra de una versión anterior —o que ya tiene snapshots— sólo quiere el interruptor, y
  recibía un error pidiéndole un directorio vacío. Ahora repone lo que falte, conserva lo que haya y
  enciende el registro.

## [0.8.0] - 2026-08-15

### Cambiado

- **El andamiaje de una integración se materializa al habilitarla, no antes.** Una instancia recibía
  32 KB de Jira —configuración, `staging/`, `proposed/`, tres READMEs— para un proveedor con
  `enabled: false` que quizá no use nunca, y que además nadie actualizaba después. Ahora llega con
  `ops integration enable <raíz> <proveedor>`, que copia el andamiaje y enciende el registro.
- `check` valida sólo los proveedores habilitados. Un proveedor registrado y apagado no tiene
  andamiaje, y exigirle configuración era pedirle a la empresa que mantenga lo que no usa.
  Nombrarlo explícitamente sí lo valida, que es como se comprueba antes de encenderlo.
- Una instancia existente conserva lo suyo: `integrations/<proveedor>/` puede tener snapshots y
  borradores de la empresa, así que no se retira nada.

### Corregido

- `integrations/README.md`, `integrations/AGENTS.md` y `organization/README.md` se actualizan con el
  toolkit. Los escribe Cauce y envejecían para siempre en cada instancia.

## [0.7.5] - 2026-08-15

### Corregido

- `learn` y `evaluate` resuelven la raíz ops como el resto de los comandos. Quedaron con `cwd` cuando
  los demás pasaron a usarla: invocados desde la carpeta de la compañía —lo normal en sidecar— no
  encontraban ningún cargo.
- **`evaluate` le exigía a una empresa un archivo del toolkit.** `learning/CODEX_AUTOMATION.md`
  documenta cómo corre nuestra automatización de aprendizaje; pedírselo a quien escribe un cargo
  propio era pedirle contabilidad interna nuestra. Ahora sólo se exige a los cargos del sistema: el
  cargo de una empresa necesita contrato, fuentes e historia, no nuestro andamiaje.

## [0.7.4] - 2026-08-15

### Corregido

- **Una bandera antes del último posicional se comía su lugar.** `agents list --json` tomaba `--json`
  como la raíz ops y devolvía `[]` sin error: no había forma de distinguir "no hay cargos" de "te
  contesté con nada". Lo destapó un agente del recorrido de `team`, que pedía ese JSON para resolver
  dónde vive cada cargo y se quedaba sin dato.
- La ruta del contrato de cada cargo es obligatoria en el manifiesto que arma `team`. Siendo opcional
  el agente la omitía, la etapa caía al camino de respaldo y salía a buscar el archivo igual.
  Y lleva el prefijo de la raíz ops: `agents list` las imprime relativas a ella y las etapas corren
  desde otro lado, así que sin prefijo la ruta tampoco resolvía.

## [0.7.2] - 2026-08-15

### Cambiado

- **`team` gastaba un cuarto del recorrido en transcribir salida determinista de un CLI.** Dos agentes
  resolvían el equipo y leían su manifiesto; ahora es uno. El segundo además leía `organization/`
  "como contexto para etapas siguientes", y cada etapa es un agente nuevo con su propio contexto: esa
  lectura no llegaba a ninguna parte y sólo costaba tokens.
- Cada etapa recibe la ruta exacta del contrato de su cargo. Antes le pasaban
  `agents/<tipo>/<slug>/SKILL.md` —con el `<tipo>` literal— y desde 0.4.0 el catálogo ni siquiera está
  en el proyecto, así que el agente salía a buscarlo antes de poder empezar.

## [0.7.1] - 2026-08-15

### Corregido

- **Los cuatro workflows reventaban en su primera línea y nunca habían corrido.** Resolvían su raíz
  con `process.env`, y el runtime de workflows es un sandbox que no expone `process`: `ReferenceError`
  antes de ejecutar nada. `/team` y `/autobuild` —el centro del producto— estaban muertos, y los tests
  no lo veían porque leían los archivos como texto en vez de ejecutarlos.

  La raíz ahora viaja escrita en el archivo, que es lo que `automation install` ya sabía completar, y
  es relativa a donde se abre la herramienta.
- **Y al arreglar eso, `team` y `autobuild` morían en la línea de cierre**: llamaban a un `finish()`
  que el runtime tampoco trae. Peor que el anterior, porque ocurría *después* de gastar cada etapa.
- Dos tests recorren los workflows: uno prohíbe lo que el sandbox no expone, otro falla si se llama a
  algo que ni el runtime da ni el archivo define.

### Cambiado

- Los workflows de integración se invocan con argumentos en vez de variables de entorno, que tampoco
  existen ahí: `/integration-sync jira` y `/integration-promote jira KEY-123`.
- Un test comprueba que ningún workflow use `process`, `require`, `Date.now` ni `Math.random`. El
  sandbox los prohíbe, y usarlos no falla en una rama rara: impide que el archivo arranque.

## [0.6.1] - 2026-08-15

### Corregido

- **El bridge de Antigravity negaba cada llamada a herramienta.** Resolvía la raíz ops subiendo por el
  árbol, y en sidecar es un *hermano* de los repos de producto: no la encontraba y, como falla cerrado,
  bloqueaba todo. Ahora `automation install` le deja escrito dónde quedó, con el recorrido hacia arriba
  como respaldo.
- **`automation install antigravity` copiaba el plugin y lo dejaba inerte.** Antigravity exige un
  registro explícito —`agy plugin install`—, así que los archivos quedaban en su lugar, `doctor` daba
  verde y no se ejecutaba nada. `install` ahora dice el comando exacto y `doctor` avisa si falta, sin
  tocar por su cuenta la configuración global del usuario.

## [0.6.0] - 2026-08-15

### Cambiado

- **Gemini deja de ser el runner sin guards.** Gemini CLI ganó hooks y skills nativas, y el adaptador
  seguía tratándolo como si no los tuviera: los guards se pedían como prechecks manuales —o sea,
  nadie lo detenía— y los 47 cargos no llegaban. Ahora recibe hooks reales en `.gemini/settings.json`
  con sus propios eventos (`BeforeTool`, `AfterAgent`) y variable (`$GEMINI_PROJECT_DIR`), y el
  catálogo completo en `.gemini/skills/`.
- `GEMINI.md` avisa de dos cosas que sólo se ven usándolo: Gemini **desactiva los hooks si la carpeta
  no es de confianza**, y `gemini hooks migrate --from-claude` reescribe `settings.json` entero y se
  lleva puesta el resto de la configuración.

## [0.5.5] - 2026-08-15

### Corregido

- Los comandos de Gemini y las skills de Antigravity también resuelven sus rutas contra la carpeta
  donde se abre la herramienta. Quedaron afuera al marcar el resto: los `.toml` mandaban a leer
  `planning/PROTOCOL.md` y las skills `AGENTS.md`, que desde la raíz de la compañía no existen.
- Un test recorre todo lo que un adaptador copia y falla si una ruta da por sentado dónde se instala.
  Revisando archivo por archivo se escapó tres veces; ahora cubre también lo que se agregue después.

## [0.5.4] - 2026-08-15

### Corregido

- `upgrade` conserva el modo de los archivos que entrega. `tools/ops.js` tiene shebang y quedaba sin
  permiso de ejecución, con el cambio de modo apareciendo en el diff de cada empresa.

## [0.5.3] - 2026-08-15

### Corregido

- `tools/ops.js` se actualiza con el toolkit. Es el shim por donde entra cada comando, no tiene una
  línea de la empresa —dice él mismo que no se edita— y sin declararlo envejecía para siempre: una
  instancia creada antes seguía sin exportar la raíz ops, así que `agents list` y `team list`
  devolvían vacío al invocarse desde la carpeta de la compañía.

## [0.5.2] - 2026-08-15

### Corregido

- **`upgrade` registraba mal lo que entregaba, y eso trababa el siguiente.** El registro se anotaba
  ruta por ruta releyendo el manifiesto del disco en cada una, así que la última anulaba a todas las
  anteriores: tras un `upgrade` casi todos los digests quedaban viejos y la actualización siguiente
  los leía como ediciones de la empresa. Un callejón sin salida sin que nadie hubiera tocado nada.

  Una instancia que ya quedó con digests viejos se destraba con `cauce upgrade --force`: el contenido
  en disco y el del paquete son el mismo, así que no se descarta nada real.

## [0.5.1] - 2026-08-15

### Agregado

- **Codex recibe su `AGENTS.md`.** Era el único runner sin archivo de instrucciones: se llevaba los
  guards y nada más, así que podía ser detenido pero no sabía que existía un protocolo, un catálogo de
  cargos ni equipos. No existe un `CODEX.md`: `AGENTS.md` es el nombre que Codex lee, compartido entre
  herramientas, y por eso en modo embedded no se instala —el de la empresa ya está ahí y manda—.

### Corregido

- **Las rutas de los adaptadores se resuelven contra la carpeta donde se abre la herramienta.** Al
  mover la instalación a la raíz de la compañía quedaron apuntando al lugar equivocado: `@AGENTS.md`
  y `@planning/PROTOCOL.md` no resolvían, y los workflows buscaban `planning/` donde no estaba. Ahora
  cada fuente marca el lugar con `{{OPS_DIR}}` y `install` lo completa; `doctor` compara contra lo
  mismo que `install` escribe.
- `tools/ops.js` exporta la raíz ops, que ya calculaba y no usaba. Invocado desde la carpeta de la
  compañía —`node <empresa>-ops/tools/ops.js team list`—, `agents list` y `team list` resolvían contra
  el cwd y devolvían **vacío en vez de fallar**. `team` además no aceptaba una raíz de ningún modo.

## [0.5.0] - 2026-08-15

### Cambiado

- **Los adaptadores de runner y los workflows tampoco se copian al proyecto.** Cierran el mismo
  criterio que ya rige para cargos y equipos: los lee el motor, no la empresa. `automation install`,
  `check` y `doctor` los resuelven desde la dependencia npm, o desde `.ops/` cuando el repo no usa
  npm. Nadie los editaba —la lista de runners es cerrada, así que una empresa ni siquiera podía
  agregar el suyo— y `automatization/workflows/` estaba además duplicado dentro de cada instancia,
  idéntico a lo que `automation install` deja en `.claude/workflows/`.
- `upgrade` retira `automatization/runners/` y `automatization/workflows/` de las instancias que los
  arrastran de una versión anterior. Un guard propio en `automatization/hooks/` no se toca.
- `automatization/hooks/` se queda en el proyecto, y esto no es una excepción arbitraria: la
  configuración de cada runner nombra cada guard por ruta literal y no sabe resolver en cascada. Ahí
  es también donde una empresa agrega el suyo.

- **En modo `sidecar`, `automation install` instala en la carpeta de la compañía, no dentro del repo
  ops.** El repo ops coordina varios repos de producto y es hermano de ellos, así que instalar el
  runner adentro lo dejaba sin ver una sola línea de código: el dev que abría su herramienta donde
  está el código no tenía guards, ni cargos, ni workflows. Las rutas de guards y los punteros de cada
  cargo se reescriben con el prefijo del repo ops al instalar.

### Corregido

- **Una mejora del toolkit en el wiring ahora llega a un runner ya instalado.** `install` conservaba
  cualquier archivo existente, así que un workflow o un `CLAUDE.md` mejorado río arriba no llegaba
  nunca. Peor: si el archivo difería, `install` fallaba —`existe y difiere de la fuente canónica`— y
  `doctor` lo reportaba como error, dejando a la empresa sin salida salvo borrar a mano. Ahora el
  registro de entrega distingue las dos cosas que antes se veían iguales: lo que la empresa editó se
  conserva o detiene la instalación, y lo que sólo cambió río arriba se actualiza.
- `automation check` compara los guards de la instancia contra los del paquete. Existir y ser
  ejecutable no alcanzaba: un guard viejo no falla, **deja de proteger en silencio**, y `check`,
  `doctor` y `upgrade` daban verde igual porque sólo miraban el número de versión. Ahora bloquea la
  instalación del runner y dice qué correr; distingue además el guard que la empresa editó del que
  simplemente quedó atrás.
- `upgrade` recuerda reinstalar los runners: el wiring vive fuera de la instancia y lo escribe otro
  comando, así que sin el aviso una mejora se quedaba en el paquete.
- Los guards resuelven la raíz ops por su cuenta. `findOpsRoot` sólo sube por el árbol, y en sidecar
  la raíz ops es un *hermano* de los repos de producto: desde ahí no la encontraba, devolvía vacío y
  el guard permitía todo **en silencio**. `run-hook.sh` ya sabía dónde vive; ahora lo exporta.
- `automatization/README.md` y `automatization/AGENTS.md` se actualizan con el toolkit. Los escribe
  Cauce y envejecían en cada instancia: después de retirar `runners/` y `workflows/`, el README de la
  empresa seguía explicando cómo usar dos carpetas que ya no existían.
- El registro de entrega olvida lo que ya no está en disco. Sólo crecía: al retirar una ruta dejaba
  su digest para siempre, y el día que un nombre se reutilizara la entrega nueva se habría leído como
  una edición local y detenido la actualización.

## [0.4.1] - 2026-08-15

### Corregido

- Al mover los equipos al paquete se fue con ellos su documentación, así que una instancia nueva se
  quedaba sin `teams/README.md` ni la plantilla: sabía que podía escribir equipos propios, pero no
  con qué contrato. Lo que le habla a la empresa viaja con la instancia aunque la colección no.

## [0.4.0] - 2026-08-15

### Cambiado

- **Ni el catálogo de cargos ni los equipos se copian al proyecto.** Son definiciones que consume el
  motor: se resuelven desde la dependencia npm, o desde `.ops/` cuando el repo no usa npm. Las reglas
  y decisiones de `planning/*/system/` sí siguen materializadas, porque la empresa las lee y las cita
  en su propio repositorio.
- **El catálogo de cargos ya no se copia al proyecto.** Se resuelve desde la dependencia npm, o desde
  `.ops/agents/` cuando el repo no usa npm. Una instancia pasa de ~950 KB a ~480 KB y su `git diff`
  muestra sólo lo que la empresa escribió.
- El ciclo mensual de aprendizaje dejó de distribuirse: investiga cómo evoluciona una profesión, y eso
  es igual para todas las empresas. Corriéndolo en cada instalación, cuatro empresas producían cuatro
  investigaciones casi idénticas del mismo tema, cada una peor que una hecha bien. Ahora vive sólo en
  el repositorio del toolkit y llega actualizando la dependencia.
- `learn` y `learn --proposal` fallan con explicación si se corren sobre un cargo del catálogo dentro
  de una instancia: escribirían en el paquete y se perderían en el próximo `npm ci`.
- Las fuentes `local://` salieron de los 47 cargos. Lo que un cargo debe saber de una empresa vive
  ahora en `organization/roles/<slug>.md`, y los 47 lo citan.

### Corregido

- `upgrade` no actualizaba el catálogo de cargos, o sea el 75% del paquete: un cargo nuevo o mejorado
  nunca llegaba a un proyecto ya inicializado. Ahora se refresca como el resto del sistema, con una
  excepción precisa: `learning/` dentro de cada cargo es del proyecto y no se toca, porque los
  informes acumulados son lo único que no se puede reponer desde el paquete.

- El `$schema` de `ops.config.json` apuntaba siempre a `.ops/engine/`, una ruta que no existe cuando el
  motor viene como dependencia. Ahora se escribe según dónde quedó el motor.

## [0.3.0] - 2026-08-15

### Añadido

- Equipo `feasibility-review`: tres etapas para decidir si una intención vale el esfuerzo con la
  evidencia que ya existe. Recomendar investigar es un resultado legítimo, no una falla.
- Equipo `incident-review`: revisión posterior de un incidente ya contenido. **No responde incidentes
  en vivo** y su documentación lo dice: un recorrido de agentes no está de guardia, no accede a
  producción y no decide bajo presión con información parcial.
- Los equipos declaran su salida en `outcome`: `epic` deja una épica candidata en `roadmap/`;
  `report` deja un informe en `planning/reports/` y sus seguimientos en el INBOX, sin promover.
  Ninguno de los dos promueve al BACKLOG.
- `/team` acepta el equipo por prefijo —`/team incident-review: se cayó el checkout`— confirmándolo
  contra los que existen, así que un texto con dos puntos no dispara un equipo inventado.
- `teams/000-template.md` y `teams/README.md`: era la única colección que se distribuía sin plantilla,
  lo que obligaba a copiar un manifiesto de nueve etapas y adivinar el esquema.
- `autobuild` ejecuta cada fase bajo el contrato del cargo que la posee, en vez de pedirle criterio
  genérico a un agente sin rol. Los dueños por defecto son deterministas; los condicionales
  —seguridad, privacidad, sre, ux— entran por riesgo, plataforma y alcance, nunca por rutina, y el
  reparto queda registrado en el WIP para poder auditar quién revisó qué.
- Cargo `growth-marketer`: adquisición y activación con economía unitaria explícita. Cubre la
  decisión que no tenía dueño —dónde invertir para adquirir y si funcionó—, entre posicionamiento
  (`product-marketing-manager`) y proceso de ingresos (`revenue-operations-manager`).
- Cargo `finops-engineer`: costo de operar visible y atribuido, incluido el gasto en modelos de IA,
  que escala con el contexto arrastrado y no con la cantidad de usuarios.
- `agents list --json` incluye la ruta resuelta de cada cargo, para que quien lo consuma no
  reconstruya dónde ganó la precedencia.

### Cambiado

- Las etapas de un equipo declaran si son de `discovery` o de `delivery`. `/team` recorre sólo las de
  descubrimiento y propone; `autobuild` ejecuta la entrega, y sólo después de la promoción humana.
- `agents/coordinators/`, `agents/specialists/` y `agents/workflows/` se eliminaron: eran directorios
  vacíos que prometían una taxonomía sin contenido, y `workflows` además colisionaba en nombre con
  `automatization/workflows/`. El mecanismo no cambia: cualquier directorio bajo `agents/` sigue
  siendo un tipo válido y se reconoce cuando tiene contenido.

### Corregido

- `/team` recorría todas las etapas del manifiesto, incluida la de construcción: un recorrido de
  descubrimiento llegaba a pedir un incremento funcionando, o sea código escrito antes de que la
  épica existiera y antes de que nadie la aprobara.
- Invocado como slash command, `/team` recibía la intención como texto y buscaba `args.intent`, así
  que se detenía antes de su primera etapa en la forma más obvia de llamarlo.
- `upgrade` sugería mover un guard editado "junto a `system/`", un mecanismo que no existe en
  `automatization/hooks/`. Ahora explica las tres vías que sí funcionan: agregar un guard propio,
  quitarlo de la configuración del runner para desactivarlo, o descartar el cambio con `--force`.
- `upgrade --force` descartaba ediciones locales sin dejar rastro; ahora las lista.

## [0.2.0] - 2026-08-14

### Añadido

- `cauce upgrade` actualiza una instancia sin tocar nada del proyecto, con `--check` para saber si hay
  algo pendiente y `--force` para descartar ediciones locales del runtime. Antes no existía forma de
  actualizar: cada proyecto quedaba congelado en la versión con la que nació.
- La frontera `system/` se extendió a `planning/rules/`, `teams/` y `agents/`. Un archivo propio con el
  mismo nombre, ID o slug reemplaza al del sistema, y `check` lo reporta como override explícito.
- Los 45 cargos del catálogo se instalan en el runner como skills invocables por nombre. Antes viajaban
  a cada proyecto sin que ningún runner los usara.
- `cauce agents list` resuelve la precedencia del catálogo y marca cuáles son propios del proyecto.
- El motor se declara como dependencia npm cuando el repo ya usa npm, y se copia sólo cuando no.
- Las instancias registran `cauceVersion` en `ops.config.json`.
- Workflow `/team`: recorre las etapas de un equipo exigiendo cada exit gate y deja una épica
  candidata en `roadmap/`. Es el espejo de `/autobuild`, que ejecuta trabajo ya aprobado. Nunca
  promueve al BACKLOG: esa firma sigue siendo humana.
- `team show --json` expone el manifiesto completo para que un workflow lo ejecute sin parsearlo.

### Cambiado

- Los cargos que trae Cauce se movieron a `agents/roles/system/`. Un proyecto que quiera su propia
  versión de un cargo la escribe en `agents/roles/` con el mismo slug.
- Las composiciones de equipo se movieron a `teams/system/`.
- `tools/ops.js`, el wrapper de hooks y el bridge de Antigravity resuelven el motor en cascada:
  dependencia npm, copia local, repositorio del toolkit.
- `automation install` reemplaza los guards que este mismo toolkit había registrado sueltos por el
  grupo que los cubre. Sin esa poda, una instalación previa ejecutaba cada guard dos veces por
  herramienta; con `verify` eso significaba correr la suite de tests dos veces en cada commit.

### Corregido

- `automation install` funcionaba sólo si el motor estaba copiado: en modo dependencia fallaba con
  "falta engine/hooks/run.js". Los tres puntos de entrada resuelven ahora la misma cascada.
- `upgrade` ya no borra archivos que el proyecto agregó al runtime, como un guard propio.
- El README de `automatization/runners/` que recibe un proyecto es el que le habla al proyecto, no el
  que documenta el contrato de adaptadores; antes llegaba uno u otro según se usara `--force`.
- `team check` distingue entre un agente inexistente y uno ambiguo.

## [0.1.0] - 2026-08-14

Primera publicación como `@ingeniomaps/cauce`.

- CLI determinista de planificación: `init`, `check`, `tree`, `context`, `archive`.
- Protocolo agnóstico al runner con adaptadores para Claude, Codex, Gemini y Antigravity.
- Once guards portables agrupados por evento, un proceso por llamada de herramienta.
- Catálogo de 45 cargos con evaluaciones y ciclo de aprendizaje mensual.
- Integraciones por staging de sólo lectura, con Jira como primer proveedor.
