Agentic QA · Instrucciones del repo

Las instrucciones, en capas

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.

L0: AGENTS.md L1: .agents/instructions/ Hook: ROUTE: read Gate: bun run instructions:check
Deck de categoría: explica un mecanismo del repo, no una skill, así que no hay slash command que invocar. Cuenta cómo se reparten las instrucciones entre lo que carga siempre y lo que se lee a pedido, y qué lo sostiene. Pulsá → para avanzar, S para modo presentador, O para overview, F para pantalla completa.

01 · Motivo · 2 de 14

Por qué partir el archivo

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

Cada sesión pagaba el archivo completo

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 lo cortaba en el medio

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

Un import no achica nada

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.

El costo visible es de tokens; el invisible es peor porque nada falla: Codex simplemente no ve la segunda mitad del archivo y trabaja sin esas reglas. El tope sale de la constante CODEX_PROJECT_DOC_MAX_BYTES de scripts/lint-instructions.ts. La medición es de la fecha del corte y vive en el ADR, no en este deck.

02 · Arquitectura · 3 de 14

Tres capas, tres momentos de carga

L0 · AGENTS.md siempre, en cada host · reglas que atan, §2 entero, §3 núcleo, router, LOAD PROTOCOL L1 · una sección agent-git.md L1 · otra sección agent-ticket-work.md L1 · del proyecto agent-project.md L2 · references/ de las skills a pedido, cuando una sección o una skill las nombra

Las flechas son lecturas a pedido: L0 nunca importa una sección. Las del dibujo son ejemplos; la lista real es la carpeta.

CapaArchivo y cuándo carga
L0AGENTS.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.
L1Un 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:.
L2Las 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.

L0 es lo único que carga siempre. L1 son los archivos de .agents/instructions/: además de las secciones de tema están agent-project.md (lo propio del proyecto), agent-project.md.template y el README. L2 ya existía antes de partir: son las references de cada skill. La regla de que una sección obliga igual que AGENTS.md está en el LOAD PROTOCOL y en .agents/instructions/README.md.

03 · Router · 4 de 14

El router fijo

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:.

ColumnaQué diceEjemplo (fila de git)
When the request involvesel tipo de pedido que dispara la filagit: branch, commit, push, PR, conflict, strategy
Readlos 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 filaagent-git.md
Wasel número de § que tenía ese contenido antes de partir, para que una cita §N siga resolviendo§11
Thenla 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.

La lista real de filas está en AGENTS.md; este deck no la copia, porque cambiaría con la vida del repo. Un tema nuevo casi nunca suma una fila: agranda una sección existente y sus triggers. Solo un tipo de pedido que ninguna fila cubre justifica una fila nueva (.agents/instructions/README.md, Editing rules).

04 · Protocolo · 5 de 14

El LOAD PROTOCOL

Un párrafo al principio de L0 convierte el router en obligación. Sin sus frases, la tabla sería una sugerencia.

  1. Antes de actuar, el pedido se compara contra el router y se lee cada archivo de sección que coincide y todavía no está en la conversación.
  2. Una línea ROUTE: que inyecta el hook es obligatoria y gana sobre el criterio propio del modelo: se lee su archivo antes de actuar.
  3. Una línea ROUTE-OPTIONAL: no obliga: se lee uno de sus archivos solo si la tarea lo pide.
  4. Una línea ROUTE-PENDING: nombra un archivo obligatorio que sigue sin leerse: se lee antes del paso siguiente.
  5. Una sección ya leída no se relee, salvo que una compactación o un /clear la haya sacado del contexto.
  6. Ante la duda, se lee. Una sección salteada es justo la falla que este diseño evita.
  7. Una sección obliga exactamente igual que AGENTS.md.
  8. Una cita §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.

Las frases son textuales del LOAD PROTOCOL de AGENTS.md, traducidas. La que dice que una sección obliga igual que AGENTS.md hace que leer a pedido no debilite nada. La de la cita §N mantiene vivas las citas viejas: §11 sigue apuntando a git porque la columna Was del router dice dónde quedó.

05 · Hook · 6 de 14

El hook y la línea ROUTE:

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.

El pedido texto + rutas que nombra Clasificador del hook router de L0 + triggers / paths El modelo lee la sección antes de actuar Estado por sesión lo ya ruteado no se repite
# una línea por archivo nuevo, después de AGENT IDENTITY:, y de ORCA: si el binario está ROUTE: read .agents/instructions/agent-git.md (git, <n> lines) before acting on this prompt ROUTE: read package.json (<n> lines) before acting on this prompt # lo que pasó el tope, en una sola línea que no obliga ROUTE-OPTIONAL: the prompt also touches .agents/instructions/agent-harnesses.md (harnesses); read one only if the task needs it.
HostCómo llega la línea
Claude CodeUserPromptSubmit corre el emisor; SessionStart compact o clear lo re-arma; PostToolUse sin matcher recuerda una ruta sin leer.
CodexLo mismo salvo el recordatorio, y solo en un repositorio de confianza.
OpenCode 1chat.message clasifica, la system transform empuja las líneas, session.compacting re-arma.
OpenCode 2Solo router: ningún hook le pasa el pedido. Degradación declarada.
ReglaQué hace
AnclaUna 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.
DisparadoresCada 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.
AlcanceUna 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 topeLas 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).
RecordatorioLa 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ónUn 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-armadoTras 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.
Un emisor, tres adaptadores: Claude Code y Codex lo ejecutan como command hook, OpenCode lo importa desde .opencode/plugins/personality-reinject.js. El estado de ruteo nunca vive en el repo: es estado de sesión. La confianza de Codex es estado de runtime, por eso setup:doctor la reporta aparte (agent-harnesses.md, Codex trust).

06 · Reglas · 7 de 14

Reglas críticas: la frase que ata en L0, el texto entero aparte

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.

# cada regla en L0: número, nombre, su frase textual y el puntero Full: 7. **QUALITY VERIFICATION**: After code changes, verify in order: tests → types → lint. Full: agent-critical-rules.md#7

En L0

La frase obligatoria, textual

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

El texto entero, en un archivo

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

Dos lugares, a propósito

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.

El ejemplo es la regla 7 tal como está en AGENTS.md. El "Full: agent-critical-rules.md#7" es el puntero al archivo de la regla completa, con su número. El lint compara número, nombre y cada frase contra el heading del mismo número en agent-critical-rules.md.

07 · Imports · 8 de 14

Dos imports para Claude, una instrucción reforzada para los otros

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.

# celda Read de la fila de variables whenever any of these apply, read @.agents/project.yaml and `agent-project-variables.md`, NEVER hardcode identity, ... # celda Read de la fila de scripts whenever any of these apply, read @package.json first, never a command quoted in a doc
HostQué pasa
Claude CodeEl @ 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 CodexCargan 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.

El ancla de la fila de variables es agent-project-variables.md, porque las secciones entre backticks van antes que los imports: un pedido sobre entornos carga las variables y el yaml. La fila de scripts no tiene sección, así que su ancla es @package.json y la dispara IMPORT_ROW_TRIGGERS. En los hosts con hook, la línea "ROUTE: read package.json" refuerza la misma instrucción.

08 · Núcleo · 9 de 14

Lo que nunca sale de L0

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

LOAD PROTOCOL y ROUTER

Sin ellos ninguna sección es alcanzable.

§1

La frase obligatoria de cada regla crítica

Lo que prohíbe tiene que estar donde el agente siempre lee.

§12

Los disparadores de memoria

Cuándo guardar en Engram, el resumen de cierre obligatorio y cómo buscar con palabras clave.

§2 · entero

La capa comportamental

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

La orquestación

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.

El detalle de orquestación que no es núcleo (executors, workers, gates) está en agent-orchestration-detail.md. §2 entero es la razón de que L0 pueda pasar el objetivo de 16 KiB (ADR-0009): se ve en el slide de presupuestos. Su espejo para humanos es docs/core/personalidad.html.

09 · Edición · 10 de 14

Cómo se edita

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 cambiarDónde
el detalle de un temasu archivo en .agents/instructions/
cuándo se rutea una secciónsus triggers: y paths: en el frontmatter
una regla críticasu 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ónque siga alcanzable: frase textual en L0 o en agent-critical-rules.md, o Rule #N, binding: `/<skill>`, enforced: `bun run <script>`
una fila del ROUTERsolo con un ADR que la decida: bun run instructions:check --accept-router ADR-NNNN y la huella que imprime va a ese ADR
CLAUDE.mdnada 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.

La regla de oro está en la cabecera de AGENTS.md y en "Editing rules" del README de instrucciones. Lo de CLAUDE.md sale de agent-harnesses.md, GENERATED vs VERSIONED: se edita la fuente y se regenera, y agents:compat:check falla si alguien le escribe prosa.

10 · Presupuestos · 11 de 14

Presupuestos de L0

L0 tiene techo porque Codex corta sin avisar. Los valores son constantes con nombre en scripts/lint-instructions.ts, iguales en los dos boilerplates.

NivelConstanteSi se pasa
Objetivo · 16 KiBL0_TARGETaviso: el número queda a la vista
Techo · 24 KiBL0_BUDGETerror en el L0 del boilerplate: el detalle va a una sección
Con agregados del proyecto · 28 KiBL0_PROJECT_BUDGETerror en un proyecto: su texto va a agent-project.md o a una context skill
Tope de Codex · 32 KiBCODEX_PROJECT_DOC_MAX_BYTESerror 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.

El techo del boilerplate está pensado para "el comportamiento entero más la frase de cada regla", como dice el comentario de L0_BUDGET. El tamaño real cambia con cada edición, por eso este deck no lo escribe: lo dice el gate.

11 · Medición · 12 de 14

Cómo se mide que funciona

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 mideDónde
Recall y precisión del clasificadorPedidos 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 tardecli/lib/instruction-router.test.ts exige que rutear un pedido quede muy por debajo de 50 ms
Que hook y lint lean igualscripts/lib/hook-router-parity.test.ts ata el parser del hook al de scripts/lib/instructions.ts
Que el hook real respondael test lanza el proceso del emisor con un pedido de git y espera su línea ROUTE:
Que el modelo obedezcabun 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.

Ojo con el comando: en este repo bun run test es Playwright; el eval corre dentro de instructions:check, y el test de tiempo con el runner de Bun, bun test. Los números medidos (recall, precisión, tiempo) van a los ADR, no al deck: cambian cada vez que se agrega un pedido al set.

12 · Referencias · 13 de 14

Dónde leer más

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.

Los tres primeros son los archivos fuente. La página de harnesses y la de personalidad explican lo mismo para lectores humanos; cuando divergen, gana el archivo fuente.

Cierre · 14 de 14

Qué cambia según quién sos

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

Fin del recorrido. La idea entera: lo que ata carga siempre, lo demás se lee cuando el pedido lo necesita, y un hook más un gate se aseguran de que eso pase en los tres hosts.
← → navegar · O resumen · S presentador · N notas · F pantalla