Ir al contenido

Debug Stages — Persistencia stage-level y flag --verbose

Origen: Claude Code / claude-sonnet-4-6 Última actualización: 2026-04-25

:::note Cambio SDD Documentación introducida el 2026-04-25 con el cambio SDD debug-stages. Archivado en openspec/changes/archive/2026-04-25-debug-stages/. :::


Antes de este cambio, el objeto PipelineRun almacenaba en su campo stages la información stage-level completa — status, duración, métricas y errores. Sin embargo, esa información nunca se persistía en disco: solo se guardaba el status top-level del pipeline en la tabla pipeline_runs.

Al investigar un fallo días después, la base de datos solo mostraba “pipeline falló” sin indicar en qué stage ocurrió el problema, cuánto duró, ni qué error se lanzó. Además, el flag debug=True estaba hardcodeado en 7 archivos de dominio, ignorando por completo el flag --verbose del CLI.


Nueva tabla en SALIDAS/db/pipelines.db que persiste el resultado de cada stage tras pipe.run().

DDL:

CREATE TABLE IF NOT EXISTS pipeline_stage_runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
run_id INTEGER NOT NULL,
stage TEXT NOT NULL,
status TEXT NOT NULL,
duration_s REAL,
metrics_json TEXT,
error_json TEXT,
created_at TEXT DEFAULT (datetime('now'))
);

Columnas:

ColumnaTipoDescripción
idINTEGERPK autoincrement
run_idINTEGERFK lógica a pipeline_runs.id
stageTEXTNombre del stage (ej. "fetch", "process", "publish")
statusTEXT"ok", "failed" — stages "skipped" no se insertan
duration_sREALDuración en segundos; NULL si no disponible
metrics_jsonTEXTJSON serializado con métricas del stage (filas procesadas, etc.)
error_jsonTEXTJSON serializado con info del error; NULL si status = "ok"
created_atTEXTTimestamp UTC de inserción

Ejemplo de fila típica:

id=42, run_id=7, stage="process", status="failed",
duration_s=3.14, metrics_json=null,
error_json='{"type": "KeyError", "msg": "Column HES_NUM missing"}'

Ubicación: core/utils.py (módulo nuevo agregado el 2026-04-25)

Firma:

def _is_debug_mode(extras: dict) -> bool:

Comportamiento:

  1. Lee extras.get("verbose", False) — si el flag --verbose fue pasado al CLI, extras lo contiene.
  2. Si el valor es False o la clave no existe, hace fallback a la variable de entorno INGEL_DEBUG.
  3. Retorna True si cualquiera de las dos fuentes indica modo debug.

Uso en los dominios: Los 7 archivos que tenían debug=True hardcodeado fueron actualizados para llamar _is_debug_mode(self.config.extras):

  • domains/pedidos_hes/pipeline.py
  • domains/pedidos_sap/pipeline.py
  • domains/facturacion/pipeline.py
  • domains/valorizaciones/pipeline.py
  • domains/gantt/pipeline.py
  • core/ingest_imap.py
  • (un séptimo dominio según el scope del cambio)

En run_pipeline_v2.py, tras llamar update_pipeline_run(run_id, ...), se invoca:

insert_stage_runs(run_id, pipeline_run)

Reglas de inserción:

  • Stages con status = "skipped" se omiten — sin valor diagnóstico.
  • Si insert_stage_runs falla internamente (ej. error de DB), no interrumpe el pipeline: el error se captura en un try/except interno y se loggea sin propagar.

SELECT pr.pipeline_name, psr.stage, psr.status, psr.duration_s, psr.error_json
FROM pipeline_runs pr
JOIN pipeline_stage_runs psr ON psr.run_id = pr.id
WHERE pr.status = 'failed'
ORDER BY pr.id DESC
LIMIT 10;
SELECT stage, AVG(duration_s) AS avg_s, COUNT(*) AS n
FROM pipeline_stage_runs
WHERE status = 'ok'
GROUP BY stage
ORDER BY avg_s DESC;
SELECT stage, status, duration_s, error_json
FROM pipeline_stage_runs
WHERE run_id = <ID>
ORDER BY id ASC;
Ventana de terminal
# Unix / Git Bash
export INGEL_DEBUG=1
python run_pipeline_v2.py pedidos_HES
# Windows CMD
set INGEL_DEBUG=1
python run_pipeline_v2.py pedidos_HES
Ventana de terminal
python run_pipeline_v2.py --all --verbose
python run_pipeline_v2.py facturacion --verbose

  • Stages skipped no se insertan — decisión de diseño: no tienen valor diagnóstico.
  • metrics_json y error_json son texto JSON serializado; para queries estructuradas se requiere json_extract() de SQLite.
  • T6.1 out-of-scope: el plan original incluía anotar el contexto de fila/correo en los errores (qué dato específico causó el fallo). Los loops de procesamiento aún no anotan eso — queda como follow-up.
  • No hay FK formal entre pipeline_stage_runs.run_id y pipeline_runs.id — la relación es lógica, no DDL.

  • Orquestador-Pipelines — flags CLI (--verbose) y estructura de waves
  • DB-Diccionario-Tablas — esquema completo de las DBs SQLite
  • Decisiones-Tecnicas — historial de ADRs
  • GSheets-Consistency-Publish — PUBLISH integrado y tabla publish_log