01 · Resumen ejecutivo

FARO debe gobernar la resolución, no solo detectar

Este documento es el constitutional document del workflow MVP. Define los enums maestros, las transiciones permitidas, los SLA, las reglas de escalamiento automático y la auditoría. Si un módulo del MVP usa un estado o una transición que no está acá, ese módulo está fuera de contrato.

FARO no es un tablero que muestra problemas. FARO es un sistema que gobierna la resolución de problemas. La diferencia entre alerta y acción es exactamente este documento: estados, plazos, escalamiento, evidencia y consecuencia.

El catálogo canónico de tensiones (catalogo-tensiones-mvp.html) define qué se detecta. El motor evaluador (motor-evaluador-mvp.html) define cómo se evalúa. Este workflow define qué pasa después: quién actúa, con qué plazo, con qué evidencia, qué pasa si no cumple y a quién se escala.

Sin workflow, FARO se convierte en lo que más debe evitar: un tablero elegante mirando cómo se incendia la cocina. Detectar sin gobernar es periodismo, no consultoría.

Tres decisiones aplicadas en este documento son fuente de verdad para el resto del pack:

  • Decisión D3: auditoría se centraliza en una sola tabla execution_events con 40 event_types tipados. La vista action_events queda como filtro sobre esa tabla, no como tabla separada.
  • Decisión D4: escalated es evento, no estado del enum. Una acción puede estar blocked y al mismo tiempo haber sido escalada. Forzar escalated como estado contamina el enum y rompe la trazabilidad.
  • Decisión empresa: el seed demo opera contra Empresa Demo Cuyo S.A.. Todos los company_id de ejemplo apuntan a esa empresa.

El contrato es duro a propósito: si un módulo del MVP introduce un estado nuevo, una transición no listada, un escalamiento fuera de matriz o un cierre sin evidencia, debe ser rechazado en code review. La disciplina del workflow se sostiene por la disciplina del documento que la define.

02 · Tesis del workflow

El workflow es la diferencia entre alerta y acción

Una empresa no se ordena con diagnósticos. Se ordena con responsables, plazos, evidencia, escalamiento y consecuencias. El workflow es el mecanismo que transforma inteligencia en disciplina.

Principio rector

La tensión no termina cuando se detecta. La acción no termina cuando se marca. El problema no termina cuando se conversa. Termina cuando hay evidencia, validación y resultado.

FARO debe responder con precisión: qué se detectó · quién debe actuar · antes de cuándo · con qué evidencia · quién aprueba · qué pasa si no cumple · a quién se escala · qué impacto tiene en Score · qué queda registrado. Si alguna de estas nueve preguntas queda sin respuesta, el workflow tiene un agujero por donde se escapa la dirección.

FARO debe evitar tres vicios empresariales clásicos. Lo hace con tres mecanismos del workflow:

Vicio 01 · "Lo estamos viendo"

Sin responsable, no es tensión: es queja

Toda tensión exige responsible_user_id. Si no se encuentra, FARO crea la acción igual con responsible_user_id = null, dispara evento responsible_missing y escala automáticamente a Gerencia General. La falta de responsable también es una tensión.

Vicio 02 · "Ya está hablado"

Sin evidencia, no hay cierre

Toda acción exige evidence_required. El cierre se valida contra evidencia approved. Cierre excepcional con EVD-009 queda marcado como auditable y reduce el aporte al Score.

Vicio 03 · "Lo vemos la semana que viene"

Sin escalamiento, los temas se eternizan

Toda acción tiene SLA. Vencida, el job OPS-001 workflow-checker dispara action_expired + escalamiento automático según severidad. No depende de que alguien se acuerde de revisar.

Lo que está fuera del MVP. Este workflow define lo mínimo necesario para que FARO Connect pase de diagnóstico a ejecución gobernada. Quedan fuera del MVP: motor BPMN drag&drop, aprobaciones múltiples complejas, firma digital, WhatsApp productivo, IA autónoma cerrando acciones, reasignación automática avanzada. Cada uno es un proyecto serio en sí mismo; meterlos al MVP es perder el MVP.

03 · Estados oficiales

Enums maestros que UI-001..005 deben respetar

Tres entidades (tensions, actions, evidence) más una transversal (execution_events). Cada entidad tiene un enum de status duro, controlado por CHECK constraint en SQL. Ningún módulo del MVP puede introducir un estado fuera de esta lista.

3.1 · 10 estados oficiales de tensión

Enum tensions.status. La tensión es el objeto raíz: si no entra a este enum, el motor evaluador no la persiste.

new

Detectada

Tensión recién creada por el motor evaluador. Aún no fue tomada por nadie.

Owner: sistema / gerencia
in_review

En revisión

Asignada y bajo análisis por el responsable. Aún sin acciones creadas.

Owner: responsable
in_execution

En ejecución

Tiene una o más acciones activas en progreso. Estado dominante durante el ciclo.

Owner: responsable
blocked

Bloqueada

No puede avanzar por dependencia externa, política o aprobación. Requiere razón documentada.

Owner: responsable / gerente
monitoring

En monitoreo

Acciones ejecutadas; FARO sigue el KPI subyacente para confirmar recuperación sostenida.

Owner: aprobador
closed

Cerrada

Resuelta con evidencia aprobada. Estado terminal positivo. Habilita recuperación de Score.

Owner: gerente / dirección
rejected

Rechazada

Descartada con justificación (falso positivo, fuera de scope, etc.). Estado terminal con auditoría.

Owner: gerente / dirección
expired

Vencida

SLA superado sin cierre. Estado intermedio que dispara escalamiento y penaliza Score.

Owner: sistema
cancelled

Cancelada

Cancelada explícitamente por la dirección. Diferente de rejected: no era inválida, ya no aplica.

Owner: dirección
archived

Archivada

Estado terminal de housekeeping para tensiones antiguas sacadas de vistas operativas.

Owner: sistema

Decisión D4 aplicada. escalated NO está en el enum de tensión. Una tensión puede estar in_execution o blocked y al mismo tiempo tener uno o más eventos tension_escalated en su timeline. El estado dice "dónde está el ticket"; el evento dice "qué le pasó". Mezclarlos rompe la auditoría.

3.2 · 12 estados oficiales de acción

Enum actions.status. La acción es la unidad de trabajo. UI-003 (Detalle de Acción) consume este enum directamente para construir el componente WorkflowStatusBadge.

new

Nueva

Acción creada por el motor o asignada manualmente. Aún no iniciada.

Owner: responsable
in_progress

En progreso

El responsable inició la ejecución. Aún no cargó evidencia.

Owner: responsable
waiting_evidence

Esperando evidencia

Acción ejecutada pero la evidencia requerida aún no fue cargada o está incompleta.

Owner: responsable
in_review

En revisión

Evidencia cargada, pendiente de aprobación por el aprobador asignado.

Owner: aprobador
approved

Aprobada

Evidencia aprobada. Lista para cierre formal por gerente o dirección.

Owner: gerente
closed

Cerrada

Cerrada formalmente con evidencia validada. Estado terminal positivo.

Owner: gerente / dirección
blocked

Bloqueada

Dependencia externa, política o aprobación faltante. Requiere blocked_reason + blocked_category.

Owner: responsable
rejected

Rechazada

Acción rechazada justificadamente (falso positivo, fuera de scope, etc.). Estado terminal con auditoría.

Owner: aprobador
expired

Vencida

SLA superado sin cierre. Dispara escalamiento automático según severidad y penaliza Score.

Owner: sistema
cancelled

Cancelada

Cancelada por decisión explícita de dirección. Cierra sin recuperación de Score.

Owner: dirección
reopened

Reabierta

Acción reabierta tras detectar evidencia débil, recurrencia o KPI sin mejora sostenida.

Owner: gerencia
archived

Archivada

Estado terminal de housekeeping para acciones antiguas sacadas de vistas operativas.

Owner: sistema

3.3 · 5 estados oficiales de evidencia

Enum evidence.status. La evidencia es lo que cierra la cadena. UI-004 (Carga de Evidencia) consume este enum para construir el estado de cada archivo cargado.

submitted

Enviada

Evidencia cargada por el responsable. Pendiente de revisión por el aprobador.

Owner: aprobador
approved

Aprobada

Evidencia aprobada formalmente. Habilita el cierre de la acción asociada.

Owner: aprobador
rejected

Rechazada

Evidencia rechazada con motivo documentado. Dos rechazos disparan escalamiento L3.

Owner: aprobador
needs_more_info

Requiere ampliación

El aprobador pide información adicional o complementaria. Vuelve al responsable sin contar como rechazo.

Owner: responsable
archived

Archivada

Evidencia obsoleta o superada por una nueva carga. No válida para cierre.

Owner: sistema
▸ SQL · CHECK constraints (DDL)
-- tensions.status (10 valores)
CHECK (status IN (
  'new', 'in_review', 'in_execution', 'blocked', 'monitoring',
  'closed', 'rejected', 'expired', 'cancelled', 'archived'
))

-- actions.status (12 valores)
CHECK (status IN (
  'new', 'in_progress', 'waiting_evidence', 'in_review', 'approved',
  'closed', 'blocked', 'rejected', 'expired', 'cancelled',
  'reopened', 'archived'
))

-- evidence.status (5 valores)
CHECK (status IN (
  'submitted', 'approved', 'rejected', 'needs_more_info', 'archived'
))
04 · Matriz de transiciones

Qué transiciones de estado están permitidas

No toda transición es válida. Una acción closed no puede volver directamente a in_progress; debe pasar por reopened. Estas tres matrices son el contrato duro. Cualquier UPDATE status que no figure como válido debe ser rechazado por la función faro.transition_action_status().

4.1 · Transiciones permitidas en tensión

Filas = estado origen. Columnas = estado destino. VAL = transición válida. = transición prohibida.

De ↓ · A → new in_review in_exec blocked monit closed rejected expired cancel archived
new·VALVALVALVALVAL
in_review·VALVALVALVALVAL
in_execVAL·VALVALVALVALVAL
blockedVALVAL·VALVAL
monitoringVAL·VALVAL
closedVAL·VAL
rejected·VAL
expiredVALVALVALVALVAL·VALVAL
cancelled·VAL
archived·

Notas clave: archived es absorbente (no se sale). expired es excepcional: puede volver a casi todo porque la idea es destrabar, no penalizar la regularización. cancelled y rejected solo van a archived.

4.2 · Transiciones permitidas en acción

Misma convención. La diferencia clave: tras approved solo se puede ir a closed (cierre formal); tras closed solo se puede ir a reopened (reapertura controlada).

De ↓ · A → new in_prog wait_ev in_rev approved closed blocked rejected expired cancel reopen arch
new·VALVALVALVALVAL
in_progress·VALVALVALVALVALVAL
waiting_evidVAL·VALVALVALVAL
in_reviewVALVAL·VALVALVALVALVAL
approved·VALVAL
closed·VALVAL
blockedVALVAL·VALVALVAL
rejected·VAL
expiredVALVALVALVALVAL·VALVAL
cancelled·VAL
reopenedVALVALVALVAL·
archived·

4.3 · Transiciones permitidas en evidencia

Más simple: una evidencia cargada solo puede ser aprobada, rechazada o pedida ampliar. Tras ampliación vuelve a submitted.

De ↓ · A → submitted approved rejected needs_more_info archived
submitted·VALVALVALVAL
approved·VAL
rejectedVAL·VAL
needs_more_infoVAL·VAL
archived·
▸ SQL · función guardia de transición (action)
CREATE OR REPLACE FUNCTION faro.transition_action_status(
  p_company_id uuid,
  p_action_id uuid,
  p_new_status text,
  p_user_id uuid,
  p_reason text DEFAULT NULL
)
RETURNS boolean AS $$
DECLARE
  v_old_status text;
  v_allowed boolean;
BEGIN
  SELECT status INTO v_old_status
  FROM faro.actions
  WHERE company_id = p_company_id AND action_id = p_action_id
  FOR UPDATE;

  -- consulta tabla canónica faro.action_status_transitions
  SELECT EXISTS (
    SELECT 1 FROM faro.action_status_transitions
    WHERE from_status = v_old_status AND to_status = p_new_status
  ) INTO v_allowed;

  IF NOT v_allowed THEN
    RAISE EXCEPTION 'INVALID_TRANSITION: % -> %', v_old_status, p_new_status;
  END IF;

  UPDATE faro.actions
  SET status = p_new_status, updated_at = now()
  WHERE company_id = p_company_id AND action_id = p_action_id;

  PERFORM faro.log_execution_event(
    p_company_id, 'action', p_action_id,
    'action_status_changed', 'workflow',
    'Transición de estado', p_reason,
    p_user_id, 'user', NULL, p_action_id, NULL, NULL,
    p_new_status, v_old_status, NULL, NULL, NULL,
    'transition_function', NULL, '{}'::jsonb
  );

  RETURN true;
END;
$$ LANGUAGE plpgsql;
05 · Flujo operativo

18 pasos de detección a impacto en Score

El ciclo de vida feliz: cómo viaja una tensión desde que el motor evaluador la detecta hasta que el reporte semanal muestra el resultado validado. Cada paso está asociado a un actor (sistema, responsable, aprobador, gerente) y dispara uno o más eventos en execution_events.

Sistema · motor

Detección de tensión

El motor evaluador corre la regla YAML, dispara la tensión y persiste en faro.tensions con status = 'new'.

Sistema · motor

Enriquecimiento canónico

FARO consulta tension_definitions y asigna severidad, prioridad e impacto Score base.

Sistema · motor

Recomendación de acciones

FARO instancia las recommended_actions del catálogo canónico como filas en faro.actions.

Sistema · motor

Asignación de responsable y SLA

Se aplica default_owner_role y se calcula due_date según prioridad. Si no hay responsable, evento responsible_missing + escalamiento L3.

Responsable

Recepción de la acción

El responsable recibe notificación in-app + email. La acción aparece en su Dashboard (UI-002).

Responsable

Inicio de ejecución

El responsable marca in_progress desde UI-003. Evento action_started.

Responsable

Trabajo de campo

Se ejecuta la acción según closure_criteria. Puede pasar por blocked si hay dependencia.

Responsable

Carga de evidencia

El responsable carga cada EVD-* requerida desde UI-004. La acción pasa a waiting_evidence y luego a in_review.

Sistema · validador

Validación técnica

FARO valida formato, MIME, tamaño y completitud mínima. Evidencia inválida queda rejected automáticamente.

Aprobador

Revisión cualitativa

El aprobador asignado revisa la evidencia desde UI-004. Decide approved, rejected o needs_more_info.

Aprobador

Aprobación / rechazo

Si aprueba, la acción pasa a approved (lista para cierre). Si rechaza dos veces, dispara escalamiento L3.

Gerente

Cierre formal de la acción

El gerente cierra la acción desde UI-003. Evento action_closed.

Sistema · agregador

Agregación a nivel tensión

Si todas las acciones críticas de la tensión están closed, la tensión pasa a monitoring.

Sistema · score

Recalibración Score

El job OPS-001 score-recompute ajusta score_recovery y score_confidence según los eventos del workflow.

Sistema · KPI watcher

Verificación de KPI subyacente

FARO monitorea el KPI que disparó la tensión. Si mejora sostenidamente, tensión cierra. Si no, vuelve a in_execution o reopened.

Gerente / dirección

Cierre formal de la tensión

Tras el monitoreo, gerente o dirección cierra la tensión. Evento tension_closed.

Sistema · timeline

Registro consolidado

UI-005 (Timeline) consolida los 12-20 eventos generados por el ciclo en una vista auditable.

Sistema · reporte

Reporte semanal ejecutivo

El template FARO-TPL-002 incluye la tensión cerrada en el resumen semanal con su impacto Score recuperado.

Variantes del flujo feliz. Los 18 pasos describen el camino positivo. En la práctica, cada paso tiene ramas: bloqueo (paso 7), rechazo de evidencia (paso 11), escalamiento (cualquier paso si SLA vence). El workflow las gobierna con eventos paralelos sin romper el flujo principal.

06 · SLA por prioridad

Plazos base y por tipo de acción

El SLA se calcula desde action_definitions.default_sla_days y se ajusta por severidad. Estos son los plazos base que el job OPS-001 usa para detectar vencimientos.

6.1 · SLA base por prioridad de acción

Critical 2-3d

Acción crítica

Tensión crítica con impacto Score > 8. Vencida, escalamiento L3 inmediato a Gerencia General.

High 5-7d

Acción alta

Tensión high con impacto Score > 5. Vencida más de 2 días, escalamiento L2 a Gerente de área.

Medium 7-14d

Acción media

Tensión medium con impacto Score 3-5. Vencida más de 5 días, escalamiento L2.

Low 14-30d

Acción baja

Tensión low con impacto Score < 3. Vencida más de 10 días, escalamiento L1/L2.

6.2 · SLA por tipo de acción

Cada categoría de acción tiene su rango natural. ACT-OPS-* de escalamiento se mueve en horas; ACT-DIR-* de governance en una a dos semanas.

Tipo de acción Código ejemplo SLA recomendado Justificación
Correctiva críticaACT-FIN-001 · gestión cobranza urgente2-5 díasImpacto en caja exige respuesta inmediata. Más allá de 5 días la corrección pierde efecto.
PreventivaACT-PUR-001 · revisión política proveedor7-15 díasAnticipa problemas futuros; admite plazo más amplio para análisis.
AnálisisACT-COM-002 · análisis margen por vendedor3-7 díasEstudio puntual con datos disponibles; no requiere semanas.
AprobaciónACT-DIR-002 · aprobar excepción de descuento1-3 díasDecisión binaria de dirección; debe resolverse rápido para no bloquear ejecución.
Data qualityACT-DQ-001 · reparar fuente atrasada2-7 díasFuente caída deteriora todos los KPIs aguas abajo; urgente pero requiere coordinación TI.
Escalamiento operativoACT-OPS-001 · asignar responsable faltante24-48 horasGobierno: la falta de owner es bloqueante para cualquier resolución.
Governance / políticaACT-DIR-001 · cambiar política de crédito5-10 díasCambio estructural; requiere análisis, validación legal y comunicación.
▸ SQL · cálculo de due_date
CREATE OR REPLACE FUNCTION faro.calculate_due_date(
  p_priority text,
  p_action_type text,
  p_default_sla_days integer
)
RETURNS date AS $$
DECLARE
  v_days integer;
BEGIN
  v_days := CASE p_priority
    WHEN 'critical' THEN 3
    WHEN 'high'     THEN 7
    WHEN 'medium'   THEN 14
    WHEN 'low'      THEN 30
    ELSE COALESCE(p_default_sla_days, 7)
  END;

  RETURN CURRENT_DATE + (v_days || ' days')::interval;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
07 · Reglas de vencimiento

Cuándo una acción se considera vencida y cómo se mide

Una acción vence cuando su due_date es anterior a hoy y su status no es terminal. El cálculo de días en mora (overdue_days) es lo que alimenta la matriz de escalamiento.

▸ SQL · regla de vencimiento + overdue_days
-- Una acción está vencida si:
SELECT
  action_code,
  due_date,
  status,
  CASE
    WHEN status IN ('closed', 'cancelled', 'rejected', 'archived') THEN false
    WHEN due_date IS NULL THEN false
    WHEN due_date < CURRENT_DATE THEN true
    ELSE false
  END AS is_overdue,

  CASE
    WHEN due_date < CURRENT_DATE
     AND status NOT IN ('closed', 'cancelled', 'rejected', 'archived')
    THEN (CURRENT_DATE - due_date)
    ELSE 0
  END AS overdue_days
FROM faro.actions
WHERE company_id = 'EMPRESA-DEMO-CUYO-SA'::uuid;

7.1 · Consecuencias del vencimiento

Cuando una acción vence, el job OPS-001 workflow-checker ejecuta cinco acciones en una sola transacción:

Consecuencia 01

Cambio de estado

El job ejecuta UPDATE actions SET status = 'expired' si el estado actual es activo (new, in_progress, waiting_evidence, in_review, blocked).

Consecuencia 02

Evento auditable

Se inserta action_expired en execution_events con category = 'deadline' y payload con due_date + overdue_days.

Consecuencia 03

Notificación dual

Notificación in-app + email a responsable y gerente de área. Si es critical, también a Gerencia General.

Consecuencia 04

Escalamiento automático

Según matriz (sección 8): critical → L3 inmediato; high → L2 a 24h; medium → L2 tras 5 días; low → L1/L2 tras 10 días.

Consecuencia 05

Penalización Score

Evento score_recovery_blocked en sección 10: la recuperación de Score queda en 0% hasta que la acción se destrabe y cierre con evidencia.

Excepción

Regularización con evento

Una acción expired puede volver a in_progress con evento action_regularized. La penalización Score se mantiene parcial hasta cierre con evidencia.

08 · Escalamiento automático

Niveles L0 a L5 · triggers · destinatarios · jobs

El escalamiento es lo que distingue a FARO de un tablero pasivo. Cuando un SLA se vence, una evidencia se rechaza dos veces, una acción crítica queda sin responsable, FARO no espera: ejecuta. Esta es la matriz completa que rige al job OPS-001.

Decisión D4 aplicada · crítica para integridad de auditoría. escalated NO es un estado. Es un evento que se registra en execution_events. Una acción puede tener status = blocked y al mismo tiempo dos eventos action_escalated a L2 y L3 en su timeline. El estado dice "dónde está parado el ticket"; los eventos dicen "qué le pasó en el camino". Forzar escalated como estado terminal o intermedio rompe la matriz de transiciones y oculta el estado real del trabajo.

8.1 · Niveles de escalamiento

Seis niveles, ordenados por autoridad. L0 es el sistema mismo; L5 son actores externos (legal, auditor, consultor) que el MVP soporta pero usa rara vez.

L0

Sistema FARO

Detección automática. No tiene destinatario humano: es el origen.

L1

Responsable

Usuario asignado a la acción. Ejecuta. La gran mayoría de acciones cierra en L1.

L2

Gerente de área

Responsable superior del responsable. Destraba bloqueos operativos y aprueba excepciones menores.

L3

Gerente General

Dirección operativa. Decide prioridades entre áreas y resuelve conflictos transversales.

L4

Directorio

Máxima decisión. Riesgo crítico de caja, margen, regulatorio o reputacional.

L5

Comité / externo

Legal, auditor, consultor externo. Casos especiales fuera del flujo operativo regular.

8.2 · Matriz de triggers automáticos

Cada fila es una regla del job OPS-001 workflow-checker. Si la condición se cumple, el job dispara escalamiento al nivel indicado, inserta evento en execution_events y notifica al destinatario.

Severidad Estado / condición Trigger Nivel Destinatario Plazo
CriticalAcción expiredSLA vencidoL3Gerente GeneralInmediato
CriticalTensión new sin responsible_user_idResponsable faltante > 60 minL3Gerente General1 hora
CriticalTensión new sin acción asociadaSin acción > 60 minL4DirectorioInmediato
CriticalAcción blocked > 24hBloqueo prolongadoL3Gerente General24 horas
CriticalAcción in_progress con >50% SLA sin evidenciaSLA consumido sin avanceL2Gerente de áreaInmediato
HighAcción expired > 2 díasSLA vencido sin acciónL2Gerente de área24 horas
HighAcción blocked > 48hBloqueo prolongadoL2Gerente de área48 horas
MediumAcción expired > 5 díasSLA vencido prolongadoL2Gerente de área5 días
LowAcción expired > 10 díasSLA vencido prolongadoL1Responsable + jefe directo10 días
AnyEvidencia rejected 2 vecesRechazo repetidoL3Gerente GeneralInmediato
AnyAcción in_review > 48h sin aprobadorAprobador inactivoL3Gerente General24 horas
AnyAcción closed con evidencia débil detectadaAuditoría posteriorL2Gerente de área + reaperturaInmediato
AnyAcción con score_impact > 8 vencidaRiesgo Score altoL4DirectorioInmediato
AnyTensión crítica reincidente (TNS-030)Reincidencia detectadaL4DirectorioInmediato

8.3 · Jobs OPS-001 que ejecutan el escalamiento

El motor de workflow se compone de cinco jobs periódicos. Corren cada 15-60 minutos en MVP. Cada uno cubre una rama de la matriz.

OPS-001.1

checkExpiredActions

Detecta acciones con due_date < CURRENT_DATE y estado activo. Marca expired + escalamiento según severidad.

Cada 30 min
OPS-001.2

checkBlockedActions

Detecta acciones con status = 'blocked' y blocked_at < now() - interval '48 hours'. Escala según severidad.

Cada 60 min
OPS-001.3

checkPendingEvidenceReview

Detecta evidencias submitted > 48h sin revisión. Escala al aprobador superior (L2/L3).

Cada 60 min
OPS-001.4

checkCriticalTensionsWithoutOwner

Detecta tensiones críticas con responsible_user_id IS NULL > 60 min. Escala a L3 + crea acción ACT-OPS-001.

Cada 15 min
OPS-001.5

checkRepeatedEvidenceRejections

Detecta evidencias con >= 2 rechazos sobre la misma acción. Escala a L3 + notifica a Gerencia General.

Cada 60 min
OPS-001.0

runWorkflowChecks (master)

Coordinador que invoca los 5 jobs anteriores en orden. Garantiza atomicidad por company_id y previene duplicación de escalamientos.

Cada 30 min
▸ SQL · escalations table (DDL extracto)
CREATE TABLE IF NOT EXISTS faro.escalations (
  escalation_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  company_id uuid NOT NULL,
  entity_type text NOT NULL CHECK (entity_type IN ('tension', 'action', 'evidence')),
  entity_id uuid NOT NULL,
  tension_id uuid NULL,
  action_id uuid NULL,
  evidence_id uuid NULL,

  escalation_level text NOT NULL CHECK (
    escalation_level IN ('L1', 'L2', 'L3', 'L4', 'L5')
  ),

  reason_code text NOT NULL,         -- 'SLA_EXPIRED', 'RESPONSIBLE_MISSING', etc.
  reason_description text NOT NULL,

  from_user_id uuid NULL,
  to_user_id uuid NULL,
  to_role text NULL,

  status text NOT NULL DEFAULT 'open' CHECK (
    status IN ('open', 'acknowledged', 'resolved', 'dismissed')
  ),

  due_at timestamptz NULL,
  acknowledged_at timestamptz NULL,
  resolved_at timestamptz NULL,

  payload jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

-- Índice anti-duplicación: una sola escalation 'open' por nivel + entidad
CREATE UNIQUE INDEX idx_escalations_no_dup_open
ON faro.escalations(company_id, entity_id, escalation_level)
WHERE status = 'open';
09 · Bloqueo · aprobación · cierre

Reglas duras de las tres operaciones críticas

Bloquear, aprobar y cerrar son las tres operaciones donde la disciplina del workflow se pone a prueba. Cada una tiene criterios duros que el backend debe verificar antes de aceptar el comando.

9.1 · Reglas de bloqueo

Bloquear una acción es legítimo cuando hay dependencia real, no como excusa. Para evitar abuso, el sistema exige campos obligatorios.

Bloqueo · 01

Razón obligatoria

Sin blocked_reason + blocked_category, el endpoint /api/actions/{id}/block devuelve 400 BLOCKED_REASON_REQUIRED.

Bloqueo · 02

Categoría tipada

10 categorías enum: approval_dependency, data_dependency, external_dependency, budget_dependency, role_conflict, system_issue, policy_conflict, capacity_issue, commercial_conflict, financial_risk.

Bloqueo · 03

Sugerencia de escalamiento

Si needs_escalation = true, el sistema crea automáticamente una escalation al rol suggested_escalation_role indicado.

Bloqueo · 04

Plazo máximo

Una acción blocked > 48 horas dispara escalamiento L2 automático. Más de 7 días, escalamiento L3 y notificación a dirección.

9.2 · Reglas de aprobación

Quién puede aprobar depende de la prioridad de la acción y del tipo de evidencia. Aprobar fuera de jerarquía dispara UNAUTHORIZED_APPROVER.

Criterio Caso Aprobador mínimo
PrioridadLowResponsable / jefe directo
MediumGerente de área
HighGerente de área / Gerente General
CriticalGerente General / Director
Tipo de evidenciaEVD-001 Documento generalGerente de área
EVD-002 Captura de sistemaGerente de área / Data Owner
EVD-006 Comprobante externoFinanzas / Gerencia
EVD-007 Cambio de políticaGerente General / Director
EVD-009 Cierre manual justificadoDirector (excepción auditada)
EVD-012 Validación de direcciónGerente General / Director

9.3 · Reglas de cierre

El cierre es la operación más importante del workflow. Sin evidencia aprobada, no hay cierre. La función faro.can_close_action() ejecuta siete validaciones antes de permitir el comando.

Cierre · 01

Estado no bloqueante

El status actual no puede ser blocked, cancelled ni rejected. Bloquear y cerrar simultáneamente es contradictorio.

Cierre · 02

Criterio de cierre definido

closure_criteria debe existir y ser texto no vacío. Sin criterio explícito, no se puede medir si se cumplió.

Cierre · 03

Evidencia aprobada (regla dura)

TODAS las evidencias de evidence_required deben estar status = 'approved'. Sin esto, error EVIDENCE_NOT_APPROVED. Sin excepciones, salvo EVD-009.

Cierre · 04

Permiso del usuario

El user_id debe tener rol que cubra la prioridad (ver sección 9.2). RLS + función verifican.

Cierre · 05

Sin escalamientos abiertos

Si existe alguna escalation.status = 'open' sobre esta acción, debe resolverse o desestimarse antes del cierre.

Cierre · 06

Evidencia crítica para acción crítica

Si priority = 'critical', al menos una evidencia debe ser de tipo EVD-006, EVD-007 o EVD-012 (peso alto).

Cierre · 07 · Excepción EVD-009

Cierre manual justificado

Si se usa EVD-009, queda marcada como cierre excepcional: requiere aprobador de nivel director, recupera menos Score (sección 10) y queda en lista de revisión posterior por auditoría.

▸ SQL · función can_close_action()
CREATE OR REPLACE FUNCTION faro.can_close_action(
  p_company_id uuid,
  p_action_id uuid,
  p_user_id uuid
)
RETURNS TABLE(can_close boolean, blocking_reasons text[]) AS $$
DECLARE
  v_reasons text[] := ARRAY[]::text[];
  v_action RECORD;
  v_evidence_pending integer;
  v_open_escalations integer;
BEGIN
  SELECT * INTO v_action
  FROM faro.actions
  WHERE company_id = p_company_id AND action_id = p_action_id;

  IF v_action.status IN ('blocked', 'cancelled', 'rejected') THEN
    v_reasons := array_append(v_reasons, 'La acción está ' || v_action.status);
  END IF;

  IF v_action.closure_criteria IS NULL OR v_action.closure_criteria = '' THEN
    v_reasons := array_append(v_reasons, 'Falta closure_criteria');
  END IF;

  SELECT COUNT(*) INTO v_evidence_pending
  FROM faro.evidence
  WHERE action_id = p_action_id AND status <> 'approved';

  IF v_evidence_pending > 0 THEN
    v_reasons := array_append(v_reasons, 'Hay evidencia no aprobada (EVIDENCE_NOT_APPROVED)');
  END IF;

  SELECT COUNT(*) INTO v_open_escalations
  FROM faro.escalations
  WHERE action_id = p_action_id AND status = 'open';

  IF v_open_escalations > 0 THEN
    v_reasons := array_append(v_reasons, 'Hay escalamientos sin resolver');
  END IF;

  RETURN QUERY SELECT (cardinality(v_reasons) = 0), v_reasons;
END;
$$ LANGUAGE plpgsql;
10 · Impacto en Score

Cómo cada evento del workflow modifica score_recovery y score_confidence

FARO Score tiene dos dimensiones móviles que el workflow ajusta: score_recovery (cuánto del impacto negativo se recuperó por ejecución) y score_confidence (cuánta confianza tiene FARO en que el resultado va a sostenerse). El principio rector es duro: el Score no se recupera por promesa; se recupera por evidencia aprobada.

Principio rector del Score en el workflow. Detectar una tensión penaliza Score (resta puntos según severidad). Iniciar la acción no recupera nada. Cargar evidencia no recupera nada (mejora confianza marginalmente). Aprobar evidencia habilita recuperación. Cerrar con evidencia aprobada recupera Score. Vencer o bloquear penaliza más. La cadena es disciplina, no relato.

10.1 · Eventos del workflow y su impacto en Score

Evento
Recovery permitido
Confidence
tension_detected · tensión nueva
0% (penaliza)
-impacto base
action_created · acción instanciada
0%
+0
action_started · responsable inicia
0% a 10%
+2 (señal ejecución)
action_blocked · bloqueada justificada
0% (congela)
-3
action_expired · SLA vencido
0% (revierte avance)
-8
evidence_submitted · evidencia cargada
0% (no acumula)
+3 (confianza parcial)
evidence_rejected · evidencia rechazada
0%
-5
evidence_approved · evidencia aprobada
10% a 30%
+10
action_approved · acción aprobada
40% a 70%
+8
action_closed · con evidencia EVD-006/007/012
70% a 100%
+12
action_closed_exception · cierre EVD-009
20% a 50%
-4 (penalización auditada)
tension_closed · tensión cerrada con KPI mejorado
+10% bonus
+15 (full recovery)
tension_reopened · recurrencia detectada
0% (resetea)
-15 (doble penalización)
action_reopened · auditoría detecta evidencia débil
0% (devuelve recovery)
-10

10.2 · Tabla de recuperación por estado de acción

Vista consolidada: cuánto del impacto Score puede recuperarse según el estado actual de la acción. Esta tabla la consume el job OPS-001 score-recompute.

Estado acción Recovery permitido Score confidence Notas
new0%Sin cambioDetección no resuelve nada.
in_progress0% a 10%+2Señal de ejecución, no resolución.
waiting_evidence0%Sin cambioEl trabajo no se valida sin evidencia.
in_review10% a 30%+5Aprobador está validando.
approved40% a 70%+8Validado, falta cierre formal.
closed con evidencia medium50% a 70%+10Cierre con evidencia estándar.
closed con evidencia high/critical70% a 100%+15Cierre con evidencia robusta (EVD-006/007/012).
closed excepcional (EVD-009)20% a 50%+5Cierre justificado pero auditable.
blocked0%-3Congela cualquier avance previo.
expired0% + penalización-8Revierte cualquier recovery acumulado.
cancelled0%Sin cambioNo recupera ni penaliza; queda neutra.
rejected0%Sin cambioFalsa positiva no penaliza.
11 · Auditoría completa

Tabla execution_events · 40 event_types tipados

Todo lo que ocurre en el workflow queda registrado en faro.execution_events. UI-005 (Timeline de Ejecución) consume esta tabla directamente. Reportes ejecutivos y auditorías posteriores también.

Decisión D3 aplicada · tabla única. No hay tension_events, action_events ni evidence_events separadas. Todo se centraliza en execution_events con discriminador entity_type. La vista v_action_events queda como filtro sobre la tabla única, no como tabla independiente. Esto evita inconsistencias de schema, simplifica la auditoría cruzada y permite reportes que mezclan eventos de tensión + acción + evidencia en un solo timeline.

▸ SQL · execution_events (DDL maestro)
CREATE TABLE IF NOT EXISTS faro.execution_events (
  event_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  company_id uuid NOT NULL,

  entity_type text NOT NULL CHECK (
    entity_type IN ('tension', 'action', 'evidence')
  ),
  entity_id uuid NOT NULL,

  event_type text NOT NULL CHECK (event_type IN (
    -- Lifecycle (12)
    'tension_detected', 'tension_assigned', 'tension_started',
    'tension_closed', 'tension_reopened', 'tension_rejected',
    'action_created', 'action_assigned', 'action_started',
    'action_closed', 'action_reopened', 'action_cancelled',

    -- Workflow (8)
    'action_status_changed', 'tension_status_changed',
    'action_blocked', 'action_unblocked',
    'action_regularized', 'tension_to_monitoring',
    'responsible_assigned', 'responsible_missing',

    -- Escalation (6)
    'tension_escalated', 'action_escalated', 'evidence_escalated',
    'escalation_acknowledged', 'escalation_resolved', 'escalation_dismissed',

    -- Evidence (5)
    'evidence_submitted', 'evidence_approved', 'evidence_rejected',
    'evidence_needs_more_info', 'evidence_archived',

    -- Score (5)
    'score_recovery_enabled', 'score_recovered', 'score_recovery_blocked',
    'score_penalized', 'score_confidence_updated',

    -- Deadline (4)
    'action_expired', 'tension_expired',
    'sla_warning', 'sla_critical'
  )),

  category text NOT NULL CHECK (category IN (
    'lifecycle', 'workflow', 'escalation',
    'evidence', 'score', 'deadline'
  )),

  title text NOT NULL,
  description text NULL,

  actor_user_id uuid NULL,
  actor_kind text NOT NULL CHECK (
    actor_kind IN ('user', 'system', 'integration')
  ),

  tension_id uuid NULL,
  action_id uuid NULL,
  evidence_id uuid NULL,
  escalation_id uuid NULL,

  new_status text NULL,
  old_status text NULL,
  severity text NULL,
  priority text NULL,
  impact_score numeric NULL,

  source text NOT NULL,             -- 'workflow_engine', 'manual_ui', 'api', etc.
  source_reason text NULL,
  payload jsonb NOT NULL DEFAULT '{}'::jsonb,

  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX idx_events_company_entity
ON faro.execution_events(company_id, entity_type, entity_id, created_at DESC);

CREATE INDEX idx_events_company_action_timeline
ON faro.execution_events(company_id, action_id, created_at DESC)
WHERE action_id IS NOT NULL;

CREATE INDEX idx_events_company_tension_timeline
ON faro.execution_events(company_id, tension_id, created_at DESC)
WHERE tension_id IS NOT NULL;

11.1 · Catálogo de los 40 event_types

Agrupados por category. Cada uno tiene title + description tipados que UI-005 muestra en el timeline visual.

tension_detectedLifecycle

Motor evaluador detecta nueva tensión.

tension_assignedLifecycle

Responsable asignado a la tensión.

tension_startedLifecycle

Responsable inicia análisis (in_review → in_execution).

tension_closedLifecycle

Tensión cerrada con KPI verificado.

tension_reopenedLifecycle

Tensión reabierta por recurrencia o evidencia débil.

tension_rejectedLifecycle

Tensión rechazada (falso positivo justificado).

action_createdLifecycle

Acción instanciada desde recommended_actions.

action_assignedLifecycle

Acción asignada a responsable concreto.

action_startedLifecycle

Responsable marca in_progress.

action_closedLifecycle

Cierre formal de acción con evidencia aprobada.

action_reopenedLifecycle

Acción reabierta tras auditoría posterior.

action_cancelledLifecycle

Acción cancelada por decisión de dirección.

action_status_changedWorkflow

Transición de status validada por matriz 4.2.

tension_status_changedWorkflow

Transición de status de tensión validada por matriz 4.1.

action_blockedWorkflow

Acción bloqueada con reason + category tipados.

action_unblockedWorkflow

Acción destrabada y vuelta a estado activo.

action_regularizedWorkflow

Acción expired regularizada y vuelta a in_progress.

tension_to_monitoringWorkflow

Todas acciones críticas cerradas; tensión pasa a monitoring.

responsible_assignedWorkflow

Asignación inicial o cambio de responsable.

responsible_missingWorkflow

No se pudo resolver responsable; dispara escalamiento L3.

tension_escalatedEscalation

Escalamiento de tensión a nivel superior (L2/L3/L4).

action_escalatedEscalation

Escalamiento de acción según matriz 8.2.

evidence_escalatedEscalation

Escalamiento por rechazo repetido o revisión vencida.

escalation_acknowledgedEscalation

Destinatario acusa recibo de escalamiento.

escalation_resolvedEscalation

Escalamiento resuelto (acción destrabada o decisión tomada).

escalation_dismissedEscalation

Escalamiento desestimado justificadamente.

evidence_submittedEvidence

Evidencia cargada por responsable.

evidence_approvedEvidence

Evidencia aprobada por aprobador.

evidence_rejectedEvidence

Evidencia rechazada con motivo.

evidence_needs_more_infoEvidence

Aprobador pide ampliación.

evidence_archivedEvidence

Evidencia obsoleta archivada por housekeeping.

score_recovery_enabledScore

Acción aprobada; recovery se habilita en próximo recompute.

score_recoveredScore

Job recompute aplica recovery efectivo al Score.

score_recovery_blockedScore

Acción cerrada sin evidencia aprobada; recovery bloqueado.

score_penalizedScore

Vencimiento o reincidencia aplica penalización adicional.

score_confidence_updatedScore

Cambio en score_confidence por carga o aprobación evidencia.

action_expiredDeadline

SLA de acción vencido; estado pasa a expired.

tension_expiredDeadline

Tensión sin cierre dentro de plazo razonable.

sla_warningDeadline

Job detecta >75% SLA consumido sin cierre; notifica al responsable.

sla_criticalDeadline

>95% SLA consumido sin evidencia; pre-escalamiento al gerente.

11.2 · Vista v_action_events

La vista filtrada que UI-005 consume cuando renderiza el timeline de una acción puntual. No es tabla independiente: es SELECT sobre execution_events (D3 aplicado).

▸ SQL · v_action_events (vista filtro)
CREATE OR REPLACE VIEW faro.v_action_events AS
SELECT
  e.event_id,
  e.company_id,
  e.action_id,
  e.tension_id,
  e.event_type,
  e.category,
  e.title,
  e.description,
  e.actor_user_id,
  u.full_name AS actor_name,
  e.actor_kind,
  e.new_status,
  e.old_status,
  e.severity,
  e.priority,
  e.payload,
  e.created_at
FROM faro.execution_events e
LEFT JOIN faro.users u ON u.user_id = e.actor_user_id
WHERE e.entity_type = 'action'
   OR e.action_id IS NOT NULL;
12 · Cross-references

Dónde se consume, valida y extiende este workflow

El workflow no vive solo. Es el contrato que rige UI, jobs operativos, motor evaluador, modelo Score, alertas y matriz RACI. Estos son los puntos de cruce.

CONSUME
catalogo-tensiones-mvp.html

30 tensiones canónicas TNS-001..TNS-030. Define las tensiones que el workflow gobierna en su ciclo de vida.

CONSUME
catalogo-acciones-mvp.html

Catálogo de acciones canónicas con default_sla_days y closure_criteria que el workflow consume.

CONSUME
catalogo-evidencias-mvp.html

15 evidencias EVD-001..EVD-015 con pesos y niveles de aprobación que rigen el cierre de acciones.

REFERENCIA FARO-ENG-003
motor-evaluador-mvp.html

Motor que detecta tensiones y crea las acciones que este workflow gobierna. En construcción.

REFERENCIA FARO-SCORE
motor-score-mvp.html

Modelo FARO Score que consume eventos del workflow para calcular recovery y confidence. En construcción.

IMPLEMENTA FARO-UI-001
ui-bandeja-tensiones.html

Bandeja de tensiones que usa enum de 10 estados y matriz de transiciones 4.1. En construcción.

IMPLEMENTA FARO-UI-002
ui-dashboard-responsable.html

Dashboard que consume v_workflow_inbox con SLA badge y banner de escalamientos. En construcción.

IMPLEMENTA FARO-UI-003
ui-workflow-accion.html

Detalle de acción + workflow que respeta enum de 12 estados y matriz de transiciones 4.2. En construcción.

IMPLEMENTA FARO-UI-005
ui-timeline-auditoria.html

Timeline de ejecución que consume execution_events con sus 40 event_types tipados. En construcción.

REFERENCIA FARO-TPL-001
alertas-notificaciones-mvp.html

Templates de alertas in-app + email que el workflow dispara en cada evento. En construcción.

VALIDA
matriz-raci-105.html

RACI por acción, área y criticidad. Define quién es responsable, aprobador, consultado e informado en cada paso.

VALIDA
frecuencia-sincronizacion.html

Frecuencias de jobs operativos. Define cada cuánto corren los OPS-001 que ejecutan el escalamiento.

12.1 · Próximos pasos para cerrar el loop

  1. FARO-ENG-003 · Motor Evaluador MVP. Construir motor-evaluador-mvp.html que documente cómo el motor crea tensiones que entran al ciclo de este workflow. Debe consumir tension_definitions canónicas y persistir en tensions con status = 'new'.
  2. FARO-SCORE · Modelo FARO Score MVP. Construir motor-score-mvp.html que detalle la fórmula de Score y cómo el job OPS-001 score-recompute consume los eventos de la sección 10 de este workflow.
  3. FARO-UI-001 · Bandeja de Tensiones. Construir ui-bandeja-tensiones.html con el enum de 10 estados de tensión y la matriz de transiciones 4.1. Debe usar componente WorkflowStatusBadge.
  4. FARO-UI-002 · Dashboard Responsable. Construir ui-dashboard-responsable.html consumiendo v_workflow_inbox. Incluir SLA badge (verde / ámbar / coral según estado) y banner de escalamientos activos.
  5. FARO-UI-003 · Detalle de Acción + Workflow. Construir ui-workflow-accion.html con enum de 12 estados, decision bar de transiciones permitidas y panel de bloqueo / aprobación.
  6. FARO-UI-005 · Timeline de Ejecución. Construir ui-timeline-auditoria.html consumiendo execution_events con render visual de los 40 event_types agrupados por category.
  7. FARO-TPL-001 · Alertas y Notificaciones MVP. Construir alertas-notificaciones-mvp.html con templates por evento. Sin esto, el workflow queda como reglamento pegado en una pared: correcto, pero ignorado.

Cierre constitutional. Este documento es la fuente de verdad de los enums maestros del MVP. Cuando UI, motor, score, alertas o cualquier otro módulo se construya, debe respetar exactamente lo definido acá: 10 estados de tensión, 12 de acción, 5 de evidencia, 40 event_types, niveles L1-L4, decisiones D3 (tabla única) y D4 (escalated como evento). Cualquier desviación se rechaza en code review.