# 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 `<x>_pure_cpp_core` (testeable, sin ImGui ni I/O) + `<x>_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<FileEntry{path, size, mtime}>`, 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
```
