# Serializacion y formato de salida

Gap identificado en el registry. Reimplementar desde cero.

Los pipelines terminan en operations.db pero no hay funciones para exportar
datos a formatos consumibles (CSV, JSONL, HTML, templates).

## Funciones a implementar

### to_csv

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `to_csv(rows: list[dict], columns: list[str] | None = None, delimiter: str = ",", include_header: bool = True) -> str`
- **Retorno:** String CSV completo
- **Descripcion:** Serializa datos tabulares a CSV. Si columns es None, usa las keys de la primera fila. Escapa campos con comillas, newlines y delimiters correctamente (RFC 4180).
- **Deps:** ninguna (no usar csv module para mantener control total)
- **Tests:**
  - Lista simple → CSV con header
  - Campos con comas, comillas, newlines (escaping correcto)
  - columns explicitas (reordena y filtra)
  - include_header=False
  - Lista vacia → string vacio
  - Valores None → campo vacio

### from_csv

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `from_csv(text: str, delimiter: str = ",", has_header: bool = True) -> list[dict]`
- **Retorno:** Lista de dicts con keys del header
- **Descripcion:** Parser CSV a datos tabulares. Complemento de to_csv. Si has_header=False, genera keys col_0, col_1, etc.
- **Deps:** ninguna
- **Tests:**
  - CSV simple con header
  - Campos con escaping (comillas, comas internas)
  - Sin header (keys generadas)
  - Lineas vacias ignoradas
  - Un solo campo por fila

### to_jsonl

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `to_jsonl(rows: list[dict]) -> str`
- **Retorno:** String con un JSON por linea
- **Descripcion:** Serializa a JSON Lines (newline-delimited JSON). Cada dict se serializa como una linea JSON independiente. Util para streaming, logging estructurado, y formatos de intercambio.
- **Deps:** `json` (stdlib)
- **Tests:**
  - Lista de dicts → JSONL
  - Valores con unicode, None, nested dicts
  - Lista vacia → string vacio
  - Cada linea es JSON parseable independientemente

### from_jsonl

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `from_jsonl(text: str) -> list[dict]`
- **Retorno:** Lista de dicts
- **Descripcion:** Parser JSONL a lista de dicts. Ignora lineas vacias. Complemento de to_jsonl.
- **Deps:** `json`
- **Tests:**
  - JSONL valido
  - Lineas vacias intercaladas
  - Linea con JSON invalido → raise con numero de linea

### render_template

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `render_template(template: str, context: dict, missing: str = "") -> str`
- **Retorno:** String con placeholders reemplazados
- **Descripcion:** Motor de templates minimalista sin dependencias. Soporta `{{variable}}`, `{{obj.field}}` (dot access en dicts anidados), `{% for item in list %}...{% endfor %}`, `{% if cond %}...{% endif %}`. No pretende reemplazar Jinja — cubre el 80% de casos simples.
- **Sintaxis:**
  - `{{var}}` — sustitucion simple, HTML-escaped por defecto
  - `{{{var}}}` — sustitucion sin escape (raw)
  - `{{obj.key.subkey}}` — dot-path traversal en dicts
  - `{% for x in items %}{{x}}{% endfor %}` — iteracion
  - `{% if var %}...{% endif %}` — condicional (truthy check)
  - `{% if not var %}...{% endif %}` — negacion
- **Deps:** `re` (solo stdlib)
- **Tests:**
  - Sustitucion simple
  - Dot-path en nested dicts
  - For loop con lista de strings
  - For loop con lista de dicts
  - If/endif condicional
  - Variable missing → string vacio (configurable)
  - HTML escaping por defecto
  - Triple braces sin escape

### generate_html_report

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `generate_html_report(title: str, sections: list[dict]) -> str`
- **Retorno:** String HTML completo (con DOCTYPE, head, body)
- **Descripcion:** Genera un reporte HTML autocontenido con CSS inline. Cada section es `{"heading": str, "type": "table"|"text"|"kpi"|"list", "data": ...}`. Para exportar resultados de pipelines o assertions a algo compartible sin servidor.
- **Tipos de seccion:**
  - `table`: data es `list[dict]` → renderiza <table> con headers
  - `text`: data es `str` → renderiza <p> con markdown basico (bold, links)
  - `kpi`: data es `list[{"label": str, "value": str|number, "delta": str|None}]` → cards
  - `list`: data es `list[str]` → renderiza <ul>
- **CSS:** Inline en <style>, tema minimalista con max-width, font-family sans-serif, tabla con zebra stripes
- **Deps:** ninguna
- **Tests:**
  - Reporte con una tabla
  - Reporte con multiples secciones mixtas
  - KPI con deltas positivos y negativos
  - Caracteres especiales HTML escapados en data
  - Titulo con caracteres especiales
