Files
fn_registry/dev/issues/completed/0049k-graph-explorer-app.md

11 KiB
Raw Permalink Blame History

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
0049k App `graph_explorer` (proyecto `osint_graph`) completado feature
app-scoped alta
2026-05-17 2026-05-17

0049k — App graph_explorer (proyecto osint_graph)

Metadata

Campo Valor
ID 0049k
Estado pendiente
Prioridad alta
Tipo feature — parte de #0049integracion final + activacion del feature flag

Dependencias

Bloqueada por:

  • 0049a — proyecto + sub-repo
  • 0049f — renderer completo
  • 0049g — source operations.db
  • 0049h — layout GPU
  • 0049i — layouts estaticos + viewport
  • 0049j — labels

Desbloquea: activacion del feature flag osint_graph_v1.


Objetivo

Construir la app C++ graph_explorer en projects/osint_graph/apps/graph_explorer/, agnostica del backend, capaz de abrir cualquier operations.db del registry y visualizarlo con shapes/iconos/layouts/filtros/labels. Cumplir la regla cpp_apps.md y apps_tbd.md.

Contexto

Toda la libreria de visualizacion ya existe en cpp/functions/viz/ tras 0049bj. Esta app es el consumer final que orquesta:

  • graph_load_from_operations para cargar datos.
  • types_registry (modulo local) para mergear types.yaml opcional con tipos descubiertos.
  • graph_force_layout_gpu (o CPU) corriendo segun toggle.
  • graph_renderer + graph_viewport + graph_labels para visualizacion.
  • fn_ui::* (toolbar, modal, select, etc.) para chrome.

Arquitectura

projects/osint_graph/apps/graph_explorer/
├── .git/                          # sub-repo dataforge/graph_explorer (creado en 0049a)
├── app.md                         # NEW
├── CMakeLists.txt                 # NEW
├── main.cpp                       # NEW
├── data.{h,cpp}                   # NEW: dispatcher GraphLoadFn
├── views.{h,cpp}                  # NEW: Toolbar, Legend, Inspector, Stats
├── types_registry.{h,cpp}         # NEW: lee types.yaml y mergea
└── examples/
    └── types.yaml                 # NEW: ejemplo OSINT con Person/Email/Domain/...

app.md frontmatter

---
name: graph_explorer
lang: cpp
domain: viz
description: "Visor de grafos GPU-accelerated agnostico del backend. Lee operations.db de cualquier app del registry y permite explorar entidades/relaciones con shapes/iconos/layouts/filtros."
tags: [imgui, graph, osint, visualization, gpu]
uses_functions:
  - graph_renderer_cpp_viz
  - graph_force_layout_cpp_viz
  - graph_force_layout_gpu_cpp_viz
  - graph_layouts_cpp_viz
  - graph_viewport_cpp_viz
  - graph_labels_cpp_viz
  - graph_icons_cpp_viz
  - graph_sources_cpp_viz
  - toolbar_cpp_core
  - modal_dialog_cpp_core
  - select_cpp_core
  - text_input_cpp_core
  - tree_view_cpp_core
  - page_header_cpp_core
  - fullscreen_window_cpp_core
uses_types: []
framework: "imgui"
entry_point: "main.cpp"
dir_path: "projects/osint_graph/apps/graph_explorer"
repo_url: "https://gitea-.../dataforge/graph_explorer"
---

CLI

graph_explorer [--input operations <path>] [--types <yaml>] [--layout force|grid|...]
graph_explorer apps/registry_dashboard/operations.db
graph_explorer --types projects/osint_graph/apps/graph_explorer/examples/types.yaml \
               apps/element_agents/operations.db

Layout de ventanas

┌──────────────────────────────────────────────────────────┐
│ MainMenuBar (View | Settings | About)                    │
├─[Toolbar: Open | Layout: [force▼] | Filters | Fit | …]──┤
├─Legend──┬───────────────────────────────────────┬─Inspector─┤
│ Type    │                                       │  id: ...   │
│ ☑ Person│                                       │  type: ... │
│ ☑ Email │           Viewport (FBO)              │  metadata… │
│ ☐ Domain│                                       │            │
│ Rels:   │                                       │  Neighbors:│
│ ☑ owns  │                                       │  • node A  │
│ ...     │                                       │  • node B  │
├─────────┴───────────────────────────────────────┴────────────┤
│ Stats: nodes=12345 edges=54321 fps=60 energy=0.04 sel=2     │
└──────────────────────────────────────────────────────────┘

Tareas

Fase 1 — Esqueleto

  • 1.1 Crear app.md con frontmatter completo.
  • 1.2 Crear CMakeLists.txt siguiendo cpp_apps.md. Listar todas las funciones del registry usadas explicitamente.
  • 1.3 main.cpp minimo con fn::run_app(cfg, render) + parseo de --input/--types/--layout.
  • 1.4 Registrar la app en cpp/CMakeLists.txt con el patron de registry_dashboard:
    set(_GE_DIR ${CMAKE_SOURCE_DIR}/../projects/osint_graph/apps/graph_explorer)
    if(EXISTS ${_GE_DIR}/CMakeLists.txt)
        add_subdirectory(${_GE_DIR} ${CMAKE_BINARY_DIR}/apps/graph_explorer)
    endif()
    

Fase 2 — data.{h,cpp} (dispatcher de sources)

  • 2.1 Implementar dispatcher segun --input:
    bool load_graph(const InputArgs& args, GraphData* out, GraphLoadStats* stats) {
        if (args.kind == INPUT_OPERATIONS) return graph_load_from_operations(args.uri, out, stats);
        // futuro: json/jsonl/graphml
        stats->errors++; return false;
    }
    
  • 2.2 Helper para reload (re-llamar la misma GraphLoadFn con la misma URI).

Fase 3 — types_registry.{h,cpp}

  • 3.1 Parser de types.yaml:
    entities:
      - name: Person
        color: "#5B8DEF"
        shape: circle
        icon: ti-user
      - name: Email
        color: "#58CA8C"
        shape: square
        icon: ti-mail
    relations:
      - name: owns
        color: "#888888"
        style: solid
    
  • 3.2 Helper apply_types_yaml(GraphData&, const ParsedTypes&): para cada EntityType/RelationType del grafo cuyo name matchee en el yaml, sobrescribir color/shape/icon_id/style. Tipos no encontrados se quedan con default (color por hash).
  • 3.3 Iconos: mapear nombre ti-user → codepoint Tabler usando una tabla en el header icons_tabler.h (helpers ya existentes o anadir uno nuevo tabler_codepoint_by_name(const char*)).
  • 3.4 Construccion del IconAtlas: collect codepoints distintos del yaml + fallback (icono "?" para no encontrados), llamar graph_icons_build(...).

Fase 4 — views.{h,cpp} (paneles)

  • 4.1 Toolbar: Open file…, Layout selector (select), Filters… (modal con checkboxes por tipo), Fit view, Save layout, Export PNG.
  • 4.2 Legend: lista de tipos con color swatch, icono y toggle de visibilidad. Toggle aplica a todos los nodos del tipo: for each node where type_id == t: flags ^= NF_VISIBLE. Igual para relaciones.
  • 4.3 Inspector: cuando hay seleccion de un solo nodo, mostrar id, type, metadata (raw JSON formateado), lista de vecinos directos clickables (click cambia seleccion).
  • 4.4 Stats: linea fija con counts + fps + energia.
  • 4.5 Paneles toggleables via AppConfig::panels.

Fase 5 — Persistencia

  • 5.1 graph_explorer.db SQLite junto al exe con tabla layouts(graph_hash TEXT, node_id TEXT, x REAL, y REAL, pinned INT, updated_at).
  • 5.2 graph_hash = hash del path del input (operations.db) para diferenciar entre grafos.
  • 5.3 Save layout boton en la toolbar: snapshot de posiciones + flags.
  • 5.4 Al cargar un grafo conocido (mismo hash), pre-aplicar las posiciones guardadas.
  • 5.5 Layout de paneles ImGui via layout_storage (ya existe como funcion publica desde 0042).

Fase 6 — types.yaml ejemplo

  • 6.1 Crear examples/types.yaml con ~10 tipos OSINT comunes (Person, Email, Domain, Phone, Org, IBAN, Account, Document, Address, Url) y 5 relaciones (owns, knows, located_in, transfers_to, member_of).

Fase 7 — TBD trabajo

  • 7.1 Trabajar en rama quick/graph-explorer o issue/0049k-graph-explorer-app segun preferencia.
  • 7.2 Commits atomicos por panel/feature.
  • 7.3 Merge --no-ff a master del sub-repo dataforge/graph_explorer.
  • 7.4 Push del sub-repo + push de fn_registry con la nueva ubicacion + app.md.

Fase 8 — Indexado + flag

  • 8.1 ./fn index desde la raiz.
  • 8.2 Verificar:
    SELECT id, name, project_id FROM apps WHERE id='graph_explorer_cpp_viz';
    
  • 8.3 Activar feature flag en dev/feature_flags.json:
    "osint_graph_v1": { "enabled": true, "issue": "0049", ... }
    
  • 8.4 Mover el issue principal 0049 + todos los sub-issues 0049ak a dev/issues/completed/ y actualizar README.

Fase 9 — Verificacion end-to-end

  • 9.1 Abrir apps/registry_dashboard/operations.db y verificar que se ven entidades.
  • 9.2 Abrir un dataset OSINT real (cuando exista) o un fixture en ~/vaults/osint_graph/raw/.
  • 9.3 Verificar todas las features: filtrado por tipo, drag, multi-select, lasso, layouts, labels, save/load layout.

Criterio de done

  • graph_explorer apps/registry_dashboard/operations.db arranca y muestra el grafo de entidades.
  • Con --types examples/types.yaml, los tipos se ven con shapes/iconos/colores correctos.
  • 50k nodos cargan y se navegan a 60fps con layout GPU.
  • Layouts intercambiables en runtime via toolbar.
  • Filtros, drag, multi-select, lasso, labels funcionando.
  • Persistencia de layout entre sesiones.
  • App registrada en registry.db con uses_functions correcto.
  • Sub-repo dataforge/graph_explorer con master limpio y pushed.
  • Feature flag osint_graph_v1 = true.
  • Issue 0049 + sub-issues movidos a completed.

Riesgos

Riesgo Mitigacion
Parser YAML pesa: anadir libreria Usar yaml-cpp si ya esta vendoreada; si no, parser minimal hand-rolled (10 tipos no necesita full YAML)
Mapeo ti-user → codepoint requiere tabla nueva Generar a partir del icons_tabler.h existente con un script una sola vez
Inspector con metadata grande satura el panel Truncar a ~512 chars + scroll del panel
Save layout en grafo grande es lento UPSERT por nodo es O(N) con prepared statement; para 50k = ~100 ms aceptable en boton, no en frame
Operations.db de un app esta locked si la app corre SQLite con mode=ro o immutable=1 en el URI