From a0c0aa4f18d05321a36600fa3f7ac294825c7d92 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Tue, 28 Apr 2026 23:38:56 +0200 Subject: [PATCH] =?UTF-8?q?docs(issues):=20a=C3=B1adir=200041-0048=20?= =?UTF-8?q?=E2=80=94=20refactor=20C++=20apps,=20tests,=20primitives=20stan?= =?UTF-8?q?darization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev/issues/0041-cpp-app-best-practices.md | 124 ++++++++++++++++++ dev/issues/0042-cpp-layout-storage-public.md | 110 ++++++++++++++++ dev/issues/0043-cpp-apps-standardize-shell.md | 92 +++++++++++++ dev/issues/0044-cpp-orphans-audit.md | 84 ++++++++++++ dev/issues/0045-cpp-extract-pure-logic.md | 98 ++++++++++++++ dev/issues/0046-cpp-refactor-raw-imgui.md | 84 ++++++++++++ dev/issues/0047-cpp-tests-foundation.md | 114 ++++++++++++++++ dev/issues/0048-cpp-visual-tests-ci-gate.md | 107 +++++++++++++++ dev/issues/README.md | 8 ++ 9 files changed, 821 insertions(+) create mode 100644 dev/issues/0041-cpp-app-best-practices.md create mode 100644 dev/issues/0042-cpp-layout-storage-public.md create mode 100644 dev/issues/0043-cpp-apps-standardize-shell.md create mode 100644 dev/issues/0044-cpp-orphans-audit.md create mode 100644 dev/issues/0045-cpp-extract-pure-logic.md create mode 100644 dev/issues/0046-cpp-refactor-raw-imgui.md create mode 100644 dev/issues/0047-cpp-tests-foundation.md create mode 100644 dev/issues/0048-cpp-visual-tests-ci-gate.md diff --git a/dev/issues/0041-cpp-app-best-practices.md b/dev/issues/0041-cpp-app-best-practices.md new file mode 100644 index 00000000..9fcbf9ac --- /dev/null +++ b/dev/issues/0041-cpp-app-best-practices.md @@ -0,0 +1,124 @@ +# 0041 — C++ app shell estandarizado: BEST_PRACTICES + AppConfig extendido + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | 0041 | +| **Estado** | pendiente | +| **Prioridad** | alta | +| **Tipo** | feature — C++ framework (`cpp/framework/`) + docs | + +## Dependencias + +Ninguna. Habilita **0043** (estandarizar apps existentes) y todo desarrollo futuro de apps C++. + +--- + +## Objetivo + +1. Documentar el patron canonico de apps C++ del registry en `cpp/PATTERNS.md` con checklist obligatorio. +2. Extender `fn::AppConfig` para que `fn::run_app()` registre About + paneles + theming en una sola llamada, eliminando boilerplate por app. + +## Contexto + +La auditoria muestra que `chart_demo`, `primitives_gallery`, `shaders_lab` y `registry_dashboard` arrancan distinto: solo `shaders_lab` aprovecha `app_menubar` con paneles, solo `registry_dashboard` registra About + Settings, `primitives_gallery` llama `apply_dark_theme()` + `gl_loader_init()` a mano que el resto delega a `run_app()`. Sin estandar, cada app proxima reinventa esto. + +## Arquitectura + +``` +cpp/ +├── PATTERNS.md # NEW — checklist y patrones +├── framework/ +│ ├── app_base.h # MOD — extender AppConfig +│ └── app_base.cpp # MOD — aplicar nuevos campos +└── functions/core/ + └── (sin cambios — app_menubar/app_about/app_settings ya existen) +``` + +### AppConfig extendido (propuesta) + +```cpp +struct AppAboutInfo { + const char* name = nullptr; + const char* version = nullptr; + const char* description = nullptr; +}; + +struct AppConfig { + const char* title = "fn app"; + int width = 1280; + int height = 800; + bool vsync = true; + bool viewports = false; + ThemeMode theme = ThemeMode::FnDark; + float bg_r=0,bg_g=0,bg_b=0; + + // NEW — registra About si name != nullptr. + AppAboutInfo about{}; + + // NEW — paneles toggleables del menubar (si != nullptr). + const fn_ui::PanelToggle* panels = nullptr; + size_t panel_count = 0; + + // NEW — callbacks de layouts persistentes (si != nullptr). + fn_ui::LayoutCallbacks* layouts_cb = nullptr; + + // NEW — si true, llama gl_loader_init() antes del primer frame. + bool init_gl_loader = false; +}; +``` + +`run_app()` aplica: +- Si `about.name != nullptr` → `fn_ui::about_window_set_info(...)`. +- Si `init_gl_loader` → `fn::gfx::gl_loader_init()` tras crear el contexto GL. +- Cada frame, si `panels != nullptr` o `layouts_cb != nullptr`, llama `app_menubar(panels, panel_count, layouts_cb)` automaticamente — la app NO la llama. + +## Tareas + +### Fase 1 — Doc + +1.1 Crear `cpp/PATTERNS.md` con checklist: +- Usa `fn::run_app()` con `AppConfig` (jamas `glfwInit` directo). +- Registra About via `AppConfig::about` o `about_window_set_info()`. +- Registra Settings extras via `settings_window_add_section()`. +- Si tienes paneles, define `static constexpr fn_ui::PanelToggle panels[]` y pasalo en `AppConfig::panels`. +- Si necesitas layouts, implementa `LayoutCallbacks` y pasalas en `AppConfig::layouts_cb`. +- Usa `fn_tokens::colors|spacing|radius` siempre. Nunca colores hex literales. +- Evita `ImGui::BeginTable / Selectable / BeginPopupModal / BeginChild` con styling manual — usa `dashboard_grid`, `tree_view`/`select`, `modal_dialog`, `dashboard_panel`. + +### Fase 2 — Framework + +2.1 Anadir campos `about`, `panels`, `panel_count`, `layouts_cb`, `init_gl_loader` a `AppConfig` en `app_base.h`. +2.2 En `run_app()` (`app_base.cpp`): +- Tras `gl_loader_init` opcional. +- Tras `about_window_set_info` si procede. +- Antes del `render_fn()` por frame, llama `app_menubar(panels, panel_count, layouts_cb)` si alguno != nullptr. +2.3 Backward-compat: campos nuevos opcionales con defaults — apps existentes compilan sin tocarlas. + +### Fase 3 — Validacion + +3.1 Compilar todas las apps existentes sin cambios, verificar que no rompe. +3.2 Build Linux + cross-compile Windows. + +### Fase 4 — Indexar + +4.1 `./fn index` para refrescar metadata (no cambia funciones — solo doc). + +## Decisiones de diseno + +- `AppConfig` con campos opcionales (no nuevos overloads de `run_app`) — minimiza la API surface. +- La menubar se llama desde `run_app` solo si la app declara paneles/layouts; en otro caso la app puede llamar `app_menubar(nullptr,0,nullptr)` ella misma (compat). + +## Riesgos + +- Apps que ya llaman `app_menubar` manualmente con `panels` distintos al `AppConfig`: documentar — `run_app` la llama una vez al inicio del frame, la app puede dejar de hacerlo. + +## Validacion + +- `cpp/PATTERNS.md` legible en GitHub. +- `chart_demo` (la mas simple) puede ahora declararse asi: + ```cpp + fn::run_app({.title="chart demo", .about={.name="chart demo", .version="0.1", .description="..."}}, render); + ``` +- Tests de compilacion OK para shaders_lab/chart_demo/primitives_gallery/registry_dashboard sin modificar. diff --git a/dev/issues/0042-cpp-layout-storage-public.md b/dev/issues/0042-cpp-layout-storage-public.md new file mode 100644 index 00000000..53dc03c3 --- /dev/null +++ b/dev/issues/0042-cpp-layout-storage-public.md @@ -0,0 +1,110 @@ +# 0042 — C++ layout_storage: extraer y publicar como API reutilizable + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | 0042 | +| **Estado** | pendiente | +| **Prioridad** | alta | +| **Tipo** | feature — C++ core (`cpp/functions/core`) | + +## Dependencias + +Ninguna. Habilita **0043** (estandarizar apps). + +--- + +## Objetivo + +Extraer la persistencia de layouts ImGui (actualmente privada en `shaders_lab/main.cpp` lineas 415-447) a una funcion publica del registry: `layout_storage_cpp_core`. Cualquier app puede pasarla a `app_menubar` via `LayoutCallbacks` con un solo `setup`. + +## Contexto + +`shaders_lab` guarda layouts (snapshots de `imgui.ini`) en SQLite via `shaderlab_db_cpp_core`. La logica que conecta SQLite con `LayoutCallbacks` (`save`, `load`, `list`, `remove`) esta inline en su `main.cpp` y no es reusable. Otras apps (registry_dashboard, primitives_gallery, chart_demo) no tienen layouts persistentes. + +## Arquitectura + +``` +cpp/functions/core/ +├── layout_storage.h # NEW — API publica +├── layout_storage.cpp # NEW — impl con SQLite +├── layout_storage.md # NEW +└── (opcional) layouts_menu.h ya existe — sin cambios + +cpp/apps/shaders_lab/ +└── main.cpp # MOD — usa layout_storage en lugar de inline +``` + +### API propuesta + +```cpp +namespace fn_ui { + +struct LayoutStorage; // opaque + +// Crea un storage que persiste layouts ImGui en una tabla SQLite del path dado. +// Si la BD no existe, la crea. Tabla: `imgui_layouts(name TEXT PRIMARY KEY, ini TEXT, updated_at)`. +LayoutStorage* layout_storage_open(const char* db_path); +void layout_storage_close(LayoutStorage* s); + +// Helper que rellena un LayoutCallbacks usando este storage. +// El caller mantiene vivo el storage durante la vida de los callbacks. +void layout_storage_make_callbacks(LayoutStorage* s, LayoutCallbacks& out); + +} +``` + +`LayoutCallbacks` ya esta definido en `panel_menu.h`/`layouts_menu.h`. Esta funcion solo wirea SQLite. + +## Tareas + +### Fase 1 — Codigo + +1.1 Crear `cpp/functions/core/layout_storage.{h,cpp,md}`. +1.2 Implementar usando sqlite3 vendoreada (`cpp/vendor/sqlite3`). Tabla unica `imgui_layouts`. +1.3 Save: serializa `ImGui::SaveIniSettingsToMemory()` y hace UPSERT por nombre. +1.4 Load: lee `ini` y llama `ImGui::LoadIniSettingsFromMemory(ini, len)`. +1.5 List: `SELECT name FROM imgui_layouts ORDER BY updated_at DESC`. +1.6 Remove: `DELETE FROM imgui_layouts WHERE name=?`. +1.7 Frontmatter `.md` con `purity: impure`, `error_type: error_go_core`, `uses_types: []`. + +### Fase 2 — Migrar shaders_lab + +2.1 Reemplazar el bloque inline (l. 415-447) por: +```cpp +auto* g_layouts = fn_ui::layout_storage_open("shaders_lab.db"); +fn_ui::LayoutCallbacks layouts_cb; +fn_ui::layout_storage_make_callbacks(g_layouts, layouts_cb); +// ...pasar layouts_cb a app_menubar +``` +2.2 Mantener `shaderlab_db_cpp_core` como esta (no es lo mismo: guarda shaders, no layouts) — pero quitar de el la parte de layouts si la tiene. +2.3 Verificar que los layouts existentes siguen cargando (compatibilidad de schema o migracion automatica). + +### Fase 3 — Tests + +3.1 Test unitario: open → save("test", ini) → list() == ["test"] → load("test") devuelve el ini → remove("test") → list() == []. +3.2 Test de regresion en shaders_lab (build + abrir/cerrar layout manual). + +### Fase 4 — Indexar + +4.1 `./fn index` y verificar `fn show layout_storage_cpp_core`. + +## Decisiones de diseno + +- BD SQLite por app (no compartida) — cada app gestiona sus layouts. +- Schema simple (`name PRIMARY KEY, ini, updated_at`) — sin namespaces ni jerarquia. +- API opaca (`LayoutStorage*`) para no exponer sqlite3 en headers publicos. + +## Riesgos + +- shaders_lab tiene layouts existentes en su `shaders_lab.db`. Si la tabla actual difiere del schema nuevo: migracion automatica al primer open o conservar ambas tablas. +- Threading: ImGui::SaveIniSettingsToMemory solo es seguro desde el thread principal — documentar. + +## Validacion + +```bash +cd cpp/build && cmake --build . --target shaders_lab +# Abrir shaders_lab, guardar/cargar layouts, restart, verificar que persiste. +./fn show layout_storage_cpp_core +``` diff --git a/dev/issues/0043-cpp-apps-standardize-shell.md b/dev/issues/0043-cpp-apps-standardize-shell.md new file mode 100644 index 00000000..ecf2303c --- /dev/null +++ b/dev/issues/0043-cpp-apps-standardize-shell.md @@ -0,0 +1,92 @@ +# 0043 — Estandarizar shell de las 4 apps C++ (About + Settings + paneles + tokens) + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | 0043 | +| **Estado** | pendiente | +| **Prioridad** | alta | +| **Tipo** | refactor — `cpp/apps/*` y `projects/fn_monitoring/apps/registry_dashboard` | + +## Dependencias + +Bloquea-por: **0041** (AppConfig extendido), **0042** (layout_storage publico). + +--- + +## Objetivo + +Que las 4 apps C++ con GUI (`shaders_lab`, `chart_demo`, `primitives_gallery`, `registry_dashboard`) sigan el mismo patron de inicializacion documentado en `cpp/PATTERNS.md` (issue 0041): About + Settings + paneles + tokens, sin reinventar boilerplate. + +## Contexto + +Auditoria muestra que cada app arranca distinto: solo `shaders_lab` aprovecha menubar con paneles, solo `registry_dashboard` registra About + Settings, `primitives_gallery` llama `apply_dark_theme()` + `gl_loader_init()` a mano. Tras 0041 y 0042, se puede unificar. + +## Arquitectura + +``` +cpp/apps/ +├── shaders_lab/main.cpp # MOD — usa layout_storage publico, About, init via AppConfig +├── chart_demo/main.cpp # MOD — registra About, simplifica +├── primitives_gallery/main.cpp # MOD — gl_loader_init via AppConfig, About +└── text_editor_smoke/ # SIN CAMBIOS (test CLI) + +projects/fn_monitoring/apps/registry_dashboard/ +└── main.cpp # MOD — usa AppConfig.about en lugar de about_window_set_info inline +``` + +## Tareas + +### Fase 1 — chart_demo (mas simple, validacion temprana) + +1.1 Cambiar a: +```cpp +return fn::run_app({ + .title="chart_demo", .width=1280, .height=800, + .about={.name="chart_demo", .version="0.1.0", + .description="Demo de primitivos viz: line, scatter, bar, heatmap"} +}, render); +``` +1.2 Quitar la llamada manual a `app_menubar(nullptr,0,nullptr)` del render — `run_app` la invoca por nosotros si hay panels/layouts; en este caso sin panels, dejar render limpio (o llamar `app_menubar(nullptr,0,nullptr)` solo si hace falta menubar para Settings). +1.3 Build OK. + +### Fase 2 — primitives_gallery + +2.1 Quitar `apply_dark_theme()` y `gl_loader_init()` del init manual (l. 129-134). Pasar `init_gl_loader=true` y `theme=ThemeMode::FnDark` en `AppConfig`. +2.2 Anadir About (`name="Primitives Gallery"`, version, description). +2.3 Mantener `app_menubar(nullptr,0,nullptr)` solo si la menubar la quiere la app — opcionalmente delegar a `run_app` extendido. +2.4 Build OK. + +### Fase 3 — registry_dashboard + +3.1 Reemplazar la llamada manual `about_window_set_info(...)` (l. 121-127) por `AppConfig::about={...}`. La seccion `Status` se queda como `settings_window_add_section` (no cambia). +3.2 Build OK. + +### Fase 4 — shaders_lab + +4.1 Reemplazar el bloque de layouts inline por `fn_ui::layout_storage_open("shaders_lab.db")` + `layout_storage_make_callbacks` (consume issue 0042). +4.2 Anadir About (`name="shaders_lab"`, version, description). +4.3 Pasar `panels` y `layouts_cb` via `AppConfig` (issue 0041) — quitar la llamada manual a `app_menubar`. +4.4 Build OK. + +### Fase 5 — Verificacion cross + +5.1 Build Linux y Windows de las 4 apps. +5.2 Run rapido en Linux para confirmar que About y Settings/Status aparecen en el menubar de cada una. + +## Decisiones de diseno + +- Tocar registry_dashboard para que sea consistente con el patron, aunque hoy ya tenia About/Settings via APIs sueltas. +- text_editor_smoke se queda igual (es un test CLI). + +## Riesgos + +- Si `run_app` invoca `app_menubar` y la app tambien lo llama, salen dos menubars. Verificar que cada main.cpp solo llama `app_menubar` si NO esta declarado en AppConfig. +- shaders_lab: la migracion a `layout_storage` debe preservar layouts existentes — ver riesgo de 0042. + +## Validacion + +- Las 4 apps abren y muestran menu Settings con Settings… + About… (mas Status… donde aplique). +- shaders_lab guarda/carga layouts despues de la migracion. +- `cmake --build` pasa para todas. Cross-compile Windows OK. diff --git a/dev/issues/0044-cpp-orphans-audit.md b/dev/issues/0044-cpp-orphans-audit.md new file mode 100644 index 00000000..5940fbab --- /dev/null +++ b/dev/issues/0044-cpp-orphans-audit.md @@ -0,0 +1,84 @@ +# 0044 — Auditar 52 funciones C++ huerfanas y poblar uses_functions + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | 0044 | +| **Estado** | pendiente | +| **Prioridad** | media | +| **Tipo** | chore — registry hygiene | + +## Dependencias + +Ninguna. + +--- + +## Objetivo + +Resolver las 52 funciones C++ que aparecen sin `uses_functions` en `registry.db`: o son legitimamente independientes, o se usan pero no se registra la dependencia (caso `fps_overlay` que se invoca desde `app_base.cpp` pero no aparece). Resultado: `uses_functions` reflejando la verdad. + +## Contexto + +Ejemplos detectados: +- `fps_overlay` — usado por `app_base.cpp`, no registrado. +- `tokens` — usado por 16+ componentes, ya registrado bien. +- `tween_curves`, `orbit_camera`, `time_series_buffer`, `graph_force_layout`, `plot_theme` — sin consumidores visibles. Verificar si estan en gallery/chart_demo o son scaffolding. +- `app_settings`, `app_about`, `app_menubar`, `icon_font` — usados por `app_base.cpp`, no registrados. + +## Arquitectura + +Este issue NO modifica codigo C++. Solo modifica: + +- Frontmatter `.md` de funciones huerfanas para anadir `uses_functions: [...]` reflejando llamadas reales. +- Eventualmente borrar funciones que se confirmen sin uso real ni plan de uso (con el OK del usuario via comment en el issue). + +## Tareas + +### Fase 1 — Lista exacta + +1.1 Generar la lista actual: +```bash +sqlite3 registry.db "SELECT id FROM functions WHERE lang='cpp' AND uses_functions = '[]' ORDER BY id;" +``` +1.2 Para cada id, hacer `grep -rn "" cpp/ projects/*/apps/*/` y ver donde se invoca. + +### Fase 2 — Categorizar + +Para cada huerfana, asignar uno de estos estados (anotar en una tabla en este issue): +- **USED** — se invoca desde otra funcion/app. Anadir el caller a `uses_functions` del consumidor (no del huerfano). +- **DEMO_ONLY** — solo en `primitives_gallery` o demos. OK como esta. Documentar en `.md` como `# scaffolding/demo only`. +- **PLANNED** — sin uso pero hay issue/plan futuro. Anotar en `.md` con referencia. +- **DEAD** — sin uso real ni plan. Candidato a borrar (preguntar al usuario antes). + +### Fase 3 — Actualizar frontmatter + +3.1 Para cada **USED**: editar el `.md` del CONSUMIDOR para anadir el huerfano a su `uses_functions`. Ejemplo: `app_base.cpp` consume `fps_overlay_cpp_core`, `app_settings_cpp_core`, `app_about_cpp_core`, `app_menubar_cpp_core`, `icon_font_cpp_core`, `tokens_cpp_core`. Pero `app_base` NO esta en functions/ del registry — es un .cpp del framework. **Corregir**: si `app_base.cpp` no esta indexado, registrar como funcion en `cpp/framework/` o documentar la convencion en notes. +3.2 Para **DEMO_ONLY**: anadir nota en frontmatter `notes: "demo en primitives_gallery, scaffolding"`. +3.3 Para **PLANNED**: anadir `notes: "planned use in issue NNNN"`. +3.4 Para **DEAD**: listar en este issue y abrir un sub-issue de delete (no borrar de oficio). + +### Fase 4 — Reindex + +4.1 `./fn index` para regenerar `registry.db`. +4.2 Verificar: +```bash +sqlite3 registry.db "SELECT COUNT(*) FROM functions WHERE lang='cpp' AND uses_functions = '[]';" +``` +La cifra debe bajar significativamente (objetivo: <20 huerfanas reales). + +### Fase 5 — Documentar la convencion + +5.1 Anadir a `.claude/rules/uses_functions.md` (NEW) la regla: cada vez que un .cpp llama a otra funcion del registry, el `.md` del CONSUMIDOR debe listar la dependencia en `uses_functions`. El indexer no lo deduce automaticamente (el codigo C++ no es trivial de parsear). + +## Decisiones + +- No borrar funciones sin OK explicito. +- `app_base.cpp` no esta hoy indexado como funcion del registry (es framework). Si decidimos indexarlo, eso es 0048 — ahora solo registramos en uses_functions de los CONSUMIDORES que si esten indexados (gallery, dashboard, etc.). + +## Validacion + +- `uses_functions` poblados para los casos reales (verificable por SQL). +- `cpp/PATTERNS.md` (de 0041) referencia esta convencion. +- Nada compilado se rompe (este issue NO toca .cpp/.h). diff --git a/dev/issues/0045-cpp-extract-pure-logic.md b/dev/issues/0045-cpp-extract-pure-logic.md new file mode 100644 index 00000000..f7bd9559 --- /dev/null +++ b/dev/issues/0045-cpp-extract-pure-logic.md @@ -0,0 +1,98 @@ +# 0045 — Extraer logica pura de componentes C++ impuros (sql_workbench, process_runner, file_watcher) + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | 0045 | +| **Estado** | pendiente | +| **Prioridad** | media | +| **Tipo** | refactor — `cpp/functions/core` | + +## Dependencias + +Ninguna. + +--- + +## Objetivo + +Extraer la parte pura (parsing, state machines, diff) de componentes hoy mezclados con I/O y UI. Resultado: cada uno se descompone en `_pure_cpp_core` (testeable, sin ImGui ni I/O) + `_ui_cpp_core` (thin wrapper impuro). Tambien aplica al compiler de `shaders_lab` que vive en su `main.cpp`. + +## Contexto + +- `sql_workbench_cpp_core` mezcla parsing SQL, ejecucion SQLite y render ImGui en una sola funcion. +- `process_runner_cpp_core` mezcla state machine de proceso (idle/running/success/error) con UI helpers. +- `file_watcher_cpp_core` mezcla polling de filesystem con notificaciones UI. +- `shaders_lab/main.cpp` tiene `compile_code()`, `compile_dag()`, `mark_code_dirty()` inline. + +Sin separacion no hay tests unitarios, no es reutilizable y crece la deuda con cada feature. + +## Arquitectura + +``` +cpp/functions/core/ +├── sql_parse.{h,cpp,md} # NEW — pure +├── sql_workbench.{h,cpp,md} # MOD — thin wrapper, llama sql_parse +├── process_state_machine.{h,cpp,md} # NEW — pure (transiciones) +├── process_runner.{h,cpp,md} # MOD — usa process_state_machine +├── file_poll_diff.{h,cpp,md} # NEW — pure (calcula diff de snapshots) +└── file_watcher.{h,cpp,md} # MOD — usa file_poll_diff + +cpp/apps/shaders_lab/ +├── compiler.h # NEW +├── compiler.cpp # NEW (extrae compile_code/compile_dag) +└── main.cpp # MOD (461 → ~250 lineas) +``` + +## Tareas + +### Fase 1 — sql_workbench + +1.1 `sql_parse_cpp_core` (pure): tokeniza SQL minimal (separa statements por `;`, detecta keyword inicial: SELECT/INSERT/UPDATE/DELETE/CREATE/etc., devuelve struct `SqlStatement{kind, text, line}`). +1.2 `sql_workbench` pasa a llamar `sql_parse` para detectar tipo de statement antes de ejecutar; el parser puro es testeable sin SQLite. +1.3 Test unitario: dado SQL multi-statement, devuelve N statements con kind correcto. + +### Fase 2 — process_runner + +2.1 `process_state_machine_cpp_core` (pure): funcion `process_transition(current_state, event) -> new_state` con eventos `{Trigger, Spawned, Finished, Failed, Timeout}` y estados `{Idle, Running, Success, Error}`. +2.2 `process_runner` pasa a usar la maquina pura para gestionar transiciones; la parte impura solo dispara `popen`/`spawn`/`wait` y traduce a eventos. +2.3 Test unitario: secuencia de eventos → estado final esperado. + +### Fase 3 — file_watcher + +3.1 `file_poll_diff_cpp_core` (pure): dado dos snapshots `vector`, devuelve `FileDiff{added, modified, removed}`. +3.2 `file_watcher` pasa a hacer el polling impuro y delegar el diff al puro. +3.3 Test unitario: dado A=[a,b,c], B=[a,b',d] → modified=[b], added=[d], removed=[c]. + +### Fase 4 — shaders_lab compiler + +4.1 Crear `cpp/apps/shaders_lab/compiler.{h,cpp}` con `compile_code(code, &out_log) -> bool`, `compile_dag(...)`, `mark_code_dirty(...)`. Estas funciones llaman al backend GLSL existente — no son del registry porque dependen de estado interno de shaders_lab. +4.2 Mover el codigo correspondiente desde `main.cpp`. +4.3 `main.cpp` queda <250 lineas. + +### Fase 5 — Indexar y validar + +5.1 `./fn index` para registrar las 3 nuevas funciones puras. +5.2 Verificar `purity: pure`, `returns_optional: false`, `error_type: ""` en cada una. +5.3 Build Linux + Windows. Tests pasan. + +## Decisiones + +- `sql_parse` es deliberadamente minimal — no es un parser SQL completo, solo distingue tipos de statement. Si en el futuro hace falta mas, se anade. +- `compile_code/compile_dag` quedan como utilities de la app, NO en el registry (dependen de tipos internos de shaders_lab). + +## Riesgos + +- Tests unitarios C++: no hay framework configurado todavia (issue 0047 lo monta). Mientras tanto, anadir tests dentro del archivo via `#ifdef TESTING` + un main pequeno, o esperar a 0047. +- Decision: usar `#ifdef TESTING` por ahora; cuando 0047 monte Catch2, migrar. + +## Validacion + +```bash +./fn run sql_parse_cpp_core # ejecuta el test si lo hay +./fn show sql_parse_cpp_core +./fn show process_state_machine_cpp_core +./fn show file_poll_diff_cpp_core +cmake --build cpp/build --target shaders_lab +``` diff --git a/dev/issues/0046-cpp-refactor-raw-imgui.md b/dev/issues/0046-cpp-refactor-raw-imgui.md new file mode 100644 index 00000000..dc9339da --- /dev/null +++ b/dev/issues/0046-cpp-refactor-raw-imgui.md @@ -0,0 +1,84 @@ +# 0046 — Reemplazar raw ImGui en apps por primitivos del registry + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | 0046 | +| **Estado** | pendiente | +| **Prioridad** | media | +| **Tipo** | refactor — `cpp/apps/*` y `projects/fn_monitoring/apps/registry_dashboard` | + +## Dependencias + +Bloquea-por: **0043** (apps con shell estandarizado, evita conflictos al refactor). + +--- + +## Objetivo + +Eliminar el uso innecesario de raw ImGui en las 4 apps GUI: tablas custom, `Selectable` con styling manual, modales propios y `BeginChild` con borde+padding repetido. Sustituir por primitivos del registry (`dashboard_grid`, `dashboard_panel`, `tree_view`/`select`, `modal_dialog`, `tokens`). + +## Contexto + +Auditoria conto: +- `registry_dashboard/views.cpp`: 33 raw calls — tablas con `BeginTable` + `PushStyleColor` repetido (l. 87, 143, 321), `BeginChild` con borde+padding (l. 122). +- `primitives_gallery/main.cpp`: sidebar con `Selectable` + `PushStyleColor` (l. 91-124). +- `shaders_lab/main.cpp`: modal save-as custom (l. 272-300) que reimplementa lo que hace `modal_dialog`. + +## Arquitectura + +``` +projects/fn_monitoring/apps/registry_dashboard/views.cpp # MOD — usar dashboard_panel/grid en lugar de BeginTable+styling +cpp/apps/primitives_gallery/main.cpp # MOD — sidebar via tree_view o select +cpp/apps/shaders_lab/main.cpp # MOD — save-as via modal_dialog + text_input +``` + +Posibles primitivos faltantes detectados (crear si no existen): +- **No crear** `tab_container` por ahora — `chart_demo` lo usa OK con tabs nativos. Solo extraer si se repite. + +## Tareas + +### Fase 1 — registry_dashboard/views.cpp + +1.1 Sustituir `chart_panel_begin/end` (l. 105-127) por llamadas a `dashboard_panel` si encaja. +1.2 Cambiar `BeginTable("##chart_grid", 4, ...)` por `dashboard_grid` (4 cols stretchsame). +1.3 Mismo cambio en `BeginTable("##kpi_grid", 4, ...)` y `BeginTable("##proj_layout", 2, ...)`. +1.4 `BeginChild` en `draw_projects_list` (proj_tree, proj_detail) → usar `dashboard_panel` con `Borders=true`. +1.5 Al final del refactor: contar raw `ImGui::Begin*` en views.cpp. Meta: <10 (de 33). + +### Fase 2 — primitives_gallery sidebar + +2.1 Sustituir el bloque `for (...) { Selectable + PushStyleColor }` (l. 96-122) por `fn_ui::select(...)` con la lista de demos, o por `tree_view` agrupado por categoria si la API encaja. +2.2 Mantener visualmente equivalente (separadores por categoria, item activo coloreado). + +### Fase 3 — shaders_lab save-as modal + +3.1 Reemplazar el bloque `BeginPopupModal` (l. 272-300) por `fn_ui::modal_dialog_begin("Save layout as...", &g_show_save_as, ImVec2(360, 0)) + text_input(...) + buttons + modal_dialog_end()`. + +### Fase 4 — Build cross + +4.1 Build Linux y Windows de las 3 apps modificadas. +4.2 Run rapido en Linux para verificar que el comportamiento es identico (solo cambia la implementacion). + +### Fase 5 — Reporte + +5.1 Anadir al final del issue una tabla "antes/despues" con: +- LoC de cada main.cpp / views.cpp. +- Numero de raw `ImGui::*` calls antes/despues. + +## Decisiones + +- Mantener `ImGui::BeginTabBar/EndTabBar` para tabs simples (chart_demo, dashboard) — son delgaditos y no aportan valor envolverlos. +- No crear primitivos nuevos en este issue. Si en el camino aparece un patron repetido, abrir issue separado. + +## Riesgos + +- `select` puede no soportar grupos por categoria con encabezado — verificar API actual. Si no lo soporta, usar `tree_view`. +- Conflicto potencial con 0043 si ambos tocan los mismos `main.cpp`. Por eso 0046 depende de 0043. + +## Validacion + +- Las 3 apps se ven igual visualmente (mismo dark theme, mismas zonas). +- Build OK. +- Reduccion clara de raw calls (verificable con `grep -c "ImGui::Begin" archivo`). diff --git a/dev/issues/0047-cpp-tests-foundation.md b/dev/issues/0047-cpp-tests-foundation.md new file mode 100644 index 00000000..059453e6 --- /dev/null +++ b/dev/issues/0047-cpp-tests-foundation.md @@ -0,0 +1,114 @@ +# 0047 — C++ tests foundation: Catch2 + tests para top-20 primitivos + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | 0047 | +| **Estado** | pendiente | +| **Prioridad** | alta | +| **Tipo** | feature — testing infra | + +## Dependencias + +Ninguna. + +--- + +## Objetivo + +Montar Catch2 (single-header, vendoreado) en `cpp/`, integrar con CMake y `ctest`, y escribir tests unitarios para el top-20 de primitivos puros del registry. Pasar de 4% a >25% de cobertura en C++. + +## Contexto + +Hoy hay 3 funciones C++ con `tested: true` (3.7% de 81). El indexer ya soporta extraer tests automaticamente al campo `tests` del `.md` cuando hay `test_file_path`. Falta el framework, la integracion y la cobertura inicial. + +## Arquitectura + +``` +cpp/ +├── vendor/catch2/ +│ └── catch_amalgamated.{hpp,cpp} # NEW — Catch2 v3 amalgamated, MIT +├── tests/ +│ ├── CMakeLists.txt # NEW — un test per-file +│ ├── test_tokens.cpp # NEW +│ ├── test_button.cpp # NEW (state-only — sin GUI render) +│ ├── test_select.cpp # NEW +│ ├── test_text_input.cpp # NEW +│ ├── test_badge.cpp # NEW +│ ├── test_kpi_card.cpp # NEW (calculo de delta/format) +│ ├── test_pie_chart.cpp # NEW (slice_at logic) +│ ├── test_bar_chart.cpp # NEW (label rotation logic) +│ ├── test_tree_view.cpp # NEW +│ ├── test_modal_dialog.cpp # NEW +│ ├── test_toolbar.cpp # NEW +│ ├── test_toast.cpp # NEW +│ ├── test_empty_state.cpp # NEW +│ ├── test_page_header.cpp # NEW +│ ├── test_dashboard_panel.cpp # NEW +│ ├── test_dashboard_grid.cpp # NEW +│ ├── test_sparkline.cpp # NEW +│ ├── test_table_view.cpp # NEW +│ ├── test_icon_button.cpp # NEW +│ └── test_tween_curves.cpp # NEW (puro, ideal candidato) +└── CMakeLists.txt # MOD — anadir tests subdir si BUILD_TESTING +``` + +## Tareas + +### Fase 1 — Vendoreado de Catch2 + +1.1 Bajar `catch_amalgamated.hpp` + `catch_amalgamated.cpp` (Catch2 v3 single-header) a `cpp/vendor/catch2/`. Anadir LICENSE y README pequeno. +1.2 No usar FetchContent — stay offline. Catch2 v3 es BSL-1.0. + +### Fase 2 — Integracion CMake + +2.1 En `cpp/CMakeLists.txt`, anadir option `BUILD_TESTING` (default ON). Si activo, `add_subdirectory(tests)`. +2.2 `cpp/tests/CMakeLists.txt`: una funcion CMake `add_fn_test(name srcs)` que: +- Crea un executable test linkeado contra `fn_framework`, ImGui (sin GLFW si no hace falta), Catch2. +- Anade al `ctest` con `add_test(NAME ${name} COMMAND ${name})`. + +### Fase 3 — Tests para puros (sin GUI) + +3.1 `tween_curves`: lerp, ease_in, ease_out, edge cases (t=0, t=1, valores fuera de rango). +3.2 `slice_at` de pie_chart (es funcion interna — exponer via header de test o moverla a `pie_math.h`). +3.3 Cualquier helper puro de `bar_chart` (rotacion de labels), `kpi_card` (formato de delta). + +### Fase 4 — Tests para componentes con state pero sin render (parcial) + +4.1 Para `process_state_machine`, `file_poll_diff`, `sql_parse` (creados en 0045 si existen): tests directos. +4.2 Para componentes UI (`button`, `select`, `tree_view`, etc.): solo logica accesible sin contexto ImGui (callbacks, state setters, formateo). Render real queda para tests visuales (issue 0048). + +### Fase 5 — Hook con el indexer + +5.1 Para cada funcion testeada, actualizar su `.md`: `tested: true`, `test_file_path: cpp/tests/test_.cpp`. El indexer puebla `tests` automaticamente desde el archivo. +5.2 `./fn index` y verificar: +```bash +sqlite3 registry.db "SELECT COUNT(*) FROM functions WHERE lang='cpp' AND tested=1;" +``` +Meta: >=20 (de 81 = 25%). + +### Fase 6 — CI gate (warmup, sin failure) + +6.1 Anadir `cpp/scripts/run_tests.sh` que hace `cmake --build && ctest --output-on-failure`. +6.2 Documentar en `cpp/PATTERNS.md` (issue 0041) que toda funcion nueva debe tener `tested: true`. + +## Decisiones + +- Catch2 v3 amalgamated (no header-only) — compila mas rapido despues del primer build. +- Tests **no** intentan renderizar ImGui — pintar requiere GL context, complicado en CI. Tests visuales se hacen en issue 0048 con primitives_gallery + screenshots. + +## Riesgos + +- Algunos primitivos no tienen logica testeable sin contexto ImGui (ej. `toolbar` solo concatena items). En esos casos, escribir test trivial que verifica que el codigo compila + linkea, o saltarlo y documentar en `notes`. +- Catch2 puede tardar 30-60s la primera vez. Aceptable. + +## Validacion + +```bash +cd cpp/build && cmake -DBUILD_TESTING=ON .. && cmake --build . -j +ctest --output-on-failure +sqlite3 registry.db "SELECT id FROM functions WHERE lang='cpp' AND tested=1;" +``` + +20+ tests pasando, 20+ funciones con `tested: true`. diff --git a/dev/issues/0048-cpp-visual-tests-ci-gate.md b/dev/issues/0048-cpp-visual-tests-ci-gate.md new file mode 100644 index 00000000..f99fd3c4 --- /dev/null +++ b/dev/issues/0048-cpp-visual-tests-ci-gate.md @@ -0,0 +1,107 @@ +# 0048 — Visual tests via primitives_gallery (golden screenshots) + CI gate para nuevas funciones + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | 0048 | +| **Estado** | pendiente | +| **Prioridad** | media | +| **Tipo** | feature — testing infra + CI | + +## Dependencias + +Bloquea-por: **0047** (Catch2 montado). + +--- + +## Objetivo + +1. Anadir captura automatica de screenshots por demo en `primitives_gallery`, comparados pixel-a-pixel contra goldens en `cpp/tests/golden/`. Cada PR que toque un primitivo de UI muestra el diff. +2. CI gate: PR que anada funcion nueva en `cpp/functions/` exige `tested: true` en el `.md`. + +## Contexto + +`primitives_gallery` ya renderiza cada primitivo del registry con datos sinteticos. Si capturamos PNGs por demo y los comparamos con goldens commiteados, tenemos test visual automatico para cualquier cambio que afecte a render. + +## Arquitectura + +``` +cpp/ +├── apps/primitives_gallery/ +│ ├── main.cpp # MOD — flag --capture +│ └── capture.{h,cpp} # NEW — render headless por demo, dump PNG +├── tests/ +│ ├── golden/ # NEW +│ │ ├── button.png +│ │ ├── select.png +│ │ ├── kpi_card.png +│ │ └── ... (1 png por demo del gallery) +│ ├── test_visual.cpp # NEW — corre gallery con --capture, compara con golden +│ └── CMakeLists.txt # MOD +└── scripts/ + ├── run_tests.sh # MOD — incluye visual + └── update_goldens.sh # NEW — regenera goldens +``` + +## Tareas + +### Fase 1 — Capture mode en primitives_gallery + +1.1 Anadir flag `--capture `. Cuando esta activo: +- Inicializa GLFW en modo offscreen (`glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE)`) o usa headless via EGL si esta disponible. +- Para cada demo en `k_demos[]`: setea `g_selected_id`, hace 3 frames de warmup (para tooltips/animaciones estables), captura framebuffer con `glReadPixels`, guarda como PNG via `stb_image_write`. +- Output: `/.png`. +- Sale tras procesar todas. +1.2 Si `--capture` no esta presente: comportamiento normal (interactivo). + +### Fase 2 — Goldens iniciales + +2.1 `scripts/update_goldens.sh`: +```bash +#!/usr/bin/env bash +mkdir -p cpp/tests/golden +./cpp/build/apps/primitives_gallery/primitives_gallery --capture cpp/tests/golden +``` +2.2 Generar y commitear los PNGs como linea base. + +### Fase 3 — Test visual + +3.1 `test_visual.cpp` (Catch2): para cada demo, lanzar gallery con `--capture` a un dir temporal, comparar contra `cpp/tests/golden/.png` con tolerancia configurable (por defecto: 1% pixels distintos, threshold por canal 5/255). +3.2 Si difiere, fallar con info: `expected golden/X.png, actual /tmp/.../X.png, diff Y%`. + +### Fase 4 — CI gate para tested:true + +4.1 Crear `cpp/scripts/check_tested.sh` que valida: +```bash +sqlite3 registry.db "SELECT id FROM functions + WHERE lang='cpp' AND tested=0 AND created_at > date('now','-30 days')" +``` +Si hay alguno → exit 1 con mensaje "anade test antes de mergear". +4.2 Ganchar en `cpp/scripts/run_tests.sh` (post-ctest). + +### Fase 5 — Documentar en PATTERNS + +5.1 En `cpp/PATTERNS.md` (de 0041) anadir seccion "Tests visuales": +- Como capturar nuevo golden cuando se anade un primitivo (`update_goldens.sh`). +- Como diagnosticar diff (renderiza el png actual vs golden lado a lado). + +## Decisiones + +- Headless via GLFW invisible es lo simple. Si no funciona en algun entorno, usar EGL/swiftshader como fallback. +- Tolerancia 1% para evitar flakes por antialiasing/font rendering distinto. Ajustable. +- Goldens en repo (PNGs binarios) — pesa poco si los demos son pequenos (200x200 max). + +## Riesgos + +- Diferencias de rendering entre Linux dev y CI: usar mesa software rendering (`LIBGL_ALWAYS_SOFTWARE=1`) en ambos lados para consistencia. +- Cambios cosmeticos legitimos en primitivos requieren regenerar goldens — flujo: `update_goldens.sh && git diff cpp/tests/golden/` para revisar visualmente. + +## Validacion + +```bash +cmake --build cpp/build -j +ctest --test-dir cpp/build -R visual --output-on-failure +# Cambia un color en tokens, run otra vez, debe FALLAR. +# update_goldens.sh + git diff muestra los pngs cambiados. +``` diff --git a/dev/issues/README.md b/dev/issues/README.md index bece9399..7b5ba697 100644 --- a/dev/issues/README.md +++ b/dev/issues/README.md @@ -46,3 +46,11 @@ | [0038](0038-gliner-entity-extractor.md) | GLiNER entity extractor (zero-shot NER multilingue) | pendiente | alta | feature | 0039, 0040 | | [0039](0039-glirel-relation-extractor.md) | GLiREL relation extractor (zero-shot triplets) | pendiente | media | feature | 0040 | | [0040](0040-hybrid-extraction-pipeline.md) | Pipeline hibrido extraccion grafos (regex + GLiNER + GLiREL + LLM fallback) | pendiente | media | feature | — | +| [0041](0041-cpp-app-best-practices.md) | C++ app shell estandarizado (PATTERNS.md + AppConfig extendido) | pendiente | alta | feature | 0043 | +| [0042](0042-cpp-layout-storage-public.md) | C++ layout_storage publico (extraer de shaders_lab) | pendiente | alta | feature | 0043 | +| [0043](0043-cpp-apps-standardize-shell.md) | Estandarizar shell de las 4 apps C++ | pendiente | alta | refactor | 0046 | +| [0044](0044-cpp-orphans-audit.md) | Auditar 52 funciones C++ huerfanas y poblar uses_functions | pendiente | media | chore | — | +| [0045](0045-cpp-extract-pure-logic.md) | Extraer logica pura de impuros (sql_workbench, process_runner, file_watcher, shaders_lab compiler) | pendiente | media | refactor | — | +| [0046](0046-cpp-refactor-raw-imgui.md) | Reemplazar raw ImGui en apps por primitivos del registry | pendiente | media | refactor | — | +| [0047](0047-cpp-tests-foundation.md) | C++ tests foundation (Catch2 + top-20 primitivos) | pendiente | alta | feature | 0048 | +| [0048](0048-cpp-visual-tests-ci-gate.md) | Visual tests via primitives_gallery + CI gate tested:true | pendiente | media | feature | — |