# FARO-CFG-001 · Reglas MVP en YAML

**Código:** FARO-CFG-001
**Nombre:** Reglas MVP FARO Connect en YAML
**Versión:** v1.0
**Estado:** Configuración oficial MVP
**Prioridad:** P1 · Crítico para motor de detección
**Formato:** YAML + JSON Schema conceptual + tests declarativos
**Uso:** Backend · data engineer · socio técnico · motor de reglas · QA · producto
**Depende de:**

* FARO-SQL-001 · Migraciones Base MVP
* FARO-SQL-002 · Multiempresa, Roles y RLS
* FARO-SQL-003 · Seeds Empresa Demo

**Conecta con:**

* FARO-ENG-002 · DSL Parser Reglas YAML
* FARO-ENG-003 · Motor Evaluador MVP
* FARO-TEST-002 · Tests Reglas MVP
* FARO-UI-001 · Bandeja de Tensiones
* FARO-TPL-001 · Templates Email Alertas
* FARO-TPL-002 · Reporte Semanal Ejecutivo

---

# 1. Objetivo

El objetivo de FARO-CFG-001 es definir el estándar oficial para escribir reglas FARO en YAML.

Estas reglas permiten convertir:

```text
KPIs
→ señales
→ condiciones
→ reglas
→ alertas
→ tensiones
→ acciones sugeridas
→ responsables
→ evidencia
→ impacto en FARO Score
```

La idea central es que FARO no tenga las tensiones “metidas a mano” dentro del código.

El código debe ejecutar reglas.
Las reglas deben vivir como configuración.
La dirección debe poder auditar qué regla disparó qué tensión y por qué.

---

# 2. Tesis técnica

FARO Connect no debe depender de lógica hardcodeada para detectar tensiones.

Un MVP serio necesita reglas:

1. declarativas;
2. versionadas;
3. auditables;
4. testeables;
5. editables;
6. trazables;
7. asociadas a KPIs;
8. asociadas a acciones;
9. asociadas a responsables;
10. asociadas a evidencia;
11. con impacto sobre FARO Score;
12. con control de confianza del dato.

La regla no debe ser una “caja negra”.

Debe poder explicarse así:

```text
La tensión TNS-001 se disparó porque:
- ventas netas subieron 18%;
- margen bruto cayó 24%;
- descuento promedio subió 98%;
- confianza del dato: 86%;
- severidad calculada: crítica;
- acción sugerida: revisar política de descuentos;
- responsable: comercial;
- evidencia requerida: matriz de descuentos aprobada.
```

Eso es FARO. Lo otro es un semáforo con traje.

---

# 3. Diferencia entre KPI, señal, regla y tensión

| Elemento  | Qué es                           | Ejemplo                                       |
| --------- | -------------------------------- | --------------------------------------------- |
| KPI       | Medición del negocio             | Margen bruto = 21%                            |
| Señal     | Cambio o condición relevante     | Margen cayó más de 10%                        |
| Regla     | Lógica que cruza señales         | Ventas suben + margen baja + descuentos suben |
| Tensión   | Diagnóstico ejecutivo accionable | Crecimiento no rentable                       |
| Acción    | Qué debe hacerse                 | Revisar política de descuentos                |
| Evidencia | Cómo se prueba el cierre         | Nueva política aprobada                       |

---

# 4. Ubicación recomendada en repositorio

```text
config/
  rules/
    mvp/
      commercial/
        TNS-001_crecimiento_no_rentable.yaml
        TNS-002_descuento_fuera_politica.yaml
        TNS-003_vendedor_erosiona_margen.yaml
      finance/
        TNS-004_venta_sin_caja.yaml
        TNS-005_mora_critica_cliente.yaml
      stock/
        TNS-006_stock_critico_alta_rotacion.yaml
        TNS-007_stock_inmovilizado.yaml
      purchasing/
        TNS-008_reposicion_reactiva.yaml
      execution/
        TNS-009_acciones_vencidas.yaml
        TNS-010_acciones_sin_evidencia.yaml
      data_quality/
        TNS-011_baja_confianza_dato.yaml
    industry/
      retail/
      construction_supplies/
      services/
      manufacturing/
    shared/
      thresholds.yaml
      severity_matrix.yaml
      score_impact.yaml
      evidence_types.yaml
      role_mapping.yaml
```

---

# 5. Principios de diseño de reglas

## 5.1 Regla simple

Cada regla debe responder:

| Pregunta                        | Respuesta esperada         |
| ------------------------------- | -------------------------- |
| ¿Qué detecta?                   | Una tensión específica     |
| ¿Con qué KPIs?                  | Lista explícita            |
| ¿Qué condiciones usa?           | Umbrales claros            |
| ¿Qué severidad tiene?           | Baja, media, alta, crítica |
| ¿A quién se asigna?             | Rol o usuario responsable  |
| ¿Qué acción recomienda?         | Acción concreta            |
| ¿Qué evidencia pide?            | Evidencia verificable      |
| ¿Cómo afecta el Score?          | Penalización o impacto     |
| ¿Qué confianza mínima requiere? | Umbral de calidad de dato  |
| ¿Cómo se prueba?                | Casos de test              |

## 5.2 Regla auditable

Cada regla debe conservar:

* `rule_code`;
* `tension_code`;
* `version`;
* `author`;
* `created_at`;
* `updated_at`;
* `status`;
* `change_reason`.

## 5.3 Regla segura

La regla no puede:

* inventar datos;
* consultar empresas cruzadas;
* modificar datos RAW;
* cerrar acciones automáticamente;
* cambiar Score sin registro;
* usar IA para decidir si se dispara;
* ocultar baja confianza de datos.

---

# 6. Estructura estándar YAML

```yaml
rule_code: RULE-TNS-001
tension_code: TNS-001
version: 1
status: active
name: Crecimiento no rentable
description: Detecta crecimiento de ventas acompañado de caída de margen y aumento de descuentos.

scope:
  company_types:
    - commercial
    - retail
    - distribution
    - construction_supplies
  modules:
    - sales
    - margin
    - discounts
  frequency: weekly
  evaluation_window: current_period
  comparison_window: previous_period

data_requirements:
  required_kpis:
    - KPI-SAL-001
    - KPI-SAL-002
    - KPI-SAL-003
  minimum_confidence_score: 75
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-SAL-001
      metric: delta_pct
      operator: ">="
      value: 0.10
    - kpi: KPI-SAL-002
      metric: delta_pct
      operator: "<="
      value: -0.10
    - kpi: KPI-SAL-003
      metric: delta_pct
      operator: ">="
      value: 0.30

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-SAL-002
            metric: value
            operator: "<="
            value: 0.22
          - kpi: KPI-SAL-003
            metric: value
            operator: ">="
            value: 0.10
      set: critical

output:
  create_tension: true
  title: Crecimiento no rentable
  diagnosis_template: >
    Las ventas crecieron, pero el margen cayó y los descuentos aumentaron.
    La empresa está vendiendo más, pero capturando menos rentabilidad.
  recommended_actions:
    - ACT-COM-001
    - ACT-COM-002
  assign_to_role: commercial_manager
  approver_role: general_manager
  evidence_required:
    - EVD-007
    - EVD-012
  default_sla_days: 7
  score_impact:
    base: -8
    max: -12

tests:
  - name: dispara_con_ventas_arriba_margen_abajo_descuento_arriba
    input:
      KPI-SAL-001:
        delta_pct: 0.18
        confidence_score: 90
      KPI-SAL-002:
        value: 0.21
        delta_pct: -0.24
        confidence_score: 88
      KPI-SAL-003:
        value: 0.12
        delta_pct: 0.98
        confidence_score: 86
    expect:
      triggered: true
      severity: critical
      tension_code: TNS-001

  - name: no_dispara_si_ventas_no_crecen
    input:
      KPI-SAL-001:
        delta_pct: 0.02
        confidence_score: 90
      KPI-SAL-002:
        delta_pct: -0.20
        confidence_score: 88
      KPI-SAL-003:
        delta_pct: 0.50
        confidence_score: 86
    expect:
      triggered: false
```

---

# 7. Campos oficiales del YAML

## 7.1 Identificación

| Campo          | Tipo    | Obligatorio | Descripción                       |
| -------------- | ------- | ----------: | --------------------------------- |
| `rule_code`    | string  |          Sí | Código único de regla             |
| `tension_code` | string  |          Sí | Tensión que genera                |
| `version`      | integer |          Sí | Versión de la regla               |
| `status`       | enum    |          Sí | active, draft, inactive, archived |
| `name`         | string  |          Sí | Nombre legible                    |
| `description`  | string  |          Sí | Qué detecta                       |

## 7.2 Scope

| Campo                     | Tipo   | Obligatorio | Descripción            |
| ------------------------- | ------ | ----------: | ---------------------- |
| `scope.company_types`     | array  |          Sí | Rubros donde aplica    |
| `scope.modules`           | array  |          Sí | Módulos requeridos     |
| `scope.frequency`         | enum   |          Sí | daily, weekly, monthly |
| `scope.evaluation_window` | string |          Sí | Periodo evaluado       |
| `scope.comparison_window` | string |          No | Periodo comparativo    |

## 7.3 Data requirements

| Campo                      | Tipo   | Obligatorio | Descripción                  |
| -------------------------- | ------ | ----------: | ---------------------------- |
| `required_kpis`            | array  |          Sí | KPIs necesarios              |
| `minimum_confidence_score` | number |          Sí | Confianza mínima             |
| `missing_data_policy`      | enum   |          Sí | Qué hacer si falta dato      |
| `stale_data_policy`        | enum   |          Sí | Qué hacer si dato está viejo |

## 7.4 Conditions

| Campo        | Tipo   | Descripción                 |
| ------------ | ------ | --------------------------- |
| `all`        | array  | Todas deben cumplirse       |
| `any`        | array  | Al menos una debe cumplirse |
| `none`       | array  | Ninguna debe cumplirse      |
| `expression` | string | Futuro DSL avanzado         |

## 7.5 Output

| Campo                 | Tipo    | Obligatorio |
| --------------------- | ------- | ----------: |
| `create_tension`      | boolean |          Sí |
| `title`               | string  |          Sí |
| `diagnosis_template`  | string  |          Sí |
| `recommended_actions` | array   |          Sí |
| `assign_to_role`      | string  |          Sí |
| `approver_role`       | string  |          No |
| `evidence_required`   | array   |          Sí |
| `default_sla_days`    | integer |          Sí |
| `score_impact.base`   | number  |          Sí |
| `score_impact.max`    | number  |          No |

---

# 8. Operadores soportados MVP

| Operador          | Uso                       |
| ----------------- | ------------------------- |
| `>`               | Mayor que                 |
| `>=`              | Mayor o igual             |
| `<`               | Menor que                 |
| `<=`              | Menor o igual             |
| `==`              | Igual                     |
| `!=`              | Distinto                  |
| `between`         | Entre dos valores         |
| `in`              | Dentro de lista           |
| `not_in`          | Fuera de lista            |
| `exists`          | Existe                    |
| `missing`         | Falta                     |
| `changed_by_pct`  | Cambió cierto porcentaje  |
| `older_than_days` | Antigüedad mayor a X días |

---

# 9. Políticas ante datos faltantes

| Política                      | Significado                       |
| ----------------------------- | --------------------------------- |
| `do_not_trigger`              | No dispara la regla               |
| `trigger_with_warning`        | Dispara pero marca baja confianza |
| `create_data_quality_tension` | Genera tensión de calidad de dato |
| `use_last_available`          | Usa último dato disponible        |
| `manual_review`               | Enviar a revisión humana          |

Recomendación MVP:

```yaml
missing_data_policy: do_not_trigger
stale_data_policy: warn
```

Para dirección, mejor una alerta menos que una falsa certeza. La falsa precisión es prima hermana del desastre.

---

# 10. Tipos de severidad

| Severidad  | Uso                                                |
| ---------- | -------------------------------------------------- |
| `low`      | Observación                                        |
| `medium`   | Requiere seguimiento                               |
| `high`     | Requiere acción                                    |
| `critical` | Requiere acción prioritaria y posible escalamiento |

## Matriz base

| Impacto | Urgencia baja | Urgencia media | Urgencia alta |
| ------- | ------------- | -------------- | ------------- |
| Bajo    | low           | medium         | medium        |
| Medio   | medium        | medium         | high          |
| Alto    | high          | high           | critical      |
| Crítico | critical      | critical       | critical      |

---

# 11. Tipos de evidencia conectados

| Código    | Evidencia                    |
| --------- | ---------------------------- |
| `EVD-001` | Documento cargado            |
| `EVD-002` | Captura de sistema           |
| `EVD-003` | Registro de aprobación       |
| `EVD-004` | Comentario validado          |
| `EVD-005` | Orden emitida                |
| `EVD-006` | Comprobante externo          |
| `EVD-007` | Cambio de política           |
| `EVD-008` | KPI posterior                |
| `EVD-009` | Cierre manual justificado    |
| `EVD-010` | Acta de reunión              |
| `EVD-011` | Cliente/proveedor contactado |
| `EVD-012` | Validación de dirección      |

---

# 12. Acciones sugeridas base

| Código        | Acción                                      |
| ------------- | ------------------------------------------- |
| `ACT-COM-001` | Revisar política de descuentos              |
| `ACT-COM-002` | Revisar comisiones vinculadas a margen      |
| `ACT-COM-003` | Bloquear descuentos fuera de autorización   |
| `ACT-COM-004` | Revisar performance de vendedor             |
| `ACT-FIN-001` | Priorizar cobranza de clientes vencidos     |
| `ACT-FIN-002` | Bloquear venta a crédito de cliente crítico |
| `ACT-FIN-003` | Definir plan de cobranza semanal            |
| `ACT-STK-001` | Generar reposición priorizada               |
| `ACT-STK-002` | Transferir stock entre sucursales           |
| `ACT-STK-003` | Liquidar stock inmovilizado controladamente |
| `ACT-PUR-001` | Revisar proveedor/costo crítico             |
| `ACT-OPS-001` | Regularizar acciones vencidas               |
| `ACT-OPS-002` | Exigir evidencia de cierre                  |
| `ACT-DQ-001`  | Corregir fuente de datos                    |
| `ACT-DIR-001` | Escalar a dirección                         |

---

# 13. Role mapping inicial

```yaml
roles:
  commercial_manager:
    role_codes:
      - commercial_user
      - area_manager
    fallback_role: general_manager

  finance_manager:
    role_codes:
      - finance_user
      - area_manager
    fallback_role: general_manager

  stock_manager:
    role_codes:
      - stock_user
      - area_manager
    fallback_role: general_manager

  purchasing_manager:
    role_codes:
      - area_manager
    fallback_role: general_manager

  general_manager:
    role_codes:
      - general_manager
    fallback_role: director

  director:
    role_codes:
      - director
    fallback_role: company_admin

  data_owner:
    role_codes:
      - company_admin
      - integration_service
    fallback_role: general_manager
```

---

# 14. Reglas MVP oficiales

## 14.1 TNS-001 · Crecimiento no rentable

```yaml
rule_code: RULE-TNS-001
tension_code: TNS-001
version: 1
status: active
name: Crecimiento no rentable
description: Detecta aumento de ventas acompañado de caída de margen y aumento de descuentos.

scope:
  company_types: [commercial, retail, distribution, construction_supplies]
  modules: [sales, margin, discounts]
  frequency: weekly
  evaluation_window: current_period
  comparison_window: previous_period

data_requirements:
  required_kpis: [KPI-SAL-001, KPI-SAL-002, KPI-SAL-003]
  minimum_confidence_score: 75
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-SAL-001
      metric: delta_pct
      operator: ">="
      value: 0.10
    - kpi: KPI-SAL-002
      metric: delta_pct
      operator: "<="
      value: -0.10
    - kpi: KPI-SAL-003
      metric: delta_pct
      operator: ">="
      value: 0.30

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-SAL-002
            metric: value
            operator: "<="
            value: 0.22
          - kpi: KPI-SAL-003
            metric: value
            operator: ">="
            value: 0.10
      set: critical

output:
  create_tension: true
  title: Crecimiento no rentable
  diagnosis_template: >
    Las ventas crecieron, pero el margen cayó y los descuentos aumentaron.
    La empresa está vendiendo más, pero capturando menos rentabilidad.
  recommended_actions: [ACT-COM-001, ACT-COM-002, ACT-COM-003]
  assign_to_role: commercial_manager
  approver_role: general_manager
  evidence_required: [EVD-007, EVD-012]
  default_sla_days: 7
  score_impact:
    base: -8
    max: -12

tests:
  - name: dispara_con_crecimiento_no_rentable
    input:
      KPI-SAL-001: {delta_pct: 0.18, confidence_score: 90}
      KPI-SAL-002: {value: 0.21, delta_pct: -0.24, confidence_score: 88}
      KPI-SAL-003: {value: 0.12, delta_pct: 0.98, confidence_score: 86}
    expect:
      triggered: true
      severity: critical

  - name: no_dispara_si_margen_no_cae
    input:
      KPI-SAL-001: {delta_pct: 0.18, confidence_score: 90}
      KPI-SAL-002: {value: 0.29, delta_pct: 0.02, confidence_score: 88}
      KPI-SAL-003: {value: 0.12, delta_pct: 0.98, confidence_score: 86}
    expect:
      triggered: false
```

---

## 14.2 TNS-002 · Descuento fuera de política

```yaml
rule_code: RULE-TNS-002
tension_code: TNS-002
version: 1
status: active
name: Descuento fuera de política
description: Detecta descuentos superiores al umbral permitido por política comercial.

scope:
  company_types: [commercial, retail, distribution, construction_supplies]
  modules: [sales, discounts]
  frequency: weekly
  evaluation_window: current_period
  comparison_window: policy_threshold

data_requirements:
  required_kpis: [KPI-SAL-003]
  minimum_confidence_score: 75
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-SAL-003
      metric: value
      operator: ">="
      value: 0.10

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-SAL-003
            metric: value
            operator: ">="
            value: 0.15
      set: critical

output:
  create_tension: true
  title: Descuento fuera de política
  diagnosis_template: >
    El descuento promedio superó el umbral permitido.
    Esto puede erosionar margen aunque la venta parezca positiva.
  recommended_actions: [ACT-COM-001, ACT-COM-003]
  assign_to_role: commercial_manager
  approver_role: general_manager
  evidence_required: [EVD-007, EVD-012]
  default_sla_days: 5
  score_impact:
    base: -5
    max: -9

tests:
  - name: dispara_descuento_alto
    input:
      KPI-SAL-003: {value: 0.12, confidence_score: 86}
    expect:
      triggered: true
      severity: high
```

---

## 14.3 TNS-003 · Vendedor erosiona margen

```yaml
rule_code: RULE-TNS-003
tension_code: TNS-003
version: 1
status: active
name: Vendedor erosiona margen
description: Detecta vendedores con ventas altas, descuento elevado y margen bajo.

scope:
  company_types: [commercial, retail, distribution, construction_supplies]
  modules: [sales, margin, discounts, employees]
  frequency: weekly
  evaluation_window: current_period
  dimension: employee

data_requirements:
  required_kpis: [KPI-SAL-004, KPI-SAL-005, KPI-SAL-006]
  minimum_confidence_score: 75
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-SAL-004
      metric: percentile
      operator: ">="
      value: 0.70
    - kpi: KPI-SAL-005
      metric: value
      operator: "<="
      value: 0.22
    - kpi: KPI-SAL-006
      metric: value
      operator: ">="
      value: 0.12

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-SAL-005
            metric: value
            operator: "<="
            value: 0.18
      set: critical

output:
  create_tension: true
  title: Vendedor erosiona margen
  diagnosis_template: >
    Un vendedor está generando volumen comercial con descuentos altos y margen inferior al objetivo.
  recommended_actions: [ACT-COM-004, ACT-COM-002, ACT-COM-003]
  assign_to_role: commercial_manager
  approver_role: general_manager
  evidence_required: [EVD-010, EVD-012]
  default_sla_days: 7
  score_impact:
    base: -6
    max: -10

tests:
  - name: dispara_vendedor_alto_volumen_bajo_margen
    input:
      KPI-SAL-004: {percentile: 0.85, confidence_score: 88}
      KPI-SAL-005: {value: 0.18, confidence_score: 86}
      KPI-SAL-006: {value: 0.15, confidence_score: 87}
    expect:
      triggered: true
      severity: critical
```

---

## 14.4 TNS-004 · Venta sin conversión a caja

```yaml
rule_code: RULE-TNS-004
tension_code: TNS-004
version: 1
status: active
name: Venta sin conversión a caja
description: Detecta ventas relevantes acompañadas de deterioro de cobranza.

scope:
  company_types: [commercial, retail, distribution, services, construction_supplies]
  modules: [sales, receivables, finance]
  frequency: weekly
  evaluation_window: current_period
  comparison_window: previous_period

data_requirements:
  required_kpis: [KPI-SAL-001, KPI-FIN-001, KPI-FIN-002]
  minimum_confidence_score: 75
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-SAL-001
      metric: delta_pct
      operator: ">="
      value: 0.05
    - kpi: KPI-FIN-001
      metric: delta_pct
      operator: ">="
      value: 0.15
    - kpi: KPI-FIN-002
      metric: delta_pct
      operator: ">="
      value: 0.20

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-FIN-002
            metric: delta_pct
            operator: ">="
            value: 0.75
      set: critical

output:
  create_tension: true
  title: Venta sin conversión a caja
  diagnosis_template: >
    La empresa vende, pero la cobranza se deteriora.
    El crecimiento comercial no se está convirtiendo en caja.
  recommended_actions: [ACT-FIN-001, ACT-FIN-002, ACT-FIN-003]
  assign_to_role: finance_manager
  approver_role: general_manager
  evidence_required: [EVD-011, EVD-006, EVD-012]
  default_sla_days: 5
  score_impact:
    base: -7
    max: -12

tests:
  - name: dispara_venta_sin_caja
    input:
      KPI-SAL-001: {delta_pct: 0.18, confidence_score: 89}
      KPI-FIN-001: {delta_pct: 0.34, confidence_score: 82}
      KPI-FIN-002: {delta_pct: 1.11, confidence_score: 82}
    expect:
      triggered: true
      severity: critical
```

---

## 14.5 TNS-005 · Mora crítica por cliente

```yaml
rule_code: RULE-TNS-005
tension_code: TNS-005
version: 1
status: active
name: Mora crítica por cliente
description: Detecta clientes con monto vencido relevante y atraso superior al umbral.

scope:
  company_types: [commercial, distribution, services, construction_supplies]
  modules: [receivables, customers, finance]
  frequency: weekly
  evaluation_window: current_period
  dimension: customer

data_requirements:
  required_kpis: [KPI-FIN-003, KPI-FIN-004]
  minimum_confidence_score: 75
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-FIN-003
      metric: value
      operator: ">="
      value: 30
    - kpi: KPI-FIN-004
      metric: value
      operator: ">="
      value: 1000000

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-FIN-003
            metric: value
            operator: ">="
            value: 45
          - kpi: KPI-FIN-004
            metric: value
            operator: ">="
            value: 3000000
      set: critical

output:
  create_tension: true
  title: Mora crítica por cliente
  diagnosis_template: >
    Un cliente acumula deuda vencida relevante y supera el umbral de atraso.
  recommended_actions: [ACT-FIN-001, ACT-FIN-002]
  assign_to_role: finance_manager
  approver_role: general_manager
  evidence_required: [EVD-011, EVD-006]
  default_sla_days: 3
  score_impact:
    base: -6
    max: -10

tests:
  - name: dispara_cliente_mora_critica
    input:
      KPI-FIN-003: {value: 47, confidence_score: 86}
      KPI-FIN-004: {value: 3800000, confidence_score: 86}
    expect:
      triggered: true
      severity: critical
```

---

## 14.6 TNS-006 · Stock crítico en productos de alta rotación

```yaml
rule_code: RULE-TNS-006
tension_code: TNS-006
version: 1
status: active
name: Stock crítico en productos de alta rotación
description: Detecta productos de alta demanda con cobertura insuficiente.

scope:
  company_types: [commercial, retail, distribution, construction_supplies]
  modules: [stock, sales]
  frequency: weekly
  evaluation_window: current_period
  dimension: product

data_requirements:
  required_kpis: [KPI-STK-001, KPI-STK-003]
  minimum_confidence_score: 70
  missing_data_policy: trigger_with_warning
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-STK-001
      metric: value
      operator: "<="
      value: 7
    - kpi: KPI-STK-003
      metric: percentile
      operator: ">="
      value: 0.70

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-STK-001
            metric: value
            operator: "<="
            value: 3
      set: critical

output:
  create_tension: true
  title: Stock crítico en productos de alta rotación
  diagnosis_template: >
    Hay productos de venta relevante con cobertura de stock insuficiente.
    Existe riesgo de perder ventas o incumplir entregas.
  recommended_actions: [ACT-STK-001, ACT-STK-002]
  assign_to_role: stock_manager
  approver_role: general_manager
  evidence_required: [EVD-005, EVD-002]
  default_sla_days: 3
  score_impact:
    base: -6
    max: -10

tests:
  - name: dispara_stock_critico_alta_rotacion
    input:
      KPI-STK-001: {value: 4, confidence_score: 78}
      KPI-STK-003: {percentile: 0.82, confidence_score: 84}
    expect:
      triggered: true
      severity: high
```

---

## 14.7 TNS-007 · Stock inmovilizado

```yaml
rule_code: RULE-TNS-007
tension_code: TNS-007
version: 1
status: active
name: Stock inmovilizado
description: Detecta inventario con alto valor y baja o nula rotación.

scope:
  company_types: [commercial, retail, distribution, construction_supplies]
  modules: [stock, sales]
  frequency: weekly
  evaluation_window: current_period
  dimension: product

data_requirements:
  required_kpis: [KPI-STK-002, KPI-STK-004]
  minimum_confidence_score: 70
  missing_data_policy: trigger_with_warning
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-STK-002
      metric: value
      operator: ">="
      value: 5000000
    - kpi: KPI-STK-004
      metric: value
      operator: ">="
      value: 60

severity:
  default: medium
  escalation:
    - when:
        all:
          - kpi: KPI-STK-002
            metric: value
            operator: ">="
            value: 10000000
      set: high

output:
  create_tension: true
  title: Stock inmovilizado
  diagnosis_template: >
    Hay capital atrapado en inventario con baja rotación.
    Esto afecta liquidez y capacidad de compra.
  recommended_actions: [ACT-STK-003, ACT-COM-001]
  assign_to_role: stock_manager
  approver_role: general_manager
  evidence_required: [EVD-010, EVD-012]
  default_sla_days: 10
  score_impact:
    base: -4
    max: -8

tests:
  - name: dispara_stock_inmovilizado
    input:
      KPI-STK-002: {value: 12250000, confidence_score: 75}
      KPI-STK-004: {value: 90, confidence_score: 72}
    expect:
      triggered: true
      severity: high
```

---

## 14.8 TNS-008 · Reposición reactiva

```yaml
rule_code: RULE-TNS-008
tension_code: TNS-008
version: 1
status: active
name: Reposición reactiva
description: Detecta compras urgentes recurrentes sobre productos críticos.

scope:
  company_types: [commercial, retail, distribution, construction_supplies, manufacturing]
  modules: [stock, purchasing]
  frequency: weekly
  evaluation_window: current_period

data_requirements:
  required_kpis: [KPI-STK-001, KPI-PUR-001]
  minimum_confidence_score: 70
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-STK-001
      metric: value
      operator: ">="
      value: 3
    - kpi: KPI-PUR-001
      metric: value
      operator: ">="
      value: 2

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-PUR-001
            metric: value
            operator: ">="
            value: 5
      set: critical

output:
  create_tension: true
  title: Reposición reactiva
  diagnosis_template: >
    La empresa está comprando tarde o por urgencia, en lugar de anticipar reposición.
  recommended_actions: [ACT-STK-001, ACT-PUR-001]
  assign_to_role: purchasing_manager
  approver_role: general_manager
  evidence_required: [EVD-005, EVD-010]
  default_sla_days: 5
  score_impact:
    base: -5
    max: -9

tests:
  - name: dispara_reposicion_reactiva
    input:
      KPI-STK-001: {value: 4, confidence_score: 78}
      KPI-PUR-001: {value: 3, confidence_score: 80}
    expect:
      triggered: true
      severity: high
```

---

## 14.9 TNS-009 · Acciones vencidas

```yaml
rule_code: RULE-TNS-009
tension_code: TNS-009
version: 1
status: active
name: Acciones vencidas
description: Detecta acciones abiertas con fecha vencida.

scope:
  company_types: [all]
  modules: [actions, workflow]
  frequency: daily
  evaluation_window: current_date

data_requirements:
  required_kpis: [KPI-ACT-001]
  minimum_confidence_score: 80
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-ACT-001
      metric: value
      operator: ">="
      value: 1

severity:
  default: medium
  escalation:
    - when:
        all:
          - kpi: KPI-ACT-001
            metric: value
            operator: ">="
            value: 3
      set: high
    - when:
        all:
          - kpi: KPI-ACT-001
            metric: value
            operator: ">="
            value: 7
      set: critical

output:
  create_tension: true
  title: Acciones vencidas
  diagnosis_template: >
    Existen acciones vencidas que no fueron cerradas ni reprogramadas.
    La ejecución perdió ritmo.
  recommended_actions: [ACT-OPS-001, ACT-DIR-001]
  assign_to_role: general_manager
  approver_role: director
  evidence_required: [EVD-004, EVD-012]
  default_sla_days: 2
  score_impact:
    base: -4
    max: -10

tests:
  - name: dispara_acciones_vencidas
    input:
      KPI-ACT-001: {value: 3, confidence_score: 90}
    expect:
      triggered: true
      severity: high
```

---

## 14.10 TNS-010 · Acciones sin evidencia

```yaml
rule_code: RULE-TNS-010
tension_code: TNS-010
version: 1
status: active
name: Acciones sin evidencia
description: Detecta acciones marcadas como cerradas o avanzadas sin evidencia suficiente.

scope:
  company_types: [all]
  modules: [actions, evidence, workflow]
  frequency: daily
  evaluation_window: current_date

data_requirements:
  required_kpis: [KPI-ACT-002]
  minimum_confidence_score: 80
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-ACT-002
      metric: value
      operator: ">="
      value: 1

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-ACT-002
            metric: value
            operator: ">="
            value: 5
      set: critical

output:
  create_tension: true
  title: Acciones sin evidencia
  diagnosis_template: >
    Hay acciones sin evidencia suficiente.
    El sistema no puede considerar resuelto lo que no está probado.
  recommended_actions: [ACT-OPS-002, ACT-DIR-001]
  assign_to_role: general_manager
  approver_role: director
  evidence_required: [EVD-004, EVD-012]
  default_sla_days: 2
  score_impact:
    base: -5
    max: -10

tests:
  - name: dispara_accion_sin_evidencia
    input:
      KPI-ACT-002: {value: 2, confidence_score: 90}
    expect:
      triggered: true
      severity: high
```

---

# 15. Reglas MVP adicionales resumidas

Estas reglas forman el paquete de 30 tensiones MVP iniciales. Las primeras 10 anteriores son las reglas núcleo del piloto. Las 20 siguientes se pueden activar según industria, datos disponibles y madurez del cliente.

## 15.1 Comercial

```yaml
rules:
  - rule_code: RULE-TNS-011
    tension_code: TNS-011
    name: Ventas concentradas en pocos clientes
    required_kpis: [KPI-SAL-007, KPI-CUS-001]
    trigger:
      all:
        - {kpi: KPI-CUS-001, metric: value, operator: ">=", value: 0.40}
    output:
      severity: high
      assign_to_role: commercial_manager
      recommended_actions: [ACT-COM-004]
      evidence_required: [EVD-010]
      score_impact: {base: -4, max: -8}

  - rule_code: RULE-TNS-012
    tension_code: TNS-012
    name: Ticket promedio cae con ventas estables
    required_kpis: [KPI-SAL-001, KPI-SAL-008]
    trigger:
      all:
        - {kpi: KPI-SAL-001, metric: delta_pct, operator: ">=", value: -0.03}
        - {kpi: KPI-SAL-008, metric: delta_pct, operator: "<=", value: -0.15}
    output:
      severity: medium
      assign_to_role: commercial_manager
      recommended_actions: [ACT-COM-001]
      evidence_required: [EVD-010]
      score_impact: {base: -3, max: -6}

  - rule_code: RULE-TNS-013
    tension_code: TNS-013
    name: Caída de ventas en sucursal relevante
    required_kpis: [KPI-SAL-009]
    trigger:
      all:
        - {kpi: KPI-SAL-009, metric: delta_pct, operator: "<=", value: -0.12}
    output:
      severity: high
      assign_to_role: commercial_manager
      recommended_actions: [ACT-COM-004]
      evidence_required: [EVD-010]
      score_impact: {base: -5, max: -9}

  - rule_code: RULE-TNS-014
    tension_code: TNS-014
    name: Margen bajo por familia de producto
    required_kpis: [KPI-SAL-010]
    trigger:
      all:
        - {kpi: KPI-SAL-010, metric: value, operator: "<=", value: 0.20}
    output:
      severity: high
      assign_to_role: commercial_manager
      recommended_actions: [ACT-COM-001, ACT-PUR-001]
      evidence_required: [EVD-010, EVD-012]
      score_impact: {base: -5, max: -9}

  - rule_code: RULE-TNS-015
    tension_code: TNS-015
    name: Venta crece por descuento, no por demanda sana
    required_kpis: [KPI-SAL-001, KPI-SAL-003, KPI-SAL-011]
    trigger:
      all:
        - {kpi: KPI-SAL-001, metric: delta_pct, operator: ">=", value: 0.10}
        - {kpi: KPI-SAL-003, metric: delta_pct, operator: ">=", value: 0.50}
        - {kpi: KPI-SAL-011, metric: delta_pct, operator: "<=", value: 0.02}
    output:
      severity: high
      assign_to_role: commercial_manager
      recommended_actions: [ACT-COM-001, ACT-COM-003]
      evidence_required: [EVD-007]
      score_impact: {base: -6, max: -10}
```

---

## 15.2 Finanzas y cobranza

```yaml
rules:
  - rule_code: RULE-TNS-016
    tension_code: TNS-016
    name: Cliente supera límite de crédito
    required_kpis: [KPI-FIN-005, KPI-FIN-006]
    trigger:
      all:
        - {kpi: KPI-FIN-005, metric: value, operator: ">=", value: 1.00}
    output:
      severity: critical
      assign_to_role: finance_manager
      recommended_actions: [ACT-FIN-002, ACT-FIN-001]
      evidence_required: [EVD-011, EVD-012]
      score_impact: {base: -7, max: -12}

  - rule_code: RULE-TNS-017
    tension_code: TNS-017
    name: Cobranza concentrada en pocos responsables
    required_kpis: [KPI-FIN-007]
    trigger:
      all:
        - {kpi: KPI-FIN-007, metric: value, operator: ">=", value: 0.50}
    output:
      severity: medium
      assign_to_role: finance_manager
      recommended_actions: [ACT-FIN-003]
      evidence_required: [EVD-010]
      score_impact: {base: -3, max: -6}

  - rule_code: RULE-TNS-018
    tension_code: TNS-018
    name: Facturación alta con cobranza parcial
    required_kpis: [KPI-SAL-001, KPI-FIN-008]
    trigger:
      all:
        - {kpi: KPI-SAL-001, metric: delta_pct, operator: ">=", value: 0.08}
        - {kpi: KPI-FIN-008, metric: value, operator: "<=", value: 0.70}
    output:
      severity: high
      assign_to_role: finance_manager
      recommended_actions: [ACT-FIN-001, ACT-FIN-003]
      evidence_required: [EVD-011]
      score_impact: {base: -6, max: -10}

  - rule_code: RULE-TNS-019
    tension_code: TNS-019
    name: Caja proyectada insuficiente
    required_kpis: [KPI-FIN-009, KPI-FIN-010]
    trigger:
      all:
        - {kpi: KPI-FIN-009, metric: value, operator: "<=", value: 15}
        - {kpi: KPI-FIN-010, metric: value, operator: ">=", value: 1.00}
    output:
      severity: critical
      assign_to_role: finance_manager
      recommended_actions: [ACT-FIN-003, ACT-DIR-001]
      evidence_required: [EVD-010, EVD-012]
      score_impact: {base: -8, max: -14}

  - rule_code: RULE-TNS-020
    tension_code: TNS-020
    name: Mora creciente sin gestión registrada
    required_kpis: [KPI-FIN-002, KPI-ACT-003]
    trigger:
      all:
        - {kpi: KPI-FIN-002, metric: delta_pct, operator: ">=", value: 0.20}
        - {kpi: KPI-ACT-003, metric: value, operator: "<=", value: 0}
    output:
      severity: high
      assign_to_role: finance_manager
      recommended_actions: [ACT-FIN-001, ACT-OPS-002]
      evidence_required: [EVD-011]
      score_impact: {base: -6, max: -10}
```

---

## 15.3 Stock y compras

```yaml
rules:
  - rule_code: RULE-TNS-021
    tension_code: TNS-021
    name: Quiebre de stock con ventas perdidas
    required_kpis: [KPI-STK-005, KPI-SAL-012]
    trigger:
      all:
        - {kpi: KPI-STK-005, metric: value, operator: ">=", value: 1}
        - {kpi: KPI-SAL-012, metric: value, operator: ">=", value: 500000}
    output:
      severity: high
      assign_to_role: stock_manager
      recommended_actions: [ACT-STK-001, ACT-STK-002]
      evidence_required: [EVD-005]
      score_impact: {base: -6, max: -10}

  - rule_code: RULE-TNS-022
    tension_code: TNS-022
    name: Compra con costo creciente y margen sin ajustar
    required_kpis: [KPI-PUR-002, KPI-SAL-002]
    trigger:
      all:
        - {kpi: KPI-PUR-002, metric: delta_pct, operator: ">=", value: 0.08}
        - {kpi: KPI-SAL-002, metric: delta_pct, operator: "<=", value: -0.05}
    output:
      severity: high
      assign_to_role: purchasing_manager
      recommended_actions: [ACT-PUR-001, ACT-COM-001]
      evidence_required: [EVD-010, EVD-012]
      score_impact: {base: -6, max: -11}

  - rule_code: RULE-TNS-023
    tension_code: TNS-023
    name: Proveedor crítico con demora recurrente
    required_kpis: [KPI-PUR-003, KPI-STK-001]
    trigger:
      all:
        - {kpi: KPI-PUR-003, metric: value, operator: ">=", value: 2}
        - {kpi: KPI-STK-001, metric: value, operator: ">=", value: 2}
    output:
      severity: high
      assign_to_role: purchasing_manager
      recommended_actions: [ACT-PUR-001, ACT-DIR-001]
      evidence_required: [EVD-010]
      score_impact: {base: -5, max: -9}

  - rule_code: RULE-TNS-024
    tension_code: TNS-024
    name: Stock alto con deuda a proveedores
    required_kpis: [KPI-STK-002, KPI-PUR-004]
    trigger:
      all:
        - {kpi: KPI-STK-002, metric: value, operator: ">=", value: 10000000}
        - {kpi: KPI-PUR-004, metric: value, operator: ">=", value: 1}
    output:
      severity: high
      assign_to_role: general_manager
      recommended_actions: [ACT-STK-003, ACT-DIR-001]
      evidence_required: [EVD-010, EVD-012]
      score_impact: {base: -6, max: -10}

  - rule_code: RULE-TNS-025
    tension_code: TNS-025
    name: Reposición sin rotación suficiente
    required_kpis: [KPI-PUR-005, KPI-STK-004]
    trigger:
      all:
        - {kpi: KPI-PUR-005, metric: value, operator: ">=", value: 1}
        - {kpi: KPI-STK-004, metric: value, operator: ">=", value: 60}
    output:
      severity: medium
      assign_to_role: purchasing_manager
      recommended_actions: [ACT-PUR-001, ACT-STK-003]
      evidence_required: [EVD-010]
      score_impact: {base: -4, max: -7}
```

---

## 15.4 Operación, ejecución y calidad de datos

```yaml
rules:
  - rule_code: RULE-TNS-026
    tension_code: TNS-026
    name: Dirección sin visibilidad semanal
    required_kpis: [KPI-DQ-001, KPI-REP-001]
    trigger:
      any:
        - {kpi: KPI-DQ-001, metric: value, operator: "<=", value: 70}
        - {kpi: KPI-REP-001, metric: value, operator: ">", value: 7}
    output:
      severity: high
      assign_to_role: general_manager
      recommended_actions: [ACT-DQ-001, ACT-DIR-001]
      evidence_required: [EVD-010, EVD-012]
      score_impact: {base: -5, max: -9}

  - rule_code: RULE-TNS-027
    tension_code: TNS-027
    name: Fuente crítica atrasada
    required_kpis: [KPI-DQ-002]
    trigger:
      all:
        - {kpi: KPI-DQ-002, metric: value, operator: ">", value: 2}
    output:
      severity: high
      assign_to_role: data_owner
      recommended_actions: [ACT-DQ-001]
      evidence_required: [EVD-002, EVD-004]
      score_impact: {base: -4, max: -8}

  - rule_code: RULE-TNS-028
    tension_code: TNS-028
    name: Baja confianza de dato en KPI crítico
    required_kpis: [KPI-DQ-003]
    trigger:
      all:
        - {kpi: KPI-DQ-003, metric: value, operator: "<=", value: 70}
    output:
      severity: high
      assign_to_role: data_owner
      recommended_actions: [ACT-DQ-001]
      evidence_required: [EVD-002, EVD-004]
      score_impact: {base: -5, max: -10}

  - rule_code: RULE-TNS-029
    tension_code: TNS-029
    name: Responsable no asignado en acción crítica
    required_kpis: [KPI-ACT-004]
    trigger:
      all:
        - {kpi: KPI-ACT-004, metric: value, operator: ">=", value: 1}
    output:
      severity: critical
      assign_to_role: general_manager
      recommended_actions: [ACT-OPS-001, ACT-DIR-001]
      evidence_required: [EVD-004]
      score_impact: {base: -6, max: -10}

  - rule_code: RULE-TNS-030
    tension_code: TNS-030
    name: Tensión crítica reincidente
    required_kpis: [KPI-TNS-001]
    trigger:
      all:
        - {kpi: KPI-TNS-001, metric: value, operator: ">=", value: 2}
    output:
      severity: critical
      assign_to_role: general_manager
      approver_role: director
      recommended_actions: [ACT-DIR-001, ACT-OPS-002]
      evidence_required: [EVD-010, EVD-012]
      score_impact: {base: -8, max: -15}
```

---

# 16. Archivo compartido de thresholds

```yaml
thresholds:
  sales_growth_relevant_pct: 0.10
  sales_drop_relevant_pct: -0.12
  margin_drop_warning_pct: -0.08
  margin_drop_critical_pct: -0.15
  gross_margin_min_warning: 0.24
  gross_margin_min_critical: 0.22
  discount_warning_pct: 0.08
  discount_critical_pct: 0.12
  collection_days_warning: 35
  collection_days_critical: 45
  overdue_amount_warning: 1000000
  overdue_amount_critical: 3000000
  stock_coverage_warning_days: 10
  stock_coverage_critical_days: 5
  stock_immobilized_warning_days: 60
  stock_immobilized_critical_days: 90
  data_confidence_minimum: 75
  data_confidence_critical: 60
  action_overdue_warning_count: 1
  action_overdue_critical_count: 5
```

---

# 17. Archivo de pesos por severidad

```yaml
severity_weights:
  low:
    priority_base: 25
    score_impact_base: -1
    sla_days_default: 14

  medium:
    priority_base: 50
    score_impact_base: -3
    sla_days_default: 10

  high:
    priority_base: 75
    score_impact_base: -6
    sla_days_default: 5

  critical:
    priority_base: 90
    score_impact_base: -10
    sla_days_default: 2
```

---

# 18. Score impact mapping

```yaml
score_impact_policy:
  dimensions:
    commercial_health:
      related_tensions:
        - TNS-001
        - TNS-002
        - TNS-003
        - TNS-011
        - TNS-012
        - TNS-013
        - TNS-014
        - TNS-015

    cash_health:
      related_tensions:
        - TNS-004
        - TNS-005
        - TNS-016
        - TNS-017
        - TNS-018
        - TNS-019
        - TNS-020

    stock_health:
      related_tensions:
        - TNS-006
        - TNS-007
        - TNS-008
        - TNS-021
        - TNS-022
        - TNS-023
        - TNS-024
        - TNS-025

    execution_health:
      related_tensions:
        - TNS-009
        - TNS-010
        - TNS-029
        - TNS-030

    data_quality:
      related_tensions:
        - TNS-026
        - TNS-027
        - TNS-028

  cap_policy:
    max_negative_score_impact_per_period: -25
    max_single_tension_impact: -15
    require_confidence_over: 70
```

---

# 19. JSON Schema conceptual

Este schema sirve para que el parser valide reglas antes de cargarlas.

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "FARO Rule Schema MVP",
  "type": "object",
  "required": [
    "rule_code",
    "tension_code",
    "version",
    "status",
    "name",
    "description",
    "scope",
    "data_requirements",
    "conditions",
    "severity",
    "output",
    "tests"
  ],
  "properties": {
    "rule_code": {
      "type": "string",
      "pattern": "^RULE-TNS-[0-9]{3}$"
    },
    "tension_code": {
      "type": "string",
      "pattern": "^TNS-[0-9]{3}$"
    },
    "version": {
      "type": "integer",
      "minimum": 1
    },
    "status": {
      "type": "string",
      "enum": ["draft", "active", "inactive", "archived"]
    },
    "name": {
      "type": "string",
      "minLength": 3
    },
    "description": {
      "type": "string",
      "minLength": 10
    },
    "scope": {
      "type": "object"
    },
    "data_requirements": {
      "type": "object",
      "required": [
        "required_kpis",
        "minimum_confidence_score",
        "missing_data_policy",
        "stale_data_policy"
      ]
    },
    "conditions": {
      "type": "object"
    },
    "severity": {
      "type": "object"
    },
    "output": {
      "type": "object",
      "required": [
        "create_tension",
        "title",
        "diagnosis_template",
        "recommended_actions",
        "assign_to_role",
        "evidence_required",
        "default_sla_days",
        "score_impact"
      ]
    },
    "tests": {
      "type": "array",
      "minItems": 1
    }
  }
}
```

---

# 20. Comportamiento esperado del motor evaluador

El motor debe ejecutar esta secuencia:

```text
1. Leer reglas activas.
2. Validar schema.
3. Validar KPIs requeridos.
4. Obtener kpi_snapshots del período.
5. Verificar confianza mínima.
6. Evaluar condiciones all/any/none.
7. Calcular severidad.
8. Calcular priority_score.
9. Crear rule_evaluation.
10. Crear tension si corresponde.
11. Asignar responsable.
12. Crear acción sugerida.
13. Registrar evidencia requerida.
14. Calcular impacto Score.
15. Registrar auditoría.
```

---

# 21. Pseudocódigo del evaluador

```ts
type RuleEvaluationResult = {
  triggered: boolean;
  severity?: "low" | "medium" | "high" | "critical";
  tensionCode?: string;
  scoreImpact?: number;
  diagnostics: string[];
};

async function evaluateRule(rule, context): Promise<RuleEvaluationResult> {
  validateRuleSchema(rule);

  const snapshots = await loadRequiredKpis({
    companyId: context.companyId,
    period: context.period,
    kpiCodes: rule.data_requirements.required_kpis,
  });

  const confidenceOk = validateMinimumConfidence(
    snapshots,
    rule.data_requirements.minimum_confidence_score
  );

  if (!confidenceOk && rule.data_requirements.missing_data_policy === "do_not_trigger") {
    return {
      triggered: false,
      diagnostics: ["Minimum confidence not met"],
    };
  }

  const conditionsResult = evaluateConditions(rule.conditions, snapshots);

  if (!conditionsResult.passed) {
    return {
      triggered: false,
      diagnostics: conditionsResult.diagnostics,
    };
  }

  const severity = calculateSeverity(rule.severity, snapshots);

  return {
    triggered: true,
    severity,
    tensionCode: rule.tension_code,
    scoreImpact: calculateScoreImpact(rule.output.score_impact, severity),
    diagnostics: conditionsResult.diagnostics,
  };
}
```

---

# 22. Tests obligatorios por regla

Cada regla debe tener mínimo:

| Test                        | Objetivo                        |
| --------------------------- | ------------------------------- |
| Caso dispara                | Confirma que la regla se activa |
| Caso no dispara             | Evita falsos positivos          |
| Caso baja confianza         | Valida política de datos        |
| Caso severidad alta/crítica | Valida escalamiento             |
| Caso dato faltante          | Valida `missing_data_policy`    |

Ejemplo:

```yaml
tests:
  - name: dispara
    input:
      KPI-SAL-001: {delta_pct: 0.18, confidence_score: 90}
      KPI-SAL-002: {delta_pct: -0.20, confidence_score: 88}
      KPI-SAL-003: {delta_pct: 0.60, confidence_score: 86}
    expect:
      triggered: true

  - name: no_dispara
    input:
      KPI-SAL-001: {delta_pct: 0.02, confidence_score: 90}
      KPI-SAL-002: {delta_pct: -0.20, confidence_score: 88}
      KPI-SAL-003: {delta_pct: 0.60, confidence_score: 86}
    expect:
      triggered: false

  - name: no_dispara_por_baja_confianza
    input:
      KPI-SAL-001: {delta_pct: 0.18, confidence_score: 50}
      KPI-SAL-002: {delta_pct: -0.20, confidence_score: 50}
      KPI-SAL-003: {delta_pct: 0.60, confidence_score: 50}
    expect:
      triggered: false
      reason: confidence_below_minimum
```

---

# 23. Relación con tablas SQL

| YAML                 | Tabla destino                     |
| -------------------- | --------------------------------- |
| `rule_code`          | `faro.rule_definitions.rule_code` |
| `tension_code`       | `faro.tensions.tension_code`      |
| `rule_body` completo | `faro.rule_definitions.rule_body` |
| resultado evaluación | `faro.rule_evaluations`           |
| tensión generada     | `faro.tensions`                   |
| acciones sugeridas   | `faro.actions`                    |
| evidencia requerida  | `faro.evidence` / metadata        |
| impacto score        | `faro.score_snapshots.components` |

---

# 24. SQL conceptual para cargar regla YAML convertida a JSON

```sql
INSERT INTO faro.rule_definitions (
  company_id,
  rule_code,
  name,
  description,
  rule_type,
  rule_format,
  rule_body,
  severity_default,
  is_mvp,
  status,
  version
)
VALUES (
  NULL,
  'RULE-TNS-001',
  'Crecimiento no rentable',
  'Detecta ventas en crecimiento con caída de margen y aumento de descuentos.',
  'tension',
  'yaml',
  :rule_body_json,
  'high',
  true,
  'active',
  1
);
```

Reglas globales pueden tener `company_id = NULL`.

Reglas específicas por cliente deben tener `company_id` de la empresa correspondiente.

---

# 25. Política de versionado

## 25.1 Versión nueva

Cuando cambia la lógica de una regla, no se pisa la anterior.

Se crea:

```yaml
version: 2
change_reason: Ajuste de umbral de descuento crítico de 12% a 10%.
```

## 25.2 Estados

| Estado     | Uso                       |
| ---------- | ------------------------- |
| `draft`    | En construcción           |
| `active`   | Evaluable por motor       |
| `inactive` | No se evalúa              |
| `archived` | Histórica, no modificable |

## 25.3 Regla de oro

Nunca borrar reglas históricas si generaron tensiones.

Una tensión del pasado debe poder explicar con qué versión de regla fue creada.

---

# 26. Política de reglas globales vs cliente

| Tipo        | `company_id` | Uso                                        |
| ----------- | ------------ | ------------------------------------------ |
| Global FARO | `NULL`       | Regla estándar aplicable a muchos clientes |
| Cliente     | UUID empresa | Regla ajustada a un cliente                |
| Industria   | Metadata     | Regla por rubro                            |
| Piloto      | Metadata     | Regla temporal de validación               |

Prioridad de evaluación:

```text
1. Regla específica cliente
2. Regla específica industria
3. Regla global FARO
```

---

# 27. Qué NO debe hacer una regla

Una regla YAML no debe:

1. modificar RAW;
2. borrar datos;
3. cerrar acciones automáticamente;
4. aprobar evidencia;
5. enviar reportes sin control;
6. consultar datos de otra empresa;
7. inventar datos faltantes;
8. llamar directamente a IA;
9. cambiar permisos;
10. ejecutar SQL arbitrario sin validación.

---

# 28. Riesgos y mitigaciones

| Riesgo                         | Severidad | Mitigación                        |
| ------------------------------ | --------- | --------------------------------- |
| Reglas mal escritas            | Alta      | JSON Schema + tests               |
| Demasiadas alertas             | Alta      | Activar solo MVP inicial          |
| Falsos positivos               | Alta      | Revisión humana y tests           |
| Umbrales genéricos incorrectos | Media     | Pesos por industria               |
| Reglas hardcodeadas            | Alta      | YAML obligatorio                  |
| Baja confianza de datos        | Alta      | `minimum_confidence_score`        |
| Reglas sin acción              | Alta      | `recommended_actions` obligatorio |
| Reglas sin responsable         | Alta      | `assign_to_role` obligatorio      |
| Reglas sin evidencia           | Alta      | `evidence_required` obligatorio   |
| IA inventando diagnóstico      | Crítica   | IA solo redacta desde payload     |

---

# 29. Criterios de aceptación de FARO-CFG-001

FARO-CFG-001 queda aceptado si:

| Criterio                              | Estado esperado |
| ------------------------------------- | --------------- |
| Define estructura YAML estándar       | Sí              |
| Define campos obligatorios            | Sí              |
| Define operadores MVP                 | Sí              |
| Define políticas de datos faltantes   | Sí              |
| Define severidad                      | Sí              |
| Define evidencia                      | Sí              |
| Define acciones sugeridas             | Sí              |
| Define 30 reglas MVP                  | Sí              |
| Incluye tests por regla núcleo        | Sí              |
| Conecta con SQL                       | Sí              |
| Conecta con motor evaluador           | Sí              |
| Evita hardcoding                      | Sí              |
| Permite versionado                    | Sí              |
| Permite reglas globales y por cliente | Sí              |

---

# 30. Próximo paso recomendado

Después de FARO-CFG-001 corresponde construir:

## FARO-ENG-002 · DSL Parser Reglas YAML

Objetivo:

Crear el parser técnico que:

1. lee archivos YAML;
2. valida contra JSON Schema;
3. verifica KPIs existentes;
4. verifica acciones existentes;
5. verifica roles existentes;
6. convierte YAML a JSON;
7. guarda regla en `faro.rule_definitions`;
8. permite ejecutar tests declarativos;
9. reporta errores claros;
10. bloquea reglas inseguras.

Sin parser, las reglas son documentación.
Con parser, las reglas empiezan a ser producto.

---

# 31. Frase ejecutiva

FARO-CFG-001 transforma las tensiones FARO en un sistema gobernable.

La diferencia es simple:

```text
Sin YAML:
la lógica vive en código y solo el programador la entiende.

Con YAML:
la lógica es visible, auditable, versionable y testeable.
```

Ese es el paso donde FARO deja de ser una buena idea y empieza a comportarse como plataforma.
