Reviewer
reviewer es la persona que segunda-firma cada operación mutadora. A diferencia de los otros workers nunca la lanza el coordinator — el catálogo la excluye deliberadamente. El único spawner es el decorator ReviewGate, que envuelve cada tool cuya Class es "write" o "destructive".
El reviewer es la respuesta a una pregunta: "¿quién vigila al vigilante?"
No deshabilites esto.
El reviewer es lo que hace seguro darle a Ongrid host_restart_service o execute_skill siquiera. Saltarse el gate es una elección explícita de configuración y aparece en la pista de auditoría. Si forkeas la persona a "siempre aprueba", el audit log mostrará cada decisión — los auditores lo notarán.
El decorator ReviewGate
Posición en la cadena de decorators de tools (chain.go):
tenant_bind → REVIEW_GATE → timeout → audit → ratelimit → metric → <inner tool>Por qué este orden exacto:
- Fuera de
timeout. El reviewer es por sí mismo ungraph.Invokecon su propio presupuesto de turnos. Envolverlo dentro del timeout de 15s del tool interior obligaría al reviewer a terminar en 15s — irrealista. El gate lleva su propio techo independiente de 60s (DefaultReviewerTimeout). - Fuera de
audit. Los rechazos no deberían escribir una fila de ejecución sintética. El gate escribe una filachat_mutating_proposalsen su lugar. El audit solo loguea la ejecución del tool interior, que solo ocurre en approve. - Dentro de
tenant_bind. El payload de propuesta incluye eluser_iddel operador, quetenant_bindresuelve desde ctx — el gate debe correr después de quetenant_bindlo haya poblado.
Condición de trigger
El decorator inspecciona la Class del tool envuelto:
| Class | Comportamiento del gate |
|---|---|
"read" | Pasa. El tool interior corre. |
"write" | Intercepta. Lanza reviewer. El interior corre solo en approve. |
"destructive" | Igual que "write". Ambas clases pasan por el gate. |
Tools concretos Class: "write" en el bag hoy:
host_restart_service— el skill mutador canónico.execute_skill— el shim del marketplace que ejecuta cualquier skill instalado desde el marketplace (el gate no puede leer el cuerpo del skill para clasificar, así que los gatea todos).AgentTool— sí, la propia primitiva de dispatch es"write"porque lanza un worker y por lo tanto compromete recursos. El reviewer raramente gatea llamadas a AgentTool en la práctica — ver la allowlist de la persona abajo.
El payload de propuesta
Cuando intercepta, el gate construye:
{
"action": "host_restart_service",
"target": {"device_id": 7, "service": "nginx"},
"reason": "User reported 502; nginx error log shows OOM",
"blast_radius": "single_device",
"operator": "user_42",
"context_summary": "<coordinator's running summary of the conversation>"
}blast_radius es uno de single_device | cluster | tenant_wide, derivado de la metadata del tool. El reviewer lo usa para pesar la decisión "es esto razonable" — una acción tenant_wide enfrenta más escrutinio que una single_device.
La persona reviewer
Highlights del frontmatter (verbatim de agents/reviewer.md):
name: reviewer
description: SOP 二审 reviewer worker,对 mutating / destructive 提案做静态审查
permission_mode: read-only
max_turns: 5
model: anthropic/claude-opus-4-7 # 关键路径用最强
background: true # async: spawn returns immediately
tools:
- get_incident_detail
- get_edge_summary
- query_promql
- query_logql
- get_sop_text # planned (HLD-003 PR-D)
disallowed_tools:
- "*_skill" # wildcard — no skill execution
- run_shell
- execute_skill
- host_restart_service # explicitly: reviewer cannot itself act
- kill_process
critical_reminder: |
你是高危操作二审 reviewer。reject 是默认选项,approve 必须三条都满足:
1. 找得到对应 SOP 且明确覆盖此场景
2. 当前没有并行的同类操作(看告警 / 看运维窗口)
3. 回滚路径已知Observaciones clave:
max_turns: 5. Los reviewers no andan con rodeos. Cinco turnos para decidir; el prompt de la persona dice explícitamente "看不清就 reject".model: anthropic/claude-opus-4-7. La llamada de ruta crítica obtiene el modelo más fuerte, sin importar el default de la organización. Las reviews no deberían degradarse a un modelo rápido — el costo de un approve equivocado supera con creces el costo de una review lenta.background: true. El reviewer corre async. La UI del coordinator no bloquea por la review; eltask_notificationde la review postea de vuelta cuando termina. (En el cableado actual, el tool interior bloquea hasta que el reviewer devuelve — pero la UI no, así que el usuario ve "review pending" mientras corre.)- Wildcard
disallowed_tools*_skill. El reviewer no puede ejecutar ningún skill. Puede leer contexto (get_incident_detail,get_edge_summary, queries de telemetría) pero nunca actuar. - Sin
run_shell, sinhost_restart_service, sinkill_process. Incluso si el cuerpo de la persona se lo dijera, el filtro del runtime elimina esas tools. Defensa en profundidad.
El workflow de 5 pasos
Del cuerpo de la persona, el reviewer corre:
- Encuentra el SOP.
get_sop_text(action)por el procedimiento oficial. Sin SOP → reject con"no SOP for action <X>". - Verifica el estado del target.
get_edge_summary(device_id). Device offline / en restart loop / última acción mutadora en los últimos 5min → reject. - Verifica operaciones paralelas.
query_logqlpara entradasaudit:tocando el mismo target en los últimos 10 minutos. Acción paralela → reject con"parallel operation detected; wait for X to complete". - Valida la razón contra alertas.
get_incident_detailpara el incidente referenciado. La razón contradice el contenido de la alerta → reject ("el operador puede haber leído mal el síntoma"). - Decide.
- Approve cuando los tres gates principales pasan. Output
{decision: approve, sop_id, rollback_path, gates_passed}. - Reject en caso contrario. Output
{decision: reject, reason, missing_gates}.
- Approve cuando los tres gates principales pasan. Output
La regla tres-de-tres
El critical reminder es explícito:
reject 是默认选项,approve 必须三条都满足:
- 找得到对应 SOP 且明确覆盖此场景
- 当前没有并行的同类操作(看告警 / 看运维窗口)
- 回滚路径已知
"No estoy seguro" equivale a "reject". Esta es una calibración deliberada — la asimetría de costo entre un approve equivocado (daño en producción) y un reject equivocado (un round-trip extra) favorece fuertemente a este último.
Formato de salida
La respuesta final del reviewer (posteada de vuelta al coordinator vía <task-notification>):
**Decision: approve | reject**
**Gates**
- ✓ SOP-007 covers restart nginx
- ✓ node-01 status online; last mutating 17min ago
- ✓ No parallel operation
- ✓ Rollback: `systemctl start nginx`
**Notes**
{1-2 sentence risk note; included even on approve.}El decorator parsea por Decision: approve (case-insensitive, debe aparecer en la primera línea no en blanco de la respuesta). Cualquier otra cosa — incluyendo redacciones ambiguas como "approve con caveats" — se trata como reject.
Qué pasa en approve
El decorator:
- Escribe una fila
chat_mutating_proposalscon status=approved, el texto de decisión del reviewer y el id de SOP. - Deja que la llamada caiga al tool interior.
- El decorator de audit (downstream en la cadena) escribe la fila de ejecución.
- El tool interior devuelve su resultado normal al LLM del coordinator.
El LLM del coordinator ve un resultado de tool normal y procede.
Qué pasa en reject
El decorator:
- Escribe una fila
chat_mutating_proposalscon status=rejected, la razón del reviewer y los gates faltantes. - Devuelve
ErrReviewRejectedenvolviendo la razón del reviewer. - El decorator de audit no escribe una fila de ejecución (no ocurrió ninguna ejecución).
El LLM del coordinator ve un mensaje de error — review rejected: <reviewer reason> — y se espera que explique la situación al usuario. No debería reintentar la misma llamada de tool; la protección de dedupe más un LRU de corto plazo sobre razones de rechazo mantienen al LLM fuera del loop.
Edge cases
¿Y si el usuario es el operador y el admin?
El reviewer sigue corriendo. No hay flag "skip review porque soy admin". La auditabilidad es el punto — la fila de decisión del reviewer es la pista en papel que dice "esta acción fue aprobada contra SOP-X con rollback Y en el tiempo T". Un override de admin borraría la pista.
¿Y si no existe SOP de get_sop_text?
Hoy el corpus de SOPs es escaso — get_sop_text es un tool planeado (HLD-003 PR-D). En el MVP actual el reviewer usa un placeholder y cae en una heurística: acciones críticas en devices con tag de producción necesitan force_approve_no_sop: true explícito en el contexto de propuesta, que el coordinator solo puede setear tras confirmación explícita del usuario. Esto será reemplazado por el corpus de SOPs cuando se pueble.
¿Y si el reviewer hace timeout?
Dispara el timeout de gate de 60s. El decorator devuelve ErrReviewTimeout. El LLM del coordinator ve un error de timeout y debería reportárselo al usuario — no reintentar. Los timeouts persistentes del reviewer son un problema del lado del manager (probablemente latencia del modelo); el audit log deja que el SRE vea el patrón.
¿Y las llamadas read-only del worker?
Las llamadas de tool read-only de los workers (query_promql, get_host_load, etc.) tienen Class: "read" y nunca disparan el gate. Solo los tools "write" / "destructive" lo hacen. El reviewer mismo solo lleva tools Class: "read", así que un worker reviewer no puede disparar un ReviewGate anidado.
Customizando el gate
Cosas que podrías cambiar en un fork:
- El modelo del reviewer. Si tu organización no tiene acceso a Anthropic, reescribe el
model:del frontmatter al modelo más fuerte que sí tengas. El gate usa lo que declare la persona. - El techo de 60s. Sobrescribe vía la opción de constructor de
ReviewGatesi tu reviewer regularmente toma más. No bajes de 30s; el reviewer necesita espacio para 5 turnos × LLM + round-trips de tools. - El cuerpo de la persona reviewer. Añade checklists específicos del equipo (scope PCI, ventanas de freeze de cambios, calendario de on-call). Al gate no le importa qué hay en el cuerpo siempre que la línea final de decisión sea parseable.
Cosas que no debes cambiar:
- El
name: reviewer— el gate lo busca por nombre (DefaultReviewerAgent). - El wildcard
disallowed_tools*_skill— el reviewer no debe realizar acciones por sí mismo; la defensa en profundidad dice que el filtro del runtime lo refuerza, no el prompt.
Para un reviewer custom por-tool (p. ej. uno específico de db), el constructor del decorator acepta un override reviewerAgent para que distintos tools puedan rutear a distintas personas reviewer. Úsalo con moderación — múltiples reviewers fragmentan la pista de auditoría.