docs(flows): 0012 fase 1 hecha — watcher dentro de fleetview + cola JSONL

Refleja la decisión real: el watcher no es un daemon aparte sino que se
embebe en fleetview (reutiliza su polling); la cola es JSONL en
~/.claude/fleet/events.jsonl (sin SQLite/CGO).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
agent
2026-06-20 20:02:01 +02:00
parent 28a53ee357
commit 0e93258974
+10 -8
View File
@@ -21,7 +21,7 @@ tags: [orchestration, fleet, dod, multi-agent, watcher]
dod_evidence_schema:
- id: watcher_events
kind: cmd
expected: "sqlite3 apps/fleet_watcher/operations.db 'SELECT count(*) FROM fleet_events WHERE created_at > date(now,-1 day)' > 0; cada fila es una TRANSICION de estado, no un nivel repetido"
expected: "wc -l ~/.claude/fleet/events.jsonl > 0; cada linea es una TRANSICION de estado (edge), no un nivel repetido"
required: true
- id: dod_contract_on_spawn
kind: cmd
@@ -97,14 +97,16 @@ El hook GOAL-TRACKER debe respetar `dod_contract`/`dod_status` (solo reescribe `
## Fases de construcción
### Fase 1 — fleet_watcher (cerebro barato, sin LLM)
### Fase 1 — watcher (cerebro barato, sin LLM) — DENTRO de fleetview [HECHO 2026-06-20]
Service liviano (Go o bash) que corre como step de dag_engine o systemd-user. Cada N segundos:
1. Snapshot del fleet (`fleetview list --json`, extendido con `dod_contract`/`dod_status`/`phase`).
2. Diff contra el snapshot anterior -> transiciones (edge-triggered, no nivel).
3. Clasifica cada transición según la máquina de terminación.
4. Escribe un evento por transición en `apps/fleet_watcher/operations.db` (tabla `fleet_events`).
5. Push inmediato (PushNotification) para clasificación RECLAMA.
Decisión: NO es un daemon aparte. fleetview ya es un proceso vivo que pollea la flota cada segundo y vive mientras la sesión tmux fleet (y por tanto la flota) exista. El watcher se embebe ahí (KISS). En cada refresco de la TUI:
1. Snapshot del fleet (`list_claude_fleet`, con `dod_contract`/`dod_status`/`role`).
2. Clasifica cada agente con `classify_fleet_termination` (función pura del registry).
3. Diff contra el snapshot anterior (en memoria) -> transiciones (edge-triggered, no nivel).
4. Escribe un evento por transición en la cola JSONL `~/.claude/fleet/events.jsonl` (sin SQLite/CGO — KISS). Línea: `{ts, session_id, pid, from, to, goal, phase, urgent}`.
5. Marca `urgent=true` en transición a RECLAMA. El push real al móvil lo hace el orquestador (Fase 2) leyendo la cola; el watcher solo marca.
Estado: modelo de datos (`DodContract`/`DodStatus`/`Role` en `ClaudeFleet`) + `classify_fleet_termination` + watcher embebido (`watcher.go`) — construidos y testeados (7 tests del watcher + 34 del clasificador). Pendiente: validación en vivo (relanzar fleetview con el binario nuevo y observar la cola con agentes reales cambiando de estado).
DoD Fase 1:
- Golden: un agente pasa busy->idle -> aparece 1 evento `DICE-TERMINADO` o `ESTANCADO` en `fleet_events`.