750b7abcd5
- .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>
163 lines
6.3 KiB
Markdown
163 lines
6.3 KiB
Markdown
## 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
|
|
|
|
```yaml
|
|
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
|
|
|
|
```yaml
|
|
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
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
- id: ops_audit
|
|
ref: "fn-recopilador:apps/myapp"
|
|
```
|
|
|
|
Esto invoca al recopilador en modo audit sobre `apps/myapp/operations.db`.
|
|
|
|
### Reglas
|
|
|
|
1. **Idempotente**: cada check debe poderse correr N veces sin efectos secundarios. Usar BDs en `/tmp/`, puertos altos, `--port 0` cuando se pueda.
|
|
2. **Sin credenciales reales**: ningun check toca produccion ni servicios externos sensibles. Si necesita HTTP de prueba, usar `httpbin.org` o un mock local.
|
|
3. **Tiempo acotado**: cada check declara `timeout_s`. Suma total de la app < 10 min como objetivo razonable.
|
|
4. **Determinista**: si el check depende de red flaky, marcalo `severity: warning` o usalo solo como diagnostico, no como gate.
|
|
5. **Cleanup implicito**: si el check arranca un proceso en background (`&`), debe morir al final. `fn-analizador` mata 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:
|
|
|
|
1. Inspecciona `app.md` (lang, framework, entry_point, uses_functions).
|
|
2. Revisa estructura del directorio (presencia de `tests/`, `frontend/`, `Makefile`, `CMakeLists.txt`, etc.).
|
|
3. Audita `operations.db` (si existe) para sugerir `ops_audit`.
|
|
4. Devuelve bloque `e2e_checks_suggested:` listo para copiar al `app.md` tras 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_checks` declarado: `fn doctor` muestra warning, no bloquea nada.
|
|
- Apps CON `e2e_checks`: `fn-analizador` corre la suite. Si critical falla → `fn-mejorador` crea 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:
|
|
|
|
```sql
|
|
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.
|