---
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.

## Definition of Done

Ver `README.md` seccion DoD + user-facing. **Risk=high** -> DoD strict obligatorio.

### Generico

- [ ] **Repetibilidad**: re-login + extraccion mensual reproducible (no flaky por DOM changes inesperados).
- [ ] **Observabilidad**: `call_monitor.calls` muestra ejecucion sin valores; `data_factory.runs` registra ambos nodos.
- [ ] **Error-path**: sesion expirada → mensaje claro al usuario para re-login (no datos corruptos).
- [ ] **Idempotencia**: re-extraer mismo mes = upsert por `movimiento_id`, 0 duplicados.
- [ ] **Secrets**: credenciales BBVA solo en `pass bbva/login`; vault `~/vaults/finanzas/` gitignored verificado.
- [ ] **Docs**: `## Notas` con onboarding + procedimiento de rotacion mensual.
- [ ] **Registry-first**: recipe + persistencia duckdb usan funciones del registry.
- [ ] **INDEX + status**: `status: done` + INDEX + movido.

### User-facing

- [ ] **User-facing**: usuario abre Metabase LOCAL :3000 → dashboard `Finanzas personales` → card `Gasto mensual` con grafico actualizado.
- [ ] **User-facing repeat**: misma URL manana muestra movimientos del mes hasta hoy; despues de re-login mensual, mes nuevo aparece automatico.
- [ ] **User-facing onboarding**: parrafo en `## Notas`: "Para revisar gasto: abrir http://localhost:3000 (creds en `pass metabase/local`) → dashboard `Finanzas personales`. Re-login BBVA: lanzar navegator → recipe `bbva_movimientos` → click Run."
- [ ] **User-facing latencia**: tras re-login + run manual, card actualizada en <2 min.

### Custom (risk=high)

- [ ] **No-leak**: `fn sync` NO sube duckdb (verificado: `pc_locations` registra path, sync no transmite bytes).
- [ ] **No-leak**: recipe extrae solo campos minimos (fecha, concepto, importe, categoria); NO DNI, NO saldo, NO IBAN completo.
- [ ] **No-leak**: Metabase corre LOCAL; verificar `auto_metabase.app.md` declara `tags: [local-only]`.
- [ ] **Rotacion**: re-login mensual probado sin perder datos historicos.
- [ ] **Red-team**: ningun log/screenshot/traza del flow contiene valores sensibles (grep IBAN/saldo en `call_monitor.calls`, `data_factory.runs`, `~/.cache/`).

## 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.
