docs(viz): agent_runs_timeline.md — frontmatter + self-doc

Issue 0118. Frontmatter completo (kind, lang, domain, version, purity,
signature, params, tags=agents/timeline/sse/imgui/viz/panel,
uses_functions=http_request_cpp_core, error_type, tested).

Secciones obligatorias por contrato self-doc:
- ## Ejemplo — wiring concreto con TimelineState g_state + lock + render
- ## Cuando usarla — dashboard cross-app de agentes
- ## Gotchas — SSE stub, ts en segundos, mutex obligatorio, no autoscroll,
  ImGuiSelectableFlags_AllowOverlap (rename desde AllowItemOverlap),
  app chip hex hardcoded, since_ts no determinista en tests

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 18:31:45 +02:00
parent ecd864f2d3
commit da56085e74
+113
View File
@@ -0,0 +1,113 @@
---
name: agent_runs_timeline
kind: function
lang: cpp
domain: viz
version: 0.1.0
description: "Panel ImGui timeline de agent runs cross-app, filtros + sort + click row + connection badge"
tags: [agents, timeline, sse, imgui, viz, panel]
purity: impure
signature: "fn_viz::render_agent_runs_timeline(state) — ImGui draw call"
params:
- name: state
desc: "TimelineState con runs, filter, callbacks. Mutex protege runs ante el thread del SSE/poll"
output: "void — pinta el panel inline en el current ImGui window"
uses_functions:
- http_request_cpp_core
uses_types: []
returns: []
returns_optional: false
error_type: error_go_core
imports: [imgui]
example: "ver ## Ejemplo abajo; demo visual TODO en cpp/apps/primitives_gallery/demos_viz.cpp"
tested: true
tests: [test_agent_runs_timeline]
test_file_path: "cpp/tests/test_agent_runs_timeline.cpp"
file_path: "cpp/functions/viz/agent_runs_timeline.cpp"
framework: "imgui"
---
# agent_runs_timeline
Panel ImGui que pinta una tabla cross-app de "agent runs" (ejecuciones de
`fn-orquestador` / `/autonomous-task` / hooks que disparan agentes) con:
- Filtros: multi-select de **apps**, multi-select de **statuses**, slider
`Since (days)` para acotar `started_at`.
- **Connection badge** (● verde / ○ rojo / ◐ amarillo) segun
`state.connection_status`.
- Tabla ordenada por `started_at` DESC con columnas:
status icon | app (chip coloreado) | issue/card | branch | dod
(badge `done/validated/total`) | duration | started (humano).
- Row click → setea `state.selected_run_id` e invoca `state.on_select(id)`
si existe.
- Footer: contadores por status sobre el set FULL (no el filtrado).
La logica pura (filter / sort / formato duration / mapping status→color/icon
/ app→chip hex) vive en `agent_runs_timeline_helpers.{h,cpp}` y es testable
sin contexto ImGui (ver `cpp/tests/test_agent_runs_timeline.cpp`).
## Ejemplo
```cpp
#include "viz/agent_runs_timeline.h"
static fn_viz::TimelineState g_runs_state;
// En setup (una vez):
g_runs_state.sse_url = "http://127.0.0.1:8497/api/runs/stream";
g_runs_state.on_select = [](const std::string& run_id) {
open_run_detail_window(run_id);
};
// Por algun mecanismo (test fixture, http poll, future SSE) — poblar runs:
{
std::lock_guard<std::mutex> lk(g_runs_state.runs_mutex);
g_runs_state.runs = {
{"r1", "kanban_cpp", "0118", "c1", "issue/0118", "running",
/*started*/ 1731920000, /*finished*/ 0, /*dod*/ 5, 2, 0},
};
g_runs_state.connection_status = "connected";
}
// En render (cada frame):
ImGui::Begin("Agent runs");
fn_viz::render_agent_runs_timeline(g_runs_state);
ImGui::End();
```
## Cuando usarla
Cuando construyas un dashboard cross-app que exponga el estado de los agentes
(orquestador, autonomous-task, hooks reactivos): registry_dashboard, kanban,
agent_runner_ui. La quieres siempre que el usuario necesite una vista unica
"que esta corriendo / que merge / que fallo" sin abrir N apps. Tambien util
como panel `Recent runs` dentro de una app que dispara agentes propios.
## Gotchas
- **SSE client NO esta implementado.** `poll_sse_runs()` es un stub
documentado. Hoy el consumer tiene que poblar `state.runs` por su cuenta
(fixture, orquestador in-proc, o polling manual con
`http_request_cpp_core` contra `GET /api/runs`). Cuando exista un endpoint
estable `/api/runs/stream`, conectar via libcurl multi en thread aparte y
empujar updates bajo `state.runs_mutex`.
- **Timestamps en segundos epoch** (no ms). `format_duration` asume `int64_t`
unix-epoch seconds; si tu fuente emite ms, divide entre 1000 antes de
rellenar `started_at`/`finished_at`.
- **`runs_mutex` es obligatorio** cuando algun thread distinto del UI escribe
`state.runs` o `state.connection_status`. El render hace un snapshot bajo
lock al principio del frame para no bloquear el resto del dibujo.
- **No autoescroll**. Si la tabla crece y queres mantener "lo mas reciente
visible", anade tu scroll-to-top tras detectar `runs.size()` cambia.
Decision deliberada: muchas vistas prefieren no saltar mientras lees.
- **Selectable + SpanAllColumns**: usa `ImGuiSelectableFlags_AllowOverlap`
(Dear ImGui >= 1.91). En forks viejos pasaba a llamarse
`AllowItemOverlap` — si tu vendor de imgui es viejo, sustituye en
`agent_runs_timeline.cpp`.
- **App chip colors** estan hardcodeados en `app_chip_hex`. Anadir entrada
cuando salga una app nueva; sin entrada cae a gris neutral. Mantener el
hex aligned con `icon.accent` del `app.md` de cada app C++.
- **Filter "Since (days)"** convierte a/desde epoch usando `std::time(nullptr)`:
no determinista en tests. Por eso los tests del helper pasan `since_ts`
directamente en segundos epoch ficticios (ej. `f.since_ts = 1000`).