--- id: 0036d title: Promote en NodeGroups ramificado por kind (Table row vs Group child) status: done priority: high created: 2026-05-04 parent: 0036 depends_on: [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) ```cpp // 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 --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.