Agentic QA · Instrucciones del repo
El agente no necesita todas las reglas del repo en cada turno. Necesita siempre las que lo atan, y el resto cuando el pedido lo pide. Por eso AGENTS.md es el núcleo que carga en cada sesión, cada tema vive en su archivo bajo .agents/instructions/, y un router fijo, reforzado por un hook, dice cuál leer y cuándo.
01 · Motivo · 2 de 14
Antes, AGENTS.md era un solo archivo que todas las sesiones cargaban entero, hiciera falta o no. Tenía dos costos, y el segundo no se veía.
Costo visible
Una charla sobre un selector pagaba la política de git, el caché del PBI y el contrato de los tres harnesses. Las secciones condicionales cargaban siempre, aunque la sesión no las usara.
Costo invisible
Codex corta las instrucciones del proyecto en el byte que cruza project_doc_max_bytes, sin avisarle al modelo. Medido al partir (ADR-0009): el archivo viejo pasaba más de tres veces ese tope y el corte caía dentro de §4, el mapa de carga de contexto. En Codex, todo lo que venía después nunca llegó al modelo.
Lo que sí sirve
En Claude Code un @archivo se expande entero al arrancar, y ni OpenCode ni Codex expanden imports. Lo que funciona en Claude Code, OpenCode y Codex por igual es que el modelo lea un archivo porque una instrucción se lo pide, y un hook que agrega texto en cada pedido. El diseño usa esas dos cosas.
Lo que Codex perdía después del corte: el contrato de harnesses, la tabla de skills, la resolución de herramientas, las variables, el PBI, KATA, git y la memoria. Los números exactos están en .context/ADR/ADR-0009-progressive-disclosure-of-instructions.md.
02 · Arquitectura · 3 de 14
Las flechas son lecturas a pedido: L0 nunca importa una sección. Las del dibujo son ejemplos; la lista real es la carpeta.
| Capa | Archivo y cuándo carga |
|---|---|
| L0 | AGENTS.md, en cada sesión y en cada host. OpenCode y Codex lo cargan nativo; Claude Code llega por CLAUDE.md, que es exactamente @AGENTS.md más un salto de línea. |
| L1 | Un archivo por tema en .agents/instructions/, a pedido: el router de L0 dice cuándo, y el hook lo recuerda con una línea ROUTE:. |
| L2 | Las references/ de cada skill, a pedido, cuando una sección o una skill las nombra. |
Una vez que aplica
Una sección obliga exactamente igual que AGENTS.md. Leerla a pedido cambia cuándo entra al contexto, no cuánto pesa lo que dice.
03 · Router · 4 de 14
Una tabla en L0, entre <!-- router:start --> y <!-- router:end -->, con cuatro columnas. Las filas son tipos de pedido, no features: la tabla no crece con el proyecto, crecen la sección y sus triggers:.
| Columna | Qué dice | Ejemplo (fila de git) |
|---|---|---|
| When the request involves | el tipo de pedido que dispara la fila | git: branch, commit, push, PR, conflict, strategy |
| Read | los archivos a leer: cada sección por su nombre de archivo entre backticks, que resuelve bajo .agents/instructions/. El primero es el ancla de la fila | agent-git.md |
| Was | el número de § que tenía ese contenido antes de partir, para que una cita §N siga resolviendo | §11 |
| Then | la skill o el archivo que se carga después | /git-flow-master |
Una sola tabla
El hook lee esta misma tabla del disco en cada pedido, junto con el frontmatter de cada sección. No hay copia generada: la tabla que lee el modelo y la que corre el clasificador no pueden divergir.
Marcadores sin costo en Claude
Claude Code saca los comentarios HTML de bloque antes de inyectar AGENTS.md, así que los dos marcadores no le cuestan nada al modelo. El hook los lee del archivo en disco.
04 · Protocolo · 5 de 14
Un párrafo al principio de L0 convierte el router en obligación. Sin sus frases, la tabla sería una sugerencia.
ROUTE: que inyecta el hook es obligatoria y gana sobre el criterio propio del modelo: se lee su archivo antes de actuar.ROUTE-OPTIONAL: no obliga: se lee uno de sus archivos solo si la tarea lo pide.ROUTE-PENDING: nombra un archivo obligatorio que sigue sin leerse: se lee antes del paso siguiente./clear la haya sacado del contexto.AGENTS.md.§N, en cualquier lado, nombra el heading numerado que se conserva textual en el archivo que el router lista para ella.La regla de edición (editar la sección dueña, nunca pegar su prosa de vuelta en AGENTS.md) no es parte del protocolo: vive en la cabecera de AGENTS.md y en "Editing rules" de .agents/instructions/README.md.
05 · Hook · 6 de 14
El router en L0 depende de que el modelo lo consulte. El hook no: clasifica cada pedido con código y deja escrito qué leer. Es el mismo emisor que ya pone la línea AGENT IDENTITY:: .agents/hooks/personality-reinject.mjs.
| Host | Cómo llega la línea |
|---|---|
| Claude Code | UserPromptSubmit corre el emisor; SessionStart compact o clear lo re-arma; PostToolUse sin matcher recuerda una ruta sin leer. |
| Codex | Lo mismo salvo el recordatorio, y solo en un repositorio de confianza. |
| OpenCode 1 | chat.message clasifica, la system transform empuja las líneas, session.compacting re-arma. |
| OpenCode 2 | Solo router: ningún hook le pasa el pedido. Degradación declarada. |
| Regla | Qué hace |
|---|---|
| Ancla | Una fila se dispara cuando el pedido coincide con su primer archivo de Read, y entonces se rutean todos los de la fila. Así un archivo que comparten dos filas no arrastra los de la otra. |
| Disparadores | Cada sección declara en su frontmatter triggers: (regex sin distinguir mayúsculas, en inglés y en español) y paths: (rutas del repo que el pedido nombra). Un import que ancla solo su fila (@package.json) usa IMPORT_ROW_TRIGGERS del emisor. Ajustar el ruteo es editar esas listas, nunca L0. |
| Alcance | Una línea ROUTE-SCOPE: en el pedido reemplaza al pedido para clasificar: cada ítem es un id de sección (se rutea directo) o palabras (se clasifican); none no rutea nada. Sin ella, un pedido con el preámbulo de un orquestador se clasifica solo por el bloque de tarea que sigue a su marcador. |
| Ranking y tope | Las filas disparadas se ordenan por la fuerza de su ancla. Solo las primeras MAX_ROUTED_SECTIONS reciben un ROUTE: obligatorio; el resto va a una sola línea ROUTE-OPTIONAL: que no se registra como ruteada. Un agente lee una ruta sola mucho más que una de cinco en el mismo turno (ADR-0017). |
| Recordatorio | La primera herramienta del turno que no lee ninguna sección ruteada, mientras alguna sigue sin leerse, recibe UNA línea ROUTE-PENDING:. No se repite en el turno y calla para los subagentes. |
| Dedupe por sesión | Un JSON por checkout y por sesión, en agentic-instruction-routes/ bajo el temp del sistema, recuerda qué se ruteó. Un pedido que no necesita nada nuevo no agrega nada. |
| Re-armado | Tras una compactación o un /clear, el emisor borra ese estado y no imprime nada: lo que salió del contexto se vuelve a rutear la próxima vez que haga falta. |
06 · Reglas · 7 de 14
Una regla que obliga tiene que estar donde el agente siempre lee. Su fundamento, sus casos borde y sus mediciones no necesitan ocupar cada turno.
En L0
Mismo número, mismo nombre. Cada frase es un fragmento textual del texto completo: L0 puede acortar una regla, nunca reescribirla. Lo que obliga sigue cargando en cada sesión.
En L1
agent-critical-rules.md tiene cada regla bajo ## N. NOMBRE, con la regla completa, su fundamento y sus mediciones. El router lo manda a leer cuando el agente está por romper una regla, duda o le preguntan por ella.
Al editar
Cambiar una regla es tocar su texto en agent-critical-rules.md y su frase en AGENTS.md. bun run instructions:check verifica que cada frase siga textual y termine en Full: agent-critical-rules.md#N.
07 · Imports · 8 de 14
package.json y .agents/project.yaml son los dos archivos de datos que más se consultan. Las dos filas del router que los nombran hacen dos cosas a la vez.
| Host | Qué pasa |
|---|---|
| Claude Code | El @ va como texto plano, fuera de backticks: Claude Code salta los imports que están dentro de código, y el lint y el hook tampoco los cuentan. Los dos archivos entran enteros al arrancar la sesión. |
| OpenCode y Codex | Cargan AGENTS.md nativo, sin el shim de CLAUDE.md. Siguen la instrucción reforzada de la misma fila ("whenever any of these apply, read ...") y abren el archivo cuando la condición se cumple. |
Costo
En Claude Code los dos imports cargan siempre: suman al arranque de la sesión aunque no cuentan contra el presupuesto de L0, que mide los bytes de AGENTS.md. La medición está en ADR-0009. bun run instructions:check falla si un @ de una fila del router no resuelve desde la raíz del repo.
08 · Núcleo · 9 de 14
L0 guarda lo que tiene que atar en cada turno, lo pida o no el pedido. Todo lo demás vive en una sección.
Acceso
Sin ellos ninguna sección es alcanzable.
§1
Lo que prohíbe tiene que estar donde el agente siempre lee.
§12
Cuándo guardar en Engram, el resumen de cierre obligatorio y cómo buscar con palabras clave.
§2 · entero
Todo lo que §2 de AGENTS.md fija sobre cómo razona el agente y qué dice. Qué bloques tiene lo dice §2, no este slide.
§3 · núcleo
Qué va a subagentes y qué queda en la conversación principal, el briefing de siete componentes, la alcanzabilidad de las reglas y el protocolo de error. Gobierna cada despacho, sea cual sea el pedido.
09 · Edición · 10 de 14
Toda edición entra por un solo camino: /framework-development modo instructions, que ubica cada frase con la doctrina de references/instructions-doctrine.md. Las secciones son doctrina compartida: bun sync las sincroniza como una skill. agent-project.md es del proyecto y nunca se sincroniza; un proyecto sin él lo recibe una vez desde agent-project.md.template. Regla de oro: el texto va al archivo dueño, nunca de vuelta a AGENTS.md.
| Quiero cambiar | Dónde |
|---|---|
| el detalle de un tema | su archivo en .agents/instructions/ |
| cuándo se rutea una sección | sus triggers: y paths: en el frontmatter |
| una regla crítica | su texto en agent-critical-rules.md y su frase textual en AGENTS.md |
| una regla que solo tiene este proyecto | .agents/instructions/agent-project.md (o una context skill, si es conocimiento del sistema bajo prueba) |
| una línea con NEVER o MUST en una sección | que siga alcanzable: frase textual en L0 o en agent-critical-rules.md, o Rule #N, binding: `/<skill>`, enforced: `bun run <script>` |
| una fila del ROUTER | solo con un ADR que la decida: bun run instructions:check --accept-router ADR-NNNN y la huella que imprime va a ese ADR |
CLAUDE.md | nada a mano: es el shim generado por bun run agents:compat |
Gate
bun run instructions:check revisa presupuesto, stub, router, frontmatter, triggers que compilan, frases de las reglas y que cada NEVER o MUST sea alcanzable. Además sostiene tres candados (ADR-0013): el ROUTER bloqueado detrás de un ADR, el eval del router con pisos de recall, de recall en las líneas obligatorias y de precisión en cada corrida, y que una sección nueva llegue completa. Corre dentro de bun run repo:check. La guía completa está en .agents/instructions/README.md.
10 · Presupuestos · 11 de 14
L0 tiene techo porque Codex corta sin avisar. Los valores son constantes con nombre en scripts/lint-instructions.ts, iguales en los dos boilerplates.
| Nivel | Constante | Si se pasa |
|---|---|---|
| Objetivo · 16 KiB | L0_TARGET | aviso: el número queda a la vista |
| Techo · 24 KiB | L0_BUDGET | error en el L0 del boilerplate: el detalle va a una sección |
| Con agregados del proyecto · 28 KiB | L0_PROJECT_BUDGET | error en un proyecto: su texto va a agent-project.md o a una context skill |
| Tope de Codex · 32 KiB | CODEX_PROJECT_DOC_MAX_BYTES | error siempre, digan lo que digan los otros: es el valor por defecto de project_doc_max_bytes |
El aviso es a propósito
§2 entra entero por decisión del dueño (ADR-0009), así que L0 puede pasar el objetivo sin pasar el techo. El aviso queda a la vista para que nadie sume texto a L0 sin pensarlo. El tamaño actual y su estado los imprime bun run instructions:check.
11 · Medición · 12 de 14
Dos preguntas distintas: si el clasificador rutea lo que tiene que rutear, y si el modelo después lee lo que le rutearon.
| Qué se mide | Dónde |
|---|---|
| Recall y precisión del clasificador | Pedidos etiquetados a mano, en inglés y en español y con vocabulario de QA (shift-left, ATP y ATR, Xray, KATA, regresión, caché del PBI, handoff, flotas, {{VAR}}), en cli/lib/fixtures/instruction-router-eval.json. Lo puntúa bun run instructions:check en cada corrida contra su campo targets, que nunca baja de los pisos de scripts/lib/router-eval.ts (recall, recall en las líneas obligatorias y precisión). |
| Que no tarde | cli/lib/instruction-router.test.ts exige que rutear un pedido quede muy por debajo de 50 ms |
| Que hook y lint lean igual | scripts/lib/hook-router-parity.test.ts ata el parser del hook al de scripts/lib/instructions.ts |
| Que el hook real responda | el test lanza el proceso del emisor con un pedido de git y espera su línea ROUTE: |
| Que el modelo obedezca | bun run instructions:audit: de las líneas ROUTE: inyectadas, cuántas leyó el agente en el mismo turno y cuántas lecturas siguieron a un recordatorio ROUTE-PENDING:, medido sobre las transcripciones locales de Claude Code (solo nombres y conteos). Los resultados están en ADR-0013. |
Si falla
Un pedido que no rutea lo que debía se arregla en los triggers: de la sección, y el pedido entra al set etiquetado. Etiquetar de nuevo para esconder un falso positivo no es un arreglo.
12 · Referencias · 13 de 14
Una fuente, tres harnesses
La página harnesses.es.html del sitio publicado: cómo cada host lee las instrucciones y el hook. En el repo, el contrato está en .agents/instructions/agent-harnesses.md.
La capa comportamental
Lo que L0 nunca recorta, §2, tiene su espejo para humanos en docs/core/personalidad.html.
Cierre · 14 de 14
Si usás el agente
Nada: el router y el hook deciden qué leer, y el LOAD PROTOCOL hace que el modelo lo lea antes de actuar. En OpenCode 2 no hay hook de ruteo, y el modelo sigue el router solo.
Si editás instrucciones
Por /framework-development modo instructions. El texto va al archivo dueño, nunca de vuelta a AGENTS.md; lo propio del proyecto va a agent-project.md, y una fila del ROUTER necesita un ADR. Antes de commitear, bun run instructions:check.
Si algo no rutea
Se ajustan los triggers: de la sección y el pedido entra a cli/lib/fixtures/instruction-router-eval.json, para que el eval lo vigile desde ahí.
Fuente de verdad
AGENTS.md · .agents/instructions/README.md · .agents/hooks/personality-reinject.mjs · scripts/lint-instructions.ts