Files
fn_registry/dev/issues/0071a-extract-claude-chat-panel.md
T
egutierrez 0c1727fef7 docs(issues): add 0071a chat, 0071b jobs, 0071g app_db_init extraction plans
Sub-issues activables de 0071 con plan concreto: API, dependencias, migracion, tests, anti-patrones.

- 0071a (alta): claude_chat_panel — 2 consumidores reales (graph_explorer + navegator_dashboard, ~2500 LoC dup). Depende de 0071f.
- 0071b (media): jobs_queue_panel — absorbe issue 0065. Depende de 0071f. Pre-requisito: auditar dup vs odr_console.
- 0071g (media): app_db_init Tier 4 — 4+ duplicaciones en graph_explorer. Bajo riesgo.

README actualizado.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 01:32:51 +02:00

5.6 KiB

id, title, status, priority, created, parent, related_apps, depends_on
id title status priority created parent related_apps depends_on
0071a Extraer `claude_chat_panel` a cpp/functions/core/ (sub-issue de 0071) pending alta 2026-05-10 0071
graph_explorer
navegator_dashboard
0071f

Contexto

Sub-issue derivado de 0071. Rule of three cumplida hoy: existe duplicacion masiva del panel de chat con claude -p:

Consumidor Archivos LoC Estado
projects/osint_graph/apps/graph_explorer/chat.{cpp,h} 2 1241 + ~80 Original
projects/navegator/apps/navegator_dashboard/chat.{cpp,h} 2 1252 + ~80 Copy+adapt reciente

~2500 LoC duplicados con divergencia minima (env vars, CLI tool name, mutations counter). Bug fixes hay que aplicarlos en ambos. 3er consumidor potencial: kanban (agente para cards), registry_dashboard (agente para proposals).

Objetivo

Funcion claude_chat_panel en cpp/functions/core/ que encapsule:

  • Spawn de claude -p --dangerously-skip-permissions (via subprocess_streamer de 0071f)
  • Parser stream-json line-by-line (turn/role/tool_use/content)
  • Render historial ImGui con bubbles user/assistant + tool_use detectado
  • Input multi-linea con Ctrl+Enter send + history (flecha arriba/abajo)
  • Persistencia opcional de conversacion en SQLite local
  • Callback de mutations counter (la app recarga su BD cuando el agente la modifica)

Dependencia previa

0071f (subprocess_streamer) DEBE estar mergeado. El chat_panel lo usa internamente. Sin ese building block, este issue no se aborda — se duplicaria la logica de spawn/pipes una vez mas.

API propuesta

namespace fn_core {

struct ClaudeChatConfig {
    // Binario claude (default: "claude" en PATH; en WSL puede ser path absoluto a Windows).
    std::string claude_bin = "claude";

    // CLI helper que el agente invoca (ej. "gx-cli", "cdp-cli", "kanban-cli").
    std::string cli_tool_name;
    std::string cli_tool_dir;   // dir donde vive el binario CLI helper

    // Variables de entorno que el subproceso hereda (ej. GX_OPS_DB, NAVD_PROFILE_DIR).
    std::map<std::string, std::string> env_vars;

    // Path del .mcp.json que se le pasa a claude --mcp-config.
    // Si vacio, no se pasa flag (usa el default del proyecto).
    std::string mcp_config_path;

    // Prompt sistema opcional (claude -p ... --append-system-prompt "...").
    std::string system_prompt;

    // Callback que la app implementa: devuelve N de mutaciones desde la ultima vez.
    // El panel lo usa para mostrar "agent modificado N entities, recargar?" boton.
    std::function<int()> mutations_counter;

    // Path para persistencia local del historial (SQLite). Vacio = sin persistencia.
    std::string history_db_path;

    // Path de log de stream-json crudo (debug). Vacio = sin log.
    std::string log_file_path;
};

struct ChatHandle;  // opaco

ChatHandle* chat_create(const ClaudeChatConfig& cfg);
void chat_send(ChatHandle*, const char* user_text);  // thread-safe
void chat_render(ChatHandle*, bool* p_open);          // ImGui::Begin/End
void chat_clear_history(ChatHandle*);
void chat_destroy(ChatHandle*);

// Para apps que quieren leer el historial sin tocar la UI:
struct ChatMessage {
    enum Role { User, Assistant, System, ToolUse, ToolResult } role;
    std::string text;
    std::string tool_name;       // solo si role==ToolUse|ToolResult
    int64_t timestamp_ms;
};
std::vector<ChatMessage> chat_history_snapshot(ChatHandle*);

}  // namespace fn_core

Migracion

Orden:

  1. Crear cpp/functions/core/claude_chat_panel.{h,cpp,md} con la API arriba.
  2. Tests unitarios en cpp/apps/primitives_gallery/demos_chat.cpp:
    • chat_create con config minima → no crashea.
    • mock subprocess (echo de JSON canned) → render muestra mensajes correctos.
    • chat_send → input limpia, mensaje aparece como User.
  3. Migrar graph_explorer primero (codigo mejor estructurado).
    • chat.{cpp,h} reducido a un wrapper de ~30 LoC que construye ClaudeChatConfig con GX_* env vars.
    • app.md añade claude_chat_panel_cpp_core a uses_functions.
  4. Migrar navegator_dashboard segundo.
    • Mismo wrapper, env vars NAVD_*.
  5. Verificacion manual: en cada app, chat funciona igual que antes.

Definicion de hecho

  • claude_chat_panel.{h,cpp,md} registrado (fn index).
  • Tests pasan (>= 3 casos en primitives_gallery).
  • 2 consumidores migrados (chat.cpp en ambas apps reducido a ~30 LoC).
  • Net LoC eliminadas: ~2400 (se reemplazan ~2500 LoC duplicadas por ~30 LoC config + ~700 LoC en cpp/functions/core).
  • Tests visuales: golden image del panel renderizado en primitives_gallery.

Anti-patrones

  • "Mock everything" — la API NO acepta provider abstracto (Anthropic/OpenAI/...). Es claude-cli especifico. Si emerge un 4to consumidor con OpenAI, abrir issue separado.
  • Async co_await / promises — callbacks bastan, KISS.
  • Pre-extraer un claude_subprocess_runner separado del panel — lo hace 0071f (subprocess_streamer) generico.
  • Hardcodear el render style en la funcion — exponer flags cfg.render_compact / cfg.render_avatars SOLO si un consumidor lo pide.
  • Acoplar a un schema concreto (entities, cards, tabs) — la funcion solo conoce mensajes; las apps interpretan tool_use_calls a su manera.

Riesgos

  • Divergencia oculta entre las dos chat.cpp. Mitigacion: hacer un diff exhaustivo antes de extraer y documentar las diferencias en el .md.
  • Stream-json parser es fragil ante cambios de formato de claude-cli. Mitigacion: tests con golden JSON capturado, version-pin del binario en docs.
  • History persistence podria pisar conversaciones si dos apps usan el mismo path. Mitigacion: el path es config — cada app pasa su propio archivo.