---
id: "0068"
title: "Cerrar bucle reactivo — fn-analizador (fase 4) y fn-mejorador (fase 5) con contrato e2e_checks"
status: completado
type: feature
domain:
  - meta
scope: multi-app
priority: alta
depends: []
blocks: []
related:
  - "22"
  - "23"
  - "0028"
created: 2026-05-09
updated: 2026-05-17
tags: []
---

## Cierre 2026-05-14

Toda infra implementada y operativa:

- Migration `fn_operations/migrations/005_e2e_runs.sql` aplicada.
- Funcion `e2e_run_checks_go_infra` (Cmd/Health/Ref con expect_exit/stdout_contains, background via `&`). Tipos `E2ECheck_go_infra` + `CheckResult_go_infra`.
- Subagentes `fn-analizador` (Fase 4) y `fn-mejorador` (Fase 5) en `.claude/agents/`.
- `fn-recopilador` extendido con modo `design-e2e <app_id>`.
- Skill `/validate-app <app_id>` orquesta cadena completa.
- Regla `.claude/rules/e2e_validation.md` documenta contrato + patrones por stack.
- Pilotos: `apps/kanban/app.md` y `projects/osint_graph/apps/graph_explorer/app.md` declaran `e2e_checks`.
---

## Contexto

El bucle reactivo del registry (CONSTRUIR → EJECUTAR → RECOPILAR → ANALIZAR → MEJORAR) tiene agentes para fases 1-3:

- **Fase 1 — CONSTRUIR**: `fn-constructor` (existe)
- **Fase 2 — EJECUTAR**: `fn-executor` (existe)
- **Fase 3 — RECOPILAR**: `fn-recopilador` (existe)
- **Fase 4 — ANALIZAR**: falta agente
- **Fase 5 — MEJORAR**: falta agente

Sin fases 4 y 5 el bucle no cierra. Cada app sigue requiriendo iteracion manual: el humano lanza, mira, decide si funciona, y propone fixes. Objetivo: que apps lleguen a master correctas sin esa iteracion manual.

## Objetivo

Disponer de un gate automatico pre-merge que valide end-to-end cualquier app del registry, y un mejorador que proponga cambios cuando la validacion falla. Capacidad reutilizable, no especifica de un proyecto.

## Diseno

### Contrato `e2e_checks` en `app.md`

Cada app declara su validacion end-to-end en el frontmatter:

```yaml
e2e_checks:
  - id: build
    cmd: "cd frontend && pnpm build && cd .. && CGO_ENABLED=1 go build -tags fts5 -o kanban ."
    timeout_s: 120
  - id: smoke
    cmd: "./kanban --port 8095 --db /tmp/kanban_e2e.db &"
    health: "http://127.0.0.1:8095/api/board"
    timeout_s: 10
  - id: ops_audit
    ref: "fn-recopilador:apps/kanban"
  - id: migrations
    cmd: "sqlite3 /tmp/kanban_e2e.db 'SELECT version FROM schema_migrations;'"
    expect_exit: 0
```

Tipos de check:
- `cmd` — comando shell, exit 0 = OK.
- `health` — espera `cmd` en background, hace GET y verifica 200.
- `ref` — apunta a otro agente/funcion del registry (ej. `fn-recopilador`, `fn-doctor`).
- `expect_exit`, `expect_stdout_contains`, `expect_stdout_json` — predicados sobre la salida.

### fn-analizador (fase 4)

Subagente nuevo. Input: `app_id`. Pasos:

1. Lee `e2e_checks` del `app.md`.
2. Ejecuta cada check en orden, captura stdout/stderr/exit/duration.
3. Eval assertions activas via `fn ops assertion eval --react`.
4. Compara `executions.metrics` actual vs ventana historica (drift > umbral = warning).
5. Diff golden outputs si app declara `tests/golden/`.
6. Persiste resultado en nueva tabla `e2e_runs` de `operations.db`.
7. Devuelve veredicto caveman:
   ```
   build           ✓ 42s
   smoke           ✓ 0.8s
   ops_audit       ✓
   assertion:R1    ✗  warning  duration drift +47% vs p50
   ```

Tools: Read, Bash, Grep, Glob. Escribe SOLO `assertion_results`, `entity.status`, `e2e_runs`. NO toca registry.db.

### fn-mejorador (fase 5)

Subagente nuevo. Input: salida de `fn-analizador` + `app_id`. Pasos:

1. Filtra fallos: `critical` → `kind=bug`, `warning` → `kind=optimization`.
2. Por cada fallo, busca contexto en registry: funciones tocadas, ultimas N proposals.
3. Crea proposal con `created_by=reactive_loop`, `evidence=[assertion_id, execution_id, e2e_run_id]`.
4. Sugiere fix concreto (parametro, funcion a partir, refactor) — texto, NO codigo.
5. Si fallo recurrente (>3 veces misma assertion) → `priority=high`.

Tools: Read, Bash, Grep. Escribe SOLO `proposals` en registry.db. Nunca modifica funciones.

### Skill `/validate-app <app_id>`

Orquesta cadena: `fn-executor` → `fn-recopilador` → `fn-analizador` → `fn-mejorador`. Devuelve tabla pass/fail + IDs de proposals creadas.

### Migracion `006_e2e_runs.sql`

```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         -- detalle por check
);
CREATE INDEX IF NOT EXISTS e2e_runs_app_idx ON e2e_runs(app_id, started_at DESC);
```

### Funciones nuevas del registry

Delegables a `fn-constructor`:

| ID | Domain | Que hace |
|---|---|---|
| `e2e_run_checks_go_infra` | infra | Ejecuta lista de checks, devuelve `[]CheckResult` |
| `golden_diff_go_core` | core | Compara archivo vs golden con umbral |
| `metrics_drift_go_datascience` | datascience | p50/p95 historico vs actual |
| `proposal_from_failure_go_infra` | infra | Formatea evidencia → proposal |
| `health_probe_go_infra` | infra | GET con timeout, retry, codigo esperado |

Tipos:

| ID | Que |
|---|---|
| `E2ECheck_go_infra` | struct check (id, cmd, ref, health, expect_*) |
| `CheckResult_go_infra` | struct resultado (id, status, duration_ms, stdout, stderr, exit) |

### fn-recopilador como diseñador de checks

`fn-recopilador` se extiende para **proponer e2e_checks por app** (modo design):

- Inspecciona `app.md` (lang, framework, entry_point, uses_functions).
- Detecta patrones conocidos:
  - Go service con frontend Vite → propone build (pnpm + go), smoke (puerto + endpoint health).
  - C++ ImGui app → propone build (cmake), smoke (`--self-test` o lanzar y matar tras N segundos).
  - Python pipeline → propone run con args dummy y verificar exit 0.
- Audita `operations.db` y deriva `ops_audit` automatico.
- Escribe propuesta en `app.md` como bloque `e2e_checks_suggested:` para que humano apruebe → renombre a `e2e_checks:`.

Comando: `fn-recopilador design-e2e <app_id>`.

Asi `fn-analizador` recibe contratos completos de fabrica y solo necesita ejecutar.

## Plan de ejecucion

| Paso | Tarea | Estado |
|---|---|---|
| 1 | Contrato `e2e_checks` en `docs/templates/app.md` + 2 apps piloto (kanban, graph_explorer) | en curso |
| 2 | Funciones registry: `e2e_run_checks`, `golden_diff`, `metrics_drift`, `proposal_from_failure`, `health_probe` | pendiente |
| 3 | Migracion `006_e2e_runs.sql` en `fn_operations/migrations/` | pendiente |
| 4 | Subagente `fn-analizador` + skill `/validate-app` | pendiente |
| 5 | Extender `fn-recopilador` con modo `design-e2e` | pendiente |
| 6 | Subagente `fn-mejorador` | pendiente |
| 7 | Gate opcional en `/git-push` para apps con `e2e_checks` declarado | pendiente |

## Criterios de aceptacion

- [x] Template `docs/templates/app.md` con seccion `e2e_checks` documentada.
- [x] `apps/kanban/app.md` declara `e2e_checks` (build_frontend + build_backend + smoke + tests + ops_audit).
- [x] `projects/osint_graph/apps/graph_explorer/app.md` declara `e2e_checks` (build + self_test + pytest + enricher smoke).
- [x] `fn-recopilador` puede sugerir `e2e_checks` para una app dada (modo `design-e2e`).
- [x] `fn-analizador` corre los checks y devuelve veredicto caveman.
- [x] `fn-mejorador` crea proposals con evidencia cuando hay fallos.
- [x] Skill `/validate-app <app_id>` orquesta la cadena completa.
- [x] Documentacion en `.claude/rules/e2e_validation.md`.

## Riesgos

- **Golden tests para C++/UI son caros**. Empezar build+smoke+assertions; goldens solo donde aporten (graph_explorer ya tiene capture system).
- **Apps sin operations.db** (kanban usa `kanban.db` propia, no `operations.db`). El check `ops_audit` debe aceptar BD alternativa o saltarse.
- **Smoke tests con puertos en uso**. Los pilotos deben usar puertos efimeros (`--port 0` o range alto) y BDs en `/tmp/`.
- **Adopcion gradual**. Apps sin `e2e_checks` no se validan (y no bloquean merge). Visible en `fn doctor`.

## Out of scope

- Reemplazar `fn doctor` (que sigue siendo diagnostico read-only del estado).
- Tests unitarios de funciones (siguen en `*_test.go`, `pytest`, etc.).
- Performance benchmarks formales (los `metrics_drift` son aproximacion, no benchmark).
