From f3c0a5c532c2d027645f894a27d9c839223694e6 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Sun, 10 May 2026 01:26:42 +0200 Subject: [PATCH] docs(issues): add 0071 cpp panels roadmap + 0071f subprocess_streamer 0071 cataloga paneles ImGui candidatos a extraccion por tier (rule of three). 0071f es el primer sub-issue activable: subprocess_streamer ya tiene 3 consumidores reales en graph_explorer (chat, jobs, extract_panel). README actualizado con 0068-0071+0071f. Co-Authored-By: Claude Opus 4.7 (1M context) --- ...071-extract-reusable-cpp-panels-roadmap.md | 207 ++++++++++++++++++ .../0071f-extract-subprocess-streamer.md | 105 +++++++++ dev/issues/README.md | 5 + 3 files changed, 317 insertions(+) create mode 100644 dev/issues/0071-extract-reusable-cpp-panels-roadmap.md create mode 100644 dev/issues/0071f-extract-subprocess-streamer.md diff --git a/dev/issues/0071-extract-reusable-cpp-panels-roadmap.md b/dev/issues/0071-extract-reusable-cpp-panels-roadmap.md new file mode 100644 index 00000000..8a2bd6b0 --- /dev/null +++ b/dev/issues/0071-extract-reusable-cpp-panels-roadmap.md @@ -0,0 +1,207 @@ +--- +id: 0071 +title: Extraer paneles ImGui reutilizables de las apps C++ a cpp/functions/ — roadmap +status: pending +priority: medium +created: 2026-05-09 +related_apps: [graph_explorer, registry_dashboard, odr_console, navegator_dashboard] +related_issues_app: [navegator/0001] +--- + +## Contexto + +Las apps C++ del registry (`cpp/apps/*`, `projects/*/apps/*`) llevan acumulando paneles ImGui que comparten patron pero viven dentro de cada app. Hay duplicacion observable y mas que va a aparecer (navegator_dashboard quiere chat panel igual que graph_explorer; el dashboard de Tabs va a parecerse al panel "Browsers"). + +Hoy ya estan en el registro `cpp/functions/core/`: `dashboard_panel`, `fullscreen_window`, `log_window`, `modal_dialog`, `tree_view`, `selectable_text`. La capa visual (tokens, theming, iconos, settings/about/menubar) vive en `fn_framework`. Esto cubre primitivas. Falta una tier intermedia: **paneles compuestos** (mas grandes que un widget, mas pequeños que una app). + +Este issue documenta el roadmap de que extraer, cuando, y por que. + +## Principios + +1. **Rule of three** — no extraer hasta que haya 2-3 consumidores reales. Pre-extraer crea abstracciones malas. +2. **Consumidor primero** — escribir el panel inline en la app. Si una segunda app lo necesita, copiar+adaptar. Si una tercera lo pide, extraer. +3. **Parametrizable, no monolitico** — los paneles extraidos reciben un struct de config (callbacks, paths, env vars) en vez de hardcodear nombres. +4. **No exponer estado global del modulo extraido** — usar contexto opaco que cada app crea/destruye. + +## Catalogo: paneles candidatos detectados + +Revisado todas las apps C++ vivas. Por cada panel: lineas, consumidores actuales, consumidores potenciales, decision. + +### Tier 0 — ya extraidos ✓ + +| Panel | Ubicacion | +|---|---| +| dashboard cards grid | `cpp/functions/core/dashboard_panel.{h,cpp}` | +| fullscreen window wrapper | `cpp/functions/core/fullscreen_window.{h,cpp}` | +| log window con buffer | `cpp/functions/core/log_window.{h,cpp}` | +| modal dialog | `cpp/functions/core/modal_dialog.{h,cpp}` | +| tree view | `cpp/functions/core/tree_view.{h,cpp}` | +| selectable text | `cpp/functions/core/selectable_text.{h,cpp}` | + +Estos definen el "lower-tier" — primitivas. Los nuevos extracts componen sobre estos. + +### Tier 1 — extraer cuando aparezca el 3er consumidor + +#### 1.1 `claude_chat_panel` (origen: graph_explorer/chat.cpp, 1241 LoC) + +**Que es:** panel ImGui que arranca un subprocess `claude -p`, le envia mensajes del usuario, parsea stream-json, renderiza historial con tool-use detectado, expone callbacks para que la app recargue cuando el agente muta su BD. + +**Consumidores hoy:** graph_explorer. + +**Consumidores potenciales:** +- navegator_dashboard (issue navegator/0001) — chat para controlar Chrome. +- kanban (futuro) — agente para crear/mover cards. +- registry_dashboard — agente para crear functions/proposals. + +**Por que tier 1 (no tier 0):** +- 59 referencias a `gx-cli`, `GX_OPS_DB`, `GX_APP_DB`, `agent_mutations`. Acoplamiento alto a graph_explorer. +- Refactor a config parametrizado es ~4-6h y modifica codigo que YA funciona — riesgo regresion. +- Con solo 2 consumidores no hay claridad de que parametrizar. + +**Cuando extraer:** 3er consumidor pide chat. Hasta entonces: copiar+adaptar (graph_explorer mantiene el suyo, navegator copia el suyo). + +**API propuesta cuando se extraiga:** + +```cpp +struct ClaudeChatConfig { + const char* claude_bin; // "claude" o WSL path + const char* cli_tool_name; // "gx-cli", "cdp-cli", "kanban-cli", ... + const char* cli_tool_dir; + std::map env_vars; // GX_*, NAVD_*, ... + const char* mcp_config_path; // o func que lo genera on-demand + const char* system_prompt; // opcional + std::function mutations_counter; // app-side reload trigger + const char* log_file_path; +}; + +namespace fn_ui { + struct ChatHandle; // opaco + ChatHandle* chat_create(const ClaudeChatConfig& cfg); + void chat_send(ChatHandle*, const char* user_text); + void chat_render(ChatHandle*, bool* p_open); + void chat_destroy(ChatHandle*); +} +``` + +#### 1.2 `jobs_queue_panel` (origen: graph_explorer/jobs.cpp + odr_console/jobs) + +**Que es:** panel + sistema de jobs asincronos. Lista jobs en cola/running/done con barra de progreso, stdout/stderr capture, cancelable, persistente entre sesiones. + +**Consumidores hoy:** graph_explorer (jobs.cpp + views_jobs.cpp), odr_console (panel Jobs). + +**Consumidores potenciales:** +- navegator_dashboard — scraping jobs (un crawl es un job). +- registry_dashboard — fn index, fn run jobs. + +**Por que tier 1:** ya hay 2 consumidores, parece duplicacion clara. **Inspeccionar diff entre ambos antes de extraer** — pueden estar implementados distinto y no reusarse del todo. + +**Cuando extraer:** despues de auditar que graph_explorer/jobs.cpp y odr_console/jobs comparten >70% del codigo. Si si: 3er consumidor (navegator scraping) detona la extraccion. + +**API propuesta:** + +```cpp +namespace fn_ui { + struct Job { + std::string id; + std::string title; + std::function run; // retorna exit code + // ... + }; + void jobs_panel_init(const char* persistence_db_path); + void jobs_panel_submit(const Job&); + void jobs_panel_render(bool* p_open); +} +``` + +### Tier 2 — extraer cuando aparezca el 2do consumidor (todavia no detectado) + +#### 2.1 `project_switcher_panel` (origen: graph_explorer/project_manager.cpp) + +**Que es:** sidebar/menu para cambiar entre subcarpetas de proyectos (cada proyecto tiene su DB y settings). Persiste el ultimo proyecto abierto. + +**Hoy en:** graph_explorer. + +**Potencial:** navegator_dashboard (perfiles browser como proyectos), registry_dashboard (apps como proyectos), kanban (boards). + +**Cuando extraer:** cuando navegator_dashboard quiera multi-perfil persistente con settings por perfil. v3 del dashboard probablemente. + +#### 2.2 `key_value_inspector_panel` (origen: graph_explorer/views.cpp `Inspector`) + +**Que es:** panel "Inspector" generico que muestra propiedades de un objeto seleccionado en formato key:value, con edicion inline opcional. Hoy es ad-hoc para entities. + +**Potencial:** navegator_dashboard (Tab Detail mostrar propiedades de pestaña), registry_dashboard (function detail), kanban (card details). + +**Cuando extraer:** cuando dos paneles de "ver propiedades de cosa seleccionada" tengan codigo casi identico. + +#### 2.3 `paste_and_extract_panel` (origen: graph_explorer/extract_panel.cpp) + +**Que es:** panel que recibe texto pegado, invoca enricher Python, muestra entities extraidas + boton para insertarlas. + +**Potencial:** navegator_dashboard (extraer texto seleccionado de pestaña), registry_dashboard (extraer descripcion para auto-fill function metadata). + +**Cuando extraer:** 2do consumidor con flujo similar. + +#### 2.4 `http_request_inspector_panel` (origen: registry_dashboard/http_client.cpp + futuro navegator_dashboard/network) + +**Que es:** panel para ver una peticion HTTP (request line, headers, body) renderizado bonito. Read-only. + +**Potencial:** navegator_dashboard (Network panel renderiza HAR entries), registry_dashboard (mostrar requests outgoing del fn_registry_api). + +**Cuando extraer:** cuando 0070b (HAR record) sea consumido por algo que quiera renderizarlo bien. + +### Tier 3 — NO extraer (especificos de su app) + +| Panel | Por que NO | +|---|---| +| graph_explorer/types_registry | Acoplado a `types.yaml` schema especifico. | +| graph_explorer/node_groups | DuckDB + grafo + tipo "Table" del schema graph_explorer. | +| graph_explorer/layout_store | Posiciones de nodos del grafo, schema especifico. | +| graph_explorer/views.cpp::Legend | Mapa de tipos→colores especifico del grafo. | +| graph_explorer/views.cpp::Stats | Conteos por tipo de entity, schema especifico. | +| navegator_dashboard/browsers panel | Detector chrome.exe Win32, hereda WMI/CIM, especifico Windows. | +| odr_console/datasets panel | Listado especifico del flujo ODR. | + +Estos viven en su app. Si emerge un patron despues, se reconsidera. + +### Tier 4 — primitivas que sirven a TODOS los paneles (extraccion oportunista, sin app-driver) + +Util tener antes de los Tier 1-2 para hacer la extraccion mas limpia: + +| Primitiva | Para que | +|---|---| +| `subprocess_streamer.{h,cpp}` | Encapsula spawn + pipes + read-thread + queue. Lo usaria chat_panel + jobs_panel. | +| `mcp_config_writer.{h,cpp}` | Genera `.mcp.json` para Claude. Lo usaria chat_panel. | +| `app_db_init.{h,cpp}` | Patron generico de "abrir SQLite junto al exe + aplicar migrations embed.FS". Hoy cada app reimplementa. | +| `key_input_history.{h,cpp}` | Input + flecha arriba/abajo recupera entradas previas. Util en chat + REPL evaluate. | +| `json_log_renderer.{h,cpp}` | Render de stream JSON estilo claude-code (rol, tool_use, contenido). Util tanto en chat de graph_explorer como en futuros chats. | + +Estas primitivas se extraen cuando empieza la extraccion de un Tier 1 que las necesita (no antes — caen en el "rule of three" tambien). + +## Estrategia de adopcion + +Por **demanda**: + +1. **Hoy (tras este issue):** copiar+adaptar `chat.cpp` a `navegator_dashboard`. Documentar la duplicacion. NO extraer. +2. **Cuando aparezca 3er consumidor de chat** (o `jobs`, o `project_switcher`): pausar feature work, refactorizar el extract con vista 3D. +3. **Auditoria periodica trimestral**: cada 3 meses revisar este issue, contar consumidores actuales, decidir si activar extracts. + +## Sub-issues iniciales (no abrir hasta tener 3er consumidor identificado) + +- `0071a` — extract `claude_chat_panel` (cuando 3 apps lo pidan). +- `0071b` — extract `jobs_queue_panel` (auditar duplicacion graph_explorer/odr_console primero). +- `0071c` — extract `project_switcher_panel`. +- `0071d` — extract `key_value_inspector_panel`. +- `0071e` — extract `http_request_inspector_panel`. + +## Definicion de hecho (issue 0071 cerrado) + +- Catalogo arriba esta vivo, refleja apps actuales. +- Auditoria trimestral mantiene la lista actualizada. +- Sub-issues 0071a-e: cada uno abre cuando se cumple su criterio. +- Cuando todos los Tier 1 + 2 esten extraidos, este issue se marca completed. + +## Anti-patron a evitar + +- **Big-bang refactor**: extraer 5 paneles a la vez, con interfaces no validadas, rompiendo todas las apps. NO. +- **Pre-extraer "por si acaso"**: el panel mas dificil es el que solo tiene 1 consumidor — no sabes que parametrizar. NO. +- **Library wrapper sin control**: meter `claude_chat_panel` con 30 hooks/callbacks "para flexibilidad". KISS — solo expone lo que el 3er consumidor demuestra que necesita. diff --git a/dev/issues/0071f-extract-subprocess-streamer.md b/dev/issues/0071f-extract-subprocess-streamer.md new file mode 100644 index 00000000..417fe091 --- /dev/null +++ b/dev/issues/0071f-extract-subprocess-streamer.md @@ -0,0 +1,105 @@ +--- +id: 0071f +title: Extraer `subprocess_streamer` a cpp/functions/core/ (sub-issue de 0071) +status: pending +priority: media +created: 2026-05-10 +parent: 0071 +related_apps: [graph_explorer] +--- + +## Contexto + +Sub-issue derivado de 0071 tras auditar paneles C++. La regla "rule of three" se cumple HOY para una primitiva Tier 4: spawn de subprocesos con captura de stdout/stderr en stream, sin wrapper compartido. + +Tres reimplementaciones del mismo patron en `projects/osint_graph/apps/graph_explorer/`: + +| Sitio | Linea | Mecanismo | +|---|---|---| +| `chat.cpp` | 295 | `popen(...)` + `fgets` loop | +| `chat.cpp` | 412 | `execvp(...)` con pipes manuales | +| `jobs.cpp` | 817 | `execvp(...)` con pipes + read-thread | +| `extract_panel.cpp` | 507 | `execvp(...)` para Python enricher | + +Cada una hace: setup pipes, fork/exec, read loop, parse JSON line-by-line, push a UI queue. Codigo duplicado ~60-80 LoC en cada sitio. + +## Objetivo + +Funcion `subprocess_streamer` en `cpp/functions/core/` que encapsule: +- Spawn (POSIX `fork+execvp`, Windows `CreateProcess`) +- Pipes para stdin/stdout/stderr +- Read-thread con callback por linea +- Cancel/kill +- Wait + exit code + +## API propuesta + +```cpp +namespace fn_core { + +struct SubprocessConfig { + std::vector argv; // [exe, arg1, arg2, ...] + std::vector env; // ["KEY=VAL", ...]; vacio = heredar + std::string cwd; // vacio = heredar + bool merge_stderr_to_stdout = false; + std::function on_stdout_line; + std::function on_stderr_line; + std::function on_exit; +}; + +struct SubprocessHandle; // opaco + +// Lanza subproceso. on_* se llaman desde un read-thread interno. +SubprocessHandle* subprocess_spawn(const SubprocessConfig& cfg); + +// Escribe en stdin. Thread-safe. Devuelve bytes escritos o -1. +int subprocess_write_stdin(SubprocessHandle* h, const char* data, size_t n); + +// Cierra stdin (EOF al child). +void subprocess_close_stdin(SubprocessHandle* h); + +// Mata el proceso (SIGTERM en POSIX, TerminateProcess en Win). +void subprocess_kill(SubprocessHandle* h); + +// Espera fin. Devuelve exit_code. Si ya termino, retorna inmediato. +int subprocess_wait(SubprocessHandle* h); + +// Libera recursos. Si el proceso sigue vivo, lo mata primero. +void subprocess_destroy(SubprocessHandle* h); + +} // namespace fn_core +``` + +## Tests + +- Spawn `echo hello` → on_stdout_line recibe "hello", exit 0. +- Spawn `cat` con stdin "abc\n" → on_stdout_line recibe "abc", exit 0 tras close_stdin. +- Spawn `false` → exit 1. +- Spawn comando inexistente → handle nullable o exit code distintivo. +- Kill proceso vivo → wait retorna codigo de señal. + +`cpp/apps/primitives_gallery/` añade demo "Subprocess Streamer" con boton "spawn echo hello" + lista de lineas recibidas. + +## Migracion de consumidores (orden) + +1. **Crear** `cpp/functions/core/subprocess_streamer.{h,cpp}` + `.md` + tests. +2. **Migrar `chat.cpp`** primero (mas critico, define el flujo de error). Si funciona en chat → patron validado. +3. **Migrar `jobs.cpp`**. +4. **Migrar `extract_panel.cpp`**. + +Cada migracion en su propio commit, validando por inspeccion manual que el panel sigue funcionando. + +## Definicion de hecho + +- `subprocess_streamer.{h,cpp,md}` registrado en `registry.db` (`fn index`). +- Tests pasan (al menos los 5 listados). +- 3 consumidores migrados (chat, jobs, extract_panel). +- `app.md` de graph_explorer actualizado: `uses_functions` añade `subprocess_streamer_cpp_core`. +- LoC eliminadas en graph_explorer: ~150-200 (3 reimplementaciones colapsadas a 1 import). + +## Anti-patrones a evitar + +- Async/promesa con `std::future` — over-engineering. Callbacks bastan. +- Buffering "smart" line-aware sobre bytes raw — usar `std::getline` simple. +- Soporte ptys (terminal real) — fuera de scope. Si una app necesita pty, abre issue propio. +- Prematura abstraccion de "shell vs exec". Solo `argv` directo (execvp). Si alguien quiere shell pipes, lo hace en `bash -c "..."`. diff --git a/dev/issues/README.md b/dev/issues/README.md index 0b888cdd..9957504c 100644 --- a/dev/issues/README.md +++ b/dev/issues/README.md @@ -83,3 +83,8 @@ | [0065](0065-extract-jobs-system-to-registry.md) | Extraer jobs system de graph_explorer al registry (jobs_pool + cache + subprocess worker) | pendiente | alta | refactor | 0066 | | [0066](0066-online-data-recopilation-mvp.md) | online_data_recopilation — odr_console MVP (lanzador GUI + 5-pasos + 1 collector) | pendiente | alta | feature | — | | [0067](0067-odr-osint-prereqs-roadmap.md) | Roadmap de prereqs — issues de osint_graph que odr_console necesita antes/durante MVP | pendiente | alta | planning | — | +| [0068](0068-e2e-validation-loop-fn4-fn5.md) | Validacion e2e fase 4-5 del bucle reactivo (fn-analizador + fn-mejorador) | pendiente | alta | feature | — | +| [0069](0069-autonomous-agent-loop-self-iterating-tasks.md) | Bucle autonomo de agente — tareas auto-iterativas | pendiente | media | feature | — | +| [0070](0070-browser-helpers-global-roadmap.md) | Roadmap helpers globales para browser/CDP | pendiente | media | planning | — | +| [0071](0071-extract-reusable-cpp-panels-roadmap.md) | Roadmap de extraccion de paneles ImGui reutilizables a cpp/functions/ | pendiente | media | planning | — | +| [0071f](0071f-extract-subprocess-streamer.md) | Extraer `subprocess_streamer` a cpp/functions/core/ (sub-issue de 0071) | pendiente | media | refactor | parte de 0071 |