Piloto 0120 convergio en 2 iter (2m28s). PR creado en dataforge/chart_demo/pulls/1 (no en dataforge/fn_registry — sub-repo). Anadido a autonomous_loop.md: - Seccion "Sub-repos vs worktree padre": orquestador opera en sub-repo cuando issue toca apps/, projects/*/apps/, cpp/apps/ o analysis/. - Seccion "Gitea API vs gh": gh auth es smoke, real es curl + pass token. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.1 KiB
Bucle autonomo (fn-orquestador + /autonomous-task) — issue 0069
fn-orquestador recorre el ciclo reactivo (CONSTRUIR → EJECUTAR → RECOPILAR → ANALIZAR → MEJORAR) sin intervencion humana, hasta convergencia (suite verde), estancamiento (no progreso N iteraciones), timeout, o tope de iteraciones. Trabaja SIEMPRE en sandbox auto/<issue>, NUNCA merge a master.
Cuando se invoca
- Skill
/autonomous-task <issue_id>(humano lanza explicitamente). - Cron / dag_engine (
schedule:en YAML; planificable, no implementado por defecto). - NO se invoca como reaccion a hooks ni a fallos de tests "en caliente". Siempre tarea explicita.
Reglas duras
- Sandbox obligatorio: rama
auto/<issue_id>-<slug>. Si la rama existe -> reset hard contra master y reanudar. NUNCA commits a master, NUNCA push --force-with-lease a master. - Paths protegidos: respetar
dev/autonomous_protected_paths.jsonexactamente. Cualquier intento de modificar un path protegido aborta la iteracion y registratask_runs.status='aborted_protected_path'. - Filtro de proposals auto-aplicables: el orquestador SOLO aplica proposals que cumplen:
kind in (bug_fix, e2e_check_add, doc_update, capability_tag_add)-> auto-aplicable.kind in (new_function, deprecate_function, refactor, schema_change)-> NO auto-aplicable (quedapendingpara humano).priority in (low, medium)-> auto-aplicable.high|critical-> requiere humano salvo override--allow-high.
- Watchdog: si la metrica de progreso (
checks_pass / checks_total) no sube enN=3iteraciones consecutivas -> abort. Registrartask_runs.status='stalled'. - Tiempo: cada
task_runcon timeout default 30 min. Override con--timeout-min Nhasta max 4h. - Idempotencia: re-ejecutar
/autonomous-task <id>sobre la misma issue reanuda desde la ultima iteracion exitosa, NO reinicia desde cero (lookup entask_runsporissue_id). - Trazabilidad: cada decision se persiste en
task_runs.events_json[]con{ts, agent, action, evidence, diff_summary}. El humano puede leer el log entero para auditar. - No self-modification: orquestador NUNCA modifica
.claude/agents/,.claude/commands/,.claude/rules/,.claude/scripts/,.claude/CLAUDE.md. Reforzado enautonomous_protected_paths.json. - NUNCA paths absolutos fuera del worktree. Refuerzo del piloto 1 (2026-05-15): el orquestador uso
/home/lucas/fn_registry/bash/functions/...para fixear hooks bash y contamino el repo principal. Solucion correcta: fix vive solo en el worktree. Post-cada-iteracion:git -C <main_repo> status --shortdebe permanecer igual al baseline; cualquier diff =status=sandbox_breach-> ABORT. - Pre-commit hooks compartidos. Worktrees comparten
.git/hooks/con main. Si un hook llama scripts via path absoluto, ejecutara la version de main. Si el hook bloquea progreso por bug en main: aplica el fix EN EL WORKTREE (commit en auto/*); si el bug del hook excede scope:git commit --no-verifypara ESE commit contask_runs.events_json[].decision="skip_hook"+ razon. NO editar main.
Sub-repos vs worktree padre
Cuando el issue toca app.md o codigo dentro de apps/<name>/, projects/<p>/apps/<name>/, cpp/apps/<name>/, o analysis/<a>/ — estos directorios son sub-repos Gitea independientes y estan .gitignored en el repo padre fn_registry (regla apps_subrepo.md). El orquestador:
- Crea worktree padre
auto/<issue>en/tmp/fn_orq_<issue>_<ts>/por protocolo, pero no escribe alli porque los cambios no se versionan en el padre. - Opera DIRECTAMENTE en el sub-repo de la app/analysis target. Branch
auto/<issue>-<slug>se crea dentro deapps/<name>/.git, NO en el padre. - PR draft sale al sub-repo en
dataforge/<name>(NO adataforge/fn_registry). Humano revisa+mergea en el sub-repo. - Worktree padre queda vacio y se limpia normal con
git worktree removeal terminar.
Validado en piloto 0120 (add_e2e_check sobre chart_demo): PR creado en dataforge/chart_demo/pulls/1, sanity check del main repo fn_registry confirmo cero contaminacion.
Si el issue toca AMBOS lados (codigo del registry padre + app de sub-repo), el orquestador commitea separado: cambios del padre en auto/<issue> (worktree padre), cambios de la app en auto/<issue>-<slug> (sub-repo). Dos PRs draft. Humano coordina merge.
Gitea API vs gh
Pre-condicion gh auth status es smoke check (target github.com). Mecanismo real de PR es curl a Gitea API:
GITEA_TOKEN=$(pass gitea/dataforge-git-token | head -n1)
curl -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"...","head":"auto/<issue>-<slug>","base":"master","draft":true,"body":"..."}' \
"https://gitea-.../api/v1/repos/dataforge/<repo>/pulls"
Validado en pilotos 0076 y 0120.
Estructura task_run
Migration fn_operations/migrations/006_task_runs.sql. Campos minimos: id, issue_id, branch, started_at, finished_at, status (running|done|failed|aborted_protected_path|stalled|timeout), iterations, checks_pass, checks_fail, proposals_applied_json, proposals_skipped_json, events_json, final_diff_sha.
Fases por iteracion
loop:
1. fn-constructor (Read+Edit+Write+Bash limitados) - aplica fix segun ultima proposal seleccionada
2. fn-executor - corre build + tests + smoke
3. fn-recopilador - audita operations.db de la app
4. fn-analizador - corre e2e_checks (registra e2e_runs)
5. SI todos los checks pasan -> commit + push rama + abre PR. status=done. exit.
6. SI no progreso N iteraciones -> abort. status=stalled.
7. fn-mejorador - crea proposals desde fallos
8. orquestador filtra proposals auto-aplicables -> selecciona la primera -> goto 1.
Output al humano
=== /autonomous-task 0068 ===
task_run_id: run_e2e_a1b2c3
branch: auto/0068-e2e-validation
iterations: 4
status: done
checks_pass: 8/8
proposals_applied: 3 (run_e2e_run_001, run_e2e_run_002, run_e2e_run_003)
proposals_skipped: 1 (refactor — needs human review)
PR: https://gitea.../pulls/42
Anti-patrones
| Anti-patron | Por que es malo |
|---|---|
Mergear auto/<issue> a master sin PR + humano |
Salta gate, riesgo de regresion |
Auto-aplicar proposal kind=refactor |
Cambios sistemicos requieren revision |
Modificar go.sum, package-lock.json, uv.lock |
Cambios de deps requieren CVE/license review |
| Bucle infinito sin watchdog | Coste descontrolado de tokens |
Borrar archivos sin backup en task_runs.events_json |
Pierde auditoria |
| Override de paths protegidos via env var | Bypass de seguridad |
Relacion con otras reglas
- e2e_validation — fn-analizador (fase 4) lee el contrato
e2e_checksque el orquestador usa como gate. - apps_tbd — el orquestador opera en rama
auto/*, no exenta de TBD. - feature_flags — si el fix no esta terminado, el orquestador puede meterlo detras de flag OFF antes de PR.
- registry_calls — toda invocacion del orquestador y sub-agentes pasa por MCP/
fn run/heredoc canonico, registrada en call_monitor.