chore: auto-commit (286 archivos)

- .claude/agents/fn-orquestador/SKILL.md
- .claude/commands/fn_claude.md
- .claude/rules/INDEX.md
- .claude/rules/cpp_apps.md
- .claude/rules/ids_naming.md
- CHANGELOG.md
- apps/dag_engine/README.md
- apps/dag_engine/api.go
- apps/dag_engine/dags_migrated/example.yaml
- apps/dag_engine/dags_migrated/example_lineage_tracking.yaml
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 16:33:22 +02:00
parent 0b9af8f1bb
commit a03675113a
281 changed files with 12596 additions and 19526 deletions
+73
View File
@@ -0,0 +1,73 @@
---
name: hn-top-stories
id: 0001
status: pending
created: 2026-05-16
updated: 2026-05-16
priority: high
risk: low
related_issues: [0097, 0098]
apps:
- navegator_dashboard
- dag_engine
- data_factory
- agents_and_robots
trigger: cron
schedule: "*/30 * * * *"
expected_runtime_s: 30
tags: [scraping, news, smoke-test, multi-app]
---
## Goal
Probar end-to-end el stack: navegator AutoExtract -> recipe -> dag_engine schedule -> data_factory.runs -> matrix bot. Pagina cero-auth + cero-coste. Si esto funciona, todo el plumbing es solido.
## Pre-requisitos
- Chrome lanzado con `--remote-debugging-port=9222` (via navegator_dashboard "Open visible browser").
- `claude` CLI en PATH (auto-extract requiere LLM).
- sqlite_api activo en `:8484`.
- dag_engine activo en `:8090`.
- (opcional) Bot Matrix en sala `#fn-registry-news` para el sink final.
## Flow
1. Lanzar Chrome via navegator (puerto 9222).
2. AutoExtract panel: URL `https://news.ycombinator.com`. Click "Open & Analyze".
3. Esperar ~10-20s. Verificar schema propuesto: `rank`, `title`, `url`, `points`, `comments`, `age`.
4. Refinar selectors si IA proponen rotos. Test extraction -> preview rows >= 20.
5. Save as recipe `hn_top.yaml` (en `projects/navegator/profiles/default/recipes/`).
6. Crear DAG `~/.dagu/dags/hn-top.yaml` (manual o copy de `apps/dag_engine/dags_migrated/`):
```yaml
name: hn-top-stories
description: Scrape HN top stories cada 30 min
schedule: "*/30 * * * *"
steps:
- name: extract
function: cdp_extract_recipe_py_pipelines
args: ["projects/navegator/profiles/default/recipes/hn_top.yaml"]
```
7. Reload dag_engine + activar scheduler. Trigger Run Now una vez para probar.
8. dag_engine_ui: verificar run con status=success + function_id correcto en step.
9. data_factory: tab Extractors muestra nodo `hn_top_stories` (creado por save recipe). Tab "All Runs" muestra runs nuevos.
10. (opcional) Anadir step transformer filtra `points > 100` -> sink matrix bot.
## Acceptance
- [ ] Recipe creada y validada (`validate_recipe_yaml_py_core` OK).
- [ ] DAG corre OK 2 veces consecutivas via scheduler.
- [ ] `data_factory.runs` tiene >=2 entries con `node_id='hn_top_stories'`.
- [ ] `cdp_extract_recipe_py_pipelines` aparece en `call_monitor.calls`.
- [ ] Schema extraido cubre 6/6 fields (rank, title, url, points, comments, age).
- [ ] (opcional) Matrix bot recibe >=1 mensaje con top story filtrada.
## Telemetria esperada
- `function_stats.cdp_extract_recipe_py_pipelines`: calls_24h += 2.
- `data_factory.runs`: 2 nuevas filas con `trigger='cron'`.
- `dag_engine.dag_step_results`: step `extract` con `function_id='cdp_extract_recipe_py_pipelines'`.
- `call_monitor.calls`: chain function call.
## Notas
(rellenas tras correr)
+69
View File
@@ -0,0 +1,69 @@
---
name: aemet-madrid
id: 0002
status: pending
created: 2026-05-16
updated: 2026-05-16
priority: medium
risk: low
related_issues: [0097]
apps:
- dag_engine
- data_factory
- footprint_geo_stack
trigger: cron
schedule: "0 * * * *"
expected_runtime_s: 10
tags: [api, weather, geo, http-only]
---
## Goal
Probar path HTTP-only (sin Chrome/CDP). Extractor REST -> data_factory -> sink geo (PostGIS via footprint_geo_stack). Demuestra que el stack tambien sirve para APIs publicas + datos georeferenciados.
## Pre-requisitos
- API key AEMET (gratis, signup). Guardar en `pass insert aemet/api-key`.
- `footprint_geo_stack` corriendo (PostGIS :5432 + Martin tiles :3000).
- dag_engine activo.
## Flow
1. Crear funcion del registry `aemet_get_weather_py_infra` (o usar `http_get_json_py_infra` directamente si la API responde JSON simple).
2. Endpoint AEMET observacion convencional: `GET https://opendata.aemet.es/opendata/api/observacion/convencional/datos/estacion/<id>` con header `api_key`.
3. Schema esperado: `[{ts, temp, humidity, pressure, wind_speed, lat, lon}, ...]`.
4. Sink: INSERT en PostGIS tabla `weather_madrid (ts, temp, humidity, pressure, geom geometry(Point, 4326))`.
5. Crear node en data_factory: `{kind: 'database', label: 'postgis_weather'}` (sink declarado).
6. DAG `aemet_madrid_hourly.yaml`:
```yaml
name: aemet-madrid
schedule: "0 * * * *"
steps:
- name: extract
function: aemet_get_weather_py_infra
args: ["--station", "3195", "--out", "/tmp/aemet.json"]
- name: load_postgis
function: db_insert_row_go_infra
args: ["--db", "postgres://...", "--table", "weather_madrid", "--from-json", "/tmp/aemet.json"]
depends: [extract]
```
7. Verificar Martin tiles renderiza overlay (opcional).
## Acceptance
- [ ] Funcion AEMET extractor existe (creada o reusada `http_get_json_*`).
- [ ] DAG corre 2x consecutivas via scheduler.
- [ ] PostGIS tabla `weather_madrid` tiene >=2 filas.
- [ ] data_factory muestra node `aemet_madrid` kind=extractor + node `postgis_weather` kind=database con `last_seen_at` reciente.
- [ ] Martin tile server sirve overlay weather (opcional).
## Telemetria esperada
- `function_stats.http_get_json_py_infra` o `aemet_get_weather_py_infra`: calls_24h += 24 (1/hora).
- `data_factory.runs`: 24 entries/dia.
- `data_factory.databases.last_seen_at` actualizado por sink.
## Notas
- Sin LLM/CDP. Mas barato que flow 0001.
- Caso minimal para servicios geo. Si funciona, sirve de plantilla para mas extractores API.
+69
View File
@@ -0,0 +1,69 @@
---
name: bbva-movimientos
id: 0003
status: pending
created: 2026-05-16
updated: 2026-05-16
priority: high
risk: high
related_issues: [0097, 0098]
apps:
- navegator_dashboard
- dag_engine
- data_factory
- auto_metabase
trigger: manual
schedule: ""
expected_runtime_s: 120
tags: [scraping, banking, auth-required, sensitive-data]
---
## Goal
Caso de uso REAL con auth + datos sensibles. Probar persistencia local (duckdb en vault privado) + visualizacion en Metabase. Demuestra que el stack funciona para finanzas personales sin exfiltrar datos a la nube.
## Pre-requisitos
- Vault `~/vaults/finanzas/` existe + symlink en `projects/finanzas_personales/vaults/` (crear projecto si no).
- Chrome con sesion BBVA logueada manualmente (no automatizar login).
- Metabase local en docker (`auto_metabase` provee).
- `claude` CLI para AutoExtract.
## Flow
1. User abre Chrome + login BBVA + navega a "Movimientos cuenta principal".
2. navegator_dashboard panel Pick: click sobre primera fila de la tabla movimientos. Verifica selector capturado.
3. Panel AutoExtract: URL actual del tab. Click "Open & Analyze" (puede tardar, pagina compleja).
4. Esperar schema propuesto. Refinar:
- `date` (string DD/MM/YYYY o YYYY-MM-DD)
- `concept` (string)
- `amount` (float, parsea `,` -> `.`)
- `balance` (float)
5. Save recipe `bbva_movimientos.yaml`.
6. **Trigger manual SOLO** (no schedule — requiere login activo).
7. Sink: duckdb local en `~/vaults/finanzas/bbva.duckdb` (tabla `movimientos`).
8. data_factory: node `bbva_movimientos` kind=extractor + node `vault_finanzas_duckdb` kind=database.
9. Transformer: `aggregate_by_group_py_datascience` por mes -> tabla `mensual_summary`.
10. Sink Metabase: `metabase_create_card_py_infra` con SQL contra duckdb (via attachment Metabase si soporta, o via parquet export).
## Acceptance
- [ ] Recipe creada y testeada.
- [ ] Run manual extrae >=30 movimientos (1 mes).
- [ ] DuckDB `~/vaults/finanzas/bbva.duckdb` tabla `movimientos` poblada.
- [ ] data_factory muestra ambos nodos con runs.
- [ ] Card Metabase creado con grafico "gasto mensual" via auto_metabase.
- [ ] Datos NO viajan a registry.organic-machine (verificar: solo `pc_locations` registra que existe; el vault esta gitignored).
## Telemetria esperada
- `function_stats.cdp_extract_recipe_py_pipelines`: calls += 1 (manual).
- `data_factory.runs`: 1 entry status=success.
- `auto_metabase`: 1 card creado.
## Notas
- **NO commitear** `~/vaults/finanzas/` (gitignored por defecto).
- Si la sesion expira: re-login manual + re-run.
- Si BBVA cambia DOM: AutoExtract identifica nuevos selectors. Edit recipe + redeploy.
- Privacidad: revisar `call_monitor.calls` que solo guarda `args_hash`, NUNCA valores concretos de movimientos.
+68
View File
@@ -0,0 +1,68 @@
---
name: gitea-releases-monitor
id: 0004
status: pending
created: 2026-05-16
updated: 2026-05-16
priority: medium
risk: low
related_issues: []
apps:
- registry_api
- data_factory
- agents_and_robots
trigger: webhook
schedule: ""
expected_runtime_s: 5
tags: [git, webhook, multi-repo, monitoring]
---
## Goal
Probar webhooks como trigger (no cron, no manual). Cada push a un repo `dataforge/*` registra commit en data_factory + notifica via Matrix bot. Demuestra reactividad event-driven.
## Pre-requisitos
- `gitea_create_webhook_bash_infra` disponible.
- Servidor HTTP receptor (reusar `registry_api` o `deploy_server`).
- Bot Matrix en sala `#fn-registry-releases`.
- GITEA_TOKEN en `pass registry/api-token`.
## Flow
1. Crear endpoint receptor `POST /webhook/gitea` en una app (probable: nueva mini-app `gitea_webhook_listener` o anadir a `deploy_server`).
2. Iterar repos `dataforge/<name>` (35 apps + analyses). Instalar webhook en cada uno apuntando al receptor:
```bash
for app in $(ls apps/); do
gitea_create_webhook "dataforge" "$app" "http://<vps>/webhook/gitea" "$secret"
done
```
3. Receptor parsea payload Gitea (event `push`): `{ref, commits: [{id, message, author, url}]}`.
4. INSERT en `data_factory.runs` con `node_id='gitea_push_<repo>'` (upsert node si no existe).
5. Notifica Matrix:
```
[<repo>] <author>: <commit message>
<url>
```
6. dag_engine recibe trigger `api` con info del run.
7. data_factory tab Extractors muestra `gitea_push_<repo>` por cada repo con activity.
## Acceptance
- [ ] Webhook instalado en >=3 repos.
- [ ] Push de prueba dispara recepcion + entry en `data_factory.runs`.
- [ ] Matrix recibe mensaje formateado.
- [ ] data_factory muestra nodos `gitea_push_<repo>` con last_run reciente.
- [ ] Receptor maneja fallos (timeout, payload invalido) sin crash.
## Telemetria esperada
- `data_factory.runs` con `trigger='api'`.
- Por repo: 1 nodo extractor.
- Matrix: 1 msg por push.
## Notas
- Webhook secret debe estar en `pass gitea/webhook-secret` o env var.
- Si el receptor se cae, los pushes se pierden (no hay queue). Mejora futura: redis/sqlite queue.
- Cuidado con loops: commits que GENERA el bot pueden disparar webhooks.
+65
View File
@@ -0,0 +1,65 @@
---
name: osint-person-lookup
id: 0005
status: pending
created: 2026-05-16
updated: 2026-05-16
priority: medium
risk: medium
related_issues: [0098]
apps:
- navegator_dashboard
- odr_console
- graph_explorer
- agents_and_robots
trigger: manual
schedule: ""
expected_runtime_s: 300
tags: [osint, multi-tab, parallel, graph]
---
## Goal
Probar paralelismo (multiples scraping jobs concurrentes) + agregacion a grafo. Demuestra que graph_explorer cierra el circulo visualizando datos extraidos.
## Pre-requisitos
- odr_console activa (jobs queue).
- Chrome con sesiones activas en LinkedIn / Twitter / GitHub (no automatizar login).
- `projects/osint_graph/operations.db` accesible (graph_explorer la lee).
- claude CLI para resumen final.
## Flow
1. Input: nombre + apellido (`Juan Perez`).
2. odr_console crea 3 jobs concurrentes:
- Job A: navegator recipe `osint_linkedin_search.yaml` con query `{name}`.
- Job B: navegator recipe `osint_twitter_search.yaml` con query `{name}`.
- Job C: navegator recipe `osint_github_search.yaml` con query `{name}`.
3. Cada job extrae snippets: `{source, url, title, snippet, timestamp}`.
4. Cada snippet -> insert en `projects/osint_graph/operations.db` como entity `Snippet` + relations `mentions(Person, Snippet)`.
5. graph_explorer abre el operations.db -> renderiza red de menciones.
6. `claude -p` resume hallazgos en Markdown: dada lista de snippets, devuelve `{summary, confidence, suggested_next_steps}`.
7. Sink: report `.md` en `projects/osint_graph/reports/<person>-<date>.md`.
8. Matrix bot envia link al report.
## Acceptance
- [ ] 3 recipes osint creadas (LinkedIn, Twitter, GitHub).
- [ ] odr_console lanza 3 jobs paralelos sin race conditions.
- [ ] >= 5 snippets totales en operations.db.
- [ ] graph_explorer renderiza grafo con >=1 Person + N Snippets.
- [ ] Claude resumen generado y valido (no error).
- [ ] Report .md commiteado en repo osint_graph.
## Telemetria esperada
- 3 runs en `data_factory.runs` (uno por source).
- `operations.db` de osint_graph: entities += N, relations += N.
- `function_stats.claude_cli_prompt_py_infra`: calls += 1.
## Notas
- Consideracion legal: extracciones publicas (perfiles abiertos). NO bypassear paywalls/captchas.
- LinkedIn detecta scraping agresivo -> usar rate-limit por job en navegator.
- Caso ambicioso (5 apps + paralelismo + LLM). Reservar como hito.
+78
View File
@@ -0,0 +1,78 @@
---
name: metabase-versioning
id: 0006
status: pending
created: 2026-05-16
updated: 2026-05-16
priority: medium
risk: medium
related_issues: []
apps:
- auto_metabase
- dag_engine
- agents_and_robots
trigger: cron
schedule: "0 2 * * *"
expected_runtime_s: 60
tags: [metabase, sync, gitops, backup]
---
## Goal
Probar flujo INVERSO al tipico: extraer estado de un servicio interno (Metabase) y persistirlo como codigo. Sirve de backup + auditoria + reproducibilidad.
## Pre-requisitos
- Metabase corriendo (local docker o produccion).
- `auto_metabase` configurado con credenciales (`METABASE_URL`, `METABASE_TOKEN`).
- `projects/metabase_registry/` existe con sub-repo git inicializado.
## Flow
1. DAG `metabase-snapshot.yaml` diario 02:00:
```yaml
name: metabase-snapshot
schedule: "0 2 * * *"
steps:
- name: pull_metabase
function: auto_metabase_pull_bash_pipelines
args: ["--target", "projects/metabase_registry"]
- name: git_diff_check
command: cd projects/metabase_registry && git diff --stat
- name: commit_if_changes
command: |
cd projects/metabase_registry
git add -A
if ! git diff --cached --quiet; then
git commit -m "snapshot: $(date -u +%FT%TZ)"
git push origin master
echo "CHANGES_PUSHED"
else
echo "NO_CHANGES"
fi
depends: [pull_metabase]
- name: notify
function: matrix_send_message_<id>
args: ["#fn-registry-ops", "Metabase snapshot: <stdout from prev step>"]
depends: [commit_if_changes]
```
2. data_factory: node `metabase_snapshot` kind=extractor (source=metabase). Tag `gitops`.
3. Verificar que YAML files generados son legibles y diff-friendly.
## Acceptance
- [ ] DAG corre 3 dias consecutivos sin error.
- [ ] >=1 commit registrado en repo `metabase_registry`.
- [ ] data_factory.runs muestra historico.
- [ ] Matrix recibe 1 mensaje/dia (con "NO_CHANGES" o "CHANGES_PUSHED").
- [ ] Si se rompe un dashboard manualmente en Metabase -> push de YAML viejo lo restaura (test).
## Telemetria esperada
- 1 run/dia en data_factory.
- 7 commits en metabase_registry repo (1 semana baseline).
## Notas
- Riesgo: si Metabase token expira, el DAG falla silenciosamente. Anadir healthcheck pre-pull.
- Restore es manual hoy. Futuro: comando `auto_metabase push --from-commit <SHA>`.
+79
View File
@@ -0,0 +1,79 @@
---
name: matrix-telemetry-bot
id: 0007
status: pending
created: 2026-05-16
updated: 2026-05-16
priority: high
risk: low
related_issues: [0085]
apps:
- data_factory
- dag_engine
- call_monitor
- agents_and_robots
trigger: webhook
schedule: ""
expected_runtime_s: 1
tags: [observability, alerts, matrix, sink]
---
## Goal
Probar agents_and_robots como sink universal de telemetria. Cada falla critica en cualquier app dispara un mensaje a sala Matrix `#fn-registry-ops`. Cierra el bucle observability del stack.
## Pre-requisitos
- Bot Matrix activo, joineado a `#fn-registry-ops`.
- `agents_and_robots` con accion `matrix_send_message` operativa.
- Variables expuestas como funcion del registry: `matrix_send_message_py_infra` o equivalente.
## Flow
Tres triggers distintos, mismo sink.
### Trigger 1: data_factory run failed
1. En `data_factory_record_run_py_pipelines`, si `status == 'failed'` -> tambien llama `matrix_send_message`.
2. Formato:
```
:rotating_light: <node_id> FAILED in <duration_ms>ms
error: <error[:200]>
```
### Trigger 2: dag_engine DAG final status
1. En executor de dag_engine, handler `on_failure` invoca `matrix_send_message`.
2. Formato:
```
:x: DAG <dag_name> run <run_id> FAILED
step <step_name> exit <code>
```
### Trigger 3: call_monitor anomaly
1. Cron 1h analiza `call_monitor.violations_24h`. Si > threshold (default 50):
```
:warning: violations_24h = <N> (threshold <T>) — top rules: <rule_ids>
```
2. Si `error_rate_7d > 0.1` para alguna funcion top -> alert.
## Acceptance
- [ ] `matrix_send_message_py_infra` existe en el registry (crear si no — usa agents_and_robots backend).
- [ ] Trigger 1 dispara mensaje cuando un node de data_factory falla deliberadamente.
- [ ] Trigger 2 dispara mensaje cuando un DAG falla (test con DAG que falla a proposito).
- [ ] Trigger 3 dispara mensaje cuando se inyectan violations sinteticas.
- [ ] Mensajes llegan en <3 segundos del evento.
## Telemetria esperada
- `function_stats.matrix_send_message_*`: calls dependientes de eventos reales.
- Sala Matrix recibe mensajes de los 3 origenes.
## Notas
- Throttling: max 1 mensaje/minuto por origen para evitar spam.
- Severidad: prefix emoji indica nivel.
- Mejora futura: comandos en sala (`!status`, `!ack <run_id>`) para responder desde Matrix.
- Pre-condicion: `agents_and_robots` necesita estar deployado + bot autenticado.
+239
View File
@@ -0,0 +1,239 @@
# Agent guide — como armar (o asistir) un flow
Este archivo lo lee el LLM/agente que crea o edita flows. Su trabajo es **recomendar piezas concretas del registry** para cada flow, no inventarlas. Todo lo que se recomiende debe existir ya en el sistema (registry / apps / projects / vaults / DAGs) — o quedar marcado explicitamente como "FALTA: crear".
## Que es un flow
Caso de uso end-to-end reutilizable que cruza N apps del registry. Vive en `dev/flows/NNNN-<slug>.md`. Cada flow:
- describe Goal + Pre-requisitos + Flow steps + Acceptance + Telemetria,
- esta tageado con `apps:` (apps tocadas) en frontmatter,
- se ejecuta manualmente (fase 1) o via `/flow run` (fase 2 futura),
- alimenta `data_factory.runs` + `call_monitor.calls` al correr.
## Tu trabajo como agente cuando un flow nace
Al recibir "crea flow para <X>" o `/flow create <slug>`:
1. **Entiende el goal** en una frase. Si ambiguo, pregunta antes de scaffold.
2. **Recomienda piezas concretas** del registry para cada rol (extractor, transformer, sink, scheduler, notify, storage). Ver tabla de roles abajo.
3. **Cita IDs reales** del registry/apps. Si la pieza no existe -> escribe `FALTA: crear <id> via fn-constructor` en la seccion correspondiente.
4. **Marca riesgo** (low/medium/high) por sensibilidad de datos.
5. **Sugiere schedule** (cron / webhook / manual) basado en el tipo de fuente.
6. **Sugiere apps** del stack que encajan, sin inflar — solo las que realmente tocara.
## Mapa de discovery — donde mirar para cada decision
### Extractor (de donde sacan los datos)
| Tipo de fuente | Donde buscar | Filtro MCP |
|---|---|---|
| API REST publica (JSON) | tag `extractor` + `http` | `mcp__registry__fn_search query="http" tag="extractor"` |
| BigQuery | tag `bigquery` + `extractor` | `mcp__registry__fn_search query="" tag="bigquery"` |
| Metabase | tag `metabase` + `extractor` | `mcp__registry__fn_search query="" tag="metabase"` |
| Web scraping (con auth) | apps `navegator_dashboard` + recipes YAML | `ls projects/navegator/profiles/*/recipes/` |
| Web scraping (sin auth, API JSON) | `http_get_json_*` (Tier 1 Tabla extractor.md) | `docs/capabilities/extractor.md` |
| CSV/Parquet local | `load_csv_go_datascience`, `load_parquet_go_datascience`, `from_csv_py_core` | tag `extractor` lang `go|py` |
| Webhooks | apps `registry_api` o `deploy_server` (receivers) | inspeccionar handlers existentes |
| Jupyter notebook | `jupyter_read_py_notebook` | tag `notebook` |
### Transformer (que se hace con los datos)
| Operacion | Donde buscar |
|---|---|
| Dedup / Aggregate / Filter | `docs/capabilities/transformer.md` |
| Tipos / Validacion de schema | `docs/capabilities/validator.md` (puede actuar como gate transformer) |
| Geo (PostGIS, tiles) | `footprint_geo_stack` (app sink + transformer geo) |
| NLP / Embeddings / entities | `docs/capabilities/nlp.md` |
| Lua / TQL (table query) | `cpp-tables` group + `tql_*` funcs |
### Sink (donde acaban los datos)
| Destino | Recomendar |
|---|---|
| BigQuery | `bq_insert_rows_py_infra`, `bq_load_from_*` |
| Metabase card / alert | `metabase_create_card_*`, `metabase_create_card_alert_*` |
| PostgreSQL / SQLite local | `db_insert_row_go_infra` |
| Notificacion Matrix | `agents_and_robots` (app) + funcion bot |
| DuckDB en vault | listar `~/vaults/*` para destino + `csv_to_parquet_duckdb_py_core` |
| HTTP POST a webhook ajeno | `http_post_json_*` |
| Email / Slack | (todavia no — anadir si flow lo pide) |
| Dashboard | `metabase_create_dashboard_*` + `auto_metabase` |
| Grafo entidades | INSERT operations.db de algun project (ej `osint_graph`) + visualizar con `graph_explorer` |
### Scheduler / Trigger
| Caso | Recomendar |
|---|---|
| Recurrente cada N min/hora/dia | DAG en `apps/dag_engine/dags_migrated/<slug>.yaml` con `schedule:` cron. Step usa `function: <id>` directo. |
| Manual (auth requiere user) | `trigger: manual` en flow. Sin DAG. |
| Event-driven (push) | Webhook receiver — depende app (`registry_api`, `deploy_server`). |
| Notebook colaborativo | `jupyter_*` funcs + analysis dir |
| Ejecucion ad-hoc | `./fn run <id>` directo |
### Storage / Artefacto
| Tipo | Donde vive |
|---|---|
| BD app-local | `apps/<app>/operations.db` (entities, runs, assertions) |
| Pipeline factory | `apps/data_factory/data_factory.db` (nodes, runs) |
| DAG runs | `apps/dag_engine/dag_engine.db` |
| Telemetria agente | `projects/fn_monitoring/apps/call_monitor/operations.db` |
| Vault privado (datos sensibles) | `~/vaults/<vault>/` declarado en `projects/<p>/vaults/vault.yaml` |
| Sub-repo Gitea (codigo/artefacto) | `dataforge/<name>` |
| Configs reproducibles | `projects/metabase_registry/`, `projects/element_agents/`, etc. |
### Apps disponibles (snapshot 2026-05-16)
| App | Rol tipico en un flow |
|---|---|
| `navegator_dashboard` | extractor con auth / AutoExtract / recipes |
| `dag_engine` + `dag_engine_ui` | scheduler / orquestador / vista runs |
| `data_factory` | catalogo nodes + runs live |
| `registry_dashboard` | vista codigo registry |
| `call_monitor` | telemetria agent calls |
| `sqlite_api` | HTTP server read-only de todas las BDs |
| `agents_and_robots` | bot Matrix (sink notificaciones) |
| `element_matrix_chat` | backend Matrix |
| `auto_metabase` | sync YAML <-> Metabase |
| `metabase_registry` | snapshots Metabase como codigo |
| `odr_console` | jobs paralelos scraping |
| `graph_explorer` | grafo entidades/relaciones |
| `script_navegador` | runner YAML CDP headless |
| `footprint_geo_stack` | PostGIS + Martin (tiles) + Valhalla (routing) |
| `deploy_server` | rsync/systemd a VPS |
| `docker_tui` | gestion docker |
| `fn_match` | matcher funciones |
| `kanban` | gestion tareas (sink reports?) |
| `pipeline_launcher` | TUI lanzador pipelines |
| `registry_api` | REST API del registry remoto |
| `registry_mcp` | MCP server (Claude) |
| `chart_demo / shaders_lab / primitives_gallery / runtime_test / engine_smoke / text_editor_smoke / altsnap_jitter_test` | demos C++ (raro que un flow las use) |
### Projects disponibles
| Project | Tema |
|---|---|
| `fn_monitoring` | observabilidad del registry |
| `osint_graph` | grafos OSINT |
| `navegator` | scraping CDP |
| `element_agents` | agentes Matrix |
| `online_data_recopilation` | scraping batch |
| `imagegen` | image generation |
| `app_turismo` | turismo |
### Vaults disponibles
`fn sync locations | grep vault` o `cat projects/*/vaults/vault.yaml`. Tipicos:
- `~/vaults/imagegen_models/`
- `~/vaults/odr_data/`
- `~/vaults/osint_graph/`
- `~/vaults/osint_nlp_models/`
- `~/vaults/turismo_spain/`
- `~/vaults/market_data/` (pendiente)
- `~/vaults/finanzas/` (pendiente para flow 0003)
### Capability groups (mother pages, lectura preferente)
`docs/capabilities/INDEX.md`. Cuando recomiendes, lee la mother page del grupo antes de citar funciones sueltas:
- `extractor` (15 funcs)
- `transformer` (15 funcs)
- `sink` (11 funcs)
- `validator` (6 funcs)
- `scheduler` (4 funcs cron parsing)
- `navegator` (10 funcs CDP + LLM)
- `notebook` (Jupyter)
- `metabase` (106 funcs)
- `bigquery` (26 funcs)
- `nlp` (33 funcs)
- `docker, ssh, systemd, deploy, registry, doctor, git, mantine, android, playwright, cpp-tables, cpp-windows, data-table-renderers`
## Estructura de recomendacion a producir
Cuando creas un flow, rellena estas secciones del .md (la template ya las tiene como placeholders):
```markdown
## Funciones del registry recomendadas
| Rol | Funcion candidata | Si falta |
|---|---|---|
| Extractor | `http_get_json_py_infra` (URL: ...) | OK |
| Validator | `validate_recipe_yaml_py_core` | OK |
| Transformer | FALTA: crear `parse_aemet_response_py_core` via fn-constructor |
| Sink | `db_insert_row_go_infra` (target PostGIS) | OK |
| Scheduler | DAG `aemet-madrid.yaml` con cron `0 * * * *` | OK |
| Notify | `matrix_send_message_*` (sala #ops) | FALTA: crear wrapper |
## Apps tocadas
- `dag_engine` (schedule)
- `data_factory` (visibility runs)
- `footprint_geo_stack` (sink PostGIS)
## Projects relacionados
- `fn_monitoring` (telemetria observable)
- `osint_graph` (si lineage)
## Vaults / storage
- PostGIS (footprint_geo_stack): tabla `weather_madrid`
- DuckDB local: NO
## Schedule
`0 * * * *` (cada hora). Razon: AEMET refresca cada hora.
## Capability groups consultados
- `extractor` (http_get_json), `validator`, `sink`
```
Si una recomendacion es FALTANTE: anota en la seccion + abre issue paralelo (`/create-issue`) o flag al user "necesitamos crear estas N funciones antes de poder correr el flow".
## Reglas duras al recomendar
1. **NUNCA inventes IDs**. Si dudas, busca con MCP. Cita solo IDs que respondan a `mcp__registry__fn_show`.
2. **REUTILIZA antes que crear**. Solo FALTA si nada del registry encaja despues de >=3 queries FTS distintas.
3. **MARCA riesgo explicito** en frontmatter. Datos publicos = `low`. Auth = `medium`. Datos personales/financieros = `high`.
4. **NO sugieras schedule cron si requiere auth manual**. `trigger: manual`.
5. **CITA capability group** preferido cuando exista, no funcion suelta. Ej. "ver `docs/capabilities/sink.md`" mejor que "usar `bq_insert_rows_py_infra`".
6. **DOCUMENTA gotchas** especificos del flow en seccion `## Notas`.
7. **NO mezcles N flows en uno**. Si el goal toca >5 apps -> probable que sean 2 flows distintos.
## Plantilla de prompt para futuro agente
Cuando user invoca `/flow create <slug>` o pide "ayuda para crear flow":
```
Eres asistente de flow design. Lee dev/flows/AGENT_GUIDE.md primero.
Pasos:
1. Pregunta al user: "Que quieres lograr en 1 frase? + datos sensibles?"
2. Identifica fuente -> recomienda extractor del registry.
3. Identifica destino -> recomienda sink del registry.
4. Identifica frecuencia -> recomienda schedule (cron / webhook / manual).
5. Identifica notify channel (matrix / email / ninguno).
6. Lista apps tocadas + projects + vaults.
7. Marca FALTA para piezas inexistentes con sugerencia de fn-constructor prompt.
8. Crea NNNN-<slug>.md desde template con las secciones rellenadas.
9. Actualiza INDEX.md.
10. Imprime resumen: "Flow 0008 creado. Faltan N piezas: lista".
```
## Donde escribir hallazgos
Tras correr un flow:
- Marca acceptance checkboxes `[x]`.
- Rellena `## Notas` con tiempos reales, problemas, mejoras.
- Si descubres patron repetido en varios flows: candidato a sub-feature de `/flow run` (fase 2) o nueva funcion del registry.
## Mantenimiento del guide
Este archivo crece. Cuando:
- Una app nueva aparece -> anadir fila a "Apps disponibles".
- Un capability group nuevo -> anadir a "Capability groups".
- Un vault nuevo -> anadir a "Vaults".
`fn doctor` deberia detectar drift entre AGENT_GUIDE.md y BD del registry (TBD: nuevo subcomando `fn doctor flows`).
+22
View File
@@ -0,0 +1,22 @@
# Flows Index
Tabla de casos de uso multi-app. Mantenida por `/flow create` y `/flow done`.
| ID | Slug | Apps | Status | Risk | Updated |
|----|------|------|--------|------|---------|
| [0001](0001-hn-top-stories.md) | hn-top-stories | navegator_dashboard, dag_engine, data_factory, agents_and_robots | pending | low | 2026-05-16 |
| [0002](0002-aemet-madrid.md) | aemet-madrid | dag_engine, data_factory, footprint_geo_stack | pending | low | 2026-05-16 |
| [0003](0003-bbva-movimientos.md) | bbva-movimientos | navegator_dashboard, dag_engine, data_factory, auto_metabase | pending | high | 2026-05-16 |
| [0004](0004-gitea-releases-monitor.md) | gitea-releases-monitor | registry_api, data_factory, agents_and_robots | pending | low | 2026-05-16 |
| [0005](0005-osint-person-lookup.md) | osint-person-lookup | navegator_dashboard, odr_console, graph_explorer | pending | medium | 2026-05-16 |
| [0006](0006-metabase-versioning.md) | metabase-versioning | auto_metabase, dag_engine | pending | medium | 2026-05-16 |
| [0007](0007-matrix-telemetry-bot.md) | matrix-telemetry-bot | data_factory, dag_engine, call_monitor, agents_and_robots | pending | low | 2026-05-16 |
## Leyenda
- **Status**: `pending` (no arrancado) / `running` / `done` / `failed` / `deferred`.
- **Risk**: `low` (datos publicos), `medium` (auth pero no sensible), `high` (datos personales/financieros).
## Completados
Ver `completed/` para flows cerrados.
+110
View File
@@ -0,0 +1,110 @@
# Flows
Casos de uso end-to-end **reutilizables** que prueban el sistema multi-app del registry.
Un flow describe una secuencia de pasos que atraviesa varias apps (`navegator_dashboard`, `dag_engine`, `data_factory`, `agents_and_robots`, ...) para producir valor real (extraer datos, sincronizar, notificar).
## Convencion
- Archivo por flow: `NNNN-<slug>.md` (numeracion zero-padded propia, NO comparte con `dev/issues/`).
- Estado vivo en frontmatter (`status`).
- Acceptance checkboxes `[ ]` en el body — `/flow status` calcula % completado.
- Cerrados se mueven a `completed/`.
## Para agentes / LLMs
Antes de crear o editar un flow, lee `AGENT_GUIDE.md`. Define:
- donde buscar funciones (capability groups, MCP search por tag, etc.),
- mapa actual de apps / projects / vaults disponibles,
- reglas duras para recomendar piezas reales del registry,
- plantilla de prompt para el agente cuando recibe `/flow create`.
Cada flow debe citar IDs reales del registry. Si una pieza no existe, marcarla `FALTA: crear <id>` — NUNCA inventar nombres.
## Slash command `/flow`
| Subcomando | Que hace |
|---|---|
| `/flow create <slug>` | Scaffold `NNNN-<slug>.md` desde `template.md` con siguiente ID libre. |
| `/flow list` | Tabla resumen desde `INDEX.md` + checkbox %. |
| `/flow show <NNNN>` | Imprime el `.md`. |
| `/flow status <NNNN>` | Status + acceptance % + ultima run. |
| `/flow done <NNNN> [--notes "..."]` | Marca status=done, anade notas, mueve a `completed/`, actualiza INDEX. |
| `/flow run <NNNN>` | **Fase 2** (no implementado). Ejecuta steps automatizables. |
## Estructura archivo
```yaml
---
name: hn-top-stories
id: 0001
status: pending # pending | running | done | failed | deferred
created: 2026-05-16
updated: 2026-05-16
priority: high # low | medium | high
risk: low # low | medium | high (sensibilidad de datos)
related_issues: [0097, 0098]
apps: [navegator_dashboard, dag_engine, data_factory, agents_and_robots]
trigger: manual # manual | cron | webhook
schedule: ""
expected_runtime_s: 60
tags: [scraping, news]
---
## Goal
Una frase: que estamos probando.
## Pre-requisitos
- Lista de requisitos manuales (ej. Chrome con remote-debugging).
## Flow
Pasos numerados. Cada paso puede ser:
- texto libre (manual)
- `function: <id>` (registry function)
- `cmd: <bash>`
- `js: <expression>` (en tab Chrome)
## Acceptance
- [ ] Checklist
- [ ] ...
## Telemetria esperada
Que cambia en call_monitor / data_factory.runs / dag_engine.
## Notas
Hallazgos tras correr.
```
## Numeracion + status workflow
```
pending --create--> in-progress --run OK--> done --move--> completed/
| ^
v |
failed -----------fix-----+
```
`deferred` para flows fuera de scope actual pero que conservas.
## Trazabilidad
Cada `function: X` invocado dentro de un flow pasa por hook PostToolUse -> queda en `call_monitor.calls`. Si la funcion graba en `data_factory.runs` (ej. `cdp_extract_recipe_py_pipelines`), tienes cadena:
```
flow 0001 -> /flow run -> ./fn run cdp_extract_recipe -> call_monitor.calls
\-> data_factory.runs
```
Asi la observabilidad cross-app es gratis.
## Cuando crear flow vs issue
| Caso | Donde |
|---|---|
| Bug del registry / funcion | `dev/issues/NNNN-...` |
| Feature de una app | `dev/issues/NNNN-...` |
| Caso de uso real que cruza N apps | **`dev/flows/NNNN-...`** |
| Trabajo recurrente reutilizable | **`dev/flows/NNNN-...`** |
| Refactor de codigo | `dev/issues/NNNN-...` |
Un flow puede generar issues secundarios si descubres bugs corriendo el flow.
+87
View File
@@ -0,0 +1,87 @@
---
name: <slug>
id: NNNN
status: pending
created: YYYY-MM-DD
updated: YYYY-MM-DD
priority: medium # low | medium | high
risk: low # low | medium | high (sensibilidad datos)
related_issues: []
apps: [] # apps tocadas — usadas por /flow list --app
projects: [] # projects relacionados
vaults: [] # vaults sink/source
capability_groups: [] # extractor, transformer, sink, navegator, ...
trigger: manual # manual | cron | webhook
schedule: "" # cron expr si trigger=cron
expected_runtime_s: 60
tags: []
---
## Goal
Una frase: que prueba este flow del sistema multi-app.
## Pre-requisitos
- Lista de cosas manuales/externas que deben estar listas antes (Chrome logueado, vault montado, service corriendo, token en `pass`, ...).
## Funciones del registry recomendadas
Tabla rol -> funcion candidata (ver `AGENT_GUIDE.md` para discovery). Si falta una pieza: marca `FALTA: crear <id>` con prompt sugerido para fn-constructor.
| Rol | Funcion candidata | Estado |
|---|---|---|
| Extractor | `<id>` | OK / FALTA |
| Validator | `<id>` | OK / FALTA |
| Transformer | `<id>` | OK / FALTA |
| Sink | `<id>` | OK / FALTA |
| Scheduler | DAG `<path>.yaml` / webhook / manual | OK / FALTA |
| Notify | `<id>` | OK / FALTA |
## Apps tocadas
- `<app>` (rol en el flow)
## Projects relacionados
- `<project>` (razon)
## Vaults / storage
- `<vault o BD>` (origin / sink)
## Capability groups consultados
- `<group>` (ver `docs/capabilities/<group>.md`)
## Flow
Pasos numerados. Cada paso puede ser:
- texto libre (manual)
- `function: <id>` (registry function)
- `cmd: <bash>`
- `js: <expression>` (en tab Chrome)
- `dag: <name>` (DAG en `apps/dag_engine/dags_migrated/`)
1. Paso 1.
2. Paso 2.
## Acceptance
- [ ] Criterio 1.
- [ ] Criterio 2.
## Telemetria esperada
- `call_monitor.calls`: que aparece.
- `data_factory.runs`: que aparece.
- `<app>.operations.db`: que aparece.
- Matrix / email / dashboard: que aparece visible.
## Riesgos / gotchas
- Lista de cosas que pueden romperse y como detectarlas.
## Notas
(rellenas tras correr o tras hallazgos)