Files
graph_explorer/issues/0036d-promote-kind-aware.md
T
egutierrez f0d8a5ad04 feat(0036d): promote kind-aware (Group → clear group_id)
NodeGroups window kind=Group ahora expone un boton SmallButton(TI_ARROW_UP)
por fila que saca la entidad del grupo (group_id = NULL) y dispara
reload del grafo. kind=Table mantiene el comportamiento de issue 0011.

- entity_ops: nueva op `entity_clear_group_id(db, id)` idempotente. Si
  la columna group_id no existe (BD pre-0035a) retorna true como no-op.
  Falla solo si la entidad no existe o SQLite revienta.
- views.cpp: extra columna "promote" en kind=Group, tooltip header
  diferenciado por kind, boton conectado a app.want_clear_group_id_entity.
- main.cpp: handler que ejecuta entity_clear_group_id, marca windows
  como dirty, llama reload_after_mutation y loguea
  `[node_groups] promoted X out of group`.
- gx-cli: flag `node update --clear-group-id` (booleano) y exposicion
  MCP en inputSchema + MCP_DISPATCH defaults para que el agente Echo
  pueda promover via tool calls.
- tests: 3 nuevos CLI (clear, idempotente, combinable con --name) y
  4 MCP (defaults, schema, dispatch end-to-end, idempotente).

WSL: 102 passed (95 base + 7).
Windows: 91 passed, 11 skipped (84 base + 7).

Refs: issues/0036d-promote-kind-aware.md
2026-05-04 01:03:11 +02:00

2.5 KiB

id, title, status, priority, created, parent, depends_on
id title status priority created parent depends_on
0036d Promote en NodeGroups ramificado por kind (Table row vs Group child) done high 2026-05-04 0036
0036b

Objetivo

Boton "Promote" por fila en la NodeGroups window. La accion se ramifica por kind:

  • kind = Table (DuckDB row): comportamiento actual del issue 0011 — INSERT entity nueva apuntando a la fila DuckDB. Idempotente: si ya existe entidad para ese row_id, no duplica.
  • kind = Group (entidad hija): UPDATE clear group_id. La entidad sale del grupo y aparece como nodo libre en el canvas tras reload.

Cambios

Nueva op en entity_ops (Group promote)

// Saca la entidad de su grupo (group_id = NULL). No-op si ya estaba
// fuera (group_id ya era NULL). Idempotente.
bool entity_clear_group_id(const char* db_path, const char* entity_id);

Tambien anyadir su pareja MCP en gx-cli:

gx-cli node update <id> --clear-group-id

(Para que el agente Echo pueda promover entidades del grupo via MCP. Argumento booleano que dispara la op directa, sin pasar por el flujo de --notes/--name.)

Render del boton Promote

En la pintura de la window (en views.cpp), por cada fila visible mostrar un SmallButton(TI_ARROW_UP) con tooltip:

  • kind = Table: "Promote row to entity" — comportamiento existente.
  • kind = Group: "Promote out of group (move to canvas)" — llama a la nueva op.

Tras la accion, marcar g_app.want_reload = true para que el grafo se refresque y la entidad reaparezca suelta.

Tooltip del header

Cuando entras a la window, mostrar un texto suave indicando el comportamiento del Promote segun kind. Una sola linea, color text muted.

Acceptance criteria

  • Click promote en row de Group: la entidad pierde group_id (verificable por SQL), reload del grafo la muestra suelta colgando del source via la relacion existente.
  • Click promote en row de Group ya promovida (group_id ya NULL): no-op sin error.
  • Click promote en row de Table: comportamiento como hoy. Sin regresion en flow existente.
  • Tooltip diferenciado entre kinds.
  • Tests pytest:
    • test_entity_clear_group_id_removes_membership
    • test_entity_clear_group_id_idempotent
    • test_gx_cli_node_update_clear_group_id (CLI)
    • MCP regression: clear_group_id en defaults

TBD

Branch issue/0036d-promote-kind-aware, merge --no-ff a master.

Out of scope

  • Promote masivo (multi-select) — fase 2.
  • Re-agrupar por tipo (multi-select dentro de NodeGroups window) — fase 2.