From 4ab3678b4559fe5d7c20a7389968282073f6797b Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Mon, 18 May 2026 18:31:45 +0200 Subject: [PATCH] =?UTF-8?q?docs(viz):=20agent=5Fruns=5Ftimeline.md=20?= =?UTF-8?q?=E2=80=94=20frontmatter=20+=20self-doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- cpp/functions/viz/agent_runs_timeline.md | 113 +++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 cpp/functions/viz/agent_runs_timeline.md diff --git a/cpp/functions/viz/agent_runs_timeline.md b/cpp/functions/viz/agent_runs_timeline.md new file mode 100644 index 00000000..b23e5a90 --- /dev/null +++ b/cpp/functions/viz/agent_runs_timeline.md @@ -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 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`).