---
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 <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.
