chore: snapshot WIP previo + flow 0008 + 7 sub-issues (0112-0119)
Snapshot de WIP acumulado de sesiones previas antes de merge wave 1 del flow 0008 (kanban_cpp + agent_runner_api + DoD schema). Incluye: - dev/flows/0008-kanban-cpp-and-agent-workflows.md - dev/issues/0112-0119*.md (7 sub-issues) - WIP previo en cmd/fn/doctor.go, registry/*, modules/, cpp/, etc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
---
|
||||
id: "0107"
|
||||
title: "Estandarizar sistema de modulos C++: limpiar drift data_table + politica API + version pinning + /version command"
|
||||
status: pendiente
|
||||
type: refactor
|
||||
domain:
|
||||
- meta
|
||||
- cpp-stack
|
||||
- tooling
|
||||
scope: multi-app
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks:
|
||||
- "0108"
|
||||
related:
|
||||
- "0097"
|
||||
- "0081"
|
||||
- "0086"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: [modules, cpp, data-table, framework, refactor, fn-doctor, versioning]
|
||||
---
|
||||
|
||||
# 0107 — Estandarizar sistema de modulos C++
|
||||
|
||||
## Problema
|
||||
|
||||
Auditoria 2026-05-17 sobre `modules/` (framework + data_table) revela que el sistema de modulos C++ esta a medio camino: la idea (modulos opt-in versionados con manifest auditable) es solida, pero su implementacion tiene fugas que invalidan el contrato.
|
||||
|
||||
### Drift uses_modules ↔ uses_functions (7/7 apps consumidoras)
|
||||
|
||||
`module.md` dice "cuando declaras `uses_modules`, NO repetir los miembros en `uses_functions`". Realidad medida hoy:
|
||||
|
||||
| App | uses_functions total | miembros data_table duplicados |
|
||||
|---|---|---|
|
||||
| services_monitor | 12 | 12 |
|
||||
| dag_engine_ui | 13 | 12 |
|
||||
| odr_console | 5 | 5 |
|
||||
| navegator_dashboard | 20 | 12 |
|
||||
| graph_explorer | 42 | 12 |
|
||||
| registry_dashboard | 37 | 11 |
|
||||
| app_gestion | 12 | 12 |
|
||||
|
||||
7 de 7 apps violan la regla clave que justifica el sistema. `fn doctor cpp-apps` no detecta el drift.
|
||||
|
||||
### `data_table.cpp` = 4777 LOC
|
||||
|
||||
El "modulo" es un god-file con UI entera (chips, viz, grid, drill, joins, AI, button, color rules) dentro de un `.cpp`. Imposible auditar consumidores parciales, imposible registrar miembros como funciones reales del registry (cada uno con su `.md`).
|
||||
|
||||
### Boundary modulo vs funcion borrosa
|
||||
|
||||
`lua_engine`, `llm_anthropic`, `join_tables` son members de `data_table`. Pero lua/llm/join son utiles fuera de tablas. Forzar membership infla el surface del modulo y obliga a las apps a tragarse lua+llm+anthropic+join aunque solo quieran render simple. No hay tier "data_table_core" vs "data_table_full".
|
||||
|
||||
### Versionado declarado, no enforced
|
||||
|
||||
`module.md` tiene `version: 1.4.0`. `app.md` dice `uses_modules: [data_table_cpp]` sin version. Bump breaking de modulo → todas las apps rompen sin warning hasta compile error.
|
||||
|
||||
### Codegen silencioso
|
||||
|
||||
`execute_process(... codegen_app_modules)` emite WARNING solo si rc != 0 y != 2. Si Python falta → stub vacio sin error. About panel muestra "0 modules" en apps que SI deberian tener 1.
|
||||
|
||||
### Hard dep oculto
|
||||
|
||||
`fn_module_data_table` linkea `fn_framework` PRIVATE para `fn::local_path()`. `module.md` no lo documenta como precondicion publica. Si alguien intenta usar el modulo en una app no-framework, falla en link sin mensaje claro.
|
||||
|
||||
### Sin doc API de modulos
|
||||
|
||||
No hay un sitio canonico que diga "para usar el modulo X, incluye Y.h, llama X::render(...), pasa State Z". Cada modulo lo improvisa en su `module.md`.
|
||||
|
||||
### Sin /version command
|
||||
|
||||
No hay flujo estandar para bumpear semver de un modulo o framework. Cada PR lo hace a ojo, sin coherencia entre `module.md::version` y `## Capability growth log`.
|
||||
|
||||
## Decision
|
||||
|
||||
Issue desglosado en 6 sub-issues independientes detras de feature flag `modules-v2`:
|
||||
|
||||
1. **0107a** — `fn doctor modules` que detecta drift uses_modules vs uses_functions.
|
||||
2. **0107b** — Limpiar `uses_functions` de las 7 apps consumidoras de data_table (eliminar miembros duplicados).
|
||||
3. **0107c** — Partir `modules/data_table/data_table.cpp` (4777 LOC) en sub-funciones del registry (`data_table_chips`, `data_table_grid`, `data_table_viz_panels`, `data_table_drill`, `data_table_ai_panel`, `data_table_color_rules`). Cada una con `.md` propio. Entrypoint queda thin.
|
||||
4. **0107d** — Mover members generales (`lua_engine`, `llm_anthropic`, `join_tables`, `auto_detect_type`) fuera de `data_table` module. Quedan funciones sueltas que el modulo USA pero no posee. Crear tiers explicitos.
|
||||
5. **0107e** — Version pinning en `uses_modules` (`uses_modules: [{name: data_table_cpp, min_version: "1.4"}]`) + codegen fail-loud (error si Python falta o count=0 cuando deberia ser >0).
|
||||
6. **0107f** — `modules/README.md` (catalogo) + `docs/MODULES_API.md` (contrato publico por modulo: header path, namespace, entry function, State struct, lifecycle).
|
||||
|
||||
Ademas:
|
||||
|
||||
- `/version` slash command para bumpear semver de modulo/framework consistentemente (`module.md::version` + `## Capability growth log` + git commit).
|
||||
- `/fix-issue` referenciara `/version` cuando el cambio toque framework/modules.
|
||||
|
||||
Feature flag `modules-v2` activado solo cuando 0107a-f cierran. Antes de cerrar, recompilar TODAS las apps cpp para verificar que el refactor no rompe linkage. Aceptamos coste de recompilacion total.
|
||||
|
||||
## Restriccion explicita
|
||||
|
||||
Prohibido empezar `chat_ia` (proximo modulo planeado) hasta que 0107 cierre. Razon: si arrancamos otro modulo sin estandar estable, replicamos los mismos bugs en el doble de superficie.
|
||||
|
||||
## Tareas (resumen — detalle en sub-issues)
|
||||
|
||||
- [ ] **1** Auditoria automatizada → `fn doctor modules` (0107a)
|
||||
- [ ] **2** Limpiar drift en 7 apps consumidoras (0107b)
|
||||
- [ ] **3** Partir `data_table.cpp` en sub-funciones del registry (0107c)
|
||||
- [ ] **4** Politica members generales + tiers (0107d)
|
||||
- [ ] **5** Version pinning + codegen fail-loud (0107e)
|
||||
- [ ] **6** Docs API publica modulos (0107f)
|
||||
- [ ] **7** Recompilar todas las apps cpp + verificar smoke (al cerrar)
|
||||
- [ ] **8** Activar feature flag `modules-v2`
|
||||
- [ ] **9** `/version` + `/fix-issue` (no son sub-issues; tareas inline en este issue principal)
|
||||
|
||||
## Desglose multi-issue
|
||||
|
||||
| Sub-issue | Rama | Alcance | Estado |
|
||||
|-----------|------|---------|--------|
|
||||
| 0107a-fn-doctor-modules | issue/0107a-fn-doctor-modules | Check drift uses_modules vs uses_functions + version skew | pendiente |
|
||||
| 0107b-clean-data-table-consumers | issue/0107b-clean-data-table-consumers | Eliminar miembros duplicados en 7 app.md | pendiente |
|
||||
| 0107c-split-data-table | issue/0107c-split-data-table | Partir data_table.cpp 4777 LOC en sub-funciones del registry | pendiente |
|
||||
| 0107d-module-tiers-policy | issue/0107d-module-tiers-policy | Sacar lua/llm/join del modulo data_table; tiers + politica | pendiente |
|
||||
| 0107e-version-pinning-codegen | issue/0107e-version-pinning-codegen | min_version en uses_modules + codegen fail-loud | pendiente |
|
||||
| 0107f-modules-api-docs | issue/0107f-modules-api-docs | modules/README.md + docs/MODULES_API.md | pendiente |
|
||||
|
||||
### Feature flag
|
||||
|
||||
Nombre: `modules-v2`
|
||||
Se activa cuando 0107a-f cierran + recompilacion total verificada + `fn doctor modules` reporta 0 drift.
|
||||
|
||||
### Progreso por tarea
|
||||
|
||||
- [ ] **1.1** Implementar check drift en `fn doctor cpp-apps` o sub-comando nuevo — 0107a
|
||||
- [ ] **1.2** Output JSON con apps que violan regla — 0107a
|
||||
- [ ] **1.3** Tests sobre fixture sintetica (1 modulo, 3 apps simuladas) — 0107a
|
||||
- [ ] **2.1** Editar 7 `app.md` removiendo miembros data_table — 0107b
|
||||
- [ ] **2.2** Verificar build pasa post-clean (no rompe nada — solo metadata) — 0107b
|
||||
- [ ] **3.1** Identificar fronteras funcionales en data_table.cpp 4777 LOC — 0107c
|
||||
- [ ] **3.2** Crear `cpp/functions/viz/data_table_chips.cpp` + .h + .md — 0107c
|
||||
- [ ] **3.3** Crear `cpp/functions/viz/data_table_grid.cpp` + .h + .md — 0107c
|
||||
- [ ] **3.4** Crear `cpp/functions/viz/data_table_viz_panels.cpp` + .h + .md — 0107c
|
||||
- [ ] **3.5** Crear `cpp/functions/viz/data_table_drill.cpp` + .h + .md — 0107c
|
||||
- [ ] **3.6** Crear `cpp/functions/viz/data_table_ai_panel.cpp` + .h + .md — 0107c
|
||||
- [ ] **3.7** Crear `cpp/functions/viz/data_table_color_rules.cpp` + .h + .md — 0107c
|
||||
- [ ] **3.8** `data_table.cpp` queda como entrypoint thin que compone las sub-funciones — 0107c
|
||||
- [ ] **3.9** Bump `module.md::version` a 2.0.0 (breaking interno, API publica `data_table::render` intacta) — 0107c
|
||||
- [ ] **4.1** Crear `cpp/functions/core/lua_engine.cpp` (ya existe) como funcion suelta; quitar de `module.md::members` — 0107d
|
||||
- [ ] **4.2** Idem `llm_anthropic`, `join_tables`, `auto_detect_type` — 0107d
|
||||
- [ ] **4.3** Actualizar `modules/data_table/CMakeLists.txt`: estos `.cpp` ya no se enlazan dentro del modulo; apps que los necesiten los anaden a su CMakeLists — 0107d
|
||||
- [ ] **4.4** Definir tiers en `module.md`: `core_members` (esenciales) vs `optional_members` (deps externas pesadas) — 0107d
|
||||
- [ ] **5.1** Parser `app.md::uses_modules` acepta string corto y dict largo — 0107e
|
||||
- [ ] **5.2** Codegen comprueba `min_version` vs `module.md::version` — error si no cumple — 0107e
|
||||
- [ ] **5.3** Codegen: si `Python3 NOT FOUND` y app tiene `uses_modules` → CMake FATAL_ERROR — 0107e
|
||||
- [ ] **5.4** Codegen: si parser devuelve count=0 pero app.md declara `uses_modules` no-vacio → FATAL_ERROR — 0107e
|
||||
- [ ] **6.1** `modules/README.md` con tabla modulos + version + descripcion + link a contrato — 0107f
|
||||
- [ ] **6.2** `docs/MODULES_API.md` con contrato canonico (template + ejemplos data_table + framework) — 0107f
|
||||
- [ ] **6.3** Actualizar `.claude/rules/cpp_apps.md` referenciando `docs/MODULES_API.md` — 0107f
|
||||
- [ ] **7.1** `redeploy_all_cpp_apps_bash_pipelines` + verificar 0 errores de link — issue principal, al cerrar
|
||||
- [ ] **7.2** Smoke manual de cada app con `data_table::render` — issue principal, al cerrar
|
||||
- [ ] **8** Flip `modules-v2: enabled: true` en `dev/feature_flags.json` — issue principal
|
||||
- [ ] **9.1** Crear `.claude/commands/version.md` (slash command bump semver) — issue principal, ya en este turno
|
||||
- [ ] **9.2** Crear `.claude/commands/fix-issue.md` que referencie `/version` — issue principal, ya en este turno
|
||||
|
||||
## Arquitectura
|
||||
|
||||
### Archivos afectados
|
||||
|
||||
**0107a** (`fn doctor modules`):
|
||||
- `functions/infra/audit_modules_drift.go` (NEW) — funcion del registry
|
||||
- `functions/infra/audit_modules_drift.md` (NEW)
|
||||
- `cmd/fn/doctor.go` — subcomando `modules`
|
||||
- `apps/registry_mcp/...` — exponer via `mcp__registry__fn_doctor subcommand="modules"`
|
||||
|
||||
**0107b**:
|
||||
- 7 `app.md` editados: services_monitor, dag_engine_ui, odr_console, navegator_dashboard, graph_explorer, registry_dashboard, app_gestion.
|
||||
|
||||
**0107c**:
|
||||
- `modules/data_table/data_table.cpp` — pasa de 4777 LOC a ~400 (entrypoint que compone).
|
||||
- `cpp/functions/viz/data_table_chips.cpp/.h/.md` (NEW) — ~600 LOC
|
||||
- `cpp/functions/viz/data_table_grid.cpp/.h/.md` (NEW) — ~1200 LOC
|
||||
- `cpp/functions/viz/data_table_viz_panels.cpp/.h/.md` (NEW) — ~800 LOC
|
||||
- `cpp/functions/viz/data_table_drill.cpp/.h/.md` (NEW) — ~300 LOC
|
||||
- `cpp/functions/viz/data_table_ai_panel.cpp/.h/.md` (NEW) — ~500 LOC
|
||||
- `cpp/functions/viz/data_table_color_rules.cpp/.h/.md` (NEW) — ~400 LOC
|
||||
- `modules/data_table/module.md` — bump version + actualizar members.
|
||||
- `modules/data_table/CMakeLists.txt` — anadir las sub-funciones a la static lib.
|
||||
|
||||
**0107d**:
|
||||
- `modules/data_table/module.md` — quitar `lua_engine`, `llm_anthropic`, `join_tables`, `auto_detect_type` de members.
|
||||
- `modules/data_table/CMakeLists.txt` — estos `.cpp` salen.
|
||||
- Apps consumidoras que necesiten lua/llm/join → declarar en `uses_functions` + anadir el `.cpp` a su CMake.
|
||||
|
||||
**0107e**:
|
||||
- `python/functions/infra/codegen_app_modules.py` — soporte dict largo `{name, min_version}`.
|
||||
- `cpp/CMakeLists.txt::add_imgui_app` — fail-loud en codegen errors.
|
||||
- `registry/parser.go` — indexer entiende dict largo.
|
||||
|
||||
**0107f**:
|
||||
- `modules/README.md` (NEW)
|
||||
- `docs/MODULES_API.md` (NEW)
|
||||
- `.claude/rules/cpp_apps.md` — link nuevo doc.
|
||||
|
||||
**Slash commands** (este issue):
|
||||
- `.claude/commands/version.md` (NEW)
|
||||
- `.claude/commands/fix-issue.md` (NEW si no existe)
|
||||
|
||||
### pkg/ puro vs shell/ impuro
|
||||
|
||||
`audit_modules_drift_go_infra` (0107a) es **impuro** — lee `registry.db` + filesystem (`app.md`, `module.md`). Vive en `functions/infra/`. Core logico (comparar listas de IDs, detectar miembros duplicados) es **puro** y vive como sub-funcion interna del paquete `infra`.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```bash
|
||||
# 1. Detectar drift
|
||||
fn doctor modules
|
||||
# Output:
|
||||
# Module drift report
|
||||
# ===================
|
||||
# data_table_cpp (v1.4.0): 7/7 consumers list members in uses_functions
|
||||
# services_monitor — duplicates: data_table_cpp_viz, viz_render_cpp_viz, ...
|
||||
# dag_engine_ui — duplicates: ...
|
||||
# Total apps with drift: 7
|
||||
# Total modules: 2
|
||||
# Exit: 1
|
||||
|
||||
# 2. Bump version de un modulo (slash command)
|
||||
/version modules/data_table minor "split data_table.cpp into 6 sub-functions; API publica intacta"
|
||||
# - Detecta version actual en module.md (1.4.0)
|
||||
# - Calcula proxima (1.5.0 si minor, 2.0.0 si major)
|
||||
# - Anade entrada a ## Capability growth log con fecha de hoy
|
||||
# - Stage en git pero NO commit
|
||||
|
||||
# 3. Pinning version en una app
|
||||
# app.md:
|
||||
# uses_modules:
|
||||
# - name: data_table_cpp
|
||||
# min_version: "1.4"
|
||||
# Codegen falla en cmake si module.md::version < 1.4
|
||||
|
||||
# 4. fix-issue referencia /version
|
||||
/fix-issue 0107c
|
||||
# Flow normal del fix-issue + "¿este cambio bumpea version de modulo/framework? si si → /version"
|
||||
```
|
||||
|
||||
## Decisiones de diseno
|
||||
|
||||
1. **No tocar API publica `data_table::render(...)`**. Refactor interno. Apps existentes no cambian su llamada.
|
||||
2. **Aceptamos recompilacion total**. El usuario lo dijo explicito. Coste de tiempo razonable a cambio de limpieza.
|
||||
3. **Feature flag `modules-v2` no protege codigo runtime** — protege la "promesa" del sistema. Cuando flag flip, `fn doctor modules` debe pasar verde.
|
||||
4. **`/version` NO hace commit**. Solo edita archivos + stage. El commit final lo hace el flujo normal del fix-issue.
|
||||
5. **Bloqueamos `chat_ia`**. Si saltamos a otro modulo sin estandar, el caos se duplica.
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- Issue 0097 (modules infra inicial) — completado, esto es la evolucion.
|
||||
- `fn doctor cpp-apps` existe (0081) — reutilizamos paths.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Refactor 4777 LOC**: alto riesgo de regresion visual/funcional. Mitigacion: smoke manual app-por-app + `primitives_gallery --capture` golden images antes/despues.
|
||||
- **Apps que dependen indirectamente de lua/llm/join** sin declararlo: post-0107d podrian fallar en link. Mitigacion: `fn doctor uses-functions` + recompilacion total como gate.
|
||||
- **Codegen fail-loud** rompe builds que hoy pasan con stub: deliberado, parte de la idea — pero hay que arreglar todos los app.md antes de mergear 0107e.
|
||||
|
||||
## Notas
|
||||
|
||||
- `/version` se referenciara desde `/fix-issue` con prompt: "este cambio toca `modules/` o `cpp/framework/`? si si → run `/version <path> [major|minor|patch] [reason]` antes de commit".
|
||||
- Politica: bump de modulo SIN bump de version = bug. `fn doctor modules` lo detectara via diff hash de `members` + `description` vs ultima version registrada.
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
id: "0107a"
|
||||
title: "fn doctor modules — detectar drift uses_modules vs uses_functions y version skew"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- meta
|
||||
- cpp-stack
|
||||
- tooling
|
||||
scope: registry
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks:
|
||||
- "0107b"
|
||||
related:
|
||||
- "0107"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: [modules, fn-doctor, drift, audit, cpp]
|
||||
---
|
||||
|
||||
# 0107a — `fn doctor modules`
|
||||
|
||||
Parte del issue principal [0107](0107-modules-standardization.md). Feature flag `modules-v2`.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Subcomando `fn doctor modules` + funcion del registry `audit_modules_drift_go_infra` que detecta:
|
||||
|
||||
1. App declara `uses_modules: [X]` Y un miembro de X aparece en `uses_functions` → drift.
|
||||
2. App declara `uses_modules: [X]` pero su `CMakeLists.txt` NO linkea `fn_module_X` → mismatch.
|
||||
3. App linkea `fn_module_X` pero NO declara `uses_modules: [X]` → mismatch inverso.
|
||||
4. App declara `uses_modules: [{name: X, min_version: "1.4"}]` y `module.md::version` < 1.4 → version skew (post 0107e).
|
||||
|
||||
## Tareas
|
||||
|
||||
- [ ] **1.1** `functions/infra/audit_modules_drift.go` con firma:
|
||||
```go
|
||||
type ModuleDriftReport struct {
|
||||
ModuleID string
|
||||
ModuleVersion string
|
||||
ConsumersTotal int
|
||||
ConsumersWithDrift int
|
||||
Violations []DriftViolation
|
||||
}
|
||||
type DriftViolation struct {
|
||||
AppID string
|
||||
Kind string // "duplicate_members" | "uses_modules_no_link" | "link_no_uses_modules" | "version_skew"
|
||||
DuplicatedIDs []string
|
||||
Message string
|
||||
}
|
||||
func AuditModulesDrift(registryDB string, cppRoot string) ([]ModuleDriftReport, error)
|
||||
```
|
||||
- [ ] **1.2** `.md` correspondiente con frontmatter completo + ejemplo lanzable.
|
||||
- [ ] **1.3** Subcomando en `cmd/fn/doctor.go`: `fn doctor modules` + `fn doctor modules --json`.
|
||||
- [ ] **1.4** Exponer via MCP: `mcp__registry__fn_doctor subcommand="modules"`.
|
||||
- [ ] **1.5** Test sintetico: fixture con 1 modulo + 3 apps (1 limpia, 1 con drift de duplicados, 1 con version skew).
|
||||
- [ ] **1.6** Anadir entrada a `.claude/rules/fn_doctor.md` mapeando subcomando.
|
||||
|
||||
## Output esperado (texto)
|
||||
|
||||
```
|
||||
fn doctor modules
|
||||
=================
|
||||
data_table_cpp v1.4.0 — 7 consumers
|
||||
services_monitor DRIFT 12 duplicated members in uses_functions
|
||||
dag_engine_ui DRIFT 12 duplicated members in uses_functions
|
||||
odr_console DRIFT 5 duplicated members in uses_functions
|
||||
navegator_dashboard DRIFT 12 duplicated members in uses_functions
|
||||
graph_explorer DRIFT 12 duplicated members in uses_functions
|
||||
registry_dashboard DRIFT 11 duplicated members in uses_functions
|
||||
app_gestion DRIFT 12 duplicated members in uses_functions
|
||||
|
||||
framework_cpp v1.1.0 — 0 explicit consumers (transitive via add_imgui_app)
|
||||
|
||||
Summary: 2 modules, 7 apps with drift, 0 version skews.
|
||||
Exit: 1 (drift detected)
|
||||
```
|
||||
|
||||
## Riesgos
|
||||
|
||||
- Falso positivo si un app legitimamente necesita un miembro fuera del scope del modulo (ej. usar `lua_engine` standalone). Mitigacion: post-0107d (members generales fuera del modulo), este caso desaparece. Mientras tanto: flag `--ignore-known` con allowlist temporal.
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
id: "0107b"
|
||||
title: "Limpiar uses_functions de 7 apps consumidoras de data_table (eliminar miembros duplicados)"
|
||||
status: pendiente
|
||||
type: refactor
|
||||
domain:
|
||||
- meta
|
||||
- cpp-stack
|
||||
scope: multi-app
|
||||
priority: alta
|
||||
depends:
|
||||
- "0107a"
|
||||
blocks: []
|
||||
related:
|
||||
- "0107"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: [modules, drift, app-md, cleanup]
|
||||
---
|
||||
|
||||
# 0107b — Limpiar drift en 7 apps consumidoras
|
||||
|
||||
Parte del issue principal [0107](0107-modules-standardization.md).
|
||||
|
||||
## Objetivo
|
||||
|
||||
Eliminar de `uses_functions` en 7 `app.md` los IDs que ya son miembros de `data_table` module (declarado en `uses_modules`).
|
||||
|
||||
## Apps afectadas
|
||||
|
||||
| App | Path | Drift count |
|
||||
|---|---|---|
|
||||
| services_monitor | apps/services_monitor/app.md | 12 |
|
||||
| dag_engine_ui | apps/dag_engine_ui/app.md | 12 |
|
||||
| odr_console | projects/online_data_recopilation/apps/odr_console/app.md | 5 |
|
||||
| navegator_dashboard | projects/navegator/apps/navegator_dashboard/app.md | 12 |
|
||||
| graph_explorer | projects/osint_graph/apps/graph_explorer/app.md | 12 |
|
||||
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/app.md | 11 |
|
||||
| app_gestion | apps/app_gestion/app.md | 12 |
|
||||
|
||||
## Miembros a quitar (segun module.md de data_table v1.4)
|
||||
|
||||
- `data_table_cpp_viz`
|
||||
- `compute_stage_cpp_core`
|
||||
- `compute_pipeline_cpp_core`
|
||||
- `compute_column_stats_cpp_core`
|
||||
- `tql_emit_cpp_core`
|
||||
- `tql_helpers_cpp_core`
|
||||
- `tql_apply_cpp_core`
|
||||
- `tql_to_sql_cpp_core`
|
||||
- `lua_engine_cpp_core` (hasta 0107d que lo saca del modulo)
|
||||
- `join_tables_cpp_core` (idem)
|
||||
- `auto_detect_type_cpp_core` (idem)
|
||||
- `llm_anthropic_cpp_core` (idem)
|
||||
- `viz_render_cpp_viz`
|
||||
|
||||
NOTA: 0107d sacara lua/join/auto_detect/llm del modulo. Cuando eso pase, esas apps DEBEN volver a anadirlos a `uses_functions` (si los usan directamente). 0107b limpia el estado actual contra `module.md` v1.4; despues de 0107d se ejecuta `fn doctor modules` otra vez y se ajusta.
|
||||
|
||||
## Tareas
|
||||
|
||||
- [ ] **2.1** Para cada app.md, eliminar las lineas listadas en "Miembros a quitar" del bloque `uses_functions`.
|
||||
- [ ] **2.2** `fn index` despues.
|
||||
- [ ] **2.3** Verificar con `fn doctor modules` que `services_monitor` etc. reportan 0 drift.
|
||||
- [ ] **2.4** Build completo de las 7 apps. Linkage NO debe cambiar (los .cpp seguian viniendo via `fn_module_data_table` enlazado en su CMake).
|
||||
- [ ] **2.5** Smoke manual rapido (lanzar y cerrar) de cada app.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- Si `fn doctor uses-functions` se ejecuta antes de que `uses_modules` se entienda como cobertura, marcara las apps como "missing imports". Mitigacion: arreglar primero `audit_uses_functions_go_infra` para que considere `uses_modules` como cobertura transitiva. Tarea inline 2.0 antes de 2.1.
|
||||
|
||||
## Notas
|
||||
|
||||
- Es solo metadata. No toca codigo, no rompe build. Coste = editar 7 archivos + fn index.
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
id: "0107c"
|
||||
title: "Partir modules/data_table/data_table.cpp (4777 LOC) en sub-funciones del registry"
|
||||
status: pendiente
|
||||
type: refactor
|
||||
domain:
|
||||
- cpp-stack
|
||||
- meta
|
||||
scope: module
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks:
|
||||
- "0107d"
|
||||
related:
|
||||
- "0107"
|
||||
- "0081"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: [modules, data-table, refactor, registry, viz]
|
||||
---
|
||||
|
||||
# 0107c — Partir `data_table.cpp` 4777 LOC
|
||||
|
||||
Parte del issue principal [0107](0107-modules-standardization.md).
|
||||
|
||||
## Problema
|
||||
|
||||
`modules/data_table/data_table.cpp` es un god-file de 4777 LOC con UI entera dentro: barra de chips, tabla grid, paneles de viz, drill-down, joins, panel Ask AI, button event sink, color rules, breadcrumb, tooltips. Imposible auditar consumidores parciales. Cada bloque deberia ser una funcion del registry con su `.md` propio (Ejemplo + Cuando usarla + Gotchas).
|
||||
|
||||
## Decision
|
||||
|
||||
Partir en **6 sub-funciones** dentro de `cpp/functions/viz/` (no en `modules/data_table/` — son funciones del registry reutilizables). El modulo bundla las 6 + el entrypoint thin.
|
||||
|
||||
| Sub-funcion | LOC objetivo | Responsabilidad |
|
||||
|---|---|---|
|
||||
| `data_table_chips_cpp_viz` | ~600 | Barra de chips superior: filtros activos, TQL preview, save/load query |
|
||||
| `data_table_grid_cpp_viz` | ~1200 | Render del grid: cells, sorting, freeze cols, declarative renderers (Badge/Progress/Duration/Icon/Button/Dots/CategoricalChip/ColorScale) |
|
||||
| `data_table_viz_panels_cpp_viz` | ~800 | Paneles de viz lateral: histograms, line, scatter, value-counts |
|
||||
| `data_table_drill_cpp_viz` | ~300 | Drill-down stack + breadcrumb |
|
||||
| `data_table_ai_panel_cpp_viz` | ~500 | Panel Ask AI: prompt, llamada a `llm_anthropic`, render respuesta, export |
|
||||
| `data_table_color_rules_cpp_viz` | ~400 | Editor de reglas de color por columna + aplicacion |
|
||||
|
||||
Entrypoint `data_table.cpp` queda ~400 LOC: compone las 6 sub-funciones, gestiona `State`, dispatcher de eventos.
|
||||
|
||||
## Tareas
|
||||
|
||||
- [ ] **3.1** Leer `data_table.cpp` end-to-end e identificar fronteras funcionales (comentario header de cada bloque sirve).
|
||||
- [ ] **3.2** `cpp/functions/viz/data_table_chips.cpp` + `.h` + `.md` (NEW).
|
||||
- [ ] **3.3** `cpp/functions/viz/data_table_grid.cpp` + `.h` + `.md` (NEW).
|
||||
- [ ] **3.4** `cpp/functions/viz/data_table_viz_panels.cpp` + `.h` + `.md` (NEW).
|
||||
- [ ] **3.5** `cpp/functions/viz/data_table_drill.cpp` + `.h` + `.md` (NEW).
|
||||
- [ ] **3.6** `cpp/functions/viz/data_table_ai_panel.cpp` + `.h` + `.md` (NEW).
|
||||
- [ ] **3.7** `cpp/functions/viz/data_table_color_rules.cpp` + `.h` + `.md` (NEW).
|
||||
- [ ] **3.8** Reducir `data_table.cpp` a entrypoint thin que llama las 6.
|
||||
- [ ] **3.9** `modules/data_table/CMakeLists.txt`: anadir las 6 sub-funciones a la static lib.
|
||||
- [ ] **3.10** `modules/data_table/module.md`: bump `version: 2.0.0` (breaking interno, API publica intacta) + extender `members:` con las 6 nuevas + entrada en `## Capability growth log`.
|
||||
- [ ] **3.11** Recompilar TODAS las apps que linkean `fn_module_data_table` (7 apps).
|
||||
- [ ] **3.12** Smoke manual de cada app: tabla renderiza, chips funcionan, viz panels OK, AI panel OK, drill OK, color rules OK.
|
||||
- [ ] **3.13** `primitives_gallery --capture` golden image antes vs despues — diff visual cero.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **State struct**: hoy `data_table::State` es opaco interno del .cpp. Tras split, las 6 sub-funciones necesitan acceder a partes de el. Opciones:
|
||||
- (a) Mover `State` a `data_table_types.h` publico (mas exposicion pero claro).
|
||||
- (b) Definir `State` en `data_table.h` con sub-structs por sub-funcion (`State::chips_state`, `State::grid_state`, etc.) y cada sub-funcion recibe su sub-state.
|
||||
- **Recomendacion**: (b). Mantiene encapsulacion y cada sub-funcion tiene firma clara.
|
||||
- **API publica `data_table::render(...)` no cambia**. Es la regla dura. Si la firma debe cambiar, ya no es 0107c sino issue nuevo con migration plan.
|
||||
- **Tiempo de refactor**: 4777 LOC → 6 archivos requiere cuidado quirurgico. Lanzamos `fn-constructor` en paralelo.
|
||||
|
||||
## Notas
|
||||
|
||||
- Las 6 sub-funciones son `purity: impure` (manipulan ImGui state global).
|
||||
- Cada `.md` con `tags: [viz, table, imgui, ui]` + `framework: imgui`.
|
||||
- El refactor lo hara un sub-agente fn-constructor lanzado en paralelo desde el flujo principal del issue 0107.
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
id: "0107d"
|
||||
title: "Sacar lua_engine/llm_anthropic/join_tables/auto_detect_type del modulo data_table — politica de tiers"
|
||||
status: pendiente
|
||||
type: refactor
|
||||
domain:
|
||||
- cpp-stack
|
||||
- meta
|
||||
scope: module
|
||||
priority: alta
|
||||
depends:
|
||||
- "0107c"
|
||||
blocks: []
|
||||
related:
|
||||
- "0107"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: [modules, data-table, policy, tiers, lua, llm]
|
||||
---
|
||||
|
||||
# 0107d — Politica members generales + tiers
|
||||
|
||||
Parte del issue principal [0107](0107-modules-standardization.md).
|
||||
|
||||
## Problema
|
||||
|
||||
`data_table` module hoy lleva como miembros `lua_engine`, `llm_anthropic`, `join_tables`, `auto_detect_type`. Esos 4 son **utiles fuera de tablas**:
|
||||
|
||||
- `lua_engine`: scripting general.
|
||||
- `llm_anthropic`: LLM wrapper, util en chat_ia (proximo modulo) y otros.
|
||||
- `join_tables`: util en cualquier app que combine tablas (no solo data_table::render).
|
||||
- `auto_detect_type`: util en data import generico.
|
||||
|
||||
Forzar membership infla el modulo y obliga a las apps a tragarse 4 deps pesadas (lua + curl + libllm + http) aunque solo quieran render basico.
|
||||
|
||||
## Decision
|
||||
|
||||
Politica de tiers:
|
||||
|
||||
```yaml
|
||||
# module.md (post-0107d)
|
||||
members:
|
||||
# core_members: esenciales, sin ellos no hay funcionalidad
|
||||
- data_table_chips_cpp_viz
|
||||
- data_table_grid_cpp_viz
|
||||
- data_table_drill_cpp_viz
|
||||
- data_table_color_rules_cpp_viz
|
||||
- data_table_viz_panels_cpp_viz
|
||||
- compute_stage_cpp_core
|
||||
- compute_pipeline_cpp_core
|
||||
- compute_column_stats_cpp_core
|
||||
- tql_emit_cpp_core
|
||||
- tql_helpers_cpp_core
|
||||
- tql_apply_cpp_core
|
||||
- tql_to_sql_cpp_core
|
||||
- viz_render_cpp_viz
|
||||
|
||||
uses_functions:
|
||||
# Deps externas usadas por el modulo (no son miembros del modulo)
|
||||
- lua_engine_cpp_core # TQL scripting (opt-in via feature flag interno)
|
||||
- llm_anthropic_cpp_core # Ask AI panel (opt-in via FN_LLM_ANTHROPIC)
|
||||
- join_tables_cpp_core # Joins
|
||||
- auto_detect_type_cpp_core # Detect tipos al cargar nueva tabla
|
||||
```
|
||||
|
||||
Distincion:
|
||||
- **`members`**: funciones que el modulo POSEE — viven en `cpp/functions/viz/` y nadie mas las usa directamente (renderizan dentro del modulo).
|
||||
- **`uses_functions`**: funciones que el modulo CONSUME — viven en `cpp/functions/core/`, son utiles fuera del modulo, otras apps pueden importarlas directamente.
|
||||
|
||||
Apps consumidoras de `data_table`:
|
||||
- Si solo llaman `data_table::render(...)` → solo `uses_modules: [data_table_cpp]`, nada mas.
|
||||
- Si ademas usan `lua_engine` directamente para sus propios scripts → anaden `lua_engine_cpp_core` a `uses_functions` (no es duplicado, es uso directo independiente).
|
||||
|
||||
## Tareas
|
||||
|
||||
- [ ] **4.1** Editar `modules/data_table/module.md`: separar `members` core de `uses_functions`. Bump version 2.1.0.
|
||||
- [ ] **4.2** Editar `modules/data_table/CMakeLists.txt`: `lua_engine.cpp`, `llm_anthropic.cpp`, `join_tables.cpp`, `auto_detect_type.cpp` quedan dentro de la static lib (el modulo los usa internamente), pero el linkage transitivo se controla via PUBLIC vs PRIVATE. Si una app NO usa directamente lua/llm fuera del modulo, igual los recibe pero solo como impl detail del modulo. Decidir: SI mantenemos lua/llm/join dentro de la static lib del modulo (PUBLIC link) o sacamos al app para que cada una decida (PRIVATE en modulo, app linkea por su lado).
|
||||
- [ ] **4.3** Documentar tier en `docs/MODULES_API.md` (0107f).
|
||||
- [ ] **4.4** `fn doctor modules` (0107a) entiende la distincion members vs uses_functions y NO marca drift cuando una app lista en `uses_functions` algo que el modulo declara en su `uses_functions` (no es miembro).
|
||||
- [ ] **4.5** Actualizar 7 app.md: si un app necesita lua/llm/join standalone, declararlo. Si no, no.
|
||||
|
||||
## Decisiones de diseno
|
||||
|
||||
**Decision 4.2 detallada:** mantener lua/llm/join dentro de `fn_module_data_table` static lib (PUBLIC), porque:
|
||||
- 100% de las apps que linkean `fn_module_data_table` hoy lo usan para tablas, que usan internamente lua/llm/join.
|
||||
- Cero apps quieren un "data_table ligero sin lua". Si llegara ese caso → split modulo en `data_table_core` + `data_table_full`. Mientras tanto, KISS.
|
||||
- La distincion `members` vs `uses_functions` queda solo en `module.md` (metadata) — el CMakeLists agrupa todo bajo la static lib.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- Ambiguedad "¿el modulo posee X o lo usa?": resolver via 1 regla simple — si `X` aparece como funcion suelta consumida por otras apps fuera del modulo, va a `uses_functions`. Si nadie mas la usa, es `member`.
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
id: "0107f"
|
||||
title: "modules/README.md (catalogo) + docs/MODULES_API.md (contrato publico por modulo)"
|
||||
status: pendiente
|
||||
type: docs
|
||||
domain:
|
||||
- meta
|
||||
- cpp-stack
|
||||
- docs
|
||||
scope: registry
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks: []
|
||||
related:
|
||||
- "0107"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: [modules, docs, api, contract]
|
||||
---
|
||||
|
||||
# 0107f — Docs API publica de modulos
|
||||
|
||||
Parte del issue principal [0107](0107-modules-standardization.md).
|
||||
|
||||
## Objetivo
|
||||
|
||||
Resolver "no hay un sitio canonico que diga como usar un modulo". Dos archivos:
|
||||
|
||||
1. `modules/README.md` — catalogo (tabla con nombre, version, descripcion 1-linea, link a contrato).
|
||||
2. `docs/MODULES_API.md` — contrato canonico publico de cada modulo (template + ejemplos).
|
||||
|
||||
## Estructura `modules/README.md`
|
||||
|
||||
```markdown
|
||||
# Modulos C++ del registry
|
||||
|
||||
Bundles versionados de funciones del registry expuestos como static libs.
|
||||
Apps opt-in via `app.md::uses_modules` + `target_link_libraries(... fn_module_<X>)`.
|
||||
|
||||
| Modulo | Version | Static lib | Header | Entry | Descripcion | Contrato |
|
||||
|---|---|---|---|---|---|---|
|
||||
| framework_cpp | 1.1.0 | fn_framework | cpp/framework/app_base.h | fn::run_app(cfg, render) | Shell ImGui (GLFW+OpenGL+ImGui+ImPlot+themas+layouts+logger) | [framework_cpp](../docs/MODULES_API.md#framework_cpp) |
|
||||
| data_table_cpp | 2.0.0 | fn_module_data_table | modules/data_table/data_table.h | data_table::render(...) | Tabla completa TQL+viz+drill+AI+button events | [data_table_cpp](../docs/MODULES_API.md#data_table_cpp) |
|
||||
|
||||
## Como anadir un modulo
|
||||
|
||||
Ver [docs/MODULES_API.md#cycle](../docs/MODULES_API.md#ciclo-de-vida-de-un-modulo) y `.claude/rules/cpp_apps.md`.
|
||||
```
|
||||
|
||||
## Estructura `docs/MODULES_API.md`
|
||||
|
||||
```markdown
|
||||
# Modules API
|
||||
|
||||
Contrato publico canonico de cada modulo C++ del registry. Una app DEBE
|
||||
poder integrar un modulo leyendo solo esta pagina (sin abrir el .cpp).
|
||||
|
||||
## Template por modulo
|
||||
|
||||
### <module_name>
|
||||
|
||||
**Static lib target:** `<cmake_target>`
|
||||
**Header path:** `<include>`
|
||||
**Namespace:** `<namespace>`
|
||||
**Entry function:** `<signature>`
|
||||
**State struct:** `<type>` (lifecycle: <lifecycle notes>)
|
||||
**Public types:** lista de tipos publicos consumidos (TableInput, TableEvent, etc.)
|
||||
**Dependencias transitivas:** lista de libs externas (lua54, imgui, etc.)
|
||||
**Min ImGui version:** <X.Y>
|
||||
**Thread safety:** <main thread only | safe to call from any thread>
|
||||
|
||||
#### Opt-in en una app
|
||||
|
||||
1. `app.md`: anadir `uses_modules: [{name: <id>, min_version: "X.Y"}]`.
|
||||
2. `CMakeLists.txt`: `target_link_libraries(<app> PRIVATE <cmake_target>)`.
|
||||
3. Header: `#include "<header_path>"`.
|
||||
4. Reservar `<state>` persistente entre frames.
|
||||
5. Llamar `<entry>` cada frame.
|
||||
|
||||
#### Ejemplo minimo
|
||||
|
||||
```cpp
|
||||
[bloque de codigo lanzable]
|
||||
```
|
||||
|
||||
#### Eventos / callbacks
|
||||
|
||||
[Tabla de eventos publicos si aplica]
|
||||
|
||||
#### Gotchas
|
||||
|
||||
[Lista de gotchas conocidos]
|
||||
|
||||
#### Version policy
|
||||
|
||||
Semver. Major = breaking ABI/API de la entry function o State publico.
|
||||
Minor = additive (nuevo evento, nuevo renderer opcional). Patch = bugfix.
|
||||
|
||||
---
|
||||
|
||||
## framework_cpp
|
||||
|
||||
[Aplicar template]
|
||||
|
||||
## data_table_cpp
|
||||
|
||||
[Aplicar template]
|
||||
|
||||
## Ciclo de vida de un modulo
|
||||
|
||||
1. Crear `modules/<name>/` con `module.md`, `CMakeLists.txt`, `<name>.cpp`, `<name>.h`.
|
||||
2. `module.md::members` lista funciones del registry que el modulo bundla.
|
||||
3. `module.md::version` SemVer estricto.
|
||||
4. `CMakeLists.txt` define static lib `fn_module_<name>` con sus deps.
|
||||
5. Anadir entrada en `modules/README.md`.
|
||||
6. Anadir seccion en `docs/MODULES_API.md` siguiendo el template.
|
||||
7. `fn index` registra el modulo.
|
||||
8. Cada bump de version: `/version modules/<name> <major|minor|patch> "<reason>"` (ver `.claude/commands/version.md`).
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
- [ ] **6.1** Crear `modules/README.md` con tabla canonica.
|
||||
- [ ] **6.2** Crear `docs/MODULES_API.md` con template + secciones para los 2 modulos actuales.
|
||||
- [ ] **6.3** Anadir referencia desde `.claude/rules/cpp_apps.md` a `docs/MODULES_API.md`.
|
||||
- [ ] **6.4** Anadir referencia desde `cpp/PATTERNS.md` a `docs/MODULES_API.md`.
|
||||
- [ ] **6.5** Anadir referencia desde `.claude/CLAUDE.md` en la seccion "Estructura" listando `modules/` y enlazando docs.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- Doc drift: facil que `module.md` y `MODULES_API.md` se desincronicen. Mitigacion: `fn doctor modules` (0107a) verifica que cada modulo registrado tiene seccion en `MODULES_API.md`. Si no, warning.
|
||||
@@ -0,0 +1,209 @@
|
||||
---
|
||||
id: "0107g"
|
||||
title: "Migrar inline ImGui::BeginTable a data_table::render en apps con tablas de datos reales"
|
||||
status: en-progreso
|
||||
type: refactor
|
||||
domain:
|
||||
- cpp-stack
|
||||
- meta
|
||||
scope: multi-app
|
||||
priority: media
|
||||
depends:
|
||||
- "0107c"
|
||||
blocks: []
|
||||
related:
|
||||
- "0107"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: [modules, data-table, drift, audit, inline-begintable]
|
||||
---
|
||||
|
||||
# 0107g — Migrar inline BeginTable a `data_table::render` (data tables reales)
|
||||
|
||||
Parte del issue principal [0107](0107-modules-standardization.md). Detectado por `audit_data_table_usage_go_infra` (output en `dev/data_table_integration_audit.md`).
|
||||
|
||||
## Problema
|
||||
|
||||
Audit automatico identifica ~12 hits de `ImGui::BeginTable` inline en apps que YA declaran `uses_modules: [data_table_cpp]`. Mezcla legitimos + bugs:
|
||||
|
||||
- **Legitimos** (NO migrar): KPI grids, schema forms k/v, layout 2-col splitters, chart grids. NO son tablas de datos.
|
||||
- **Bugs reales**: tablas de datos con filas dinamicas + sort/filter potencial que reinventan logica que el modulo provee.
|
||||
|
||||
Resultado: codigo duplicado, comportamiento inconsistente, color/badge/sort/filter "casi-pero-no" igual entre apps. Conexiones raras: cada app personaliza su tabla a mano.
|
||||
|
||||
## Decision
|
||||
|
||||
Migrar los hits identificados como bugs reales a `data_table::render`. Dejar los legitimos como excepciones documentadas en `docs/MODULES_API.md::Cuando usar data_table::render vs BeginTable directo`.
|
||||
|
||||
## Tabla de migracion
|
||||
|
||||
| App | Path | Linea | Es bug? | Accion |
|
||||
|---|---|---|---|---|
|
||||
| dag_engine_ui | apps/dag_engine_ui/tabs.cpp | 382 | **BUG** (`##dt_run_steps`, 6 cols, scroll Y, runs dinamicas) | Migrar |
|
||||
| dag_engine_ui | apps/dag_engine_ui/tabs.cpp | 731 | LEGITIMO (`##health_kpis`, 4 cols stretch same, KPI grid) | Dejar + comentar |
|
||||
| navegator_dashboard | projects/navegator/apps/navegator_dashboard/autoextract_panel.cpp | 528 | **BUG** (`##ax_schema`, 5 cols, filas dinamicas schema) | Migrar |
|
||||
| navegator_dashboard | projects/navegator/apps/navegator_dashboard/recipes_panel.cpp | 238 | **BUG** (`##recipes_tbl`, 6 cols, filas dinamicas recipes) | Migrar |
|
||||
| graph_explorer | projects/osint_graph/apps/graph_explorer/extract_panel.cpp | 981 | **BUG** (`##ents`, 5 cols, filas dinamicas entities) | Migrar |
|
||||
| graph_explorer | projects/osint_graph/apps/graph_explorer/extract_panel.cpp | 1027 | **BUG** (`##rels`, 5 cols, filas dinamicas relations) | Migrar |
|
||||
| graph_explorer | projects/osint_graph/apps/graph_explorer/main.cpp | 1127 | LEGITIMO (`##enr_params`, form k/v) | Dejar + comentar |
|
||||
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 885 | LEGITIMO (`##insp_id`, inspector form k/v) | Dejar + comentar |
|
||||
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 958 | LEGITIMO (`##insp_fields`, inspector form) | Dejar + comentar |
|
||||
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 1546 | INFO comment, ya migrado a data_table::render | Ignorar (es comentario) |
|
||||
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 1854 | **BUG** (`##te_rows`, col_count dinamico, data table type explorer) | Migrar (segunda fase de la migration ya empezada) |
|
||||
| graph_explorer | projects/osint_graph/apps/graph_explorer/views.cpp | 2292 | DISCUTIBLE (`##te_fields`, 5 cols, fields de un tipo — semi-dinamico) | Evaluar; si rows >20 migrar, sino dejar |
|
||||
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 380 | LEGITIMO (`##kpi_grid`, KPI cards) | Dejar + comentar |
|
||||
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 436 | LEGITIMO (`##chart_grid`, plots grid) | Dejar + comentar |
|
||||
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 648 | LEGITIMO (`##monitor_kpi`, KPI cards) | Dejar + comentar |
|
||||
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 1110 | LEGITIMO (`##proj_layout`, 2-col splitter) | Dejar + comentar |
|
||||
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/views.cpp | 1448 | LEGITIMO (`##explorer_layout`, 2-col splitter) | Dejar + comentar |
|
||||
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/work_tab.cpp | 239 | **BUG** (`##flows_work`, 8 cols, filas dinamicas flows) | Migrar |
|
||||
| registry_dashboard | projects/fn_monitoring/apps/registry_dashboard/work_tab.cpp | 272 | **BUG** (`##top_issues_work`, 7 cols, filas dinamicas issues) | Migrar |
|
||||
| app_gestion | apps/app_gestion/main.cpp | 722 | DISCUTIBLE (`##linked_tbl`, 4 cols, lista de modulos linked — semi-dinamico, rows <20) | Evaluar; bias a dejar como esta |
|
||||
|
||||
**Total a migrar (BUGs): 8 tablas en 4 apps.**
|
||||
**Total LEGITIMOS (dejar + comentar): 9.**
|
||||
**Total DISCUTIBLES: 2 — decision contextual.**
|
||||
**Total comentarios/already-migrated: 1.**
|
||||
|
||||
## Tareas
|
||||
|
||||
- [x] **1.1** Migrar `dag_engine_ui/tabs.cpp:382` (`##dt_run_steps`) → `data_table::render`. HECHO. Function col → Button action="open_fn"; Status → CategoricalChip; Duration → Duration renderer.
|
||||
- [~] **1.2** Migrar `navegator_dashboard/autoextract_panel.cpp:528` (`##ax_schema`). ABORTADO: form editor con InputText/Checkbox editables inline en cada fila (field, selector, type, keep). data_table::render no soporta CellEdit como InputText inline. Comentado con LAYOUT-TABLE.
|
||||
- [x] **1.3** Migrar `navegator_dashboard/recipes_panel.cpp:238` (`##recipes_tbl`). HECHO. Patron B: 4 columnas Button (run/edit/delete/open_df) + ev.row para indexar yaml_path. last_status → CategoricalChip.
|
||||
- [~] **1.4** Migrar `graph_explorer/extract_panel.cpp:981` (`##ents`). ABORTADO: form editor con InputText editables por fila (type_buf, name_buf) + Checkbox "sel". Mutacion directa de structs entities[i]. No mapeable a data_table. Comentado con LAYOUT-TABLE.
|
||||
- [~] **1.5** Migrar `graph_explorer/extract_panel.cpp:1027` (`##rels`). ABORTADO: form editor con Checkbox "sel" + inmutabilidad necesaria (relations[i].selected se muta inline). Comentado con LAYOUT-TABLE.
|
||||
- [~] **1.6** Migrar `graph_explorer/views.cpp:1854` (`##te_rows`). ABORTADO: interactividad app-específica no mapeable — Selectable + single-click ramificado por estado de promocion (promoted/unpromoted), dblclick promote-flow, PopupContextItem con promote/demote/focus condicionales, SmallButton Promote-out-of-group, paginacion manual. Equivalente exact en data_table events no existe. Comentado explicando razon.
|
||||
- [x] **1.7** Migrar `registry_dashboard/work_tab.cpp:239` (`##flows_work`). HECHO. 8 cols. Status + Risk → CategoricalChip. BeginChild host 220px.
|
||||
- [x] **1.8** Migrar `registry_dashboard/work_tab.cpp:272` (`##top_issues_work`). HECHO. 7 cols. Status + Deps + Prio → CategoricalChip. Deps string "-"/"OK"/"blocked" preserva logica de color original. BeginChild host -1.
|
||||
- [x] **2.1** Anadir comentario `// LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline.` encima de los 9 hits LEGITIMOS para que `audit_data_table_usage` los excluya en proximas pasadas. HECHO en: ##health_kpis, ##enr_params, ##insp_id, ##insp_fields, ##kpi_grid, ##chart_grid, ##monitor_kpi, ##proj_layout, ##explorer_layout. Los 3 ABORTADOS tambien comentados con razon tecnica.
|
||||
- [ ] **2.2** Actualizar `audit_data_table_usage_go_infra` para leer ese comentario y filtrar `[warn] -> [ignored:declared_layout_table]`.
|
||||
- [x] **3.1** Decidir los 2 DISCUTIBLES (`te_fields`, `linked_tbl`) con criterio "si rows pueden crecer > 50, migrar". Decision: DEJAR. `te_fields` max ~20 fields por tipo; `linked_tbl` max ~10 modules linked. Rows no escalan.
|
||||
- [x] **4.1** Envolver TODAS las llamadas a `data_table::render` en `ImGui::BeginChild` host. HECHO en las 3 tablas migradas: flows_work (220px), top_issues_work (-1), recipes_tbl (300px), dt_run_steps (usa el BeginChild preexistente ##run_steps_wrap).
|
||||
- [ ] **5.1** Re-ejecutar audit:
|
||||
```
|
||||
./fn run audit_data_table_usage
|
||||
```
|
||||
Verificar: 0 BUG hits, 9 LEGITIMOS comentados, 11 `no_child_host` resueltos o documentados como excepcion.
|
||||
- [x] **5.2** Build de las 4 apps modificadas. HECHO: dag_engine_ui, registry_dashboard, graph_explorer compilan OK (Linux). navegator_dashboard es Windows-only (CMakeLists.txt retorna en non-WIN32); sintaxis verificada via g++ -fsyntax-only sin errores.
|
||||
|
||||
## Patrones de migracion canonicos
|
||||
|
||||
### Patron A: ImGui::BeginTable inline → data_table::render basico
|
||||
|
||||
```cpp
|
||||
// ANTES
|
||||
if (ImGui::BeginTable("##recipes_tbl", 6, flags)) {
|
||||
ImGui::TableSetupColumn("name");
|
||||
ImGui::TableSetupColumn("url_pattern");
|
||||
// ...
|
||||
for (const auto& r : recipes) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn(); ImGui::TextUnformatted(r.name.c_str());
|
||||
ImGui::TableNextColumn(); ImGui::TextUnformatted(r.url_pattern.c_str());
|
||||
// ...
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// DESPUES
|
||||
static data_table::State g_st_recipes;
|
||||
static std::vector<std::string> g_back_recipes; // backing
|
||||
static std::vector<const char*> g_ptrs_recipes; // ptrs row-major
|
||||
|
||||
g_back_recipes.clear();
|
||||
for (const auto& r : recipes) {
|
||||
g_back_recipes.push_back(r.name);
|
||||
g_back_recipes.push_back(r.url_pattern);
|
||||
// ... resto cols
|
||||
}
|
||||
g_ptrs_recipes.clear();
|
||||
for (auto& s : g_back_recipes) g_ptrs_recipes.push_back(s.c_str());
|
||||
|
||||
data_table::TableInput tbl;
|
||||
tbl.name = "recipes";
|
||||
tbl.headers = {"name", "url_pattern", "last_status", "last_at", "tries", "ok"};
|
||||
tbl.types = {data_table::ColumnType::String, data_table::ColumnType::String,
|
||||
data_table::ColumnType::String, data_table::ColumnType::Date,
|
||||
data_table::ColumnType::Int, data_table::ColumnType::Bool};
|
||||
tbl.cells = g_ptrs_recipes.data();
|
||||
tbl.rows = (int)recipes.size();
|
||||
tbl.cols = 6;
|
||||
|
||||
// Status como CategoricalChip (ganancia inmediata sobre BeginTable)
|
||||
tbl.column_specs.resize(tbl.cols);
|
||||
for (int i = 0; i < tbl.cols; i++) tbl.column_specs[i].id = tbl.headers[i];
|
||||
tbl.column_specs[2].renderer = data_table::CellRenderer::CategoricalChip;
|
||||
tbl.column_specs[2].chips = {{"ok","#22c55e"},{"error","#ef4444"},{"pending","#a3a3a3"}};
|
||||
|
||||
std::vector<data_table::TableEvent> events;
|
||||
ImGui::BeginChild("##recipes_host", ImVec2(-1, -1));
|
||||
data_table::render("##recipes_dt", {tbl}, g_st_recipes, &events);
|
||||
ImGui::EndChild();
|
||||
|
||||
for (const auto& ev : events) {
|
||||
if (ev.kind == data_table::TableEventKind::RowDoubleClick) {
|
||||
open_recipe_detail(ev.row);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Patron B: BeginTable inline con interactividad (boton por fila)
|
||||
|
||||
Si la BeginTable inline tiene un boton "Delete" / "Edit" por fila → migrar usando `CellRenderer::Button` + `action_id`:
|
||||
|
||||
```cpp
|
||||
// ANTES
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::SmallButton(("Delete##" + r.id).c_str())) { delete_recipe(r.id); }
|
||||
|
||||
// DESPUES — anadir columna actions con button renderer
|
||||
tbl.headers.push_back("actions");
|
||||
tbl.types.push_back(data_table::ColumnType::String);
|
||||
data_table::ColumnSpec actions_spec;
|
||||
actions_spec.id = "actions";
|
||||
actions_spec.renderer = data_table::CellRenderer::Button;
|
||||
actions_spec.button_action = "delete_recipe";
|
||||
actions_spec.button_label = "Delete";
|
||||
actions_spec.button_color_hex = "#ef4444";
|
||||
tbl.column_specs.push_back(actions_spec);
|
||||
tbl.cols++;
|
||||
|
||||
// Backing extra
|
||||
for (const auto& r : recipes) {
|
||||
g_back_recipes.push_back(r.id); // celda actions = el id (consumido en ev.value)
|
||||
}
|
||||
|
||||
// Handler
|
||||
for (const auto& ev : events) {
|
||||
if (ev.kind == data_table::TableEventKind::ButtonClick && ev.action_id == "delete_recipe") {
|
||||
delete_recipe(ev.value); // ev.value == r.id de la fila clicada
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Backing storage**: las apps deben mantener `std::vector<std::string>` (estable) + `std::vector<const char*>` (ptrs row-major). Helper `cells_to_ptrs()` ya esta usado en data_factory — generalizar como `cpp/functions/core/cells_to_ptrs.cpp` si patron se repite >2 veces (ya pasa).
|
||||
- **State persistente**: cada migracion requiere `static data_table::State g_st_<name>;`. Si la app tiene N tablas, N states.
|
||||
- **Comportamiento sutil**: filter/sort/freeze ahora son user-toggle, no controlados por la app. La app pierde control fino, pero gana consistencia.
|
||||
|
||||
## Bonus: nueva funcion del registry `cells_to_ptrs_cpp_core`
|
||||
|
||||
Patron `g_back + g_ptrs` aparece en data_factory + (post 0107g) en 4 apps mas. Promover a funcion del registry:
|
||||
|
||||
```cpp
|
||||
// cpp/functions/core/cells_to_ptrs.h
|
||||
namespace fn {
|
||||
// Converts a row-major flat vector<string> to a row-major vector<const char*>
|
||||
// pointing into the backing storage. Stable pointers — backing must not be
|
||||
// resized while ptrs are in use.
|
||||
void cells_to_ptrs(const std::vector<std::string>& back,
|
||||
std::vector<const char*>& ptrs);
|
||||
}
|
||||
```
|
||||
|
||||
Issue separado o sub-task de 0107g segun apetito.
|
||||
|
||||
## Notas
|
||||
|
||||
- El audit `audit_data_table_usage_go_infra` ya existe (FRESH 7d). Se referencia desde `fn doctor modules` (0107a) para mostrar drift en CI/dashboard.
|
||||
@@ -0,0 +1,356 @@
|
||||
---
|
||||
id: "0108"
|
||||
title: "App tables_qa — testbed agresivo del modulo data_table + deprecar tables_playground"
|
||||
status: in-progress
|
||||
type: app
|
||||
domain:
|
||||
- cpp-stack
|
||||
- apps-infra
|
||||
- meta
|
||||
scope: app
|
||||
priority: alta
|
||||
depends:
|
||||
- "0107"
|
||||
blocks: []
|
||||
related:
|
||||
- "0081"
|
||||
- "0097"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: [data-table, modules, testbed, qa, regression, version-selector, apphub]
|
||||
---
|
||||
|
||||
# 0108 — tables_playground como testbed agresivo del modulo data_table
|
||||
|
||||
## Problema
|
||||
|
||||
`apps/primitives_gallery/playground/tables/` hoy es el playground original de tablas — fue el origen de `modules/data_table`. Pero esta DESACOPLADO del modulo: linkea sus PROPIAS copias de `data_table.cpp`, `data_table_logic.cpp`, `tql.cpp`, `tql_to_sql.cpp`, `lua_engine.cpp`, `llm_anthropic.cpp`, `viz.cpp` (versiones legacy). El `self_test.cpp` (430 checks) prueba la logica legacy, no el modulo.
|
||||
|
||||
Consecuencias:
|
||||
- Bug en el modulo no se detecta en el playground.
|
||||
- Cualquier mejora del modulo (post-0107) NO se valida contra el playground hasta que algun app lo encuentre en runtime.
|
||||
- El playground es deuda — codigo duplicado que nadie mantiene a la par.
|
||||
|
||||
Ademas, las apps consumidoras (`app_gestion`, `data_factory`, `registry_dashboard`, `services_monitor`, etc.) usan el modulo con patrones repetidos pero no documentados como "casos de uso canonicos":
|
||||
|
||||
- `CellRenderer::CategoricalChip` con `chips` map (status, kind, enabled).
|
||||
- `CellRenderer::ColorScale` con `range_min`/`range_max`/`range_alpha` (duracion en ms).
|
||||
- `CellRenderer::Badge` con `badges` map (version pinning, status, env).
|
||||
- `CellRenderer::Duration` con `duration_warn_ms` / `duration_error_ms`.
|
||||
- `CellRenderer::Button` con `button_action` → consumido en `events_out` (`TableEventKind::ButtonClick`).
|
||||
- `TableEventKind::RowDoubleClick` → abrir detalle / drilldown.
|
||||
- Joins entre `TableInput[0]` (main) y `TableInput[i]` (joinables) con `JoinStrategy`.
|
||||
- Color rules condicionales (numericas + categoricas) — declaradas en `State.stages[k].color_rules`.
|
||||
|
||||
NO HAY un sitio donde un agente o humano pueda ver TODOS estos patrones funcionando lado-a-lado para verificar visualmente que el modulo se comporta bien. El primitives_gallery hace eso para componentes basicos, pero no para tablas avanzadas.
|
||||
|
||||
## Decision (actualizada 2026-05-17)
|
||||
|
||||
Crear **app NUEVA `tables_qa`** (no evolucionar `tables_playground` in-place). Razon: playground tiene 8 .cpp legacy + self_test 2921 LOC mezclado. Mas limpio crear desde 0 con `init_cpp_app` + `uses_modules: [data_table_cpp]` desde el principio.
|
||||
|
||||
`tables_playground` se **deprecara** tras `tables_qa` verde:
|
||||
- `apps/primitives_gallery/playground/tables/` → mover a `apps/primitives_gallery/playground/tables_legacy_archive/` (gitignored o conservado por historia).
|
||||
- Entry en `cpp/CMakeLists.txt` que registra `tables_playground` se elimina.
|
||||
- `tables_playground_self_test` deja de buildear.
|
||||
|
||||
### Identidad de la app
|
||||
|
||||
- `name`: `tables_qa`
|
||||
- `description`: "Testbed agresivo del modulo data_table — multi-tabla, menu QA toggleable, inyector de eventos, counters live, version selector + downgrade side-by-side."
|
||||
- `icon.phosphor`: `"test-tube"` (Phosphor — claro QA, sin ambiguedad)
|
||||
- `icon.accent`: `"#f59e0b"` (amber — testing zone, distinto de los colores existentes de otras apps)
|
||||
- `dir_path`: `apps/tables_qa`
|
||||
- `repo_url`: `https://gitea.organic-machine.com/dataforge/tables_qa`
|
||||
- `tags`: `[imgui, dashboard, qa, testing, data-table, regression]`
|
||||
|
||||
### Registro en App Hub
|
||||
|
||||
Tras build verde:
|
||||
1. `./fn run generate_app_icon "test-tube" "#f59e0b" "apps/tables_qa/appicon.ico"` — genera .ico multi-res.
|
||||
2. Anadir trio obligatorio (`description` + `icon.phosphor` + `icon.accent`) al `app.md` (ya cubierto en frontmatter del scaffolder).
|
||||
3. `./fn run refresh_app_hub` — regenera icons PNG + manifest TSV del hub + reinicia `app_hub_launcher` si esta corriendo.
|
||||
4. Verificar tarjeta visible en hub con icono amber + descripcion.
|
||||
|
||||
## Decision (original — refundar a aplicacion completa)
|
||||
|
||||
Refundar `tables_playground` como **testbed agresivo del modulo `fn_module_data_table`**:
|
||||
|
||||
1. **Migrar al modulo**: el playground PASA a depender de `fn_module_data_table` static lib. Eliminar las copias locales (`data_table.cpp`, `data_table_logic.cpp`, `tql.cpp`, `lua_engine.cpp`, `viz.cpp`, `tql_to_sql.cpp`, `llm_anthropic.cpp`). El playground se convierte en cliente del modulo igual que las apps reales.
|
||||
2. **Multi-tabla**: 6-8 tablas demo cubriendo cada `CellRenderer` + cada `TableEventKind` + joins + color rules. Cada una con su `State` persistente. Tab navegable (`ImGui::BeginTabBar`).
|
||||
3. **Menu de acciones replicando apps**: barra superior con toggles que activan/desactivan features tipicas usadas por apps:
|
||||
- "Buttons en celdas (Cancel/Retry/Inspect)" → emite ButtonClick.
|
||||
- "Color condicional numerico (ColorScale en duraciones)".
|
||||
- "Color condicional categorico (CategoricalChip por status)".
|
||||
- "Badge version pinning".
|
||||
- "Duration con thresholds (warn=1000ms / error=5000ms)".
|
||||
- "Dots sparkline (status timeline)".
|
||||
- "Icon map (TI_CHECK/TI_X/TI_CIRCLE)".
|
||||
- "Join Left/Inner/Right/Full entre tablas".
|
||||
- "Row double-click → modal de detalle".
|
||||
- "Row right-click → context menu".
|
||||
- "TQL pipeline (filter/breakout/agg/sort) con chips".
|
||||
- "Ask AI panel (TQL natural language)".
|
||||
- "Save/Load TQL desde disco".
|
||||
- "Export CSV".
|
||||
- "Drill-down entre stages".
|
||||
4. **Version selector + downgrade**:
|
||||
- Mostrar version actual del modulo en header (`fn::framework_version()` / `data_table::module_version()` — anadir API).
|
||||
- Selector de "modo compatibilidad" para simular comportamiento de versiones anteriores (`v1.4`, `v1.3`, etc.). Implementacion: feature flags por version dentro del modulo o branch-by-config al construir `TableInput`. Util para auditar regresiones cuando bumpemos versiones.
|
||||
- Side-by-side: renderizar la misma `TableInput` con la version actual a la izquierda y con el modo compatibilidad a la derecha. Diff visual inmediato.
|
||||
5. **Capture & compare visual**: integrar con el sistema `primitives_gallery --capture` existente (golden images) para que cada commit corra screenshots del testbed y compare contra master. Si pixel diff > threshold → CI rojo.
|
||||
6. **Self-test del modulo**: nuevo `tables_playground_module_test.cpp` que ejerza la API publica `data_table::render()` con casos sinteticos. Headless (sin GLFW window) usando `imgui_test_engine` (si FN_BUILD_TESTS=ON). Cubre:
|
||||
- Cada `CellRenderer` enum se renderiza sin crash.
|
||||
- `events_out` recibe el evento correcto al simular click.
|
||||
- State se mantiene entre frames.
|
||||
- TQL pipeline produce output esperado.
|
||||
- Joins respetan strategy.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
### Estructura final del playground
|
||||
|
||||
```
|
||||
apps/primitives_gallery/playground/tables/
|
||||
CMakeLists.txt # linka fn_module_data_table; ELIMINA sources legacy
|
||||
main.cpp # entry: fn::run_app + render_playground()
|
||||
playground.cpp # render_playground(): tab bar + acciones menu
|
||||
tables/ # NEW — 1 archivo por tabla demo
|
||||
tbl_basic.cpp # Text + numerico, base case
|
||||
tbl_renderers.cpp # 1 columna por cada CellRenderer (visual review)
|
||||
tbl_buttons.cpp # Cancel/Retry/Inspect — emite ButtonClick
|
||||
tbl_color_rules.cpp # ColorScale numerico + CategoricalChip
|
||||
tbl_durations.cpp # Duration renderer + thresholds
|
||||
tbl_dots.cpp # Dots sparkline (status timelines)
|
||||
tbl_joins.cpp # 2 tablas + Left/Inner/Right/Full
|
||||
tbl_tql.cpp # Pipeline TQL completo + Ask AI
|
||||
tbl_drill.cpp # Drill-down stages
|
||||
version_compat.cpp # version selector + side-by-side downgrade
|
||||
test_module.cpp # NEW: self_test contra API publica del modulo
|
||||
e2e_run.sh # actualizar: build + test_module + capture
|
||||
(ELIMINAR: data_table.cpp, data_table_logic.cpp, tql.cpp, tql_to_sql.cpp,
|
||||
lua_engine.cpp, llm_anthropic.cpp, viz.cpp, tql_duckdb.cpp, self_test.cpp)
|
||||
README.md # NEW: documenta cada tabla demo + checklist e2e
|
||||
```
|
||||
|
||||
### API publica del modulo expuesta al playground (documentar)
|
||||
|
||||
```cpp
|
||||
// Entry function
|
||||
namespace data_table {
|
||||
void render(const char* id,
|
||||
const std::vector<TableInput>& tables,
|
||||
State& state,
|
||||
std::vector<TableEvent>* events_out = nullptr,
|
||||
bool show_chrome = true);
|
||||
}
|
||||
|
||||
// Tipos publicos consumidos por las apps
|
||||
struct TableInput {
|
||||
std::string name;
|
||||
std::vector<std::string> headers;
|
||||
std::vector<ColumnType> types;
|
||||
const char* const* cells; // row-major, rows*cols
|
||||
int rows;
|
||||
int cols;
|
||||
std::vector<ColumnSpec> column_specs; // renderer config per col
|
||||
};
|
||||
|
||||
struct ColumnSpec {
|
||||
std::string id;
|
||||
CellRenderer renderer = CellRenderer::Text;
|
||||
// Badge / CategoricalChip / Dots
|
||||
std::vector<BadgeRule> badges;
|
||||
std::vector<ChipRule> chips;
|
||||
// Progress
|
||||
bool progress_scale_100 = false;
|
||||
std::string progress_color_hex;
|
||||
// Duration
|
||||
float duration_warn_ms = 1000.0f;
|
||||
float duration_error_ms = 5000.0f;
|
||||
// Icon
|
||||
std::vector<IconMapEntry> icon_map;
|
||||
// Button
|
||||
std::string button_action;
|
||||
std::string button_label;
|
||||
std::string button_color_hex;
|
||||
// ColorScale
|
||||
double range_min = 0.0;
|
||||
double range_max = 1.0;
|
||||
float range_alpha = 0.25f;
|
||||
std::vector<ColorStop> color_stops;
|
||||
// Tooltip
|
||||
std::string tooltip;
|
||||
bool tooltip_on_hover = false;
|
||||
};
|
||||
|
||||
enum class CellRenderer : uint8_t {
|
||||
Text=0, Badge=1, Progress=2, Duration=3, Icon=4, Button=5,
|
||||
Dots=8, CategoricalChip=9, ColorScale=10,
|
||||
};
|
||||
|
||||
enum class TableEventKind : uint8_t {
|
||||
ButtonClick=1, RowDoubleClick=2, RowRightClick=3, CellEdit=4,
|
||||
};
|
||||
|
||||
struct TableEvent {
|
||||
TableEventKind kind;
|
||||
int row;
|
||||
int col;
|
||||
std::string column_id;
|
||||
std::string action_id; // ColumnSpec::button_action on ButtonClick
|
||||
std::string value;
|
||||
};
|
||||
|
||||
struct State {
|
||||
// (opaca al consumidor — gestionada internamente; debe persistir entre frames)
|
||||
};
|
||||
|
||||
// Module metadata (NEW en este issue)
|
||||
namespace data_table {
|
||||
const char* module_version(); // ej. "2.0.0"
|
||||
const char* module_description();
|
||||
}
|
||||
```
|
||||
|
||||
Esta especificacion va a `docs/MODULES_API.md` (issue 0107f) como el contrato canonico de `data_table_cpp`.
|
||||
|
||||
### Patron de uso canonico (apps)
|
||||
|
||||
```cpp
|
||||
static data_table::State st;
|
||||
data_table::TableInput tbl;
|
||||
tbl.name = "runs";
|
||||
tbl.headers = {"id", "status", "duration_ms", "started_at"};
|
||||
tbl.types = {ColumnType::String, ColumnType::String, ColumnType::Float, ColumnType::Date};
|
||||
tbl.cells = &cells_flat[0];
|
||||
tbl.rows = N;
|
||||
tbl.cols = 4;
|
||||
|
||||
// Configure renderers
|
||||
tbl.column_specs.resize(tbl.cols);
|
||||
for (int i = 0; i < tbl.cols; i++) tbl.column_specs[i].id = tbl.headers[i];
|
||||
|
||||
tbl.column_specs[1].renderer = data_table::CellRenderer::CategoricalChip;
|
||||
tbl.column_specs[1].chips = { {"ok", "#22c55e"}, {"error", "#ef4444"}, {"running", "#f59e0b"} };
|
||||
|
||||
tbl.column_specs[2].renderer = data_table::CellRenderer::ColorScale;
|
||||
tbl.column_specs[2].range_min = 0.0;
|
||||
tbl.column_specs[2].range_max = 5000.0;
|
||||
tbl.column_specs[2].range_alpha = 0.30f;
|
||||
|
||||
// Button column for retry action
|
||||
data_table::ColumnSpec retry_col;
|
||||
retry_col.id = "actions";
|
||||
retry_col.renderer = data_table::CellRenderer::Button;
|
||||
retry_col.button_action = "retry_run";
|
||||
retry_col.button_label = "Retry";
|
||||
tbl.headers.push_back("actions");
|
||||
tbl.column_specs.push_back(retry_col);
|
||||
|
||||
// Render + consume events
|
||||
std::vector<data_table::TableEvent> events;
|
||||
data_table::render("##runs_tbl", {tbl}, st, &events);
|
||||
|
||||
for (auto& ev : events) {
|
||||
if (ev.kind == data_table::TableEventKind::ButtonClick && ev.action_id == "retry_run") {
|
||||
// app handler
|
||||
}
|
||||
if (ev.kind == data_table::TableEventKind::RowDoubleClick) {
|
||||
// open detail modal
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
- [ ] **1.1** Investigar TODOS los patrones de uso del modulo en apps (script audit):
|
||||
```bash
|
||||
grep -rhnE "data_table::(render|TableInput|State|ColumnSpec|TableEvent|CellRenderer::|TableEventKind::|JoinStrategy)" \
|
||||
apps/*/main.cpp apps/*/*.cpp projects/*/apps/*/*.cpp 2>/dev/null | sort -u > /tmp/data_table_api_usage.txt
|
||||
```
|
||||
Producir tabla de patrones canonicos en `docs/MODULES_API.md`.
|
||||
- [ ] **2.1** Backup `apps/primitives_gallery/playground/tables/{data_table.cpp,data_table_logic.cpp,tql.cpp,tql_to_sql.cpp,lua_engine.cpp,llm_anthropic.cpp,viz.cpp,tql_duckdb.cpp,self_test.cpp}` a `apps/primitives_gallery/playground/tables/legacy_archive/` (gitignored o conservado por historia).
|
||||
- [ ] **2.2** Editar `apps/primitives_gallery/playground/tables/CMakeLists.txt`:
|
||||
- Quitar sources legacy.
|
||||
- `target_link_libraries(tables_playground PRIVATE fn_module_data_table)`.
|
||||
- El target `tables_playground_self_test` se renombra a `tables_playground_module_test` con sources nuevos.
|
||||
- [ ] **3.1** Crear `playground.cpp` con tab bar + menu acciones.
|
||||
- [ ] **3.2** Crear 9 archivos `tables/tbl_*.cpp` con cada caso demo.
|
||||
- [ ] **3.3** Crear `version_compat.cpp` con selector + side-by-side.
|
||||
- [ ] **3.4** Anadir API `data_table::module_version()` + `module_description()` a `modules/data_table/data_table.h`. Implementacion lee `version_generated.h` (post 0107c bump a 2.0.0).
|
||||
- [ ] **4.1** Crear `test_module.cpp` headless con `imgui_test_engine` (gated por `FN_BUILD_TESTS=ON`):
|
||||
- 1 test por `CellRenderer` enum.
|
||||
- 1 test por `TableEventKind`.
|
||||
- 1 test de joins.
|
||||
- 1 test de TQL pipeline.
|
||||
- Compara cells output via golden TSV.
|
||||
- [ ] **4.2** Migrar lo aplicable del `self_test.cpp` legacy (430 checks) a tests del modulo. Lo que prueba logica pura ya extraida al registry (parse_number, compare, tql parsing, lua) va a `cpp/functions/core/*_test.cpp`. Lo que prueba render() va a `test_module.cpp`.
|
||||
- [ ] **5.1** Actualizar `e2e_run.sh`: build + module_test + capture screenshot.
|
||||
- [ ] **5.2** Integrar con `primitives_gallery --capture` (golden images de cada tab del testbed).
|
||||
- [ ] **5.3** README.md con checklist de QA por tab.
|
||||
- [ ] **6.1** Verificar que las apps consumidoras (7) siguen compilando y comportandose igual.
|
||||
- [ ] **6.2** `fn index` para que el playground actualizado quede registrado (aunque sea playground, su `app.md` puede declarar `uses_modules: [data_table_cpp]` con min_version 2.0.0).
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
1. **Eliminar sources legacy**: opcion alternativa era mantenerlos como golden-reference. Decision: eliminarlos. Razon: si quedan vivos, la tentacion de "arreglar el playground" sin tocar el modulo permanece. Forzar a usar el modulo cierra el loop.
|
||||
2. **Side-by-side downgrade**: opcion alternativa era branchear el modulo en `data_table_v1` static lib paralelo. Decision: feature flags por version dentro del modulo actual (`v_compat_mode`). Menos duplicacion, mas pragmatico.
|
||||
3. **`module_version()` como API publica**: alternativa era leer `version_generated.h` desde la app. Decision: API publica + estable. Las apps que quieran mostrar la version del modulo en su About panel lo hacen via la funcion, no via include privado.
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- Issue 0107 (estandarizacion modulos) cerrado y `modules-v2` activo. **Bloqueo duro**: sin 0107, el sistema sigue con drift y refactorizar el playground encima de codigo inestable es desperdicio.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Headless test con imgui_test_engine**: requiere `FN_BUILD_TESTS=ON`. Si no esta disponible en CI, los tests no corren. Mitigacion: documentar requirement en CI config + fallback a tests de logica pura sin UI.
|
||||
- **Version compat mode** es facil de hacer mal: el codigo del modulo se llena de `if (v_compat < X)`. Mitigacion: limite estricto — solo se mantiene compat para las 2 versiones previas (`v_current - 1`, `v_current - 2`). Mas alla, se elimina y se documenta breaking change.
|
||||
- **Capture and compare** depende de fonts/DPI/driver GL → false positives en CI. Mitigacion: capture solo en Linux x86_64 con driver mesa fijado.
|
||||
|
||||
## Infra de testing reutilizable (existente en el repo)
|
||||
|
||||
El testbed se construye combinando estos mecanismos ya disponibles. Ningun nuevo motor.
|
||||
|
||||
| Mecanismo | Ubicacion | Que aporta al testbed |
|
||||
|---|---|---|
|
||||
| Catch2 unit | `cpp/tests/` (50+ tests, macro `add_fn_test`) | Tests de logica pura de sub-funciones del registry usadas por el modulo (`compute_column_stats`, `tql_emit`, `lua_engine`, `join_tables`, `tql_to_sql`). Ya existen — los tomamos como dado. |
|
||||
| Golden PNG diff | `cpp/tests/golden/` (43 PNGs) + `png_diff.cpp` | Cada tab del testbed exporta golden via `primitives_gallery --capture`. Diff en CI bloquea drift visual. |
|
||||
| Auto-driving worker | `apps/altsnap_jitter_test/main.cpp` (modelo de referencia) | Pattern: worker thread fakea eventos + counters monotonicos en `fn::internal::*` + `set_force_alt_for_test()` bypass. Replicar para `data_table`: counters `fn::internal::data_table::{button_click_count, row_double_click_count, color_rule_applied_count, ...}` + worker que inyecta clicks. |
|
||||
| Dear ImGui Test Engine | `cpp/framework/app_base.h:205-225` (`fn::run_app_test`, gated por `FN_BUILD_TESTS=ON`) | UI-level tests: `IM_REGISTER_TEST` lambdas que llaman `ctx->ItemClick`, `ctx->ItemDoubleClick`, verifican events emitidos. **CERO consumidores actuales** — el testbed sera el primer cliente. |
|
||||
| `--self-test` flag | `.claude/rules/e2e_validation.md` (patron canonico) | `tables_playground --self-test` corre toda la suite headless, exit 0/1. Compatible con CI sin display. |
|
||||
| Cross-platform e2e | `bash/functions/infra/e2e_run_cpp_windows.sh` | Funcion bash: cross-compile mingw + deploy Desktop + taskkill + run native + capturar stdout/exit. Linux CI valida Windows. |
|
||||
|
||||
### Panel de control QA agresivo (UI del testbed)
|
||||
|
||||
Barra superior con:
|
||||
|
||||
1. **Toggles por feature** (15+ checkboxes). Cada toggle muta `ColumnSpec` o `State` en runtime, mismo patron que apps reales hacen en codigo de setup. El usuario QA puede activar/desactivar y ver cambio visual inmediato sin recompilar.
|
||||
2. **Inyector de eventos**: 4 botones — "Simulate ButtonClick", "Simulate RowDoubleClick", "Simulate RowRightClick", "Simulate Drill". Cada uno llama el worker thread pattern de altsnap → verifica que `TableEvent` apropiado se emite.
|
||||
3. **Counters live**: contador por evento, contador por renderer aplicado, latency p50/p95 por frame (medir desde `ImGui::GetIO().Framerate`). Visible siempre, util para detectar regresiones de performance.
|
||||
4. **Version selector**: dropdown con versiones del modulo (`2.0.0` actual, `1.4.0` compat, `1.3.0` compat...). Cambio re-renderiza la misma data side-by-side: actual vs version seleccionada. Diff visual inmediato + diff de events emitidos.
|
||||
5. **Boton "Run --self-test"**: ejecuta toda la suite headless dentro de la misma ventana. Output en log panel. Util para iteracion rapida sin relanzar.
|
||||
6. **Boton "Export golden"**: corre `--capture` sobre cada tab, escribe PNGs a `golden/data_table_testbed/`. Para regenerar baseline tras cambio visual intencional.
|
||||
|
||||
### Counters internos a anadir al modulo
|
||||
|
||||
Issue 0108 anade en `modules/data_table/data_table.cpp`:
|
||||
|
||||
```cpp
|
||||
namespace data_table::internal {
|
||||
int button_click_count();
|
||||
int row_double_click_count();
|
||||
int row_right_click_count();
|
||||
int color_rule_applied_count();
|
||||
int tql_stages_executed();
|
||||
int last_render_duration_us();
|
||||
void reset_counters();
|
||||
}
|
||||
```
|
||||
|
||||
Mismo modelo que `fn::internal::*` para altsnap. Test-only observability, cost cero en prod (counters atomicos triviales).
|
||||
|
||||
## Notas
|
||||
|
||||
- Issue 0108 NO empieza hasta 0107 cerrar. Bloqueado duro.
|
||||
- Se referencia desde `docs/MODULES_API.md` (0107f) como el ejemplo canonico de "como usar el modulo data_table".
|
||||
- Cuando 0108 cierre, abrir issue 0109 paralelo para `chat_ia` (que era el siguiente modulo planeado al inicio de 0107).
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
id: "0109a"
|
||||
title: "skill_tree app shell + parsers issues/flows"
|
||||
status: completado
|
||||
type: feature
|
||||
domain:
|
||||
- meta
|
||||
- cpp-stack
|
||||
scope: app-scoped
|
||||
priority: media
|
||||
depends: []
|
||||
blocks:
|
||||
- "0109b"
|
||||
related:
|
||||
- "0109"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags:
|
||||
- skill-tree
|
||||
- cpp
|
||||
- imgui
|
||||
- parsers
|
||||
---
|
||||
|
||||
# 0109a — skill_tree shell + parsers
|
||||
|
||||
Primer slice del epic 0109. Foco: app C++ scaffoldada, compilando, leyendo los 79 issues + 7 flows y reportando conteos en stdout. Sin render del grafo todavia — solo plumbing.
|
||||
|
||||
## Tareas
|
||||
|
||||
1. Scaffolder `./fn run init_cpp_app skill_tree --domain tools --desc "..." --tags "dashboard,meta"`.
|
||||
2. Editar `app.md` generado: trio icon (`tree-structure`, `#c026d3`), `e2e_checks`, `uses_functions` iniciales.
|
||||
3. Generar `appicon.ico` via `generate_app_icon_py_infra`.
|
||||
4. Crear funcion `parse_md_frontmatter_cpp_core` (delegar a fn-constructor):
|
||||
- Input: `std::string content` (texto del .md completo).
|
||||
- Output: `MdFrontmatter` struct con `std::unordered_map<std::string, YamlValue>` y `std::string body`.
|
||||
- `YamlValue` = variant `{string, list<string>, null}`. Subset YAML suficiente para issues actuales.
|
||||
- Pure. Test golden: parsea los 79 issues + 7 flows sin error.
|
||||
5. En `main.cpp` (scaffold inicial): al arrancar, scan `dev/issues/*.md` + `dev/flows/*.md`, parse cada uno, contar por status/domain. Log a stdout:
|
||||
```
|
||||
skill_tree v0.1.0
|
||||
issues: 79 (28 pendiente, 3 in-progress, 72 completado, ...)
|
||||
flows: 7 (5 pending, 2 completed)
|
||||
parse errors: 0
|
||||
```
|
||||
6. `e2e_checks` build + self-test warning.
|
||||
7. Compilar + deploy Windows via `redeploy_cpp_app_windows`.
|
||||
8. Refrescar hub via `refresh_app_hub`.
|
||||
|
||||
## DoD
|
||||
|
||||
- [ ] App existe en `apps/skill_tree/` con `.git/` apuntando a `dataforge/skill_tree`.
|
||||
- [ ] `app.md` con trio completo + `e2e_checks` + `uses_functions` declarados.
|
||||
- [ ] `appicon.ico` generado.
|
||||
- [ ] `fn index` exitoso, `mcp__registry__fn_show id="skill_tree"` muestra metadata.
|
||||
- [ ] `parse_md_frontmatter_cpp_core` indexado en registry.
|
||||
- [ ] `cmake --build cpp/build --target skill_tree` exitoso.
|
||||
- [ ] `./skill_tree` (Linux) o `skill_tree.exe` (Windows) imprime conteos correctos.
|
||||
- [ ] Tarjeta visible en `app_hub_launcher`.
|
||||
- [ ] `fn doctor cpp-apps` limpio.
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: "0109b"
|
||||
title: "skill_tree layout anillos + render canvas ImDrawList con cards"
|
||||
status: completado
|
||||
type: feature
|
||||
domain:
|
||||
- meta
|
||||
- cpp-stack
|
||||
scope: app-scoped
|
||||
priority: media
|
||||
depends:
|
||||
- "0109a"
|
||||
blocks:
|
||||
- "0109c"
|
||||
related:
|
||||
- "0109"
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags:
|
||||
- skill-tree
|
||||
- cpp
|
||||
- imgui
|
||||
- layout
|
||||
- canvas
|
||||
---
|
||||
|
||||
# 0109b — skill_tree layout anillos + render canvas
|
||||
|
||||
Segundo slice del epic 0109. Reemplaza la lista textual del Tree por un canvas interactivo basado en `ImDrawList`. Pivote desde `graph_renderer_cpp_viz` (GPU) → `ImDrawList` (CPU) para mantener simplicidad: 166 nodos no justifican el pipeline GPU.
|
||||
|
||||
## Decisiones tomadas durante la implementacion
|
||||
|
||||
- **Stack: `ImGui::ImDrawList`**, NO `graph_renderer_cpp_viz`. Razon: 166 nodos cabian de sobra en CPU; `graph_renderer` exige `init_gl_loader=true`, build de `GraphData` con tipos OSINT, shaders, FBO + texture flip-Y. Diferencia ~120 LOC + un monton de rebuilds para cero beneficio observable.
|
||||
- **Sin fisicas** (el usuario lo pidio explicito). Layout deterministico via `compute_ring_layout_cpp_core`.
|
||||
- **5 rings**: done (0), in-progress (1), unlocked (2), locked (3), deferred/bloqueado (4).
|
||||
- **18 sectores radiales** = 18 dominios canonicos (`dev/TAXONOMY.md`). Labels en el aro exterior.
|
||||
- **Lock derivation**: `pendiente` se subdivide en `pendiente_unlocked` (todos los `depends[]` en done) vs `pendiente_locked` (algun depends sin completar). Set de `done` IDs se computa al cargar y se cruza con cada `depends[]`.
|
||||
- **Animacion lerp 1s** entre prev y current position cuando un nodo cambia de `status_eff` entre dos `reload_scan()`s. Ease-in-out cuadratica.
|
||||
- **Cards con texto**: cada nodo muestra su ID en blanco con sombra negra para legibilidad sobre cualquier color de ring.
|
||||
- **Diferencial visual flows vs issues**: issues = circulos, flows = rombos.
|
||||
- **Pan**: drag con boton derecho o medio.
|
||||
- **Zoom**: rueda del raton, centrado en cursor (re-anchora coordenadas mundo bajo el puntero).
|
||||
- **Picking**: O(N) radius check (166 nodos = trivial; spatial hash innecesario).
|
||||
|
||||
## Tareas
|
||||
|
||||
1. Crear funcion del registry `compute_ring_layout_cpp_core` (pure, 10 tests Catch2, FNV-1a determinista para sub-jitter angular).
|
||||
2. Reescribir `main.cpp::draw_tree()` como canvas con `ImGui::InvisibleButton` + `ImDrawList`.
|
||||
3. Implementar `derive_status_eff()` para lock/unlock.
|
||||
4. Implementar `apply_layout()` con preservacion de prev_x/prev_y para animacion.
|
||||
5. Render: aristas curvas Bezier (depends + related) + nodos con outline + label ID + tooltip on hover.
|
||||
6. HUD strip con LV/XP/contadores.
|
||||
7. Self-test 0-exit cuando `parse_errors == 0 && unmapped == 0`.
|
||||
8. Build Linux + deploy Windows.
|
||||
|
||||
## DoD
|
||||
|
||||
- [x] `compute_ring_layout_cpp_core` indexada (10/10 tests, 142 assertions).
|
||||
- [x] `apps/skill_tree/main.cpp` usa `parse_md_frontmatter_cpp_core` + `compute_ring_layout_cpp_core`.
|
||||
- [x] `app.md uses_functions` actualizado con ambas.
|
||||
- [x] Self-test imprime breakdown por ring: `done=77 in-progress=2 unlocked=64 locked=22 deferred=1 unmapped=0`.
|
||||
- [x] Linux build OK.
|
||||
- [x] Windows deploy OK (PID corriendo).
|
||||
- [x] Tarjeta visible en `app_hub_launcher`.
|
||||
- [x] `fn doctor cpp-apps` limpio.
|
||||
|
||||
## Sigue
|
||||
|
||||
0109c: Inspector evolucionado con DoD parseado de la seccion `## DoD` del .md (checkboxes interactivos read-only) + lista de `uses_functions` del registry para esa issue.
|
||||
Reference in New Issue
Block a user