--- id: "0097" title: "data_factory app (Factorio-style data pipeline)" status: pendiente type: feature domain: - data-ingest scope: app-scoped priority: alta depends: [] blocks: [] related: [] created: 2026-05-17 updated: 2026-05-17 tags: [] --- # 0097 — data_factory app (Factorio-style data pipeline) **Status:** pendiente **Created:** 2026-05-15 **Type:** app **Priority:** alta **Depends:** 0096 (apps/ standard) — DONE **Blocks:** 0098 (function dep tree) — diferido tras 0097 ## Problema No hay vista unificada del flujo de datos. Hoy: - `dag_engine_ui` muestra DAGs corriendo (orquestacion). - `registry_dashboard` muestra codigo (funciones, dependencias). - Pero NO hay: que datos extraigo, donde acaban, que volumen tienen, lineage end-to-end. User analogia: factoria Factorio. Extractors (drills), Transformers (assemblers), Databases (chests), Sinks (rocket silo), Belts (lineage). ## Objetivo v1 App C++ ImGui standalone `apps/data_factory/` que muestra: 1. **Extractors** (funciones tag `extractor`) — tabla: name, source, last_run, rows/min, kb/min, success_rate, schedule. Click -> detalle + sample rows. 2. **Transformers** (tag `transformer`) — tabla con inputs/outputs, drop_ratio, last_run. 3. **Databases** — tabla: kind (sqlite/postgres/bq/parquet/mongo), uri, table_count, size, freshness. 4. **Sinks** (tag `sink`) — Metabase cards, FastAPI endpoints, alerts. 5. **Health** — KPIs derivados de runs: rows_24h, success_rate, freshness_alerts. 6. **Map (placeholder v1)** — lista plana de nodes + connections. Grafo visual sale en v2 (no en 0097). ## Fuera de v1 - **Map grafico con `imgui_node_editor`** — v2. - **Models tab (trainers ML)** — v2. - **Integracion automatica con dag_engine** — manual en v1 (usuario decide schedules en DAGs). - **Multi-tenant** — un PC, un `data_factory.db`. ## Arquitectura ``` data_factory.db (per-PC, en apps/data_factory/) ├── nodes (kind=extractor|transformer|database|sink, function_id FK registry, config_json) ├── connections (src_node, dst_node, payload_schema) ├── runs (node_id, started_at, finished_at, status, rows_in, rows_out, kb_in, kb_out, error) └── databases (kind, uri, label) sqlite_api (anade DataFactoryHub WS) └── /api/ws/datafactory (replica patron CallMonitorHub + DagRunHub) C++ ImGui (apps/data_factory/) ├── data_http.{cpp,h} (REST a sqlite_api) ├── ws_client.{cpp,h} (reusado de dag_engine_ui) ├── tabs.{cpp,h} (Extractors, Transformers, Databases, Sinks, Health, Map) └── main.cpp (fn::run_app + cfg.panels) ``` ## Fases | Fase | Que entra | |---|---| | **A. Schema + migrations** | `apps/data_factory/migrations/001_init.sql` con 4 tablas. Idempotente (embed.FS). | | **B. Tags registry** | Audit + asignar tags `extractor/transformer/sink` a funciones existentes. Target: >=3 funciones por tag (capability group threshold). | | **C. Scaffold app C++** | `fn run init_cpp_app data_factory --desc "Factorio-style data pipeline factory"`. apps/data_factory/. | | **D. DataFactoryHub** | `projects/fn_monitoring/apps/sqlite_api/datafactory_events.go` — clon de CallMonitorHub. Endpoint `/api/ws/datafactory`. | | **E. Wrappers `data_factory_record_run`** | Pipeline Python: envuelve `fn run ` capturando rows/kb/duration -> INSERT en `runs` table. Reusa `subprocess` + `psutil`. | | **F. Tabs Extractors+Transformers+Databases+Sinks** | Reusa `data_table_cpp_viz`. Lista filtrable por tag. Click row -> panel lateral con detalle (FnInfo pattern de dag_engine_ui). | | **G. Health tab** | KPIs derivados de runs (rows_24h, success_rate, freshness). Reusa pattern dag_engine_ui health. | | **H. e2e_checks + deploy** | build_cmake, binary_exists, self-test (HTTP probe a sqlite_api), cpp_apps_conformance. `redeploy_cpp_app_windows`. | ## Funciones nuevas a delegar (estimacion) | ID | Lang | Que hace | |---|---|---| | `data_factory_record_run_py_pipelines` | py pipeline | Wrappea `fn run [args]`. Captura rows (parse stdout) + kb (size of output) + duration. INSERT en data_factory.db.runs. | | `data_factory_list_nodes_go_infra` | go infra | Lista nodes desde data_factory.db filtrado por kind. | | `data_factory_open_db_go_infra` | go infra | Abre data_factory.db con embed.FS migrations. | Tags nuevos: `extractor`, `transformer`, `sink`, `database`, `validator` (no v1). ## Aceptacion v1 - `apps/data_factory/` existe con scaffolding canonico (cumple cpp/PATTERNS.md). - `data_factory.db` se crea al primer run con tablas init. - `sqlite_api` expone `/api/ws/datafactory` y `GET /api/datafactory/nodes`. - Al menos 3 funciones tageadas `extractor` y 3 `transformer` (mover de existentes). - App C++ arranca, conecta sqlite_api, muestra tabs Extractors/Transformers/Databases/Sinks/Health. - `fn doctor cpp-apps` clean. - `fn doctor uses-functions` clean para data_factory. - Build Linux + Windows pass. - e2e_check `self_test` exit 0. ## No-objetivos - Visual lineage graph (deferido a 0098 o v2). - Edicion de DAGs/extractors desde la UI (solo lectura + run-now). - ML trainers (v2). - Sync multi-PC del `data_factory.db` (igual que dag_engine.db: per-PC). ## Riesgos | Riesgo | Mitigacion | |---|---| | Tags `extractor`/etc se solapan con tags existentes (`metabase`, `bigquery`) | Aceptado: una funcion puede tener varios tags. Tag flat, no jerarquia. | | Asignar tags rompe FTS o consumers existentes | Tags son aditivos, no destructivos. `fn index` valida. | | Wrapper Python `data_factory_record_run` interfiere con telemetria call_monitor (issue 0085) | Telemetria PostToolUse sigue activa. Wrapper escribe a tabla distinta (data_factory.runs vs call_monitor.calls). Cero conflicto. | | Schema `data_factory.db` cambia mucho en v1 | Migrations aditivas estrictamente (regla db_migrations.md). Si cambio tipo: branch-by-abstraction. | ## Telemetria objetivo - `data_factory` aparece en `pc_locations` activo. - 3+ funciones nuevas creadas + usadas en mismo turno (capability-growth ratio bueno). - runs persisten en data_factory.db; visibles desde tab Health en <500ms tras `fn run `.