- .claude/CLAUDE.md - .claude/agents/fn-recopilador/SKILL.md - .claude/rules/INDEX.md - .claude/rules/cpp_apps.md - bash/functions/infra/build_cpp_windows.sh - cpp/CMakeLists.txt - cpp/PATTERNS.md - cpp/framework/app_base.cpp - cpp/framework/app_base.h - dev/issues/README.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.3 KiB
Validacion end-to-end de apps (bucle reactivo, fase 4)
Contrato obligatorio para apps que vayan a master con gate automatico: declarar e2e_checks en su app.md. Sin contrato, fn-analizador no puede validar y la app cae al modo "manual": el humano sigue iterando.
Ver tambien: apps_tbd.md, feature_flags.md, issue 0068.
Por que
El bucle reactivo del registry tiene 5 fases. Las 3 primeras (fn-constructor, fn-executor, fn-recopilador) cubren CONSTRUIR/EJECUTAR/RECOPILAR. La fase 4 (ANALIZAR) y la 5 (MEJORAR) no funcionan sin un contrato explicito de "como sabe el agente que esta app esta sana". Ese contrato es e2e_checks.
Donde vive
En el frontmatter de cada app.md, lista e2e_checks. Convencion: id unico por check, ejecucion en orden declarado, falla = stop o continue segun severidad (TBD por implementar).
Tipos de check
| Campo | Que hace |
|---|---|
id |
Identificador unico del check dentro de la app (build, smoke, tests_unit, ...) |
cmd |
Comando shell. Exit 0 = pass salvo override de expect_exit. |
health |
URL HTTP. Hace GET, espera 200, util tras un cmd que arranca un servicio en background (con &). |
ref |
Referencia a otro agente / funcion del registry (ej. fn-recopilador:apps/X, fn-doctor:artefacts). |
timeout_s |
Timeout en segundos. Default 60. |
expect_exit |
Codigo de salida esperado (default 0). |
expect_stdout_contains |
Substring que debe aparecer en stdout. |
expect_stdout_json |
JSONPath o key=value que debe satisfacer la salida. |
severity |
critical (default) o warning. Critical = bloquea merge; warning = registra y sigue. |
Patrones por stack
Go service con frontend embebido
e2e_checks:
- id: build_frontend
cmd: "cd frontend && pnpm install --frozen-lockfile && pnpm build"
timeout_s: 180
- id: build_backend
cmd: "CGO_ENABLED=1 go build -tags fts5 -o myapp ."
- id: smoke
cmd: "./myapp --port 8200 --db /tmp/myapp_e2e.db &"
health: "http://127.0.0.1:8200/api/health"
- id: tests
cmd: "go test -tags fts5 -count=1 ./..."
C++ ImGui app
e2e_checks:
- id: build
cmd: "cmake --build build --target myapp -j"
timeout_s: 300
- id: self_test
cmd: "./build/myapp --self-test"
timeout_s: 30
- id: pytest
cmd: "cd tests && python3 -m pytest -x -q"
Apps C++ deben implementar --self-test que arranca, verifica subsistemas (GL loader, fonts, DBs locales), y sale con codigo 0/1.
Python pipeline / CLI
e2e_checks:
- id: import
cmd: "python3 -c 'import myapp'"
- id: cli_help
cmd: "python3 -m myapp --help"
expect_stdout_contains: "usage:"
- id: dry_run
cmd: "python3 -m myapp --dry-run --input examples/sample.json"
App con operations.db
Anadir siempre:
- id: ops_audit
ref: "fn-recopilador:apps/myapp"
Esto invoca al recopilador en modo audit sobre apps/myapp/operations.db.
Reglas
- Idempotente: cada check debe poderse correr N veces sin efectos secundarios. Usar BDs en
/tmp/, puertos altos,--port 0cuando se pueda. - Sin credenciales reales: ningun check toca produccion ni servicios externos sensibles. Si necesita HTTP de prueba, usar
httpbin.orgo un mock local. - Tiempo acotado: cada check declara
timeout_s. Suma total de la app < 10 min como objetivo razonable. - Determinista: si el check depende de red flaky, marcalo
severity: warningo usalo solo como diagnostico, no como gate. - Cleanup implicito: si el check arranca un proceso en background (
&), debe morir al final.fn-analizadormata el grupo de procesos al terminar la suite.
Como diseñar e2e_checks para una app existente
fn-recopilador tiene un modo design-e2e <app_id> que:
- Inspecciona
app.md(lang, framework, entry_point, uses_functions). - Revisa estructura del directorio (presencia de
tests/,frontend/,Makefile,CMakeLists.txt, etc.). - Audita
operations.db(si existe) para sugerirops_audit. - Devuelve bloque
e2e_checks_suggested:listo para copiar alapp.mdtras revision humana.
Comando indicativo:
Agent(subagent_type="fn-recopilador",
prompt="design-e2e apps/<app>")
El recopilador NO escribe directo al app.md; deja la propuesta para que el humano apruebe (similar a proposals).
Adopcion gradual
- Apps SIN
e2e_checksdeclarado:fn doctormuestra warning, no bloquea nada. - Apps CON
e2e_checks:fn-analizadorcorre la suite. Si critical falla →fn-mejoradorcrea proposal. Gate opcional en/git-push. - Pilotos iniciales:
apps/kanban,projects/osint_graph/apps/graph_explorer. Resto de apps van migrando segun necesidad.
Anti-patrones
| Anti-patron | Por que es malo |
|---|---|
cmd: "make test" con make-target opaco |
Ilegible. El check debe ser ejecutable directo y auditable. |
| Check que tarda > 5 min sin razon (smoke pesado) | Bloquea iteracion. Mover a CI nocturno con tag slow. |
| Smoke que toca produccion | Riesgo. Smoke usa BD efimera, puertos altos, mocks. |
expect_stdout_contains: "" |
Vacio = siempre pass. No es un check. |
| Anidar checks (uno depende de side-effects de otro sin declararlo) | Frigil. Cada check arranca lo que necesita. |
Usar e2e_checks como sustituto de tests unitarios |
Son cosas distintas. Unit tests viven en *_test.go/pytest. e2e valida que el sistema arranque y haga su trabajo. |
Tabla e2e_runs en operations.db
Cada corrida de fn-analizador se persiste:
CREATE TABLE IF NOT EXISTS e2e_runs (
id TEXT PRIMARY KEY,
app_id TEXT NOT NULL,
started_at INTEGER NOT NULL,
finished_at INTEGER,
status TEXT NOT NULL, -- pass|fail|partial
checks_total INTEGER NOT NULL,
checks_pass INTEGER NOT NULL,
checks_fail INTEGER NOT NULL,
summary_json TEXT NOT NULL
);
Migracion: fn_operations/migrations/006_e2e_runs.sql (issue 0068, paso 3).
Output canonico de fn-analizador
Tabla caveman, una linea por check:
build ✓ 42s
smoke ✓ 0.8s
ops_audit ✓
tests ✗ 12s exit 1, 3/45 failures
assertion:R1 ✗ warning duration drift +47% vs p50
golden:home ✓
Rojo cuando severity: critical y status fail. Esto es lo que el agente principal lee y reenvia al humano.