---
id: "0118"
title: "Funcion cpp agent_runs_timeline: panel SSE de runs cross-app"
status: pendiente
type: feature
domain:
  - cpp-stack
  - agents
  - registry-quality
scope: registry
priority: alta
depends:
  - "0110"
  - "0113"
blocks: []
related:
  - "0008"
  - "0112"
  - "0116"
  - "0117"
created: 2026-05-18
updated: 2026-05-18
tags: [agents, timeline, sse, cpp, imgui, viz, registry-gap]
flow: "0008"
---

# 0118 — Funcion `agent_runs_timeline_cpp_viz`

## Problema

Panel de timeline de runs es identico en kanban_cpp (0112) y skill_tree v2 (0116). Sin funcion compartida = duplicacion.

## Decision

Funcion C++ en `cpp/functions/viz/agent_runs_timeline.{cpp,h,md}`. API:

```cpp
namespace fn_viz {
    struct AgentRun {
        std::string id;
        std::string app;            // "kanban_cpp" | "skill_tree" | ...
        std::string issue_id;
        std::string card_id;
        std::string branch;
        std::string status;          // "pending" | "running" | "done" | "validated" | "merged" | "aborted" | "failed"
        int64_t     started_at;
        int64_t     finished_at;
        int         dod_total;
        int         dod_done;
        int         dod_validated;
    };
    struct TimelineFilter {
        std::vector<std::string> apps;        // vacio = todos
        std::vector<std::string> statuses;    // vacio = todos
        int64_t since_ts;                     // 0 = sin filtro
    };
    struct TimelineState {
        std::string sse_url;                  // ej "http://localhost:8486/api/runs/sse"
        std::vector<AgentRun> runs;
        TimelineFilter filter;
        std::function<void(const std::string&)> on_select;  // callback(run_id)
    };
    void render_agent_runs_timeline(TimelineState& state);
    void poll_sse_runs(TimelineState& state);  // llamar 1x/frame, no bloquea
}
```

Renderiza tabla con `data_table_cpp_viz`:
- Cols: status (icon), app (chip color), issue/card, branch, dod_done/total, dod_validated/total, duration, started_at.
- Sort por started_at desc por defecto.
- Click row -> `on_select(run_id)`.
- Filtros arriba: combo apps multi-select + combo statuses + date picker `since`.
- SSE en background thread: append runs nuevos, update statuses en vivo.

## Criterios de aceptacion

- [ ] `cpp/functions/viz/agent_runs_timeline.{cpp,h,md}` registrado.
- [ ] `params_schema` y `output` completos.
- [ ] Tag `agents` aplicado.
- [ ] `.md` cumple contrato self-doc (`## Ejemplo`, `## Cuando usarla`, `## Gotchas`).
- [ ] SSE client en background thread con reconnect + `Last-Event-ID` resume.
- [ ] Demo en `cpp/apps/primitives_gallery/demos_viz.cpp` con TimelineState mock (5 runs varied status).
- [ ] `kanban_cpp::panel_agent_runs` usa la funcion.
- [ ] `skill_tree` `panel_timeline` usa la funcion.
- [ ] `fn doctor capabilities` muestra grupo `agents` con esta funcion.
- [ ] `uses_functions` declara: `http_request_cpp_core`, `data_table_cpp_viz`, `icons_tabler_cpp_core`, `tokens_cpp_core`.

## Gotchas

- SSE reconnect: si `agent_runner_api` cae, reintenta cada 5s con backoff. Estado `disconnected` visible.
- Thread-safety: SSE thread escribe `state.runs`; UI thread lee. Mutex o cola SPSC.
- Memory: si runs > 1000 historicos, paginar (LRU 200 visibles). Antiguos se descargan on-demand via `GET /api/runs?since=...`.
- App `chip color`: mapear app_name -> accent del trio (kanban_cpp `#a855f7`, skill_tree color actual). Cache.
- Click row durante streaming SSE: no resetear seleccion al refrescar lista; preservar por `run_id`.

## Out of scope

- Editor inline de DoD desde timeline (eso es panel DoD 0117).
- Export CSV/JSON.
- Notificaciones desktop al cambiar status.
