---
name: data-intake-normalizer
description: >
  Global data intake, validation, and normalization layer for ALL skills in the Agency
  Growth OS. ALWAYS trigger before any science or analytics skill when input arrives as
  CSV, XLSX, XLS, TSV, or JSON. Detects target skill from file structure, maps client
  columns to OS schema, coerces types, handles encoding issues, imputes nulls, and
  produces a validation report (OK / WARN / BLOCK). Also generates standardized input
  templates (XLSX) for any OS skill on demand. Trigger when user says: "aquí está el
  archivo", "te paso el CSV", "valida este Excel", "necesito la plantilla para", "el
  archivo no carga", "dame el template de", or uploads any data file before running a
  science skill. BLOCK execution of downstream skill if critical columns are missing.
metadata:
  author: Axiom Nexar / Polanyi
  version: 1.0.0
  layer: Input — runs before ALL science and analytics skills
  position: skill #26 in Agency Growth OS
---

# Data Intake Normalizer

**Pre-requisito global.** Corre antes de cualquier science skill cuando el input es
un archivo del cliente. Produce `data_normalized.csv` + `validation_report.json`.
Si el reporte tiene nivel BLOCK → no ejecutar el skill downstream.

---

## Cuándo activar

```
Cliente sube CSV/XLSX/XLS/TSV/JSON
        ↓
data-intake-normalizer  ← SIEMPRE primero
        ↓
validation_report.json
  OK   → ejecutar skill downstream con data_normalized.csv
  WARN → mostrar advertencias, ejecutar con caveats
  BLOCK → mostrar errores, solicitar corrección, ofrecer template
```

También activa cuando el usuario pide:
- "Dame la plantilla para [skill]"
- "¿Qué formato necesita el archivo?"
- "El script falla con mi CSV"

---

## Schemas por skill — columnas requeridas / opcionales

Lee `references/schemas.md` para el schema completo de cada skill.
Resumen ejecutivo:

| Skill destino | Columnas requeridas | Columnas opcionales clave |
|---|---|---|
| `customer-segmentation-clustering` | `customer_id`, `transaction_date`, `amount` | `category`, `brand`, `channel` |
| `customer-lifetime-value` | `customer_id`, `period`, `revenue`, `cost` | `retention_flag`, `channel`, `cohort` |
| `survival-analysis-marketing` | `customer_id`, `event_date`, `censored` | `discount`, `emails_sent`, `segment` |
| `response-uplift-modeling` | `customer_id`, `treatment`, `response` | `score`, `segment`, `spend`, `ltv` |
| `market-basket-analysis` | `transaction_id`, `item_id` | `amount`, `category`, `timestamp` |
| `price-demand-optimization` | `price`, `quantity` | `period`, `competitor_price`, `promotion` |
| `recommender-systems` | `user_id`, `item_id`, `rating` | `timestamp`, `context`, `category` |
| `mmm-modeling` | `date`, `kpi` + ≥1 `spend_[channel]` | `price_index`, `promo`, `seasonality` |
| `audience-segmentation-brief` | `customer_id`, `segment`, `ltv` | `channel`, `rfm_score`, `persona` |
| `media-routing-planner` | `channel`, `spend`, `impressions`, `conversions` | `cpm`, `cpa`, `roas`, `period` |
| `weekly-control-tower` | `date`, `kpi`, `actual`, `target` | `channel`, `campaign`, `segment` |
| `measurement-incrementality` | `date`, `group` (test/control), `kpi` | `spend`, `impressions`, `conversions` |
| `crm-journey-architect` | `customer_id`, `event`, `timestamp` | `channel`, `segment`, `value` |
| `scope-audit` | Texto libre (RFP/SOW) — no normalización | — |
| `margin-simulation` | `fee`, `fte_cost`, `vendor_cost`, `hours` | `segment`, `client`, `period` |

---

## Workflow

### Step 1 — Detectar archivo
```bash
python scripts/schema_detector.py \
    --file /mnt/user-data/uploads/[filename] \
    --output results/detection.json
```
Output: `{ target_skill, confidence, detected_columns, file_info }`

### Step 2 — Mapear columnas
```bash
python scripts/column_mapper.py \
    --file /mnt/user-data/uploads/[filename] \
    --target-skill [skill_name] \
    --output results/column_map.json
```
Output: `{ mappings: { client_col → os_col }, unmapped, missing_required }`

### Step 3 — Normalizar tipos + encoding
```bash
python scripts/type_coercer.py \
    --file /mnt/user-data/uploads/[filename] \
    --column-map results/column_map.json \
    --output results/data_typed.csv
```
Handles: fechas (≥15 formatos), montos (comas, símbolos), encodings (UTF-8/latin-1/CP1252/UTF-16)

### Step 4 — Manejar nulos
```bash
python scripts/null_handler.py \
    --file results/data_typed.csv \
    --target-skill [skill_name] \
    --output results/data_normalized.csv
```
Estrategias por columna: drop / median / mode / forward-fill / flag

### Step 5 — Reporte de validación
```bash
python scripts/validation_report.py \
    --normalized results/data_normalized.csv \
    --target-skill [skill_name] \
    --column-map results/column_map.json \
    --output results/validation_report.json
```

### Step 6 — Mostrar resultado al usuario
```
[show_widget] validation dashboard — siempre antes de ejecutar skill downstream
```

### Generar plantilla (modo alternativo)
```bash
python scripts/template_generator.py \
    --skill [skill_name] \
    --output /mnt/user-data/outputs/template_[skill_name].xlsx
```

---

## Niveles de validación

| Nivel | Condición | Acción |
|---|---|---|
| `OK` | Todas las columnas requeridas presentes, tipos correctos, nulos < 5% | Ejecutar skill downstream |
| `WARN` | Columnas opcionales faltantes, nulos 5–20%, mapeo fuzzy aplicado | Ejecutar con caveats visibles |
| `BLOCK` | Columnas requeridas faltantes, nulos > 20% en columna crítica, encoding irrecuperable | Detener. Mostrar errores. Ofrecer plantilla. |

---

## Output format — Visualization First

Dashboard panels (ver `references/intake_dashboard_template.html`):
1. **Status banner** — OK / WARN / BLOCK con color
2. **File info** — filas, columnas, encoding, tamaño, skill detectado
3. **Column mapping table** — cliente col → OS col, con status por columna
4. **Issues list** — errores y advertencias con descripción accionable
5. **Preview** — primeras 5 filas del archivo normalizado
6. **Next step** — botón sendPrompt() al skill downstream si OK/WARN

---

## Marketer Insights Layer

Siempre incluir después del dashboard:
- Qué columnas se renombraron automáticamente y por qué
- Qué nulos se imputaron con qué estrategia
- Cuántas filas quedaron disponibles vs total original
- Si hay BLOCK: mensaje claro + "Descarga la plantilla correcta" + botón

---

## Reglas de orquestación (OS rules)

```
Rule #19: ALWAYS run data-intake-normalizer before any science skill
          when input is CSV/XLSX. BLOCK if validation_report = BLOCK.
Rule #20: If client provides data in Google Drive → use Google Drive MCP
          read directly (no upload needed), still run normalization.
Rule #21: If normalization fails 2+ times on same file → offer template
          download for that specific skill automatically.
```

---

## Reference files

- `references/schemas.md` — Schema completo por skill (columnas, tipos, constraints)
- `references/type_rules.md` — Reglas de coerción por tipo de dato
- `references/null_strategies.md` — Estrategia de imputación por columna y skill
- `references/intake_dashboard_template.html` — Dashboard HTML; inject `SKILL_DATA_JSON`
- `templates/` — Plantillas XLSX pre-generadas por skill (generadas con template_generator.py)
