01 · Resumen ejecutivo

Verdad oficial del MVP, no biblioteca aspiracional

Este catálogo establece 30 tensiones canónicas (TNS-001 a TNS-030) que son la fuente única de verdad para todo el MVP de FARO Connect. Si un código TNS no está acá, no debería existir en ningún módulo del sistema.

FARO maneja dos namespaces de tensiones que coexisten intencionalmente:

  • Catálogo MVP plano (este documento): 30 códigos secuenciales TNS-001..TNS-030 que rigen el motor evaluador, las reglas YAML, la UI de la bandeja, la demo en Empresa Demo Cuyo S.A., los tests de integración y los reportes ejecutivos del primer release.
  • Biblioteca extendida v2 (300 tensiones): universo conceptual completo con códigos compuestos por área/módulo. Vive en biblioteca-tensiones.html y existe para roadmap multi-industria, diagnóstico empresarial y educación de equipos. Mapea bidireccionalmente con el MVP en la sección 10.

La decisión ejecutiva detrás de esta separación: el MVP necesita un set finito, estable y testeado; la biblioteca v2 necesita libertad para crecer sin romper producción. Mezclar ambos namespaces es la receta clásica para que el catálogo se contradiga consigo mismo a los seis meses.

Desde el momento en que este documento queda aprobado, aplica la regla dura: ningún módulo (SQL, YAML, motor, UI, tests, reportes, demo) puede usar un tension_code que no esté listado acá. El parser de reglas YAML rechaza códigos desconocidos con UNKNOWN_TENSION_CODE. El motor evaluador enriquece toda tensión disparada con los metadatos de faro.tension_definitions. La UI no permite nombres hardcodeados.

Lo que parece un detalle técnico es gobierno de producto: cuando FARO diga TNS-001, todos los módulos entienden “Crecimiento no rentable”, con la misma severidad base, el mismo responsable por defecto y la misma dimensión Score afectada. Sin catálogo canónico, el sistema crece torcido y después no se arregla con diseño premium; se arregla con bisturí.

02 · Reglas de gobierno del catálogo

8 reglas que protegen la coherencia del MVP

Antes de tocar cualquier código TNS-NNN, leer estas reglas. Romperlas no se nota en el commit; se nota seis meses después cuando dos módulos interpretan el mismo código distinto.

Regla 01

Código inmutable

Una vez asignado, TNS-001 = Crecimiento no rentable nunca se reutiliza para otra tensión. Reutilizar códigos rompe el histórico, los reportes y los tests.

Regla 02

No se borra, se archiva

Si una tensión deja de usarse, pasa a status = archived. Borrar tensiones rompe las referencias en datos históricos y rule_evaluations.

Regla 03

Versionado semántico

Si cambia la lógica de disparo o el impacto Score, se incrementa version. El tension_code base se mantiene. UNIQUE (tension_code, version).

Regla 04

Tensión ≠ alerta

Alerta = “Descuento alto”. Tensión = “Crecimiento no rentable”. FARO no se llena de alertitas; FARO lee el negocio. Si no es diagnóstico, no entra.

Regla 05

Toda tensión tiene acción

Si recommended_actions está vacío, la tensión no entra al catálogo MVP. Sin acción no hay cierre operativo posible.

Regla 06

Toda acción tiene evidencia

Si evidence_required está vacío, no hay cierre confiable. FARO no premia relatos; premia evidencia documentada.

Regla 07

Toda tensión afecta Score

Aunque sea poco, toda tensión tiene score_impact_min/max ambos negativos. Tensiones que no impactan Score son ruido, no señal.

Regla 08

Catálogo es fuente única

YAML, parser, motor, UI, tests y reportes consumen faro.tension_definitions. Prohibido nombres hardcodeados en código de aplicación.

03 · Estructura oficial

Los 17 campos canónicos por ficha

Toda tensión del MVP cumple este contrato. Es lo que define faro.tension_definitions en SQL y lo que el motor evaluador puede esperar al enriquecer una tensión disparada.

Campo Tipo Descripción Ejemplo (TNS-001)
tension_codetext · uniqueCódigo canónico inmutable. Formato TNS-NNN.TNS-001
nametextNombre canónico ejecutivo de la tensión.Crecimiento no rentable
short_nametext · nullNombre corto para tablas densas y UI compacta.Crecimiento no rentable
area_codetextÁrea funcional principal (commercial, finance, stock, etc.).commercial
module_codetextMódulo FARO específico dentro del área.sales_margin
business_questiontextPregunta ejecutiva que la tensión responde.¿Estamos vendiendo más pero ganando menos?
descriptiontextQué detecta técnicamente (1-2 líneas).Crecimiento de ventas + caída de margen + aumento de descuentos.
executive_diagnosistextLectura ejecutiva al estilo FARO (qué significa para dirección).La empresa crece en volumen pero sacrifica rentabilidad.
trigger_logictextLógica base de disparo en lenguaje natural.Ventas netas suben + margen bruto cae + descuento sube.
required_kpistext[]KPIs necesarios para que la regla pueda evaluarse.KPI-SAL-001 KPI-SAL-002 KPI-SAL-003
recommended_actionstext[]Acciones sugeridas por defecto al dispararse.ACT-COM-001 ACT-COM-002 ACT-COM-003
evidence_requiredtext[]Evidencia mínima para validar cierre.EVD-007 EVD-012
default_severityenumSeveridad base. Valores: low, medium, high, critical.high
severity_logictext · nullCómo escala severidad según condiciones específicas.Alta por defecto; crítica si margen cae bajo umbral.
default_owner_roletextRol responsable por defecto.commercial_manager
approver_roletext · nullRol aprobador por defecto.general_manager
score_dimensiontextDimensión del FARO Score afectada.commercial_health
score_impact_min / maxnumericRango de impacto en Score (siempre negativos).-12 a -8
mvp_priorityenumPrioridad MVP: P1 (demo), P2 (segunda capa), P3 (backlog).P1
demo_relevancebooleanSi debe aparecer en la demo de Empresa Demo Cuyo S.A.true

La tabla muestra los 17 campos lógicos del contrato. La DDL real (sección 7) agrega industry_scope, module_scope, status, version, metadata, timestamps y UUID — todos operativos, no semánticos.

04 · Las 30 tensiones canónicas

TNS-001 a TNS-030 · fichas completas

Una ficha por tensión con código, severidad base, área, owner por defecto, KPIs requeridos, acciones recomendadas, evidencia y rango de impacto Score. Las marcadas DEMO son las que aparecen en la demo de Empresa Demo Cuyo S.A.

TNS-001 DEMO
High

Crecimiento no rentable

Comercial sales_margin P1

Detecta crecimiento de ventas acompañado por caída de margen y aumento de descuentos.

La empresa crece en volumen, pero sacrifica rentabilidad. No es crecimiento sano; es crecimiento comprado con margen.

KPIs
KPI-SAL-001KPI-SAL-002KPI-SAL-003
Acciones
ACT-COM-001ACT-COM-002ACT-COM-003
Evidencia
EVD-007EVD-012
Owner
commercial_manager → general_manager
Score: commercial_health -12 a -8
TNS-002 DEMO
High

Descuento fuera de política

Comercial discounts P1

Detecta descuentos superiores a la política comercial aprobada.

El sistema detecta venta con descuento excesivo. La venta puede verse bien arriba, pero abajo erosiona margen.

KPIs
KPI-SAL-003KPI-SAL-006
Acciones
ACT-COM-001ACT-COM-003
Evidencia
EVD-007EVD-012
Owner
commercial_manager → general_manager
Score: margin_health -9 a -5
TNS-003 DEMO
High

Vendedor erosiona margen

Comercial salespeople P1

Detecta vendedores con alto volumen, descuento elevado y margen inferior al objetivo.

El vendedor cumple volumen, pero no necesariamente cumple negocio. La comisión debe premiar rentabilidad, no solo facturación.

KPIs
KPI-SAL-004KPI-SAL-005KPI-SAL-006
Acciones
ACT-COM-004ACT-COM-002ACT-COM-003
Evidencia
EVD-010EVD-012
Owner
commercial_manager → general_manager
Score: commercial_execution -10 a -6
TNS-004 DEMO
High

Venta sin conversión a caja

Finanzas receivables P1

Detecta crecimiento comercial acompañado de deterioro en cobranza.

La venta no es caja hasta que se cobra. Si la cobranza se estira, el crecimiento puede estar financiando al cliente.

KPIs
KPI-SAL-001KPI-FIN-001KPI-FIN-002
Acciones
ACT-FIN-001ACT-FIN-002ACT-FIN-003
Evidencia
EVD-011EVD-006EVD-012
Owner
finance_manager → general_manager
Score: cash_health -12 a -7
TNS-005 DEMO
High

Mora crítica por cliente

Finanzas customer_risk P1

Detecta clientes con deuda vencida relevante y días de mora superiores al umbral.

No todos los clientes vencidos pesan igual. FARO prioriza los que combinan atraso, monto y riesgo.

KPIs
KPI-FIN-003KPI-FIN-004
Acciones
ACT-FIN-001ACT-FIN-002
Evidencia
EVD-011EVD-006
Owner
finance_manager → general_manager
Score: cash_risk -10 a -6
TNS-006 DEMO
High

Stock crítico en productos de alta rotación

Stock stock_coverage P1

Detecta productos de alta rotación con cobertura insuficiente.

La empresa puede tener ventas, pero si no tiene producto, pierde margen, cliente y reputación. Tres por uno, pero malo.

KPIs
KPI-STK-001KPI-STK-003
Acciones
ACT-STK-001ACT-STK-002
Evidencia
EVD-005EVD-002
Owner
stock_manager → general_manager
Score: stock_health -10 a -6
TNS-007 DEMO
Medium

Stock inmovilizado

Stock stock_rotation P1

Detecta inventario con alto valor y baja o nula rotación.

Stock quieto es caja quieta. Y caja quieta, en empresa en crecimiento, suele ser problema disfrazado de activo.

KPIs
KPI-STK-002KPI-STK-004
Acciones
ACT-STK-003ACT-COM-001
Evidencia
EVD-010EVD-012
Owner
stock_manager → general_manager
Score: stock_cash_health -8 a -4
TNS-008
High

Reposición reactiva

Compras replenishment P1

Detecta compras urgentes recurrentes por falta de planificación de reposición.

Comprar a las corridas casi siempre sale más caro. FARO detecta si la reposición dejó de ser proceso y pasó a ser bombero.

KPIs
KPI-STK-001KPI-PUR-001
Acciones
ACT-STK-001ACT-PUR-001
Evidencia
EVD-005EVD-010
Owner
purchasing_manager → general_manager
Score: stock_execution -9 a -5
TNS-009 DEMO
Medium

Acciones vencidas

Dirección workflow P1

Detecta acciones abiertas con fecha vencida.

Una empresa no mejora por detectar problemas; mejora por cerrar acciones. Lo demás es reunión con PowerPoint.

KPIs
KPI-ACT-001
Acciones
ACT-OPS-001ACT-DIR-001
Evidencia
EVD-004EVD-012
Owner
general_manager → director
Score: execution_health -10 a -4
TNS-010 DEMO
High

Acciones sin evidencia

Dirección evidence P1

Detecta acciones cerradas o avanzadas sin evidencia suficiente.

Sin evidencia no hay cierre. Hay relato. FARO no debería premiar relatos.

KPIs
KPI-ACT-002
Acciones
ACT-OPS-002ACT-DIR-001
Evidencia
EVD-004EVD-012
Owner
general_manager → director
Score: execution_trust -10 a -5
TNS-011
High

Ventas concentradas en pocos clientes

Comercial customer_concentration P2

Detecta concentración excesiva de ventas en pocos clientes.

Vender mucho a pocos puede parecer eficiencia. También puede ser dependencia. FARO debe distinguirlo.

KPIs
KPI-SAL-007KPI-CUS-001
Acciones
ACT-COM-004ACT-DIR-001
Evidencia
EVD-010EVD-012
Owner
commercial_manager → general_manager
Score: commercial_risk-8 a -4
TNS-012
Medium

Ticket promedio cae con ventas estables

Comercial sales_mix P2

Detecta caída del ticket promedio aunque la venta total se mantenga estable.

La facturación puede estar estable, pero con más esfuerzo y menor eficiencia comercial.

KPIs
KPI-SAL-001KPI-SAL-008
Acciones
ACT-COM-001ACT-COM-004
Evidencia
EVD-010
Owner
commercial_manager → general_manager
Score: commercial_health-6 a -3
TNS-013
High

Caída de ventas en sucursal relevante

Comercial branch_sales P2

Detecta caída relevante de ventas en una sucursal con peso comercial.

No alcanza mirar la venta total. Una sucursal puede estar cayendo mientras otra maquilla el promedio.

KPIs
KPI-SAL-009
Acciones
ACT-COM-004ACT-DIR-001
Evidencia
EVD-010
Owner
commercial_manager / branch_manager → general_manager
Score: branch_commercial_health-9 a -5
TNS-014
High

Margen bajo por familia de producto

Comercial product_margin P2

Detecta familias de producto con margen por debajo del objetivo.

El margen promedio puede ocultar familias rotas. FARO debe abrir la caja negra del mix.

KPIs
KPI-SAL-010
Acciones
ACT-COM-001ACT-PUR-001
Evidencia
EVD-010EVD-012
Owner
commercial_manager → general_manager
Score: margin_health-9 a -5
TNS-015
High

Venta crece por descuento, no por demanda sana

Comercial discount_driven_growth P2

Detecta crecimiento de ventas explicado principalmente por aumento de descuentos.

No todo crecimiento es buena noticia. Si la demanda aparece solo cuando se descuenta, el negocio no está creciendo: está cediendo margen.

KPIs
KPI-SAL-001KPI-SAL-003KPI-SAL-011
Acciones
ACT-COM-001ACT-COM-003
Evidencia
EVD-007
Owner
commercial_manager → general_manager
Score: commercial_margin_health-10 a -6
TNS-016
Critical

Cliente supera límite de crédito

Finanzas credit_risk P2

Detecta clientes cuya deuda o exposición supera el límite de crédito.

El crédito sin control es venta que puede transformarse en problema financiero.

KPIs
KPI-FIN-005KPI-FIN-006
Acciones
ACT-FIN-002ACT-FIN-001
Evidencia
EVD-011EVD-012
Owner
finance_manager → general_manager
Score: credit_risk-12 a -7
TNS-017
Medium

Cobranza concentrada en pocos responsables

Finanzas collection_execution P3

Detecta concentración excesiva de gestión de cobranza en pocos responsables.

La cobranza no debe depender de héroes. Los héroes se cansan, se van o se enferman. El sistema debe sostener el proceso.

KPIs
KPI-FIN-007
Acciones
ACT-FIN-003
Evidencia
EVD-010
Owner
finance_manager → general_manager
Score: cash_execution-6 a -3
TNS-018
High

Facturación alta con cobranza parcial

Finanzas billing_collection P2

Detecta facturación relevante con porcentaje de cobranza insuficiente.

Facturar no es cobrar. Repetimos porque parece obvio, pero las empresas se funden igual por olvidarlo.

KPIs
KPI-SAL-001KPI-FIN-008
Acciones
ACT-FIN-001ACT-FIN-003
Evidencia
EVD-011
Owner
finance_manager → general_manager
Score: cash_health-10 a -6
TNS-019
Critical

Caja proyectada insuficiente

Finanzas cash_projection P2

Detecta insuficiencia de caja proyectada frente a compromisos.

Si la caja proyectada no alcanza, todo lo demás se vuelve secundario. La caja manda. Feo, antiguo y cierto.

KPIs
KPI-FIN-009KPI-FIN-010
Acciones
ACT-FIN-003ACT-DIR-001
Evidencia
EVD-010EVD-012
Owner
finance_manager → director
Score: cash_risk-14 a -8
TNS-020
High

Mora creciente sin gestión registrada

Finanzas collection_actions P2

Detecta crecimiento de mora sin acciones registradas de cobranza.

El problema no es solo que la mora suba. El problema serio es que suba y no haya gestión documentada.

KPIs
KPI-FIN-002KPI-ACT-003
Acciones
ACT-FIN-001ACT-OPS-002
Evidencia
EVD-011
Owner
finance_manager → general_manager
Score: cash_execution-10 a -6
TNS-021
High

Quiebre de stock con ventas perdidas

Stock lost_sales P2

Detecta quiebres de stock asociados a demanda o ventas perdidas estimadas.

El faltante de stock no solo es operativo: es venta perdida. Y la venta perdida no aparece en el ERP si nadie la registra.

KPIs
KPI-STK-005KPI-SAL-012
Acciones
ACT-STK-001ACT-STK-002
Evidencia
EVD-005
Owner
stock_manager → general_manager
Score: stock_commercial_health-10 a -6
TNS-022
High

Compra con costo creciente y margen sin ajustar

Compras cost_margin P2

Detecta aumento de costo de compra sin ajuste comercial suficiente.

Si el costo sube y el precio no responde, el margen paga la fiesta.

KPIs
KPI-PUR-002KPI-SAL-002
Acciones
ACT-PUR-001ACT-COM-001
Evidencia
EVD-010EVD-012
Owner
purchasing_manager → general_manager
Score: margin_purchasing_health-11 a -6
TNS-023
High

Proveedor crítico con demora recurrente

Compras supplier_risk P2

Detecta proveedores estratégicos con demoras repetidas que impactan stock.

Un proveedor que falla seguido no es “un proveedor complicado”; es un riesgo de negocio.

KPIs
KPI-PUR-003KPI-STK-001
Acciones
ACT-PUR-001ACT-DIR-001
Evidencia
EVD-010
Owner
purchasing_manager → general_manager
Score: supplier_stock_risk-9 a -5
TNS-024
High

Stock alto con deuda a proveedores

Finanzas / Stock stock_supplier_debt P2

Detecta stock elevado junto con deuda relevante a proveedores.

Tener stock no es malo. Tener stock que no rota mientras se debe a proveedores, sí.

KPIs
KPI-STK-002KPI-PUR-004
Acciones
ACT-STK-003ACT-DIR-001
Evidencia
EVD-010EVD-012
Owner
general_manager → director
Score: cash_stock_health-10 a -6
TNS-025
Medium

Reposición sin rotación suficiente

Compras purchase_rotation P3

Detecta reposiciones en productos con rotación insuficiente.

Comprar más de lo que no rota no es abastecimiento; es acumulación con factura.

KPIs
KPI-PUR-005KPI-STK-004
Acciones
ACT-PUR-001ACT-STK-003
Evidencia
EVD-010
Owner
purchasing_manager → general_manager
Score: stock_purchasing_health-7 a -4
TNS-026 DEMO
High

Dirección sin visibilidad semanal

Dirección executive_visibility P1

Detecta ausencia de reportes, KPIs o visibilidad ejecutiva semanal.

Una empresa sin visibilidad semanal dirige mirando por el espejo retrovisor. A veces funciona. Hasta que deja de funcionar.

KPIs
KPI-DQ-001KPI-REP-001
Acciones
ACT-DQ-001ACT-DIR-001
Evidencia
EVD-010EVD-012
Owner
general_manager → director
Score: direction_data_health-9 a -5
TNS-027 DEMO
High

Fuente crítica atrasada

Datos data_sources P1

Detecta fuentes de datos críticas atrasadas o no actualizadas.

Si la fuente no llega, el diagnóstico puede quedar viejo. Y dato viejo con estética moderna sigue siendo dato viejo.

KPIs
KPI-DQ-002
Acciones
ACT-DQ-001
Evidencia
EVD-002EVD-004
Owner
data_owner → general_manager
Score: data_quality-8 a -4
TNS-028 DEMO
High

Baja confianza de dato en KPI crítico

Datos kpi_confidence P1

Detecta KPIs críticos con confianza de dato menor al umbral.

No todo KPI merece el mismo nivel de confianza. FARO debe decir no solo qué mide, sino cuánto cree en lo que mide.

KPIs
KPI-DQ-003
Acciones
ACT-DQ-001
Evidencia
EVD-002EVD-004
Owner
data_owner → general_manager
Score: data_trust-10 a -5
TNS-029 DEMO
Critical

Responsable no asignado en acción crítica

Dirección governance P1

Detecta acciones o tensiones críticas sin responsable asignado.

Problema crítico sin responsable es problema que no existe para nadie. Hasta que explota.

KPIs
KPI-ACT-004
Acciones
ACT-OPS-001ACT-DIR-001
Evidencia
EVD-004
Owner
general_manager → director
Score: governance_execution-10 a -6
TNS-030 DEMO
Critical

Tensión crítica reincidente

Dirección learning P1

Detecta tensiones críticas repetidas en períodos consecutivos o recurrentes.

Una tensión crítica una vez puede ser gestión. Dos veces puede ser descuido. Tres veces ya es sistema roto.

KPIs
KPI-TNS-001
Acciones
ACT-DIR-001ACT-OPS-002
Evidencia
EVD-010EVD-012
Owner
general_manager → director
Score: learning_execution-15 a -8
05 · Prioridad MVP

P1 demo · P2 segunda capa · P3 backlog

Las 30 tensiones no se activan todas el día uno. Se escalonan por prioridad para que la demo sea sólida sin abrir frentes operativos imposibles de sostener.

P3 · BACKLOG 2

Disponibles en catálogo

Las 2 tensiones marcadas con mvp_priority = 'P3'. Existen en catálogo pero no se activan por defecto en el primer release; se prenden cuando el cliente las pide o cuando la madurez operativa las hace relevantes.

Nota. Los conteos coinciden literalmente con el seed SQL-004 V026 (15 P1 + 13 P2 + 2 P3 = 30). El set de demo (14 tensiones) se controla por demo_relevance, no por mvp_priority: la única P1 fuera de la demo es TNS-008 (Reposición reactiva).

06 · Tabla maestra resumida

Vista pájaro · 30 tensiones en una grilla

Densidad alta, lectura rápida. Útil para vista directiva, control de cobertura y validación contra reglas YAML.

Código Tensión Área Severidad Owner Score dimensión Impacto Prio Demo
TNS-001Crecimiento no rentableComercialHighcommercial_managercommercial_health-12 a -8P1
TNS-002Descuento fuera de políticaComercialHighcommercial_managermargin_health-9 a -5P1
TNS-003Vendedor erosiona margenComercialHighcommercial_managercommercial_execution-10 a -6P1
TNS-004Venta sin conversión a cajaFinanzasHighfinance_managercash_health-12 a -7P1
TNS-005Mora crítica por clienteFinanzasHighfinance_managercash_risk-10 a -6P1
TNS-006Stock crítico alta rotaciónStockHighstock_managerstock_health-10 a -6P1
TNS-007Stock inmovilizadoStockMediumstock_managerstock_cash_health-8 a -4P1
TNS-008Reposición reactivaComprasHighpurchasing_managerstock_execution-9 a -5P1No
TNS-009Acciones vencidasDirecciónMediumgeneral_managerexecution_health-10 a -4P1
TNS-010Acciones sin evidenciaDirecciónHighgeneral_managerexecution_trust-10 a -5P1
TNS-011Ventas concentradasComercialHighcommercial_managercommercial_risk-8 a -4P2No
TNS-012Ticket promedio caeComercialMediumcommercial_managercommercial_health-6 a -3P2No
TNS-013Caída ventas sucursalComercialHighcommercial_managerbranch_commercial_health-9 a -5P2No
TNS-014Margen bajo por familiaComercialHighcommercial_managermargin_health-9 a -5P2No
TNS-015Venta crece por descuentoComercialHighcommercial_managercommercial_margin_health-10 a -6P2No
TNS-016Cliente supera créditoFinanzasCriticalfinance_managercredit_risk-12 a -7P2No
TNS-017Cobranza concentradaFinanzasMediumfinance_managercash_execution-6 a -3P3No
TNS-018Facturación alta cobranza parcialFinanzasHighfinance_managercash_health-10 a -6P2No
TNS-019Caja proyectada insuficienteFinanzasCriticalfinance_managercash_risk-14 a -8P2No
TNS-020Mora sin gestiónFinanzasHighfinance_managercash_execution-10 a -6P2No
TNS-021Quiebre con ventas perdidasStockHighstock_managerstock_commercial_health-10 a -6P2No
TNS-022Costo sube, margen no ajustaComprasHighpurchasing_managermargin_purchasing_health-11 a -6P2No
TNS-023Proveedor crítico demoraComprasHighpurchasing_managersupplier_stock_risk-9 a -5P2No
TNS-024Stock alto con deuda proveedoresFinanzas/StockHighgeneral_managercash_stock_health-10 a -6P2No
TNS-025Reposición sin rotaciónComprasMediumpurchasing_managerstock_purchasing_health-7 a -4P3No
TNS-026Sin visibilidad semanalDirecciónHighgeneral_managerdirection_data_health-9 a -5P1
TNS-027Fuente crítica atrasadaDatosHighdata_ownerdata_quality-8 a -4P1
TNS-028Baja confianza KPIDatosHighdata_ownerdata_trust-10 a -5P1
TNS-029Responsable no asignadoDirecciónCriticalgeneral_managergovernance_execution-10 a -6P1
TNS-030Tensión crítica reincidenteDirecciónCriticalgeneral_managerlearning_execution-15 a -8P1
07 · DDL SQL · V025

faro.tension_definitions · estructura física

Migración V025__create_tension_definitions.sql. PostgreSQL 15+. Define la tabla canónica, sus constraints e índices. El seed (V026) carga sobre esta estructura.

▸ V025__create_tension_definitions.sql · PostgreSQL 15+
-- ============================================================
-- FARO-SQL-004 · V025__create_tension_definitions.sql
-- Catálogo canónico de tensiones FARO MVP
-- ============================================================

CREATE TABLE IF NOT EXISTS faro.tension_definitions (
  tension_definition_id   uuid PRIMARY KEY DEFAULT gen_random_uuid(),

  tension_code            text NOT NULL,
  name                    text NOT NULL,
  short_name              text NULL,

  area_code               text NOT NULL,
  module_code             text NOT NULL,

  business_question       text NOT NULL,
  description             text NOT NULL,
  executive_diagnosis     text NOT NULL,

  trigger_logic           text NOT NULL,

  required_kpis           text[] NOT NULL DEFAULT ARRAY[]::text[],
  recommended_actions     text[] NOT NULL DEFAULT ARRAY[]::text[],
  evidence_required       text[] NOT NULL DEFAULT ARRAY[]::text[],

  default_severity        text NOT NULL CHECK (
    default_severity IN ('low', 'medium', 'high', 'critical')
  ),
  severity_logic          text NULL,

  default_owner_role      text NOT NULL,
  approver_role           text NULL,

  score_dimension         text NOT NULL,
  score_impact_min        numeric(9,4) NOT NULL,
  score_impact_max        numeric(9,4) NOT NULL,

  mvp_priority            text NOT NULL CHECK (
    mvp_priority IN ('P1', 'P2', 'P3')
  ),
  demo_relevance          boolean NOT NULL DEFAULT false,

  industry_scope          text[] NOT NULL DEFAULT ARRAY['all']::text[],
  module_scope            text[] NOT NULL DEFAULT ARRAY[]::text[],

  status                  text NOT NULL DEFAULT 'active' CHECK (
    status IN ('active', 'inactive', 'archived')
  ),
  version                 integer NOT NULL DEFAULT 1,
  metadata                jsonb NOT NULL DEFAULT '{}'::jsonb,

  created_at              timestamptz NOT NULL DEFAULT now(),
  updated_at              timestamptz NOT NULL DEFAULT now(),

  CONSTRAINT uq_tension_definitions_code_version UNIQUE (tension_code, version)
);

-- Un solo activo por código (más flexible que UNIQUE compuesto)
CREATE UNIQUE INDEX IF NOT EXISTS uq_tension_definitions_one_active_per_code
ON faro.tension_definitions (tension_code)
WHERE status = 'active';

-- Índices de búsqueda y filtrado
CREATE INDEX IF NOT EXISTS idx_tension_definitions_code     ON faro.tension_definitions (tension_code);
CREATE INDEX IF NOT EXISTS idx_tension_definitions_area     ON faro.tension_definitions (area_code);
CREATE INDEX IF NOT EXISTS idx_tension_definitions_module   ON faro.tension_definitions (module_code);
CREATE INDEX IF NOT EXISTS idx_tension_definitions_priority ON faro.tension_definitions (mvp_priority);
CREATE INDEX IF NOT EXISTS idx_tension_definitions_demo     ON faro.tension_definitions (demo_relevance);
CREATE INDEX IF NOT EXISTS idx_tension_definitions_status   ON faro.tension_definitions (status);

-- GIN para búsqueda en arrays
CREATE INDEX IF NOT EXISTS idx_tension_definitions_required_kpis ON faro.tension_definitions USING gin (required_kpis);
CREATE INDEX IF NOT EXISTS idx_tension_definitions_actions       ON faro.tension_definitions USING gin (recommended_actions);
CREATE INDEX IF NOT EXISTS idx_tension_definitions_evidence      ON faro.tension_definitions USING gin (evidence_required);

COMMENT ON TABLE faro.tension_definitions IS
'Catálogo canónico de tensiones FARO. Define los códigos TNS oficiales usados por reglas, motor, UI, tests y reportes.';

Decisión técnica: se reemplaza la constraint UNIQUE (tension_code, status) propuesta inicialmente por un UNIQUE INDEX parcial filtrado por status = 'active'. Permite múltiples versiones archivadas con el mismo código sin romper la regla de “un solo activo por código”.

08 · Seed SQL · V026

30 INSERT idempotentes con upsert

Migración V026__seed_tension_definitions_mvp.sql. Carga TNS-001 a TNS-030 con ON CONFLICT (tension_code, version) DO UPDATE para re-ejecución segura.

▸ V026__seed_tension_definitions_mvp.sql · 30 INSERTs · BEGIN/COMMIT · upsert idempotente
-- ============================================================
-- FARO-SQL-004 · V026__seed_tension_definitions_mvp.sql
-- Seed Catálogo Canónico de Tensiones MVP FARO
-- Version: v1.0
-- ============================================================

BEGIN;

INSERT INTO faro.tension_definitions (
  tension_code,
  name,
  short_name,
  area_code,
  module_code,
  business_question,
  description,
  executive_diagnosis,
  trigger_logic,
  required_kpis,
  recommended_actions,
  evidence_required,
  default_severity,
  severity_logic,
  default_owner_role,
  approver_role,
  score_dimension,
  score_impact_min,
  score_impact_max,
  mvp_priority,
  demo_relevance,
  industry_scope,
  module_scope,
  status,
  version,
  metadata
)
VALUES

-- ============================================================
-- TNS-001
-- ============================================================

(
  'TNS-001',
  'Crecimiento no rentable',
  'Crecimiento no rentable',
  'commercial',
  'sales_margin',
  '¿Estamos vendiendo más pero ganando menos?',
  'Detecta crecimiento de ventas acompañado por caída de margen y aumento de descuentos.',
  'La empresa crece en volumen, pero sacrifica rentabilidad. No es crecimiento sano; es crecimiento comprado con margen.',
  'Ventas netas suben + margen bruto cae + descuento promedio sube.',
  ARRAY['KPI-SAL-001','KPI-SAL-002','KPI-SAL-003'],
  ARRAY['ACT-COM-001','ACT-COM-002','ACT-COM-003'],
  ARRAY['EVD-007','EVD-012'],
  'high',
  'Alta por defecto; crítica si margen cae debajo del umbral y descuento supera política.',
  'commercial_manager',
  'general_manager',
  'commercial_health',
  -12,
  -8,
  'P1',
  true,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['sales','margin','discounts'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-002
-- ============================================================

(
  'TNS-002',
  'Descuento fuera de política',
  'Descuento fuera de política',
  'commercial',
  'discounts',
  '¿Se están otorgando descuentos por encima de lo permitido?',
  'Detecta descuentos superiores a la política comercial aprobada.',
  'El sistema detecta venta con descuento excesivo. La venta puede verse bien arriba, pero abajo erosiona margen.',
  'Descuento promedio o descuento por operación supera umbral autorizado.',
  ARRAY['KPI-SAL-003','KPI-SAL-006'],
  ARRAY['ACT-COM-001','ACT-COM-003'],
  ARRAY['EVD-007','EVD-012'],
  'high',
  'Alta; crítica si el descuento supera fuertemente el umbral o afecta productos críticos.',
  'commercial_manager',
  'general_manager',
  'margin_health',
  -9,
  -5,
  'P1',
  true,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['sales','discounts'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-003
-- ============================================================

(
  'TNS-003',
  'Vendedor erosiona margen',
  'Vendedor erosiona margen',
  'commercial',
  'salespeople',
  '¿Hay vendedores que venden mucho pero destruyen rentabilidad?',
  'Detecta vendedores con alto volumen, descuento elevado y margen inferior al objetivo.',
  'El vendedor cumple volumen, pero no necesariamente cumple negocio. La comisión debe premiar rentabilidad, no solo facturación.',
  'Venta por vendedor alta + margen bajo + descuento alto.',
  ARRAY['KPI-SAL-004','KPI-SAL-005','KPI-SAL-006'],
  ARRAY['ACT-COM-004','ACT-COM-002','ACT-COM-003'],
  ARRAY['EVD-010','EVD-012'],
  'high',
  'Alta; crítica si el vendedor concentra volumen relevante y margen muy bajo.',
  'commercial_manager',
  'general_manager',
  'commercial_execution',
  -10,
  -6,
  'P1',
  true,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['sales','margin','discounts','employees'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-004
-- ============================================================

(
  'TNS-004',
  'Venta sin conversión a caja',
  'Venta sin caja',
  'finance',
  'receivables',
  '¿Estamos vendiendo pero no cobrando?',
  'Detecta crecimiento comercial acompañado de deterioro en cobranza.',
  'La venta no es caja hasta que se cobra. Si la cobranza se estira, el crecimiento puede estar financiando al cliente.',
  'Ventas suben + días de cobranza suben + mora vencida sube.',
  ARRAY['KPI-SAL-001','KPI-FIN-001','KPI-FIN-002'],
  ARRAY['ACT-FIN-001','ACT-FIN-002','ACT-FIN-003'],
  ARRAY['EVD-011','EVD-006','EVD-012'],
  'high',
  'Alta; crítica si la mora crece fuerte o supera umbral de caja.',
  'finance_manager',
  'general_manager',
  'cash_health',
  -12,
  -7,
  'P1',
  true,
  ARRAY['commercial','retail','distribution','services','construction_supplies'],
  ARRAY['sales','receivables','finance'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-005
-- ============================================================

(
  'TNS-005',
  'Mora crítica por cliente',
  'Mora crítica cliente',
  'finance',
  'customer_risk',
  '¿Qué cliente está poniendo en riesgo la caja?',
  'Detecta clientes con deuda vencida relevante y días de mora superiores al umbral.',
  'No todos los clientes vencidos pesan igual. FARO prioriza los que combinan atraso, monto y riesgo.',
  'Días vencidos altos + monto abierto vencido alto.',
  ARRAY['KPI-FIN-003','KPI-FIN-004'],
  ARRAY['ACT-FIN-001','ACT-FIN-002'],
  ARRAY['EVD-011','EVD-006'],
  'high',
  'Alta; crítica si supera monto y días críticos.',
  'finance_manager',
  'general_manager',
  'cash_risk',
  -10,
  -6,
  'P1',
  true,
  ARRAY['commercial','distribution','services','construction_supplies'],
  ARRAY['receivables','customers','finance'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-006
-- ============================================================

(
  'TNS-006',
  'Stock crítico en productos de alta rotación',
  'Stock crítico alta rotación',
  'stock',
  'stock_coverage',
  '¿Estamos por perder ventas por falta de stock?',
  'Detecta productos de alta rotación con cobertura insuficiente.',
  'La empresa puede tener ventas, pero si no tiene producto, pierde margen, cliente y reputación.',
  'Cobertura baja + producto de alta demanda.',
  ARRAY['KPI-STK-001','KPI-STK-003'],
  ARRAY['ACT-STK-001','ACT-STK-002'],
  ARRAY['EVD-005','EVD-002'],
  'high',
  'Alta; crítica si la cobertura es menor a 3 días o hay ventas pendientes.',
  'stock_manager',
  'general_manager',
  'stock_health',
  -10,
  -6,
  'P1',
  true,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['stock','sales'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-007
-- ============================================================

(
  'TNS-007',
  'Stock inmovilizado',
  'Stock inmovilizado',
  'stock',
  'stock_rotation',
  '¿Tenemos capital dormido en mercadería que no rota?',
  'Detecta inventario con alto valor y baja o nula rotación.',
  'Stock quieto es caja quieta. Y caja quieta, en empresa en crecimiento, suele ser problema disfrazado de activo.',
  'Valor de stock inmovilizado alto + días sin movimiento elevados.',
  ARRAY['KPI-STK-002','KPI-STK-004'],
  ARRAY['ACT-STK-003','ACT-COM-001'],
  ARRAY['EVD-010','EVD-012'],
  'medium',
  'Media; alta si el monto inmovilizado supera umbral crítico.',
  'stock_manager',
  'general_manager',
  'stock_cash_health',
  -8,
  -4,
  'P1',
  true,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['stock','sales','finance'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-008
-- ============================================================

(
  'TNS-008',
  'Reposición reactiva',
  'Reposición reactiva',
  'purchasing',
  'replenishment',
  '¿Estamos comprando tarde y por urgencia?',
  'Detecta compras urgentes recurrentes por falta de planificación de reposición.',
  'Comprar a las corridas casi siempre sale más caro. FARO detecta si la reposición dejó de ser proceso y pasó a ser bombero.',
  'Productos críticos + compras urgentes repetidas.',
  ARRAY['KPI-STK-001','KPI-PUR-001'],
  ARRAY['ACT-STK-001','ACT-PUR-001'],
  ARRAY['EVD-005','EVD-010'],
  'high',
  'Alta; crítica si ocurre en productos estratégicos o con sobrecostos.',
  'purchasing_manager',
  'general_manager',
  'stock_execution',
  -9,
  -5,
  'P1',
  false,
  ARRAY['commercial','retail','distribution','construction_supplies','manufacturing'],
  ARRAY['stock','purchasing'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-009
-- ============================================================

(
  'TNS-009',
  'Acciones vencidas',
  'Acciones vencidas',
  'direction',
  'workflow',
  '¿Estamos perdiendo disciplina de ejecución?',
  'Detecta acciones abiertas con fecha vencida.',
  'Una empresa no mejora por detectar problemas; mejora por cerrar acciones. Lo demás es reunión con PowerPoint.',
  'Cantidad de acciones vencidas mayor a cero.',
  ARRAY['KPI-ACT-001'],
  ARRAY['ACT-OPS-001','ACT-DIR-001'],
  ARRAY['EVD-004','EVD-012'],
  'medium',
  'Media; alta desde 3 vencidas; crítica desde 7 o si son críticas.',
  'general_manager',
  'director',
  'execution_health',
  -10,
  -4,
  'P1',
  true,
  ARRAY['all'],
  ARRAY['actions','workflow'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-010
-- ============================================================

(
  'TNS-010',
  'Acciones sin evidencia',
  'Acciones sin evidencia',
  'direction',
  'evidence',
  '¿Se están cerrando acciones sin prueba real?',
  'Detecta acciones cerradas o avanzadas sin evidencia suficiente.',
  'Sin evidencia no hay cierre. Hay relato. FARO no debería premiar relatos.',
  'Acciones en cierre o verificación sin evidencia aprobada.',
  ARRAY['KPI-ACT-002'],
  ARRAY['ACT-OPS-002','ACT-DIR-001'],
  ARRAY['EVD-004','EVD-012'],
  'high',
  'Alta; crítica si afecta acciones críticas o reincidentes.',
  'general_manager',
  'director',
  'execution_trust',
  -10,
  -5,
  'P1',
  true,
  ARRAY['all'],
  ARRAY['actions','evidence','workflow'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-011
-- ============================================================

(
  'TNS-011',
  'Ventas concentradas en pocos clientes',
  'Ventas concentradas',
  'commercial',
  'customer_concentration',
  '¿Dependemos demasiado de pocos clientes?',
  'Detecta concentración excesiva de ventas en pocos clientes.',
  'Vender mucho a pocos puede parecer eficiencia. También puede ser dependencia. FARO debe distinguirlo.',
  'Top clientes concentran porcentaje alto de ventas.',
  ARRAY['KPI-SAL-007','KPI-CUS-001'],
  ARRAY['ACT-COM-004','ACT-DIR-001'],
  ARRAY['EVD-010','EVD-012'],
  'high',
  'Alta si supera 40%; crítica si supera 60%.',
  'commercial_manager',
  'general_manager',
  'commercial_risk',
  -8,
  -4,
  'P2',
  false,
  ARRAY['commercial','retail','distribution','services'],
  ARRAY['sales','customers','risk'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-012
-- ============================================================

(
  'TNS-012',
  'Ticket promedio cae con ventas estables',
  'Ticket cae',
  'commercial',
  'sales_mix',
  '¿Estamos vendiendo más operaciones de menor valor?',
  'Detecta caída del ticket promedio aunque la venta total se mantenga estable.',
  'La facturación puede estar estable, pero con más esfuerzo y menor eficiencia comercial.',
  'Ventas estables + ticket promedio cae.',
  ARRAY['KPI-SAL-001','KPI-SAL-008'],
  ARRAY['ACT-COM-001','ACT-COM-004'],
  ARRAY['EVD-010'],
  'medium',
  'Media; alta si afecta margen o productividad comercial.',
  'commercial_manager',
  'general_manager',
  'commercial_health',
  -6,
  -3,
  'P2',
  false,
  ARRAY['commercial','retail','distribution'],
  ARRAY['sales','ticket','commercial_mix'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-013
-- ============================================================

(
  'TNS-013',
  'Caída de ventas en sucursal relevante',
  'Caída sucursal',
  'commercial',
  'branch_sales',
  '¿Qué sucursal está perdiendo ritmo?',
  'Detecta caída relevante de ventas en una sucursal con peso comercial.',
  'No alcanza mirar la venta total. Una sucursal puede estar cayendo mientras otra maquilla el promedio.',
  'Sucursal relevante cae más que el umbral.',
  ARRAY['KPI-SAL-009'],
  ARRAY['ACT-COM-004','ACT-DIR-001'],
  ARRAY['EVD-010'],
  'high',
  'Alta si la sucursal tiene peso relevante; crítica si la caída se sostiene.',
  'commercial_manager',
  'general_manager',
  'branch_commercial_health',
  -9,
  -5,
  'P2',
  false,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['sales','branches'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-014
-- ============================================================

(
  'TNS-014',
  'Margen bajo por familia de producto',
  'Margen bajo familia',
  'commercial',
  'product_margin',
  '¿Qué familia vende con margen insuficiente?',
  'Detecta familias de producto con margen por debajo del objetivo.',
  'El margen promedio puede ocultar familias rotas. FARO debe abrir la caja negra del mix.',
  'Margen por familia menor al umbral.',
  ARRAY['KPI-SAL-010'],
  ARRAY['ACT-COM-001','ACT-PUR-001'],
  ARRAY['EVD-010','EVD-012'],
  'high',
  'Alta; crítica si la familia representa alto volumen.',
  'commercial_manager',
  'general_manager',
  'margin_health',
  -9,
  -5,
  'P2',
  false,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['sales','margin','products'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-015
-- ============================================================

(
  'TNS-015',
  'Venta crece por descuento, no por demanda sana',
  'Crecimiento por descuento',
  'commercial',
  'discount_driven_growth',
  '¿El crecimiento es real o comprado con descuentos?',
  'Detecta crecimiento de ventas explicado principalmente por aumento de descuentos.',
  'No todo crecimiento es buena noticia. Si la demanda aparece solo cuando se descuenta, el negocio no está creciendo: está cediendo margen.',
  'Ventas suben + descuentos suben fuerte + unidades o clientes no crecen proporcionalmente.',
  ARRAY['KPI-SAL-001','KPI-SAL-003','KPI-SAL-011'],
  ARRAY['ACT-COM-001','ACT-COM-003'],
  ARRAY['EVD-007'],
  'high',
  'Alta; crítica si afecta margen y caja.',
  'commercial_manager',
  'general_manager',
  'commercial_margin_health',
  -10,
  -6,
  'P2',
  false,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['sales','discounts','demand'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-016
-- ============================================================

(
  'TNS-016',
  'Cliente supera límite de crédito',
  'Cliente supera crédito',
  'finance',
  'credit_risk',
  '¿Estamos vendiendo a crédito por encima del límite autorizado?',
  'Detecta clientes cuya deuda o exposición supera el límite de crédito.',
  'El crédito sin control es venta que puede transformarse en problema financiero.',
  'Exposición actual dividida por límite de crédito mayor o igual a 100%.',
  ARRAY['KPI-FIN-005','KPI-FIN-006'],
  ARRAY['ACT-FIN-002','ACT-FIN-001'],
  ARRAY['EVD-011','EVD-012'],
  'critical',
  'Crítica por defecto si supera el límite.',
  'finance_manager',
  'general_manager',
  'credit_risk',
  -12,
  -7,
  'P2',
  false,
  ARRAY['commercial','distribution','services','construction_supplies'],
  ARRAY['credit','customers','receivables'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-017
-- ============================================================

(
  'TNS-017',
  'Cobranza concentrada en pocos responsables',
  'Cobranza concentrada',
  'finance',
  'collection_execution',
  '¿La cobranza depende demasiado de una persona o equipo?',
  'Detecta concentración excesiva de gestión de cobranza en pocos responsables.',
  'La cobranza no debe depender de héroes. Los héroes se cansan, se van o se enferman. El sistema debe sostener el proceso.',
  'Alta proporción de cartera asignada a pocos responsables.',
  ARRAY['KPI-FIN-007'],
  ARRAY['ACT-FIN-003'],
  ARRAY['EVD-010'],
  'medium',
  'Media; alta si hay mora creciente.',
  'finance_manager',
  'general_manager',
  'cash_execution',
  -6,
  -3,
  'P3',
  false,
  ARRAY['commercial','distribution','services'],
  ARRAY['receivables','collections','users'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-018
-- ============================================================

(
  'TNS-018',
  'Facturación alta con cobranza parcial',
  'Facturación alta cobranza parcial',
  'finance',
  'billing_collection',
  '¿Estamos facturando pero cobrando parcialmente?',
  'Detecta facturación relevante con porcentaje de cobranza insuficiente.',
  'Facturar no es cobrar. Repetimos porque parece obvio, pero las empresas se funden igual por olvidarlo.',
  'Ventas altas + ratio cobrado sobre facturado bajo.',
  ARRAY['KPI-SAL-001','KPI-FIN-008'],
  ARRAY['ACT-FIN-001','ACT-FIN-003'],
  ARRAY['EVD-011'],
  'high',
  'Alta; crítica si compromete caja proyectada.',
  'finance_manager',
  'general_manager',
  'cash_health',
  -10,
  -6,
  'P2',
  false,
  ARRAY['commercial','distribution','services','construction_supplies'],
  ARRAY['sales','billing','collections'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-019
-- ============================================================

(
  'TNS-019',
  'Caja proyectada insuficiente',
  'Caja insuficiente',
  'finance',
  'cash_projection',
  '¿La caja proyectada alcanza para las obligaciones próximas?',
  'Detecta insuficiencia de caja proyectada frente a compromisos.',
  'Si la caja proyectada no alcanza, todo lo demás se vuelve secundario. La caja manda. Feo, antiguo y cierto.',
  'Días de caja bajos + obligaciones próximas altas.',
  ARRAY['KPI-FIN-009','KPI-FIN-010'],
  ARRAY['ACT-FIN-003','ACT-DIR-001'],
  ARRAY['EVD-010','EVD-012'],
  'critical',
  'Crítica si caja proyectada no cubre obligaciones esenciales.',
  'finance_manager',
  'director',
  'cash_risk',
  -14,
  -8,
  'P2',
  false,
  ARRAY['commercial','distribution','services','manufacturing','construction_supplies'],
  ARRAY['cash','finance','payments'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-020
-- ============================================================

(
  'TNS-020',
  'Mora creciente sin gestión registrada',
  'Mora sin gestión',
  'finance',
  'collection_actions',
  '¿La mora sube y nadie está gestionando?',
  'Detecta crecimiento de mora sin acciones registradas de cobranza.',
  'El problema no es solo que la mora suba. El problema serio es que suba y no haya gestión documentada.',
  'Mora sube + no hay acciones o evidencias de gestión.',
  ARRAY['KPI-FIN-002','KPI-ACT-003'],
  ARRAY['ACT-FIN-001','ACT-OPS-002'],
  ARRAY['EVD-011'],
  'high',
  'Alta; crítica si involucra clientes críticos.',
  'finance_manager',
  'general_manager',
  'cash_execution',
  -10,
  -6,
  'P2',
  false,
  ARRAY['commercial','distribution','services','construction_supplies'],
  ARRAY['receivables','actions','evidence'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-021
-- ============================================================

(
  'TNS-021',
  'Quiebre de stock con ventas perdidas',
  'Quiebre con ventas perdidas',
  'stock',
  'lost_sales',
  '¿Cuánto estamos dejando de vender por no tener stock?',
  'Detecta quiebres de stock asociados a demanda o ventas perdidas estimadas.',
  'El faltante de stock no solo es operativo: es venta perdida. Y la venta perdida no aparece en el ERP si nadie la registra.',
  'Stock cero o crítico + demanda registrada o venta perdida estimada.',
  ARRAY['KPI-STK-005','KPI-SAL-012'],
  ARRAY['ACT-STK-001','ACT-STK-002'],
  ARRAY['EVD-005'],
  'high',
  'Alta; crítica si afecta productos estratégicos.',
  'stock_manager',
  'general_manager',
  'stock_commercial_health',
  -10,
  -6,
  'P2',
  false,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['stock','sales','lost_sales'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-022
-- ============================================================

(
  'TNS-022',
  'Compra con costo creciente y margen sin ajustar',
  'Costo sube margen no ajusta',
  'purchasing',
  'cost_margin',
  '¿Subieron los costos y no ajustamos precios o margen?',
  'Detecta aumento de costo de compra sin ajuste comercial suficiente.',
  'Si el costo sube y el precio no responde, el margen paga la fiesta.',
  'Costo sube + margen cae o precio no acompaña.',
  ARRAY['KPI-PUR-002','KPI-SAL-002'],
  ARRAY['ACT-PUR-001','ACT-COM-001'],
  ARRAY['EVD-010','EVD-012'],
  'high',
  'Alta; crítica si afecta productos de alta rotación.',
  'purchasing_manager',
  'general_manager',
  'margin_purchasing_health',
  -11,
  -6,
  'P2',
  false,
  ARRAY['commercial','retail','distribution','manufacturing','construction_supplies'],
  ARRAY['purchasing','costs','margin'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-023
-- ============================================================

(
  'TNS-023',
  'Proveedor crítico con demora recurrente',
  'Proveedor crítico demora',
  'purchasing',
  'supplier_risk',
  '¿Un proveedor está generando riesgo operativo recurrente?',
  'Detecta proveedores estratégicos con demoras repetidas que impactan stock.',
  'Un proveedor que falla seguido no es un proveedor complicado; es un riesgo de negocio.',
  'Demoras proveedor + stock crítico asociado.',
  ARRAY['KPI-PUR-003','KPI-STK-001'],
  ARRAY['ACT-PUR-001','ACT-DIR-001'],
  ARRAY['EVD-010'],
  'high',
  'Alta; crítica si afecta productos críticos o ventas perdidas.',
  'purchasing_manager',
  'general_manager',
  'supplier_stock_risk',
  -9,
  -5,
  'P2',
  false,
  ARRAY['commercial','retail','distribution','manufacturing','construction_supplies'],
  ARRAY['purchasing','suppliers','stock'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-024
-- ============================================================

(
  'TNS-024',
  'Stock alto con deuda a proveedores',
  'Stock alto deuda proveedores',
  'finance',
  'stock_supplier_debt',
  '¿Tenemos mucha mercadería y a la vez presión de proveedores?',
  'Detecta stock elevado junto con deuda relevante a proveedores.',
  'Tener stock no es malo. Tener stock que no rota mientras se debe a proveedores, sí.',
  'Stock alto o inmovilizado + deuda o pagos vencidos a proveedores.',
  ARRAY['KPI-STK-002','KPI-PUR-004'],
  ARRAY['ACT-STK-003','ACT-DIR-001'],
  ARRAY['EVD-010','EVD-012'],
  'high',
  'Alta; crítica si compromete abastecimiento o caja.',
  'general_manager',
  'director',
  'cash_stock_health',
  -10,
  -6,
  'P2',
  false,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['stock','suppliers','finance'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-025
-- ============================================================

(
  'TNS-025',
  'Reposición sin rotación suficiente',
  'Reposición sin rotación',
  'purchasing',
  'purchase_rotation',
  '¿Estamos comprando productos que no rotan?',
  'Detecta reposiciones en productos con rotación insuficiente.',
  'Comprar más de lo que no rota no es abastecimiento; es acumulación con factura.',
  'Compra reciente + baja rotación o días sin movimiento.',
  ARRAY['KPI-PUR-005','KPI-STK-004'],
  ARRAY['ACT-PUR-001','ACT-STK-003'],
  ARRAY['EVD-010'],
  'medium',
  'Media; alta si el monto es relevante.',
  'purchasing_manager',
  'general_manager',
  'stock_purchasing_health',
  -7,
  -4,
  'P3',
  false,
  ARRAY['commercial','retail','distribution','construction_supplies'],
  ARRAY['purchasing','stock','rotation'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-026
-- ============================================================

(
  'TNS-026',
  'Dirección sin visibilidad semanal',
  'Sin visibilidad semanal',
  'direction',
  'executive_visibility',
  '¿La dirección tiene lectura semanal confiable?',
  'Detecta ausencia de reportes, KPIs o visibilidad ejecutiva semanal.',
  'Una empresa sin visibilidad semanal dirige mirando por el espejo retrovisor. A veces funciona. Hasta que deja de funcionar.',
  'Reporte vencido o calidad de datos insuficiente.',
  ARRAY['KPI-DQ-001','KPI-REP-001'],
  ARRAY['ACT-DQ-001','ACT-DIR-001'],
  ARRAY['EVD-010','EVD-012'],
  'high',
  'Alta; crítica si coincide con tensiones críticas activas.',
  'general_manager',
  'director',
  'direction_data_health',
  -9,
  -5,
  'P1',
  true,
  ARRAY['all'],
  ARRAY['reports','data_quality','direction'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-027
-- ============================================================

(
  'TNS-027',
  'Fuente crítica atrasada',
  'Fuente atrasada',
  'data',
  'data_sources',
  '¿Una fuente crítica no está llegando a tiempo?',
  'Detecta fuentes de datos críticas atrasadas o no actualizadas.',
  'Si la fuente no llega, el diagnóstico puede quedar viejo. Y dato viejo con estética moderna sigue siendo dato viejo.',
  'Fecha de última carga supera tolerancia.',
  ARRAY['KPI-DQ-002'],
  ARRAY['ACT-DQ-001'],
  ARRAY['EVD-002','EVD-004'],
  'high',
  'Alta; crítica si afecta KPIs de caja, ventas o stock.',
  'data_owner',
  'general_manager',
  'data_quality',
  -8,
  -4,
  'P1',
  true,
  ARRAY['all'],
  ARRAY['data_sources','ingestion','data_quality'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-028
-- ============================================================

(
  'TNS-028',
  'Baja confianza de dato en KPI crítico',
  'Baja confianza KPI',
  'data',
  'kpi_confidence',
  '¿Podemos confiar en este KPI crítico?',
  'Detecta KPIs críticos con confianza de dato menor al umbral.',
  'No todo KPI merece el mismo nivel de confianza. FARO debe decir no solo qué mide, sino cuánto cree en lo que mide.',
  'Confidence score del KPI crítico por debajo del mínimo.',
  ARRAY['KPI-DQ-003'],
  ARRAY['ACT-DQ-001'],
  ARRAY['EVD-002','EVD-004'],
  'high',
  'Alta; crítica si el KPI afecta decisiones de caja, stock o margen.',
  'data_owner',
  'general_manager',
  'data_trust',
  -10,
  -5,
  'P1',
  true,
  ARRAY['all'],
  ARRAY['kpis','data_quality','confidence'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-029
-- ============================================================

(
  'TNS-029',
  'Responsable no asignado en acción crítica',
  'Sin responsable crítico',
  'direction',
  'governance',
  '¿Hay problemas críticos sin dueño?',
  'Detecta acciones o tensiones críticas sin responsable asignado.',
  'Problema crítico sin responsable es problema que no existe para nadie. Hasta que explota.',
  'Acción o tensión crítica + responsible_user_id nulo.',
  ARRAY['KPI-ACT-004'],
  ARRAY['ACT-OPS-001','ACT-DIR-001'],
  ARRAY['EVD-004'],
  'critical',
  'Crítica por defecto.',
  'general_manager',
  'director',
  'governance_execution',
  -10,
  -6,
  'P1',
  true,
  ARRAY['all'],
  ARRAY['actions','raci','governance'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
),

-- ============================================================
-- TNS-030
-- ============================================================

(
  'TNS-030',
  'Tensión crítica reincidente',
  'Tensión reincidente',
  'direction',
  'learning',
  '¿Estamos repitiendo el mismo problema crítico?',
  'Detecta tensiones críticas repetidas en períodos consecutivos o recurrentes.',
  'Una tensión crítica una vez puede ser gestión. Dos veces puede ser descuido. Tres veces ya es sistema roto.',
  'Misma tensión crítica aparece dos o más veces en ventana definida.',
  ARRAY['KPI-TNS-001'],
  ARRAY['ACT-DIR-001','ACT-OPS-002'],
  ARRAY['EVD-010','EVD-012'],
  'critical',
  'Crítica por defecto.',
  'general_manager',
  'director',
  'learning_execution',
  -15,
  -8,
  'P1',
  true,
  ARRAY['all'],
  ARRAY['tensions','learning','execution'],
  'active',
  1,
  '{"canonical": true, "mvp": true}'::jsonb
)

ON CONFLICT (tension_code, version)
DO UPDATE SET
  name = EXCLUDED.name,
  short_name = EXCLUDED.short_name,
  area_code = EXCLUDED.area_code,
  module_code = EXCLUDED.module_code,
  business_question = EXCLUDED.business_question,
  description = EXCLUDED.description,
  executive_diagnosis = EXCLUDED.executive_diagnosis,
  trigger_logic = EXCLUDED.trigger_logic,
  required_kpis = EXCLUDED.required_kpis,
  recommended_actions = EXCLUDED.recommended_actions,
  evidence_required = EXCLUDED.evidence_required,
  default_severity = EXCLUDED.default_severity,
  severity_logic = EXCLUDED.severity_logic,
  default_owner_role = EXCLUDED.default_owner_role,
  approver_role = EXCLUDED.approver_role,
  score_dimension = EXCLUDED.score_dimension,
  score_impact_min = EXCLUDED.score_impact_min,
  score_impact_max = EXCLUDED.score_impact_max,
  mvp_priority = EXCLUDED.mvp_priority,
  demo_relevance = EXCLUDED.demo_relevance,
  industry_scope = EXCLUDED.industry_scope,
  module_scope = EXCLUDED.module_scope,
  status = EXCLUDED.status,
  metadata = EXCLUDED.metadata,
  updated_at = now();

COMMIT;

Verbatim del seed oficial. Las 30 entradas se presentan in extenso y sin recortes, idénticas a FARO-SQL-004 · V026__seed_tension_definitions_mvp.sql. El cierre con ON CONFLICT (tension_code, version) DO UPDATE SET ... updated_at = now() hace que la migración sea idempotente: aplicar dos veces produce el mismo estado final.

09 · Validaciones post-seed

7 queries que deben dar el resultado esperado

Ejecutar en orden después de aplicar V025 + V026. Cada query verifica una condición del contrato canónico. Si alguna falla, el seed no se considera aceptado.

9.1 · Conteo Esperado: 30

Existen 30 tensiones activas

El catálogo MVP debe tener exactamente 30 registros con status = 'active'. Ni más ni menos.

▸ SQL
SELECT COUNT(*) AS active_tensions
FROM faro.tension_definitions
WHERE status = 'active';
9.2 · Unicidad Esperado: 0 filas

No hay códigos duplicados activos

El índice único parcial debería prevenirlo, pero esta query lo verifica explícitamente.

▸ SQL
SELECT tension_code, COUNT(*) AS total
FROM faro.tension_definitions
WHERE status = 'active'
GROUP BY tension_code
HAVING COUNT(*) > 1;
9.3 · Acciones Esperado: 0 filas

Toda tensión tiene acciones recomendadas

Regla 05 del gobierno: sin acción no hay cierre operativo. El array recommended_actions no puede estar vacío.

▸ SQL
SELECT tension_code, name
FROM faro.tension_definitions
WHERE status = 'active'
  AND cardinality(recommended_actions) = 0;
9.4 · Evidencia Esperado: 0 filas

Toda tensión tiene evidencia requerida

Regla 06: sin evidencia no hay cierre confiable. El array evidence_required no puede estar vacío.

▸ SQL
SELECT tension_code, name
FROM faro.tension_definitions
WHERE status = 'active'
  AND cardinality(evidence_required) = 0;
9.5 · KPIs Esperado: 0 filas

Toda tensión tiene KPIs requeridos

Sin KPIs no hay regla evaluable. El array required_kpis debe tener al menos un elemento.

▸ SQL
SELECT tension_code, name
FROM faro.tension_definitions
WHERE status = 'active'
  AND cardinality(required_kpis) = 0;
9.6 · Score Esperado: 0 filas

Toda tensión impacta Score negativamente

Regla 07: una tensión que no resta Score es ruido. Ambos score_impact_min y score_impact_max deben ser negativos.

▸ SQL
SELECT tension_code, name, score_impact_min, score_impact_max
FROM faro.tension_definitions
WHERE status = 'active'
  AND (score_impact_min >= 0 OR score_impact_max >= 0);
9.7 · Demo Esperado: ~14 filas

Set de demo (demo_relevance = true)

Tensiones que deben aparecer en la demo de Empresa Demo Cuyo S.A. Lista esperada: TNS-001/002/003/004/005/006/007/009/010/026/027/028/029/030.

▸ SQL
SELECT tension_code, name, mvp_priority, default_severity, score_dimension
FROM faro.tension_definitions
WHERE demo_relevance = true
ORDER BY tension_code;
10 · Mapeo con v2

30 MVP ↔ 300 biblioteca extendida

Cada tensión MVP del catálogo tiene una equivalencia conceptual (directa, parcial o exclusiva MVP) en biblioteca-tensiones.html. Mapeo bidireccional para que ambos namespaces convivan sin perder trazabilidad.

Convención de mapeo. Directo = 1:1 conceptual. Parcial = la v2 cubre un caso más amplio o más estrecho. MVP-only = no tiene equivalente directo en la biblioteca extendida (suele ser tensión de gobierno/datos del MVP).

MVP Nombre canónico MVP Equivalente conceptual en biblioteca v2 (300) Estado
TNS-001Crecimiento no rentableTNS-014 · Margen crece, pero erosiona rentabilidad sostenida
Equivalente conceptual en bloque de Margen (no replica vector descuento)
Parcial
TNS-002Descuento fuera de políticaTNS-005 · Costos de ventas crecen más rápido que el resultado
Costos comerciales crecen más rápido que el resultado; v2 no aísla la política de descuento
Parcial
TNS-003Vendedor erosiona margenTNS-098 · Dotación crece, pero erosiona productividad humana
TNS-008 · Dependencia excesiva dentro de pipeline comercial
Dotación que erosiona productividad + Dependencia excesiva en pipeline
Múltiples
TNS-004Venta sin conversión a cajaTNS-036 · Ciclo económico de caja desfasado respecto a caja
Ciclo económico de caja desfasado respecto a caja
Directo
TNS-005Mora crítica por clienteTNS-043 · Riesgo no controlado en cuentas por cobrar
Riesgo no controlado en cuentas por cobrar
Directo
TNS-006Stock crítico alta rotaciónTNS-052 · Capacidad de inventario desalineada con demanda real
Capacidad de inventario desalineada con demanda real
Directo
TNS-007Stock inmovilizadoTNS-060 · Ciclo económico de stock desfasado respecto a caja
Ciclo económico de stock desfasado respecto a caja
Directo
TNS-008Reposición reactivaTNS-071 · Recurso crítico de base de proveedores no disponible a tiempo
Recurso crítico de base de proveedores no disponible a tiempo
Directo
TNS-009Acciones vencidasTNS-141 · Acciones sobre decisiones vencidas sin corrección visible
Acciones sobre decisiones vencidas sin corrección visible
Directo
TNS-010Acciones sin evidenciaTNS-134 · Decisiones crece, pero erosiona gobierno con seguimiento
Decisiones crecen pero erosiona gobierno con seguimiento; v2 no aísla "evidencia"
Parcial
TNS-011Ventas concentradas en pocos clientesTNS-115 · Riesgo no controlado en base de clientes
Riesgo no controlado en base de clientes
Directo
TNS-012Ticket promedio caeTNS-001 · Ventas con alta actividad pero bajo resultado ejecutivo
Ventas con alta actividad pero bajo resultado (engloba caída de ticket)
Parcial
TNS-013Caída de ventas en sucursalTNS-159 · Sucursales fuera de ritmo contra plan
Sucursales fuera de ritmo contra plan
Directo
TNS-014Margen bajo por familiaTNS-015 · Margen fuera de ritmo contra plan
Margen fuera de ritmo contra plan
Directo
TNS-015Venta crece por descuentoTNS-002 · Ventas crece, pero erosiona conversión rentable
Ventas crece, pero erosiona conversión rentable
Directo
TNS-016Cliente supera créditoTNS-043 · Riesgo no controlado en cuentas por cobrar
Riesgo no controlado en cuentas por cobrar (acotado al límite de crédito)
Parcial
TNS-017Cobranza concentradaTNS-044 · Dependencia excesiva dentro de cuentas por cobrar
Dependencia excesiva dentro de cuentas por cobrar
Directo
TNS-018Facturación alta · cobranza parcialTNS-039 · Cartera de clientes fuera de ritmo contra plan
Cartera de clientes fuera de ritmo contra plan (recorte facturado/cobrado)
Parcial
TNS-019Caja proyectada insuficienteTNS-031 · Riesgo no controlado en flujo de fondos
Riesgo no controlado en flujo de fondos
Directo
TNS-020Mora sin gestiónTNS-045 · Acciones sobre cartera de clientes vencidas sin corrección visible
Acciones sobre cartera de clientes vencidas sin corrección visible
Directo
TNS-021Quiebre con ventas perdidasTNS-058 · Promesa de servicio de stock deteriorada
Promesa de servicio de stock deteriorada
Directo
TNS-022Costo sube, margen no ajustaTNS-065 · Costos de compras crecen más rápido que el resultado
Costos de compras crecen más rápido que el resultado
Directo
TNS-023Proveedor crítico demoraTNS-067 · Riesgo no controlado en base de proveedores
Riesgo no controlado en base de proveedores
Directo
TNS-024Stock alto con deuda proveedoresTNS-053 · Costos de stock crecen más rápido que el resultado
TNS-072 · Ciclo económico de compras desfasado respecto a caja
Costos de stock + Ciclo económico de compras (cruce stock-caja-proveedores)
Múltiples
TNS-025Reposición sin rotaciónTNS-066 · Datos de compras insuficientes para decidir con confianza
Datos de compras insuficientes para decidir con confianza
Parcial
TNS-026Sin visibilidad semanalTNS-138 · Datos de decisiones insuficientes para decidir con confianza
Datos de decisiones insuficientes para decidir con confianza
Parcial
TNS-027Fuente crítica atrasadaTNS-279 · Datos e integraciones fuera de ritmo contra plan
Datos e integraciones fuera de ritmo contra plan
Parcial
TNS-028Baja confianza KPITNS-278 · Datos e integraciones crece, pero erosiona confiabilidad del modelo ejecutivo
Datos e integraciones crecen, pero erosionan confiabilidad ejecutiva
Parcial
TNS-029Responsable no asignadoTNS-296 · Dependencia excesiva dentro de marco de control interno
Dependencia excesiva dentro de marco de control interno
Parcial
TNS-030Tensión crítica reincidenteTNS-295 · Riesgo no controlado en marco de control interno
Riesgo no controlado en marco de control interno (no es reincidencia)
Parcial

Resumen del mapeo. Sobre las 30 tensiones MVP: 16 Directo · 12 Parcial · 2 Múltiples · 0 Solo MVP. Los códigos v2 que aparecen apuntan a fichas reales de biblioteca-tensiones.html; ninguna fila quedó sin equivalente conceptual porque la v2 cubre los 30 conceptos del MVP vía sus 25 áreas × 12 arquetipos (algunos parciales porque la v2 no aísla vectores muy específicos como "descuento", "evidencia" o "reincidencia").

11 · Cross-references

Dónde se cruza este catálogo con el resto del pack

El catálogo MVP no vive solo. Estos son los puntos donde se consume, se valida o se extiende.

12 · Próximos pasos

Qué falta para cerrar el loop canónico

Este catálogo es el primero de la trilogía. Los siguientes documentos cierran el contrato de tensión → acción → evidencia.

  1. FARO-SQL-005 · Catálogo Canónico de Acciones. Construir faro.action_definitions con los códigos ACT-* referenciados por las 30 tensiones. Mismo patrón (DDL + seed idempotente + 7 validaciones).
  2. FARO-SQL-006 · Catálogo Canónico de Evidencias. Construir faro.evidence_definitions con los códigos EVD-*. Permite validar que cada evidence_required apunta a un tipo de evidencia definido.
  3. FARO-ENG-003.1 · Patch Motor Evaluador con Catálogos Canónicos. Actualizar el motor para enriquecer cada tensión disparada con los metadatos de tension_definitions (severidad base, owner por defecto, dimensión Score, impacto, evidencia requerida).
  4. FARO-TEST-002.1 · Tests de integración con catálogos canónicos. Agregar test que valide rule_definitions.rule_body.tension_code contra tension_definitions activas. Bloquear cualquier código fuera de catálogo.
  5. FARO-SQL-003.1 · Patch Empresa Demo. Corregir el seed demo viejo (TNS-002→TNS-004, TNS-003→TNS-006, TNS-004→TNS-007, TNS-005→TNS-009 + TNS-010 separado) para alinear con códigos canónicos.
  6. FARO-CFG-001 · Validación de YAML. Actualizar el parser DSL para rechazar reglas que apunten a tension_code no presente en el catálogo. Error: UNKNOWN_TENSION_CODE.
  7. FARO-UI-001 · Bandeja de Tensiones. Migrar la UI para que lea nombre, área, severidad base y dimensión Score desde el catálogo en vez de strings hardcodeados.