Hooks y guards

Todo lo de esta página está apagado hasta que tú lo enciendas, y la mayor parte aconseja en lugar de denegar.

Qué es#

Los guards son chequeos que puedes correr a mano, desde un hook de git o desde un hook de IDE:

Comando Qué deniega
orch guard dup <file> Un archivo que duplica un símbolo compartido; además reporta cuerpos de función copiados
orch guard code-lang [root] Identificadores y cadenas visibles que no corresponden a workspace.code_lang
orch guard lang [root] Documentos que no corresponden a workspace.docs_lang
orch guard ddl [files...] Una migración o CREATE TABLE sin su seeder o repositorio acompañante
orch guard tracker [path] Una tabla de estado TRACKER que rompe T1–T4: status inválido, SPEC sin .md, VERIFIED sin evidencia, volcado narrativo

Hay más: comment, lock, add-all, shared-brand, control-bytes, artifacts, encoding, más seis guards que vienen encendidos por defecto en los perfiles shared/strict: docs, signature, upsert, env-drift, shared-shadow, template-drift.

.orch/gates/user/manifest.json deja que un taller agregue sus propios chequeos deny/require/cmd; corren en los mismos puntos de sellado, hook de git y hook de IDE que los guards de arriba (orch guard rules). Las escrituras de código del propio Chair también tienen un límite: la 3ra recibe un aviso que nombra dispatch_worker, la 4ta se deniega en los perfiles shared/strict; la regla completa está en la guía how-to. Cualquier guard que bloquea hace fallar la tarea despachada y hace que worktree_promote la rechace.

Los hooks son puntos de entrada a tu harness, cada uno con su propia clave de consentimiento en orch.yaml:

Clave Dispara en Qué hace
context.hooks.post_tool PostToolUse de Claude Code, tool.execute.after de OpenCode Encoge el resultado de una herramienta antes de que caiga en la ventana de contexto
context.hooks.post_tool postToolUse de cursor-agent (Shell, Read, Grep) No puede encoger el resultado — responde con additional_context nombrando la herramienta de Orchemax que habría costado menos
context.hooks.pre_compact PreCompact de Claude Code Corre el condensador de orch memory condense sobre la memoria del taller antes de que el harness compacte
guard.dup.mode PostToolUse de Claude Code y postToolUse de cursor-agent, ambos en Write/Edit Corre orch guard dup sobre el archivo que acaba de caer y devuelve el texto de la denegación como contexto. Nunca deniega la escritura — ver abajo

Las claves context.* son false por defecto. El gate de duplicados de la vía de escritura no es una de ellas: va sobre guard.dup.mode, que viene en advisory, y lo instala el mismo orch guard wire sin importar el consentimiento de contexto.

El gate de duplicados en la vía de escritura#

El ciclo 7 de la corrida end-to-end R5 despachó cinco workers reales y el gate de duplicados no corrió sobre ninguna de sus escrituras: orch guard wire --cursor instala un hook de shell, y un Write de Cursor nunca pasa por una shell. Eso se corrige en tres capas, y solo la primera depende de que el CLI tenga hooks:

  1. Al escribir — Claude Code y cursor-agent reciben el texto de la denegación como additionalContext / additional_context mientras el agente sigue en su ciclo.
  2. En la verificación — todo worker despachado, con hooks o sin ellos, incluidos Copilot CLI y agy, cuyos harness Orchemax no puede conectar. Con guard.dup: advisory se sella guardrails: warn; con block se sella guardrails: fail y el despacho responde status: failed con las líneas de la denegación como error_head.
  3. Al promover — worktree_promote rechaza una sesión cuya verificación carga una denegación.

El escaneo también lee el worktree de cada sesión que sigue corriendo, así que dos workers escribiendo el mismo bloque al mismo tiempo se detectan mientras ambos están en vuelo.

Qué permite cada CLI#

Un hook solo puede ahorrar lo que su harness le deje reemplazar, así que la respuesta honesta cambia por CLI:

CLI Superficie de hooks ¿Puede reemplazar el resultado de una herramienta?
Claude Code PostToolUse, PreCompact (~/.claude/settings.json) Sí — hookSpecificOutput.updatedToolOutput, mientras el reemplazo conserve la forma de salida de esa herramienta
OpenCode plugin tool.execute.after Sí
cursor-agent .cursor/hooks.json del proyecto (beforeShellExecution para el guard, postToolUse para contexto) No, no para Shell/Read. Su único campo de reemplazo es updated_mcp_tool_output, documentado "For MCP tools only", y el payload de Read trae content_length, no el contenido del archivo. Lo que sí puede hacer un hook es inyectar additional_context, que llega como un <system_reminder> justo después del resultado
Copilot CLI los hooks viajan dentro de plugins (copilot plugin), no hay archivo de hooks de proyecto Orchemax no lo conecta
agy sin mecanismo de hooks (agy --help) No — ahí la compresión es solo por skill
Codex CLI — No puede reemplazar la salida de una herramienta

Para los CLIs sin ruta de conexión, la palanca es el skill orch-worker: abre con las reglas de lectura (code_snippet en vez de abrir un archivo, trace_path/impact para llamadores, context_externalize para salidas grandes), y usage_status {view: "workers"} reporta orch_read_tools por sesión, así la adopción es un conteo medido y no una afirmación.

Conexión:

orch guard wire            # desde guard.profile, o --git / --claude / --cursor
orch guard unwire          # agrega --git para quitar también los hooks de git
orch guard hooks install   # pre-commit + pre-push de git, marcados por orch
orch guard hooks status
orch guard profile         # muestra, o fija: lite | shared | strict | bring | none

El guard de referencias colgantes#

Al sellar, Orchemax lee cada ruta que el change set borró o renombró y busca esa ruta en el resto del árbol trackeado — la ruta relativa completa, o su forma dir/basename. Un hit en una línea de código, deploy o config hace fallar el sellado, listando file:line → ruta vieja; un hit en una línea que es solo comentario o dentro de un doc .md es solo consultivo. Esto es exactamente lo que rompió un deploy alguna vez: deploy.ps1 seguía invocando un script borrado a mano, y nada lo avisó hasta que el deploy corrió. orch harness scan reporta la misma forma para rutas que git ya no encuentra en disco (borradas, sin git rm todavía) que un archivo trackeado sigue llamando, y por separado marca un core.hooksPath que apunta a un directorio que ya no existe — los hooks configurados ahí dejan de disparar en el momento en que el directorio desaparece, en silencio.

Anular un solo hallazgo#

Un guard equivocado sobre una línea no debería costarte los veinte que sí acertaron. git commit --no-verify omite todos los guards de golpe y no deja rastro, así que hay una salida más angosta: una marca, en la línea señalada o sola en la línea inmediatamente anterior.

secret := loadFixtureKey() // orch:allow secrets — clave rotada, queda para el test de replay

La marca se busca como subcadena, así que #, --, //, <!-- -->, { } y <# #> la llevan igual en un taller políglota. Cuatro reglas, todas verificadas por orch guard allow:

Regla Por qué
El guard se nombra como lo expone orch guard. Anulables: comment-prose, litter, minimal-code, secrets, signature Son heurísticas y juicios de estilo, donde un deny equivocado es culpa del guard
reproduce, ddl, dup, code-lang, lang y control-bytes no son anulables Una marca ahí es mentirte a ti mismo sobre el cambio, no sobre el guard
Un motivo de al menos 12 caracteres es obligatorio Una marca sin motivo no silencia nada y es un hallazgo en sí misma, que es lo que evita que la salida se vuelva costumbre
Una sola línea: la señalada, o sola en la de arriba. Un hallazgo de litter nombra la existencia del archivo, así que su ancla son las dos primeras líneas de ese archivo Nunca un archivo entero ni una carpeta: la anulación es por ocurrencia

La marca se respeta donde los guards realmente bloquean: los hooks de git y el sellado del worker. Un verbo corrido a mano (orch guard secrets x.go) igual lista el hallazgo anulado — un reporte que no bloquea nada reporta de más antes que ocultar, y orch guard allow es lo que responde si tu marca está bien escrita.

Cada marca aceptada queda en guard_events con veredicto override, y orch stats imprime overrides by gate. Ese conteo es el punto: un guard que la gente anula seguido es un guard que hay que afinar, y el número dice cuál en vez de dejarlo a la anécdota.

Novedad de 0.5.0: compuertas en el momento de la acción#

Cada llamada a una herramienta la juzgan hooks, y el commit de un worker corre las mismas compuertas que el sello final: salidas generadas sin añadir, ediciones del ledger, pruebas eliminadas, escrituras compartidas sin bloqueo y rojo primero. Un worker se entera de un error en segundos y no después de un sello completo; el sello se mantiene como respaldo.

Un comando que esperaría stdin para siempre (un intérprete o REPL sin argumentos, un heredoc vacío) se rechaza para todos los asientos, con la solución en una línea. Ver Versión 0.5.0.

Consultivo o bloqueante#

La distinción es por guard.

  • Consultivo — el escaneo de clones, por defecto. guard.dup.mode: advisory reporta lo que encontró y lo inyecta como contexto; la escritura sigue. Pon block y ese mismo hallazgo hace fallar la llamada con clone guard denied N duplicated block(s); pon off y ni mira.
  • Bloqueante — todos los demás guards. Un hallazgo se imprime en stderr y el comando sale con código distinto de cero, así que un hook de pre-commit detiene el commit.
  • Omisión suave — fuera de un taller vinculado, o cuando la configuración correspondiente no está fijada. orch guard lang sin workspace.docs_lang se omite en vez de adivinar.

Por qué existe#

Lecciones de campo, baratas de enunciar y caras de aprender. git add -A sobre una capa compartida se roba el trabajo en curso de otra sesión. Conversar con un modelo en un idioma mete ese idioma en identificadores y mensajes de error. Una migración sin seeder pasa la revisión y falla en producción. Nada de esto necesita un motor de políticas: necesita un chequeo en el momento en que se comete el error.

Son opt-in porque el taller es tuyo: orch init llama al kit inicial recommendations, not mandates.

Cómo se relaciona con el resto#

  • La misma batería de guardrails corre dos veces: desde el hook de pre-commit que instalaste, y otra vez al cerrar una sesión, sobre exactamente los archivos que esa sesión tocó. Esa segunda corrida es la mitad observada del reporte de verificación.
  • orch harness scan reporta qué herramientas de IA y superficies de doctrina alcanza a ver, antes de que decidas qué conectar.
  • Los perfiles de guard controlan qué conecta orch init: lite siembra documentos y skills sin hooks, none omite del todo las siembras de doctrina.

Aquí no hay nada que ejecutar#

Nada de esto corre si no lo conectas. Corre los guards a mano si eso es todo lo que quieres; orch guard wire es el interruptor que los hace valer.