Trabajar solo, con tarea y tope
Toma una tarea de la cola, trabaja en una rama aislada, corre el gate y repite hasta verde o hasta que el tope lo frene. El resultado queda en un pull request.
Un agente autónomo no es un modelo bueno: es un modelo metido en lazos de control bien hechos. Acá ese arnés existe y se mide sobre Hermes Agent: frenos que deciden antes de cada acción, un gate que define «terminado» con un comando, un loop que reintenta o escala, y sensores que prueban que todo eso sigue mordiendo — también donde el agente aprende, donde actúa afuera y donde corre sin nadie mirando.
Hermes son las manos, el modelo es la capa inteligente y el arnés es lo que no se negocia. Mirá la corrida (arranca sola al llegar): se enciende quién trabaja en cada paso, la consola muestra lo que pasa y la barra del arnés se pone roja, ámbar o verde según lo que decide.
El enrutamiento es una política tuya, en el config: el modelo es intercambiable y el arnés no. Cambiás de modelo y las reglas, el gate y los niveles siguen igual.
Pensalo como una persona. El alma es su identidad y su oficio. El cerebro es el modelo, y se cambia. Las manos son Hermes Agent: terminal, archivos, navegador, correo. Y la empresa que la rodea es el arnés: reglas que se cumplen solas, sin depender de que alguien se acuerde.
Toma una tarea de la cola, trabaja en una rama aislada, corre el gate y repite hasta verde o hasta que el tope lo frene. El resultado queda en un pull request.
Terminal, archivos, correo, Drive, repos. Cada integración arranca con mínimo privilegio y el arnés gobierna cada herramienta, no el servicio entero.
Ni en el nivel más alto: fusionar a main, firmar su propio ascenso o cambiar sus permisos. Eso lo hace una persona.
El modelo propone; el arnés dispone. Los frenos corren fuera del modelo, en cada llamada a una herramienta: no dependen de que el modelo se porte bien. Cuatro ejemplos reales, tal cual los devuelve el plugin:
terminal, patch, memory, un MCP.Las corridas completas de los 22 casos están en casos/CORRIDAS.md.
La autonomía se gana por repo y con evidencia que imprime un comando, no con una promesa. Sube un nivel solo cuando una persona lo firma; baja solo, sin pedir permiso, cuando intenta salirse.
Lee y explica. No escribe nada.
Propone cambios, pero los hace una persona.
Escribe, y cada escritura la aprueba un humano.
Trabaja solo dentro del loop, con gate, tope y freno de mano.
Bajar es automático. En las pruebas, un agente en L3 intentó algo que una regla crítica veda y bajó a L2; después tocó archivos que no debía y bajó a L1. El panel en vivo lo mostró como alerta, con el motivo. El nivel vive bajo .git/ (en producción, en un volumen), donde el agente no escribe: no se firma a sí mismo.
Desde L2, el porqué se comprueba. Aprobar «reiniciar el servicio» es aprobar una conclusión. La propuesta puede traer un certificado —hechos, reglas, deducción— que verifica un programa que no comparte código con el agente. Una conclusión sesgada o editada no verifica, y el ascenso puede exigir que las propuestas vengan certificadas.
Autonomía por niveles Workshop del agente que se gana la autonomía
El primer repo que instaló el arnés y salió a producción es un agente SRE de solo lectura: observa una plataforma real, abre incidentes con reglas y avisa por Telegram, desatendido, en un contenedor sin .git ni terminal. En una semana mostró qué supuestos del arnés sólo valían en un repo de desarrollo, y los cerró.
En producción no hay terminal para firmar. Un PR revisado agrega la transición a un archivo versionado y el arranque la aplica una vez, en orden. subir sigue exigiendo la evidencia: el PR firma, no regala. El agente no puede editar ese archivo.
Una etiqueta en el issue del tablero registra el juicio de una propuesta o de una alerta, con la firma de quien la puso, una vez por issue. Los falsos positivos también son evidencia: acertar, no sólo obedecer.
falla: evidencia que sólo existe si alguien abre una terminal en el contenedorEl piloto certifica la causa raíz de cada incidente investigado con oo-horn, el verificador en Lean 4 de open-ontologies. Quien aprueba, o un auditor meses después, comprueba la deducción sin confiar en el agente ni en el modelo.
El piloto tuvo que parchar su copia del arnés; un --upgrade la habría borrado sin avisar. Ahora el instalador sabe qué escribió y frena lo que el repo cambió, y las herramientas propias viven fuera del código del arnés.
Lo que enseñó el piloto (ADR 0014) Autonomía en producción Certificados
Un modelo se equivoca a veces; un agente autónomo repite ese error sin que nadie mire, y en un agente que aprende, lo vuelve permanente. Lo que lo hace confiable es lo que lo rodea.
Tres clases de control, en tres momentos: guías que orientan antes de decidir (AGENTS.md, el estado del repo al abrir, pistas por pedido), frenos que deciden entre la acción elegida y la ejecutada (terminal, archivos, memoria, skills, cron, integraciones, la respuesta) y sensores que miran después (lint, gate, loop, panel, revisor medido). El loop cierra el lazo: tarea → agente → gate, hasta verde o hasta escalar a un humano con el motivo.
La parte nueva respecto de un arnés para un asistente de código: Hermes escribe su propia memoria, crea skills y corre en cron y en el gateway. Un secreto en MEMORY.md no se filtra una vez: entra en cada sesión futura. Una skill que enseña --no-verify apaga un freno cada vez que alguien la carga. Y en un cron no hay a quién preguntar. Por eso lo aprendido y lo desatendido pasan por los mismos frenos que lo ejecutado.
La tesis se sostiene con evidencia que se puede repetir: cada freno trae un ejemplo que tiene que morder y uno que no; cada prueba tiene una mutación que la tiene que poner roja; cada caso replicable es un agente que toma un atajo real; y lo que el arnés no puede prometer está escrito en una lista de huecos, abiertos y cerrados. Está verificado contra el código de Hermes, no contra su documentación, y es el hermano de agent-harness (hecho para Claude Code).
Un agente autónomo no es un modelo bueno: es un modelo metido en lazos de control bien hechos. El arnés gobierna el turno; el loop gobierna la tarea; el panel deja ver los dos.
tarea ──▶ agente (Hermes) ──▶ gate ──▶ verde ──▶ [x] y la próxima
▲ │
└── reintento con ◀───┤ rojo distinto
el rojo en mano │
└ mismo rojo 2× · tope · stopFile ──▶ [!] escala a un humanoToma la próxima tarea de .hermes/loop/tasks.md, se la da a Hermes y corre el gate. La salida es el gate, no lo que el agente dice. El mismo rojo dos veces seguidas escala (P16 hecho mecanismo). Nunca empuja: lo verde queda en una rama.
rule add pasa la regla nueva por el mismo camino que el plugin: el ejemplo frena, ningún inocente ni el gate quedan frenados, el config pasa el lint. Recién entonces, con --apply, la escribe.
Gate, loop, tareas, frenos y cada evento —bloqueo, escalada, lint, gate— en el momento en que pasa. http.server y Server-Sent Events, sólo en 127.0.0.1, sin dependencias.
Tres horas y media en un sandbox propio, con un agente de juguete determinista (sin API key): guías, frenos, gate, el loop que llega a verde, el que escala, el panel y de un incidente a una regla.
falla: un arnés que sólo entiende quien lo escribióCada tarea corre en su git worktree: el .env y lo ignorado por git no existen ahí. Y cada proceso Python del agente carga una guardia (audit hook, PEP 578) que ve la ruta real de lo que abre, aunque se arme por partes.
El agente arranca observando. Después recomienda, después cambia con tu aprobación, y recién al final trabaja solo. Sube un nivel cuando lo que hizo en el anterior alcanza y una persona lo firma; baja solo si hace algo que contradice esa confianza. El panel muestra en qué nivel está y cuánto le falta.
falla: confiarle todo a un agente el primer día, o no confiarle nada nuncaLa inyección de prompt no se detecta: se corta su daño. Después de leer una web, un ticket o un correo, publicar, hablar con otro servidor o escribir AGENTS.md, CI o memoria escala a un humano; en el loop, se niega.
Ingeniería de loops Operar el loop CLI y panel Autonomía por niveles Workshop: el agente que se gana la autonomía
Y cada freno tiene una prueba de que muerde, otra de que no muerde de más, y una tercera de que la prueba misma muerde.
hermes-real). Sin un modelo configurado, el revisor inferencial sale omitido — y omitido no es verde.python3 scripts/gate.pyexample de cada regla, pasando por el callback real del plugin.python3 scripts/selftest.pypre_verify, ruteo, lint tras escribir, sección del prompt.python3 scripts/hermes_e2e.pyEl self-test prueba que cada regla caza su propio ejemplo: tautológico. La evaluación determinista pasa 180 casos independientes, en inglés y en español, por un copiloto recién instalado en ocho stacks, y da cada tasa con su intervalo de confianza del 95 %.
cat app/../.env, git commit -nm, un patch V4A, un scp.rules · variant: evasionStacks en verde: 8/8, con resultados idénticos. La parte inferencial (un modelo que revisa diffs y un modelo como agente en el loop) tiene su propia suite y corre cuando hay un modelo configurado.
Ver el informe completo: gráficos, intervalos y rúbrica Cómo se evalúa
Un hueco es algo que un agente puede hacer y que el arnés dice que no pasa. 40 se cerraron con un mecanismo y su prueba; 5 siguen abiertos, con su mitigación, para que nadie los dé por cubiertos.
En una sesión interactiva, un binario o un script de node que lee el .env sin nombrarlo no lo ve ningún regex. Python ya está cerrado (la guardia de sesión), y en el loop no hay .env donde trabaja el agente.
Ningún freno sabe si un texto es dato o instrucción. Lo que tiene efecto afuera escala tras leer a un tercero, y la respuesta de esa sesión lleva al pie de qué fuente leyó, también después de un reinicio. Desde L2, una conclusión certificada que fue sesgada no verifica.
mitigación: sesión contaminada · aviso en la respuesta · certificados · gate de la tareaLos casos prueban el arnés con un agente determinista; en el loop, cuánto obedece un modelo el motivo de un freno no está medido. Fuera del loop ya hay datos: el piloto en producción, 30 de 30 sesiones sin salirse del nivel. El revisor inferencial se midió con un modelo real.
mitigación: contrato con Hermes real en cada PR · piloto en producción · pipeline nocturnoA quién, cuánto, desde qué tarea, después de leer qué, y que no lleve un secreto ni un {{hueco}} de plantilla: eso se decide. Si el dato es de otro cliente o la promesa es falsa, no.
| Cerrado hace poco | Mecanismo | Prueba |
|---|---|---|
en producción (sin .git ni shell) la autonomía no se podía ganar ni firmar | estado en un volumen, transiciones firmadas por PR, juicios desde el tablero | self-test §15 y dos mutaciones |
| dos bloqueos que no eran del agente le costaron la evidencia | herramienta fuera de toolNames → origen: config; doctor.py avisa lo de Hermes | self-test §15 y una mutación |
MEDIA:/ruta citado en la respuesta adjunta el archivo al chat | output.defang rompe el marcador | self-test con el example y una mutación |
| el aviso de terceros se perdía en cada deploy | la marca se reconstruye del registro de eventos | self-test §15 y una mutación |
install.py --upgrade borraba un parche local | huellas.json, marca ! y --pisar | self-test §11 y §16, y una mutación |
Cada paso dice qué tiene que salir. Si no sale, el tablero de problemas tiene la causa: cada fila es un tropiezo real.
Python ≥ 3.11, git y Hermes Agent. En Windows, la terminal de Git for Windows o WSL2 (y sin el alias de python3 de la Microsoft Store).
Con el instalador oficial. Bajá el script y leelo antes de correrlo: es la misma regla que el arnés le aplica al agente.
hermes doctor # → sin errores rojosTiene que terminar en GATE VERDE — 14 señales, con OMITIDAS donde falte con qué correrlas (Hermes en el PATH, un modelo para el revisor).
Sin --apply sólo muestra la lista. + es nuevo, = ya existía y no se toca: nunca sobreescribe.
El instalador no toca tu config.yaml: qué frenos existen lo decidís vos.
doctor.py encuentra el "instalado y muerto": plugin sin habilitar, skills sin confiar, un .hermes.md que tapa a AGENTS.md, secretos ya guardados en la memoria.
Pedile a Hermes un git reset --hard, escribir en .env y guardar una key en su memoria: las tres tienen que frenar con el motivo. Después /harness para ver los frenos activos. Y desde ahí, las reglas de tu repo se escriben con tus incidentes (/lesson, /new-guardrail) o con cli.py rule add, que las prueba antes de escribirlas.
Una tarea chica que el gate pueda verificar, el loop y el panel en otra pestaña. Tiene que salir ✓ VERDE — gate verde en el intento N o ✗ ESCALAR con el motivo.
repo-harnessFrena en pre_tool_call la terminal, la escritura (incluido el patch V4A), la memoria, las skills, los cron y las integraciones. Después cuenta lo aprobado (post_tool_call) y tapa secretos en la respuesta (transform_llm_output). Corre en proceso: una llamada de función, no un proceso.
Reglas, rutas y hasta los nombres de las herramientas de Hermes. Cada regla trae su example: el self-test lo pasa por el plugin y es rojo si no frena.
La única definición de "terminado", declarativa, igual en CI. pre_verify no deja cerrar el turno con código editado y el gate pendiente.
Si una línea de AGENTS.md parece inyección, Hermes descarta el archivo entero. El lint te dice la línea antes de que pase, y el tope de 20 000 caracteres antes de que trunque.
/gate, /lesson, /new-guardrail, /harness-audit, /harness-review (el revisor, vía delegate_task) y /harness-port.
doctor.py mira tu máquina; hermes_e2e.py corre cada ejemplo dentro de Hermes; mutations.py prueba que el self-test muerde.
El reason es lo único que el agente ve cuando lo frenás. Es la diferencia entre que entienda y que reintente con otra forma.
"terminal": { "deny": [{ "id": "force-push", "pattern": "git(?:\\s+-[Cc]\\s+\\S+)*\\s+push\\b…", "example": "git push origin main --force", "moreExamples": ["git -C . push --force", "git push -f origin main", "git push origin +main"], "reason": "reescribe historia remota. Usá --force-with-lease y pedí confirmación." }]}
{"action": "block", "message":
COMANDO BLOQUEADO por el arnés del repo:
`git push origin main --force`
Motivo: reescribe historia remota. Usá
--force-with-lease y pedí confirmación.
Reformulá el comando o pedí confirmación
explícita al humano. No lo reintentes igual.}Y el example más cada moreExamples se prueban solos: en el self-test contra el plugin, y en el e2e contra el despacho real de Hermes. Una regla sin ejemplo no entra.
La diferencia que más importa: en Hermes, un pre_tool_call que lanza una excepción bloquea la herramienta. Un arnés con un bug no "deja de frenar": frena todo. Por eso ningún callback del plugin lanza, y el self-test lo prueba con un freno que divide por cero.
| Necesidad | Punto de Hermes | Contrato |
|---|---|---|
| frenar una herramienta | pre_tool_call | {"action":"block","message"} — sin mensaje, Hermes lo ignora |
| pedir aprobación humana | pre_tool_call | {"action":"approve"} → el gate de aprobación de Hermes; en cron se niega solo |
| no cerrar con el gate pendiente | pre_verify | {"action":"continue","message"}, sólo si el turno editó archivos, máx. 3 |
| contar lo que un humano aprobó | post_tool_call | observador, después de ejecutar; status="blocked" si se negó |
| tapar un secreto en la respuesta | transform_llm_output | el primer str reemplaza la respuesta antes de mostrarla y guardarla — no se encadena: un plugin que carga antes y devuelve texto descarta el del arnés (doctor.py lo avisa) |
| que un resultado OK no parezca fallo | _detect_tool_failure | "error" o "failed" en los primeros 500 caracteres cuentan como fallo, aunque la herramienta haya salido bien |
| mandar un mensaje sin adjuntar nada | hermes send | MEDIA:<ruta> en cualquier parte del texto adjunta ese archivo: output.defang lo rompe |
| lint del archivo recién escrito | transform_tool_result | el primer str reemplaza el resultado que ve el modelo |
| estado del repo al abrir | register_system_prompt_section | ≤ 4000 caracteres, congelada por sesión |
| pistas según el pedido | pre_llm_call | {"context"} se suma al mensaje del usuario |
| el directorio correcto | _authoritative_workspace_root | el cwd de la sesión, no el del proceso (gateway, cron, Desktop) |
Tres familias de frenos que un arnés para Claude Code no necesitaba.
Filtra secretos y "reglas" que contradicen a un freno. Mira sólo lo que se guarda: sacar un secreto que ya estaba tiene que poder hacerse. doctor.py encuentra lo que se guardó antes de instalar el arnés.
Una skill que enseña un paso prohibido es un freno apagado en todas las sesiones que la carguen. Se le aplica terminal.deny a lo que escribe, dentro de operations[].
Sin humano, la aprobación no tiene a quién preguntar. El freno decide al crear: nada que publique, y un intervalo mínimo (entiende 30s, listas y rangos de cron).
Cada una terminó en un mecanismo (y en docs/gotchas.md), no en una advertencia.
Causa: Hermes copia el directorio del plugin para validarlo e instalarlo; el núcleo vivía afuera. Mecanismo: núcleo dentro del plugin, self-test que carga desde una copia, y hermes plugins doctor en el gate.
patch en modo V4A se salteaba los frenos de escrituraCausa: ese modo no trae path; las rutas van en encabezados dentro del texto. Mecanismo: la gramática V4A va al config; el freno evalúa cada ruta y cada línea agregada.
Causa: buscaba el repo desde el cwd del proceso, no desde el de la sesión. Mecanismo: el plugin le pregunta a Hermes el cwd de la sesión; caso en el e2e con el proceso afuera y la sesión adentro.
Causa: consola cp1252; imprimir ✓ reventaba antes de verificar nada. Mecanismo: salida UTF-8 forzada y un caso del self-test que emula cp1252 — lo cazó la matriz de CI.
Causa: el nivel vivía en .git/ y la imagen no lo tiene: los días en el nivel quedaban en 0 para siempre, y firmar pedía una shell. Mecanismo: estado en un volumen, evidencia desde el primer evento y transiciones firmadas por PR — lo cazó el piloto doctiling-sre.
Causa: Hermes adjunta el archivo de MEDIA:<ruta>, y una respuesta cita logs de terceros. Mecanismo: output.defang rompe el marcador antes de que la respuesta salga, con su ejemplo en el self-test.
El plugin es el camino recomendado. El shell hook existe para quien no puede habilitar plugins.
| Plugin | Shell hook | |
|---|---|---|
| dónde | $HERMES_HOME/plugins/repo-harness + plugins.enabled | hooks: en config.yaml |
| costo | una llamada de función | un proceso de Python por herramienta |
| si revienta | _seguro → deja pasar | exit ≠ 2 → deja pasar; nunca exit 1 |
| cwd de la sesión | se lo pide a Hermes | workdir o TERMINAL_CWD |
| lint tras escribir | se inyecta en el resultado | queda en el log |
| Perfil | Qué es código | Qué es derivado | Señal de tests |
|---|---|---|---|
| python | .py .pyi | uv.lock poetry.lock .venv/ | pytest -q |
| node · front | .ts .tsx .js .vue .svelte… | package-lock.json node_modules/ dist/ | npm test (+ build) |
| go | .go | go.sum vendor/ | go vet + go test |
| rust | .rs | Cargo.lock target/ | cargo clippy + cargo test |
| dotnet | .cs .fs .csproj .razor | bin/ obj/ packages.lock.json | dotnet test |
| jvm-maven · jvm-gradle | .java .kt .scala… | target/ build/ | mvn verify · gradlew check |
Un perfil trae hechos del stack, nunca reglas de otro repo: las reglas se escriben con tus incidentes.
Un repo de gestión sin código, donde Hermes parte épicas y las despacha, y codificadores con su repo, su tablero, su cola y su agente (Claude Code o Hermes), cada uno con su arnés. Lo que los une es la épica.
Una épica es un issue con un checklist de hijos, una línea por tarea: - [ ] [api] Endpoint de login. dispatch
crea un issue hijo por línea en el repo de ese proyecto, vinculado al padre, y lo encola en el loop del codificador. Es todo o nada,
y despachar dos veces no repite.
El hijo llega con «Part of gestion#12» y a la cola como [issue: org/api#5]: el loop le pasa al agente qué issue cierra
su PR. status marca en la épica [x] lo cerrado y [!] lo que el loop escaló a un humano.
| Forja | CLI | Padre → hijo | Tablero | Pipeline | Estado |
|---|---|---|---|---|---|
| GitHub | gh | sub-issue, también entre repos | GitHub Projects | Actions | probado en vivo: repos, milestones, épica, sub-issues, tableros, status |
| GitLab | glab | issue link | board del proyecto | .gitlab-ci.yml | adaptador, en seco |
| Azure DevOps | az | Parent | Azure Boards | azure-pipelines.yml | adaptador, en seco |
Una forja es un manifiesto (plantillas/forges/<id>.json, con su esquema): agregar una
es agregar un archivo. Sin --apply todo corre en seco, y con --apply publicar lo aprueba un humano: el loop nunca publica.
Un principio que dice BLOCKING nombra el comando que falla. Los nuevos respecto de agent-harness están marcados.
example.~/.hermes/ — y los secretos tampoco se leen.COHERENCIA.timing.py en el gate.El setup paso a paso y el tablero de problemas.
contratoHooks, contexto, skills y memoria, verificado en su código.
configCada clave, y quién la lee.
mapaQué guía, freno o sensor actúa en cada momento, y su prueba.
portarPlugin o shell hook, perfiles, --upgrade.
El mapa pieza por pieza desde Claude Code.
loopsLos ocho lazos de control del agente y las siete reglas de un lazo bien hecho.
operarLa cola, los topes, el freno de mano y qué hacer cuando escala.
cli · panelParametrizar sin editar JSON, y ver el arnés en vivo.
workshopSeis módulos prácticos, con un agente de juguete y sin API key.
casosProgramación, infraestructura, oficina, datos, soporte y operación: el atajo del agente y cómo lo para el arnés.
integracionesCorreo, WhatsApp, navegador headless, Google, Microsoft, GitHub y bases: perfiles, destinatarios, secretos, recursos y dependencias con lock.
huecosLo que se cerró, con su prueba, y lo que sigue abierto.
evaluaciónGráficos, intervalos de confianza, los ocho stacks, el historial y la rúbrica.
evaluarDeterminista e inferencial, los esquemas, los casos bilingües y cómo sumar uno.
workspaceGestión y codificadores, épicas, forjas (GitHub, GitLab, Azure).