01 · Resumen ejecutivo

DSL declarativo, no código hardcodeado

FARO-CFG-001 define cómo se escriben las reglas que convierten datos en tensiones ejecutivas. Las reglas viven como configuración YAML, no como código de aplicación. El motor evaluador las lee, las valida, las ejecuta y registra cada disparo con trazabilidad completa.

El loop conceptual que FARO opera con estas reglas es siempre el mismo:

▸ Pipeline determinístico FARO
--- pipeline conceptual ---
KPIs
   señales relevantes (delta, percentil, valor absoluto)
     condiciones (operadores comparados contra umbral)
       reglas (cruce de condiciones bajo all/any/none)
         tensiones (diagnóstico ejecutivo accionable)
           acciones recomendadas (códigos ACT-*)
             responsables (rol asignado + aprobador)
               evidencia requerida (códigos EVD-*)
                 impacto sobre FARO Score (negativo, acotado)

Decisión arquitectónica. FARO Connect no detecta tensiones con if/else en código de aplicación. Cada tensión se dispara porque una regla YAML versionada y testeada cruzó condiciones declarativas sobre KPIs canónicos. Si el código tuviera la lógica adentro, ninguna dirección podría auditarla; tampoco un nuevo desarrollador podría leerla; tampoco un cliente podría ajustarla por industria sin un release. El YAML es la única forma honesta de hacer esto.

Comparación rápida entre las dos arquitecturas posibles:

DimensiónReglas hardcodeadasReglas DSL YAML (FARO)
Quién las editaSolo dev con commitProducto + dirección (revisable en repo)
Versionadogit diff sobre código fuenteversion: N + change_reason dentro del YAML
Auditable por direcciónNo (es código)Sí (es configuración legible)
Test declarativoRequiere framework de testBloque tests: dentro del propio YAML
Ajuste por industriaForks de código o feature flagsSubcarpeta industry/*/
Ajuste por clienteBranches o ramas separadascompany_id en metadata + overrides
Trazabilidad de disparoLogs si el dev los pusorule_evaluations con regla, versión, payload, severidad
ReversibilidadRollback de deploystatus: inactive sin tocar binario

Este documento define las 10 secciones obligatorias del YAML, los 13 operadores soportados, la matriz de severidad, las políticas de datos faltantes y las 11 reglas iniciales que componen el núcleo del MVP (TNS-001 a TNS-011). Las 19 reglas restantes (TNS-012 a TNS-030) viven en archivos secundarios y se activan por industria; quedan referenciadas en catalogo-tensiones-mvp.html.

El contexto operativo asumido en todos los ejemplos es Empresa Demo Cuyo S.A., la empresa demo del pack FARO Connect: rubro distribución comercial multisucursal, datos cargados en el schema faro_demo, evaluación semanal de reglas comerciales, financieras, stock, compras y ejecución.

02 · Tesis y principios

Una regla FARO es simple, auditable, segura, versionada

Antes de escribir un solo YAML, el equipo acuerda estos cuatro atributos. Una regla que no cumple los cuatro no entra al catálogo MVP, aunque funcione técnicamente.

Principio 01

Regla simple

Una regla responde a una sola pregunta del negocio. Si necesita explicarse en dos párrafos, está mal escrita. Si requiere conocer la implementación interna del motor, está mal escrita. Lectura ejecutiva en menos de 30 segundos.

Principio 02

Regla auditable

Cada regla conserva rule_code, tension_code, version, author, created_at, updated_at, status, change_reason. Ningún disparo histórico queda sin trazabilidad de qué regla lo generó.

Principio 03

Regla segura

Una regla no puede inventar datos, no puede consultar empresas cruzadas, no puede modificar datos RAW, no puede cerrar acciones automáticamente, no puede cambiar Score sin registro, no puede llamar a IA para decidir si dispara, no puede ocultar baja confianza del dato.

Principio 04

Regla versionada

Cambiar la lógica nunca pisa la versión anterior: se crea version: N+1 con change_reason explícito. Las reglas archivadas se mantienen disponibles para explicar tensiones históricas. Borrar reglas activas con histórico está prohibido.

Principio 05

Regla testeable

Cada regla incluye bloque tests: con mínimo dos casos: dispara y no dispara. Reglas críticas suman caso de severidad crítica y caso de baja confianza de datos. Sin tests, la regla queda en status: draft.

Principio 06

Regla trazable

El motor registra en faro.rule_evaluations cada ejecución: regla, versión, snapshot de KPIs evaluados, condiciones pasadas, severidad calculada, tensión generada (si la hubo), confianza promedio del dato, timestamp.

Principio 07

Regla asociada

Una regla apunta siempre a KPIs canónicos (catálogo en construcción · ver sección 11), acciones canónicas (ACT-*), responsables por rol y evidencia (EVD-*). No hay strings sueltos; todo resuelve contra catálogos.

Principio 08

Regla con confianza

Toda regla declara minimum_confidence_score y política ante datos faltantes. La regla del MVP por defecto: una alerta menos antes que una falsa certeza. missing_data_policy: do_not_trigger.

La regla no es una caja negra. Cuando FARO dispara TNS-001, la dirección debe poder leer: 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.

03 · KPI · señal · regla · tensión · acción · evidencia

Seis conceptos que no se confunden

Antes de leer una regla YAML, fijar el vocabulario. El motor distingue estos seis niveles y los serializa en tablas separadas; mezclarlos es la causa número uno de productos de BI que parecen funcionar pero no se pueden gobernar.

ElementoQué esTabla destinoEjemplo
KPIMedición del negocio en un períodofaro.kpi_snapshotsMargen bruto = 21% (semana 22)
SeñalCambio o condición relevante sobre un KPIDerivada en evaluaciónMargen cayó más de 10% vs período anterior
ReglaCruce declarativo de señales bajo all/any/nonefaro.rule_definitionsVentas suben + margen baja + descuento sube
TensiónDiagnóstico ejecutivo accionablefaro.tensionsCrecimiento no rentable (TNS-001)
AcciónQué debe hacerse para resolverfaro.actionsRevisar política de descuentos (ACT-COM-001)
EvidenciaCómo se prueba el cierre operativofaro.evidenceCambio de política aprobado (EVD-007)

El YAML que define la regla conoce todos los niveles: declara qué KPIs necesita (sección data_requirements.required_kpis), qué condiciones evalúa (conditions), qué tensión genera (tension_code), qué acciones sugiere (output.recommended_actions) y qué evidencia exige (output.evidence_required). Por eso una regla bien escrita es autosuficiente para auditoría: no hay nada más que leer.

04 · Ubicación en el repositorio

Una regla, un archivo, una carpeta por área

Las reglas viven bajo config/rules/mvp/. Una regla por archivo. Una carpeta por área funcional. Una subcarpeta opcional por industria. Una subcarpeta shared/ para thresholds, severity matrix y archivos transversales.

▸ config/rules/mvp/ · estructura de repositorio
config/
└── rules/
    └── mvp/
        ├── commercial/                                  # reglas comerciales
        │   ├── TNS-001_crecimiento_no_rentable.yaml
        │   ├── TNS-002_descuento_fuera_politica.yaml
        │   └── TNS-003_vendedor_erosiona_margen.yaml
        ├── finance/                                     # reglas financieras y cobranza
        │   ├── TNS-004_venta_sin_caja.yaml
        │   └── TNS-005_mora_critica_cliente.yaml
        ├── stock/                                       # reglas de inventario
        │   ├── TNS-006_stock_critico_alta_rotacion.yaml
        │   └── TNS-007_stock_inmovilizado.yaml
        ├── purchasing/                                  # reglas de compras
        │   └── TNS-008_reposicion_reactiva.yaml
        ├── execution/                                   # reglas de ejecución de acciones
        │   ├── TNS-009_acciones_vencidas.yaml
        │   └── TNS-010_acciones_sin_evidencia.yaml
        ├── data_quality/                                # reglas de calidad de dato
        │   └── TNS-011_baja_confianza_dato.yaml
        ├── industry/                                    # overrides por industria
        │   ├── retail/
        │   ├── construction_supplies/
        │   ├── services/
        │   └── manufacturing/
        └── shared/                                      # archivos transversales
            ├── thresholds.yaml                          # umbrales reutilizables
            ├── severity_matrix.yaml                     # pesos por severidad
            ├── score_impact.yaml                        # mapping a FARO Score
            ├── evidence_types.yaml                      # tipos de evidencia válidos
            └── role_mapping.yaml                        # roles canónicos del MVP

Convención de nombres: TNS-NNN_slug_descriptivo.yaml. El código TNS al inicio facilita encontrar el archivo por código; el slug humano facilita encontrarlo por concepto. La extensión es siempre .yaml (no .yml) para consistencia con el parser.

05 · Las 10 secciones del YAML

Contrato estructural de una regla

Toda regla del MVP cumple este contrato. El parser FARO-ENG-002 valida estas 10 secciones contra JSON Schema (sección 10) antes de cargar la regla en faro.rule_definitions.

SecciónPropósitoObligatorioValidación
metadataIdentificación de reglarule_code, tension_code, version, status, name, description
scopeDónde aplica la reglacompany_types, modules, frequency, evaluation_window, comparison_window
data_requirementsKPIs y políticas de datorequired_kpis, minimum_confidence_score, missing_data_policy, stale_data_policy
conditionsLógica de disparoall / any / none con tuplas kpi.metric.operator.value
severitySeveridad por defecto y escalamientosdefault en {low, medium, high, critical} + bloques escalation.when
outputQué emite la regla cuando disparacreate_tension, title, diagnosis_template, recommended_actions, assign_to_role, evidence_required, default_sla_days
score_impactImpacto sobre FARO Scorebase negativo + max negativo (cap absoluto por tensión)
governanceVersionado y auditoríaOpcionalauthor, created_at, updated_at, change_reason
demo_relevanceSi la regla dispara en demo de Empresa Demo Cuyo S.A.OpcionalBoolean + nota explicativa
testsCasos declarativos de validaciónArray de {name, input, expect} con mínimo 2 casos (dispara + no dispara)

Ejemplo de YAML completo con las 10 secciones

Plantilla de referencia que muestra todas las secciones en orden, con comentarios explicativos. Las reglas reales (sección 8) siguen exactamente esta estructura.

▸ template_rule.yaml · estructura canónica
# ============================================================
# SECCIÓN 1 · metadata
# ============================================================
rule_code: RULE-TNS-XXX
tension_code: TNS-XXX
version: 1
status: active           # draft | active | inactive | archived
name: Nombre legible de la regla
description: Qué detecta la regla, en una línea.

# ============================================================
# SECCIÓN 2 · scope
# ============================================================
scope:
  company_types: [commercial, retail, distribution]
  modules: [sales, margin]
  frequency: weekly      # daily | weekly | monthly
  evaluation_window: current_period
  comparison_window: previous_period

# ============================================================
# SECCIÓN 3 · data_requirements
# ============================================================
data_requirements:
  required_kpis: [KPI-SAL-001, KPI-SAL-002]
  minimum_confidence_score: 75
  missing_data_policy: do_not_trigger
  stale_data_policy: warn

# ============================================================
# SECCIÓN 4 · conditions
# ============================================================
conditions:
  all:                              # todas deben cumplirse
    - kpi: KPI-SAL-001
      metric: delta_pct
      operator: ">="
      value: 0.10

# ============================================================
# SECCIÓN 5 · severity
# ============================================================
severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-SAL-002
            metric: value
            operator: "<="
            value: 0.22
      set: critical

# ============================================================
# SECCIÓN 6 · output
# ============================================================
output:
  create_tension: true
  title: Título ejecutivo de la tensión
  diagnosis_template: >
    Descripción ejecutiva del diagnóstico, lista para mostrar en bandeja.
  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

# ============================================================
# SECCIÓN 7 · score_impact (anidado dentro de output)
# ============================================================
  score_impact:
    base: -8
    max: -12

# ============================================================
# SECCIÓN 8 · governance (opcional)
# ============================================================
governance:
  author: tomas.pombo
  created_at: "2026-05-31"
  updated_at: "2026-05-31"
  change_reason: Creación inicial MVP.

# ============================================================
# SECCIÓN 9 · demo_relevance (opcional)
# ============================================================
demo_relevance:
  empresa_demo_cuyo_sa: true
  note: Dispara en demo semana 22 con severidad crítica.

# ============================================================
# SECCIÓN 10 · tests (obligatoria)
# ============================================================
tests:
  - name: caso_que_dispara
    input:
      KPI-SAL-001: {delta_pct: 0.18, confidence_score: 90}
      KPI-SAL-002: {value: 0.21, delta_pct: -0.24, confidence_score: 88}
    expect:
      triggered: true
      severity: critical

  - name: caso_que_no_dispara
    input:
      KPI-SAL-001: {delta_pct: 0.02, confidence_score: 90}
      KPI-SAL-002: {value: 0.28, delta_pct: 0.01, confidence_score: 88}
    expect:
      triggered: false
06 · Operadores soportados

13 operadores + 3 combinadores lógicos

El DSL FARO acota los operadores deliberadamente. Más operadores = más caminos de falsos positivos. El MVP cubre todos los casos de las 30 tensiones con este set acotado.

Combinadores lógicos (estructura de conditions)

CombinadorSignificadoUso típico
allConjunciónTodas las condiciones del array deben cumplirse. Es el combinador por defecto del MVP.
anyDisyunciónAl menos una condición del array debe cumplirse. Usar con cuidado: tiende a generar falsos positivos.
noneNegación de disyunciónNinguna condición del array debe cumplirse. Útil para excepciones declarativas.

Operadores comparadores (sobre kpi.metric)

OperadorUsoEjemplo declarativo
>Mayor quekpi: KPI-FIN-003 · metric: value · operator: ">" · value: 30
>=Mayor o igualkpi: KPI-SAL-001 · metric: delta_pct · operator: ">=" · value: 0.10
<Menor quekpi: KPI-STK-001 · metric: value · operator: "<" · value: 7
<=Menor o igualkpi: KPI-SAL-002 · metric: delta_pct · operator: "<=" · value: -0.10
==Igualkpi: KPI-ACT-004 · metric: assigned · operator: "==" · value: false
!=Distintokpi: KPI-DQ-002 · metric: source_status · operator: "!=" · value: "ok"
betweenEntre dos valoreskpi: KPI-FIN-003 · metric: value · operator: "between" · value: [30, 45]
inDentro de listakpi: KPI-CUS-001 · metric: segment · operator: "in" · value: ["A", "B"]
not_inFuera de listakpi: KPI-SAL-009 · metric: branch · operator: "not_in" · value: ["pilot"]
existsExiste (no null)kpi: KPI-ACT-001 · metric: due_date · operator: "exists"
missingFalta (null)kpi: KPI-ACT-004 · metric: owner · operator: "missing"
changed_by_pctCambió cierto porcentaje vs ventana previakpi: KPI-SAL-001 · metric: value · operator: "changed_by_pct" · value: 0.10
older_than_daysAntigüedad mayor a X díaskpi: KPI-DQ-002 · metric: last_sync · operator: "older_than_days" · value: 2

Notación canónica kpi.metric.operator.value

Toda condición del DSL FARO se escribe como una tupla de cuatro campos: el KPI (código canónico contra catálogo de KPIs), la métrica (qué dimensión del snapshot leer: value, delta_pct, percentile, confidence_score, etc.), el operador (uno de los 13 anteriores) y el valor de comparación (número, string, lista o array según operador). Esta notación reemplaza la sintaxis informal de la biblioteca extendida v2 (la de 300 tensiones) y es el único formato aceptado por el parser FARO-ENG-002.

Políticas ante datos faltantes. Cada regla declara qué hacer si los KPIs requeridos no están disponibles. El MVP usa por defecto missing_data_policy: do_not_trigger + stale_data_policy: warn. Las 5 políticas soportadas son: do_not_trigger (no dispara), trigger_with_warning (dispara pero marca baja confianza), create_data_quality_tension (genera tensión TNS-028 en lugar de la original), use_last_available (usa último dato disponible) y manual_review (encola para revisión humana). Para dirección, una alerta menos antes que una falsa certeza.

07 · Severity matrix + escalations

Cuatro niveles, una matriz, escalamientos declarativos

La severidad de una tensión disparada se calcula así: primero severity.default, luego se evalúan los bloques severity.escalation[].when en orden; el último que matchea define la severidad efectiva. Si ninguno matchea, queda la default.

Niveles canónicos

SeveridadSignificadoPriority baseSLA defaultScore impact base
lowObservación · no requiere acción inmediata2514 días−1
mediumRequiere seguimiento operativo5010 días−3
highRequiere acción755 días−6
criticalRequiere acción prioritaria + posible escalamiento a dirección902 días−10

Matriz base impacto × urgencia

Cuando una regla no declara severity.escalation, el motor puede inferir severidad efectiva combinando el impacto del KPI (delta o valor absoluto) con la urgencia operativa (frecuencia, ventana, criticidad del rubro). La matriz es referencial; cada regla puede sobreescribir explícitamente.

Impacto · Urgencia → Urgencia baja Urgencia media Urgencia alta
Bajolowmediummedium
Mediomediummediumhigh
Altohighhighcritical
Críticocriticalcriticalcritical

Ejemplo declarativo de escalamiento

La regla TNS-001 (Crecimiento no rentable) declara severity default high y un bloque de escalamiento a critical cuando el margen cae por debajo de 22% y el descuento supera 10%. Así se escribe:

▸ severity con escalation · fragmento TNS-001
severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-SAL-002     # margen bruto
            metric: value
            operator: "<="
            value: 0.22            # margen <= 22%
          - kpi: KPI-SAL-003     # descuento promedio
            metric: value
            operator: ">="
            value: 0.10            # descuento >= 10%
      set: critical          # sube severidad a critical

Cap policy. Aunque varias tensiones disparen en el mismo período, el impacto agregado sobre FARO Score está capeado: max_negative_score_impact_per_period: -25 y max_single_tension_impact: -15. Evita que Score caiga 80 puntos en una semana por una racha de reglas correlacionadas. Vive en shared/score_impact.yaml.

08 · Las 11 reglas MVP

TNS-001 a TNS-011 · núcleo del piloto

Estas son las once reglas que componen el núcleo activo del MVP. Cubren los cinco bloques: comercial (3), finanzas (2), stock (2), compras (1), ejecución (2) y calidad de dato (1). Las 19 restantes (TNS-012 a TNS-030) viven en archivos secundarios y se referencian en catalogo-tensiones-mvp.html.

TNS-001 · RULE-TNS-001 · v1 high → critical

Crecimiento no rentable

Propósito: detectar cuando la empresa vende más pero captura menos rentabilidad. Cruza tres señales que aisladas no preocupan, pero juntas son síntoma claro de erosión de modelo comercial.
▸ commercial/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           # ventas netas
      metric: delta_pct
      operator: ">="
      value: 0.10                  # crecen >= 10%
    - kpi: KPI-SAL-002           # margen bruto
      metric: delta_pct
      operator: "<="
      value: -0.10                 # cae >= 10%
    - kpi: KPI-SAL-003           # descuento promedio
      metric: delta_pct
      operator: ">="
      value: 0.30                  # sube >= 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

Nota. Es la regla insignia del MVP. Dispara en demo de Empresa Demo Cuyo S.A. semana 22 con severidad crítica. Cruza tres KPIs comerciales clásicos cuyo deterioro individual pasa desapercibido en cualquier dashboard tradicional.

TNS-002 · RULE-TNS-002 · v1 high → critical

Descuento fuera de política

Propósito: detectar cuando el descuento promedio supera el umbral permitido por política comercial. Tensión simple, de un solo KPI, alta urgencia operativa.
▸ commercial/TNS-002_descuento_fuera_politica.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                  # descuento >= 10%

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-SAL-003
            metric: value
            operator: ">="
            value: 0.15              # >= 15% escala a crítica
      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

  - name: no_dispara_descuento_dentro_politica
    input:
      KPI-SAL-003: {value: 0.07, confidence_score: 86}
    expect:
      triggered: false

Nota. Frecuentemente convive con TNS-001 cuando hay erosión generalizada de modelo comercial. El parser debería emitir warning si ambas disparan en el mismo período para evitar duplicar score impact.

TNS-003 · RULE-TNS-003 · v1 high → critical

Vendedor erosiona margen

Propósito: detectar vendedores que generan volumen comercial alto pero con descuentos elevados y margen inferior al objetivo. Tensión dimensional (evalúa por empleado, no por empresa).
▸ commercial/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           # evalúa por vendedor

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           # ventas por vendedor
      metric: percentile
      operator: ">="
      value: 0.70                  # percentil 70+ (top 30%)
    - kpi: KPI-SAL-005           # margen por vendedor
      metric: value
      operator: "<="
      value: 0.22                  # margen <= 22%
    - kpi: KPI-SAL-006           # descuento por vendedor
      metric: value
      operator: ">="
      value: 0.12                  # descuento >= 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

Nota. Esta regla requiere KPIs dimensionados por empleado (sufijo _by_employee en el snapshot). El parser FARO-ENG-002 valida que los KPIs declarados soporten la dimensión employee en el catálogo (pendiente FARO-SQL-007).

TNS-004 · RULE-TNS-004 · v1 high → critical

Venta sin conversión a caja

Propósito: detectar cuando crecen las ventas pero se deteriora la cobranza. El crecimiento comercial no se convierte en caja real; deuda y mora suben mientras el equipo comercial celebra el cierre.
▸ finance/TNS-004_venta_sin_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           # ventas netas
      metric: delta_pct
      operator: ">="
      value: 0.05
    - kpi: KPI-FIN-001           # días promedio de cobro
      metric: delta_pct
      operator: ">="
      value: 0.15
    - kpi: KPI-FIN-002           # monto vencido total
      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

Nota. Esta tensión es la clásica que tira para abajo Score de salud de caja. En Empresa Demo Cuyo S.A. dispara junto con TNS-001: el crecimiento comercial vendido como buena noticia es en realidad descuento + mora.

TNS-005 · RULE-TNS-005 · v1 high → critical

Mora crítica por cliente

Propósito: detectar clientes individuales con monto vencido relevante y atraso superior al umbral. Tensión dimensional por cliente; permite al equipo financiero priorizar cobranza.
▸ finance/TNS-005_mora_critica_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           # evalúa por cliente

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           # días de atraso por cliente
      metric: value
      operator: ">="
      value: 30
    - kpi: KPI-FIN-004           # monto vencido por cliente
      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

Nota. Genera una tensión por cliente moroso, no una agregada. El motor agrupa por customer_id en faro.tensions.dimension_value. Demo Cuyo S.A. dispara tres tensiones TNS-005 simultáneas en demo semana 22.

TNS-006 · RULE-TNS-006 · v1 high → critical

Stock crítico en productos de alta rotación

Propósito: detectar productos de alta demanda con cobertura insuficiente. Genera tensión por producto y permite priorizar reposición urgente o transferencia entre sucursales.
▸ stock/TNS-006_stock_critico_alta_rotacion.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           # días de cobertura
      metric: value
      operator: "<="
      value: 7
    - kpi: KPI-STK-003           # rotación del producto
      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

Nota. Usa missing_data_policy: trigger_with_warning en lugar de do_not_trigger porque en stock el dato faltante suele ser síntoma del problema (sistema sin inventario al día). Mejor avisar con confianza baja que silencio.

TNS-007 · RULE-TNS-007 · v1 medium → high

Stock inmovilizado

Propósito: detectar inventario con alto valor y baja o nula rotación. Capital atrapado que afecta liquidez y capacidad de compra de productos rentables.
▸ stock/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           # valor inmovilizado
      metric: value
      operator: ">="
      value: 5000000
    - kpi: KPI-STK-004           # días sin rotación
      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

Nota. Severidad por defecto medium (no high) porque inmovilización es problema crónico, no urgencia. SLA largo (10 días). Sube a high cuando el valor inmovilizado supera 10M.

TNS-008 · RULE-TNS-008 · v1 high → critical

Reposición reactiva

Propósito: detectar cuando la empresa compra tarde o por urgencia, en lugar de anticipar reposición. Síntoma de gestión de compras sin política preventiva.
▸ purchasing/TNS-008_reposicion_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           # cobertura crítica
      metric: value
      operator: ">="
      value: 3
    - kpi: KPI-PUR-001           # órdenes urgentes en período
      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

Nota. El KPI KPI-PUR-001 requiere que el ERP marque qué órdenes de compra son urgentes vs planeadas. Si la integración no lo provee, esta regla queda en estado inactive hasta que el cliente lo habilite.

TNS-009 · RULE-TNS-009 · v1 medium → high → critical

Acciones vencidas

Propósito: detectar acciones abiertas con fecha vencida. Mide ritmo de ejecución y permite a dirección saber qué decidiones quedaron en el aire.
▸ execution/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           # acciones vencidas no cerradas
      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

Nota. Tres niveles de escalamiento progresivo (medium → high → critical) según volumen de acciones vencidas. Frecuencia diaria (las demás son weekly), porque ejecución requiere lectura más fina.

TNS-010 · RULE-TNS-010 · v1 high → critical

Acciones sin evidencia

Propósito: detectar acciones marcadas como cerradas o avanzadas sin evidencia suficiente. FARO no premia relatos; premia evidencia documentada.
▸ execution/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           # acciones cerradas sin evidencia
      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

Nota. Asignada a general_manager con aprobador director. Es la única tensión del MVP que escala directo a nivel director si reincide, porque el problema no es operativo sino de gobierno.

TNS-011 · RULE-TNS-011 · v1 high → critical

Baja confianza del dato en KPI crítico

Propósito: detectar cuando un KPI marcado como crítico opera con confianza de dato debajo del umbral. Genera tensión de calidad de dato en vez de silenciar.
▸ data_quality/TNS-011_baja_confianza_dato.yaml
rule_code: RULE-TNS-011
tension_code: TNS-011
version: 1
status: active
name: Baja confianza del dato en KPI crítico
description: Detecta cuando un KPI crítico opera con confianza por debajo del umbral.

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

data_requirements:
  required_kpis: [KPI-DQ-003]      # confianza promedio KPIs críticos
  minimum_confidence_score: 0          # meta: la tensión justamente trata esto
  missing_data_policy: trigger_with_warning
  stale_data_policy: warn

conditions:
  all:
    - kpi: KPI-DQ-003
      metric: value
      operator: "<="
      value: 70                    # confianza <= 70%

severity:
  default: high
  escalation:
    - when:
        all:
          - kpi: KPI-DQ-003
            metric: value
            operator: "<="
            value: 60
      set: critical

output:
  create_tension: true
  title: Baja confianza del dato en KPI crítico
  diagnosis_template: >
    Hay KPIs críticos operando con confianza de dato inferior al umbral mínimo aceptable.
    Las decisiones ejecutivas sobre estos KPIs pueden estar mal informadas.
  recommended_actions: [ACT-DQ-001]
  assign_to_role: data_owner
  approver_role: general_manager
  evidence_required: [EVD-002, EVD-004]
  default_sla_days: 3
  score_impact:
    base: -5
    max: -10

tests:
  - name: dispara_baja_confianza
    input:
      KPI-DQ-003: {value: 62, confidence_score: 95}
    expect:
      triggered: true
      severity: high

  - name: no_dispara_confianza_aceptable
    input:
      KPI-DQ-003: {value: 85, confidence_score: 95}
    expect:
      triggered: false

Nota. Es la única regla del MVP cuyo minimum_confidence_score está en 0: meta-regla que justamente trata el problema de baja confianza, así que no puede auto-bloquearse. Asignada a data_owner (admin de empresa o servicio de integración).

09 · Estructura de carpetas reales

8 carpetas funcionales + industry/ + shared/

Cada área del negocio tiene su carpeta. Una regla vive en exactamente una carpeta funcional (la primaria). Si una regla aplica a varias áreas, sigue siendo un solo archivo; las áreas extras se declaran dentro de scope.modules.

CarpetaPropósitoReglas MVPFrecuencia típica
commercial/Reglas comerciales (ventas, margen, descuentos, vendedores)TNS-001, TNS-002, TNS-003weekly
finance/Reglas financieras y cobranza (caja, mora, crédito, facturación)TNS-004, TNS-005weekly
stock/Reglas de inventario (cobertura, rotación, inmovilización)TNS-006, TNS-007weekly
purchasing/Reglas de compras (proveedores, reposición, costos)TNS-008weekly
execution/Reglas de ejecución (acciones, evidencia, workflow)TNS-009, TNS-010daily
data_quality/Reglas de calidad de dato (confianza, frescura, integridad)TNS-011daily
industry/Overrides por industria (retail, construction_supplies, services, manufacturing)Overrides opcionalessegún override
shared/Archivos transversales (thresholds, severity_matrix, score_impact, role_mapping, evidence_types)Configuración comúnn/a

Archivo shared/thresholds.yaml · umbrales reutilizables

En vez de hardcodear umbrales en cada regla, se definen una vez en shared/thresholds.yaml y se referencian con ${thresholds.X}. Permite ajustar política comercial global sin tocar 30 reglas.

▸ shared/thresholds.yaml · umbrales canónicos
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

Archivo shared/role_mapping.yaml · roles canónicos

Los roles a los que se asignan tensiones (assign_to_role) se resuelven contra este archivo. Cada rol declara role_codes (roles concretos del schema RBAC) y fallback_role (a quién escala si nadie tiene el rol primario en la empresa).

▸ shared/role_mapping.yaml · roles canónicos MVP
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
10 · Validaciones YAML obligatorias

El parser rechaza · no negocia

Antes de cargar una regla en faro.rule_definitions, el parser FARO-ENG-002 ejecuta cinco validaciones bloqueantes. Una sola falla rechaza la regla con error claro y código de error tipado.

Validación 01 · Schema SCHEMA_VALID

JSON Schema oficial

El YAML se valida contra el JSON Schema oficial de FARO Rule Schema MVP. Verifica tipos, requeridos, enums, patterns de códigos (RULE-TNS-[0-9]{3}, TNS-[0-9]{3}), longitudes mínimas y estructura anidada de output, data_requirements, severity.

▸ snippet · validador JSON Schema
required: ["rule_code", "tension_code", "version",
           "status", "name", "description",
           "scope", "data_requirements",
           "conditions", "severity", "output", "tests"]

rule_code:    { type: "string", pattern: "^RULE-TNS-[0-9]{3}$" }
tension_code: { type: "string", pattern: "^TNS-[0-9]{3}$" }
status:       { enum: ["draft", "active", "inactive", "archived"] }
Validación 02 · FK tension_codes UNKNOWN_TENSION_CODE

Tension code existe en catálogo canónico

El campo tension_code debe existir en faro.tension_definitions con status = 'active'. Si la regla apunta a un código no canónico, se rechaza con UNKNOWN_TENSION_CODE. Reglas activas que apuntan a tensiones archivadas se bloquean.

▸ snippet · validación SQL
SELECT 1 FROM faro.tension_definitions
WHERE tension_code = :tension_code
  AND status = 'active';
Validación 03 · FK action_codes UNKNOWN_ACTION_CODE

Acciones recomendadas existen

Cada elemento de output.recommended_actions debe existir en faro.action_definitions (catálogo canónico de acciones · pendiente FARO-SQL-005). El array no puede estar vacío. Acciones archivadas son rechazadas.

▸ snippet · validación SQL
SELECT action_code FROM faro.action_definitions
WHERE action_code = ANY(:recommended_actions)
  AND status = 'active';
Validación 04 · FK evidence_codes UNKNOWN_EVIDENCE_CODE

Evidencias requeridas existen

Cada elemento de output.evidence_required debe existir en faro.evidence_definitions (catálogo canónico de evidencias · pendiente FARO-SQL-006). El array no puede estar vacío: una regla sin evidencia es una regla sin cierre operativo.

▸ snippet · validación SQL
SELECT evidence_code FROM faro.evidence_definitions
WHERE evidence_code = ANY(:evidence_required)
  AND status = 'active';
Validación 05 · FK kpi_codes UNKNOWN_KPI_CODE

KPIs requeridos existen PENDIENTE · requiere FARO-SQL-007

Cada elemento de data_requirements.required_kpis debe existir en faro.kpi_definitions (catálogo canónico de KPIs · pendiente FARO-SQL-007 a generar). Cada kpi dentro de conditions.all/any/none también. Esta validación es la que cierra la cadena regla → KPI → snapshot.

▸ snippet · validación SQL (pendiente)
-- Bloquea reglas que apuntan a KPIs no canónicos.
-- Pendiente: faro.kpi_definitions debe existir primero (FARO-SQL-007).
SELECT kpi_code FROM faro.kpi_definitions
WHERE kpi_code = ANY(:required_kpis)
  AND status = 'active';

Validaciones blandas (warnings, no bloqueantes)

  • Confianza mínima razonable: warning si minimum_confidence_score < 60. Si está debajo, dirección puede recibir tensiones poco fiables.
  • Tests cubren ambos casos: warning si la regla no tiene al menos un test triggered: true y otro triggered: false.
  • Severidad coherente con score_impact: warning si la regla declara default: low pero score_impact.base < -5 (escalas inconsistentes).
  • SLA coherente con severidad: warning si default_sla_days > 7 y severity.default = critical (crítico con SLA de dos semanas no tiene sentido).
  • change_reason en bumps de versión: warning si version > 1 y governance.change_reason está vacío.
11 · Gap detectado · catálogo de KPIs

Falta FARO-SQL-007 para cerrar la cadena

El DSL referencia KPIs por código (KPI-SAL-001, KPI-FIN-002, etc.) pero el catálogo canónico de KPIs MVP todavía no existe. Hasta que exista, la validación 05 (sección 10) queda en estado soft: emite warning pero no bloquea. Esto debe cerrarse antes del piloto.

▸ GAP CONOCIDO PENDIENTE · requiere FARO-SQL-007

FARO-SQL-007 · Catálogo Canónico de KPIs MVP

Qué falta. El catálogo formal faro.kpi_definitions con todos los códigos KPI-* usados por las 30 reglas. Debe incluir: kpi_code, name, area_code, module_code, unit, dimension (empresa/sucursal/cliente/producto/empleado), aggregation (sum/avg/percentile), source_module, refresh_frequency, confidence_baseline, status.

Por qué importa. Sin este catálogo, una regla puede declarar required_kpis: [KPI-INVENTADO-999] y el parser no lo detecta. El motor evaluador después intenta cargar snapshots y falla en runtime, no en parsing. Convierte un error de configuración en error de producción.

Bloqueo temporal. Mientras FARO-SQL-007 no exista, las validaciones FK contra KPIs son warning en vez de error. La carga de reglas continúa, pero queda registrado en faro.rule_load_warnings. Cuando FARO-SQL-007 se publique, las warnings deberán resolverse y la validación pasa a bloqueante.

KPIs ya referenciados en MVP. Las 11 reglas iniciales usan los siguientes códigos: KPI-SAL-001..006 (ventas, margen, descuentos, ventas/margen/descuento por vendedor), KPI-FIN-001..004 (días de cobro, monto vencido total, días atraso por cliente, monto vencido por cliente), KPI-STK-001..004 (cobertura, valor inmovilizado, rotación, días sin rotación), KPI-PUR-001 (órdenes urgentes), KPI-ACT-001..002 (acciones vencidas, acciones sin evidencia), KPI-DQ-003 (confianza promedio de KPIs críticos). El catálogo debe incluir mínimo estos 16 + los necesarios para TNS-012 a TNS-030.

Acción recomendada. Generar FARO-SQL-007 antes del cierre del MVP. Patrón sugerido: igual que FARO-SQL-004 (catalogo-tensiones-mvp.html), con DDL V027__create_kpi_definitions.sql + seed V028__seed_kpi_definitions_mvp.sql. Después agregar pieza catalogo-kpis-mvp.html al pack NDA con la misma estructura visual que este documento.

12 · Cross-references

Cómo se cruza este DSL con el resto del pack

El YAML no es un producto en sí mismo. Es la capa que conecta el catálogo de tensiones, el motor evaluador, los catálogos de acciones y evidencia, y la bandeja UI. Estos son los puntos de cruce.

Próximos pasos

  1. FARO-SQL-005 · Catálogo Canónico de Acciones. Construir faro.action_definitions con los códigos ACT-* referenciados por las 30 reglas. Permite habilitar validación 03 bloqueante.
  2. FARO-SQL-006 · Catálogo Canónico de Evidencias. Construir faro.evidence_definitions con los códigos EVD-*. Permite habilitar validación 04 bloqueante.
  3. FARO-SQL-007 · Catálogo Canónico de KPIs MVP. PENDIENTE Genera el cuarto catálogo canónico que cierra la cadena. Sin este, la validación 05 queda como warning en lugar de error.
  4. FARO-ENG-002 · Parser DSL YAML. Construir el parser técnico que lee, valida (5 validaciones de sección 10), convierte YAML → JSON y carga en faro.rule_definitions. Sin parser, los YAMLs son documentación.
  5. FARO-ENG-003 · Motor Evaluador MVP. Construir el motor que ejecuta reglas activas, carga snapshots, valida confianza, evalúa condiciones, calcula severidad, crea tensiones y acciones, registra auditoría en faro.rule_evaluations.
  6. FARO-TEST-002 · Tests declarativos. Framework de test que lee el bloque tests: de cada YAML, ejecuta los casos contra el motor y reporta dispara/no-dispara/severidad. Permite TDD declarativo sobre reglas.
  7. Migración del seed Empresa Demo Cuyo S.A. Cargar las 11 reglas MVP en el seed demo (TNS-001 a TNS-011) con valores KPI calibrados para disparar el guion del demo semana 22. Validar que los casos coincidan con el bloque tests: de cada YAML.
  8. UI · Bandeja de Tensiones. Migrar la UI para que cada tensión disparada muestre el código de regla, versión, snapshot evaluado, severidad efectiva y enlace al YAML fuente. Trazabilidad completa de regla → tensión → acción → evidencia.