# PRD: Vigia — el reflejo antes que el plano de control

## Estado del documento

- Estado: borrador inicial
- Tipo: contrapunto a `prd-objective-guard.md` — mismo producto, distinto zoom
- Posición: este documento no compite con Objective Guard; defiende el **orden de construcción inverso** y aporta piezas que a ese PRD le faltan
- Nombre provisional: Vigia

## 1. Resumen ejecutivo

Vigia es un verificador determinista de acompañamiento para agentes de código: un ojo siempre encendido que valida cada edición en milisegundos y alerta al modelo **en el turno en que introduce el problema**, no N turnos después.

La tesis central:

> El modo de fallo caro de un agente no es el error — es el error **descubierto tarde**. Cada turno construido sobre una base rota es 100 % desperdicio, más el costo de deshacer.

Vigia es, antes que una herramienta de calidad, un **ahorrador de tokens**. Ese encuadre económico gobierna todas las decisiones de diseño: cuándo corre, qué reporta, cuánto ocupa su salida y qué jamás debe hacer.

La analogía correcta no es SonarQube sino **rust-analyzer**: un verificador incremental de latencia sub-segundo cuyo cliente no es un IDE, sino un modelo.

## 2. Dónde converge y dónde difiere de Objective Guard

Dos diseños independientes llegaron a los mismos principios: determinismo que solo bloquea con evidencia, LLM nunca como autoridad final, costo proporcional al radio de impacto, salida estructurada para agentes. Esa convergencia de-riska el concepto. Las diferencias están en el zoom y en el orden:

| Dimensión | Objective Guard | Vigia |
| --- | --- | --- |
| Latencia objetivo | fast: p50 < 2 s, p95 < 8 s | **p95 < 200 ms** warm, < 400 ms cold |
| Momento | por patch / por invocación del agente | **por edit**, automático |
| Modo de integración | pull (`oguard check`, el agente decide llamar) | **push** (hook: el agente no gasta turnos preguntando) |
| Ceremonia de entrada | sesión declarada (`start --kind --goal --acceptance`) | **cero config**: funciona en cualquier repo desde el minuto uno |
| Reporte | estado completo por invocación | **delta-only**: solo lo nuevo desde el último check |
| Primera entrega | MVP de 12 ítems, 7 subsistemas | 4 detectores + hook + protocolo, en días |
| Alcance final | plano de control completo | el reflejo; el plano de control se construye después, guiado por datos |

### 2.1 Críticas concretas a Objective Guard

1. **Es un roadmap de empresa, no una v1.** Siete subsistemas, SDK de plugins, excepciones con aprobación humana, políticas firmadas. Hasta su Fase 0 exige contrato YAML y repo de escenarios. Riesgo: meses diseñando en el vacío antes del primer catch real. Su propia tabla de riesgos lo admite ("adopción costosa").
2. **Abandonó el tier de milisegundos.** Con p95 = 8 s no se puede enganchar cada edición; el check pasa a ser una decisión del agente (pull), que debe acordarse de invocarlo y pagar el tool call — exactamente el gasto que queríamos eliminar. Debajo de su modo `fast` hay un agujero, y ese agujero es Vigia.
3. **No tiene protocolo delta.** "JSON breve" no impide re-reportar los mismos hallazgos en cada corrida. El fingerprint + solo-lo-nuevo es de las piezas que más tokens ahorran.
4. **Le faltan los detectores de drift universales.** Test debilitado, stub en producción: cero contrato, cero config, funcionan el día 1. Su pregunta abierta "¿cómo se distingue un test de regresión nuevo de uno preexistente?" la responde en parte el detector `weak-test`.
5. **Ceremonia como precondición de valor.** Si el contrato de sesión está mal formalizado, el sistema valida basura con confianza — y el usuario concluye "no sirve" por culpa del contrato, no del motor.

### 2.2 Enmiendas propuestas a Objective Guard

1. Agregar un **tier reflejo push-based < 200 ms** debajo del modo `fast` (este documento lo especifica).
2. Elevar **delta-only** a requisito del protocolo de salida.
3. Incorporar los detectores **`weak-test` y `stub-prod`** al MVP.
4. Hacer del **modo cero-ceremonia el default**: las sesiones/contratos son opt-in que activan checks adicionales, nunca precondición de las verificaciones universales.

### 2.3 Lo que Vigia adopta de Objective Guard

1. **Estados de evidencia** (`proven / violated / unknown / not_applicable`) como vocabulario de salida — y su principio rector: no convertir ausencia de conocimiento en afirmación de corrección.
2. **Obligaciones de prueba por tipo de tarea** — "un bugfix sin test de regresión no cierra" es determinista, barato y de altísimo valor (entra con la capa de contratos).
3. **Hechos ≠ inferencias ≠ decisiones** — una inferencia puede producir `warn`; solo la evidencia reproducible bloquea.
4. **Benchmark con desviaciones sembradas** como vara de éxito, en lugar de impresiones de uso.

## 3. El modelo de valor: economía de tokens

Dos drenajes que la herramienta ataca:

1. **Descubrimiento tardío.** Un error introducido en el turno N y descubierto en el turno N+k desperdicia k turnos de generación, contexto y el costo de deshacer. El valor de Vigia = (turno donde alerta) vs (turno donde el modelo lo habría descubierto solo).
2. **Auto-verificación cara.** Hoy el modelo verifica por las suyas: corre la suite, lee 400 líneas de output, razona sobre ellas. Un diagnóstico de 3 líneas empujado en el momento exacto reemplaza esa verificación.

De ahí caen cuatro principios que ningún linter actual cumple:

1. **Push, no pull** — el ojo alerta; el modelo no gasta turnos preguntando "¿está bien?".
2. **Delta-only** — solo se reporta lo que *cambió* desde el último check. Jamás re-enviar los 50 warnings preexistentes: para un humano es ruido, para un modelo es plata.
3. **Salida con presupuesto** — códigos estables, una línea por hallazgo, orden por severidad, truncado explícito. La salida se diseña en tokens, no en pantallas.
4. **Escalera de latencia** — el costo de la verificación escala con el momento, no con el tamaño del repo:

| Tier | Cuándo | Latencia | Qué corre |
| --- | --- | --- | --- |
| 0 | cada edit | ms | sintaxis, drift universal, (futuro: contrato de scope, tipos incrementales) |
| 1 | cada save / turno | ~1 s | tests afectados, cobertura de líneas cambiadas, duplicación |
| 2 | checkpoint de tarea | min | build completo, selección e2e, predicción de gate |

El modo `fast` de Objective Guard es el tier 1; su modo `final` es el tier 2. Vigia v1 es el tier 0 — la capa que ninguna herramienta existente ocupa.

## 4. "No se desvió del objetivo": el límite honesto

Un verificador determinista no puede leer intención. Puede verificar dos cosas:

1. **Drift universal** (sin contrato): las formas reales en que los agentes se desvían son en su mayoría detectables sintácticamente — debilitar tests para "hacerlos pasar", stubear y seguir, agregar dependencias, copiar en lugar de reusar. Esto funciona en cualquier repo, en cualquier tarea, sin declarar nada.
2. **La formalización de la intención** (con contrato, capa 2): scope declarado, API congelada, boundaries, anclas TDD. "Desviarse" se vuelve observable — pero solo tan bien como esté formalizado el contrato.

Vigia v1 entrega (1) completo y deja el enganche para (2): cada detector recibe `(diff, config_opcional)`. El contrato es solo config que activa detectores adicionales.

Límites declarados: si el contrato formaliza mal el objetivo, el ojo valida basura con confianza; y "el código es correcto" tiene un solo oráculo real — los tests. El ojo encuadra y acelera; no reemplaza al oráculo.

## 5. v1: el guardián sin ceremonia

### 5.1 Alcance — cuatro detectores, cero config

Todos operan sobre `(diff del archivo editado vs HEAD, contenido actual)`. Stack objetivo inicial: JS/TS + Java.

| Código | Detector | Qué mira |
| --- | --- | --- |
| `E weak-test` | Test debilitado | `.skip` / `xit` / `xdescribe` / `@Disabled` / `@Ignore` nuevo; caída del conteo de assertions en un test modificado (query tree-sitter antes/después); regeneración masiva de snapshots |
| `E syntax` | Sintaxis rota | El archivo editado no parsea |
| `W stub-prod` | Stub en producción | TODO / FIXME / placeholder / mock introducido por este diff en código no-test |
| `W new-dep` | Dependencia nueva | El diff toca `dependencies` en `package.json` / bloque `dependencies` de `build.gradle` |

### 5.2 Arquitectura y flujo

CLI de binario/ejecutable único, sin daemon:

```text
hook PostToolUse (Edit|Write)         [primer consumidor: Claude Code]
        │  payload JSON por stdin
        ▼
vigia hook ──► git diff HEAD -- <archivo>
        ▼
detectores en paralelo (timeout 50 ms c/u)
        ▼
merge ──► diff contra fingerprints previos (.vigia/state.json, gitignored)
        ▼
emite SOLO lo nuevo ──► actualiza estado
```

Un dato de ingeniería que elimina la necesidad de daemon en v1: el hook entrega *un* archivo editado. Parsear un archivo con tree-sitter cuesta ~2-5 ms; el `git diff`, ~30 ms. El daemon con índices residentes (estilo salsa/rust-analyzer) es la evolución para cuando el tier 1 lo exija — no el punto de partida.

**Presupuesto (criterio de aceptación, no aspiración): p95 < 200 ms warm, < 400 ms cold.**

### 5.3 Protocolo de salida

- **Fingerprint** = `(regla, archivo, ventana normalizada, hash del mensaje)`. Solo se emiten hallazgos nuevos; los resueltos se colapsan a `✓ N resueltos` (una línea).
- **Presupuesto**: máx. 5 líneas, orden E > W, overflow → `…y N más (vigia list)`.
- **Formato**: `SEV regla archivo:línea — mensaje ≤ 100 chars`.
- `vigia list` = escape hatch pull: estado completo cuando el modelo lo pida.
- Compatibilidad futura con Objective Guard: `--format json` con códigos de salida `pass / warn / block / error`.

Ejemplo de lo que ve el modelo:

```text
[vigia] 2 hallazgos nuevos (edit: DetailView.jsx)
E weak-test  tests/detail.spec.ts:88 — assertion eliminada: expect(nav).toHaveBeenCalled
W stub-prod  src/DetailView.jsx:1502 — "TODO: handle missing id" introducido
```

### 5.4 Errores y apagado

Regla de oro: **el ojo jamás bloquea el loop.**

- Error interno o timeout → exit 0 silencioso, log a `.vigia/log`.
- Más de 10 hallazgos en un archivo → colapsa a una línea resumen (anti-spam).
- Kill switch: `VIGIA_OFF=1`.

Un ojo que molesta es peor que no tener ojo. La métrica de muerte del proyecto es que el usuario lo apague.

### 5.5 Fuera de v1, explícitamente

- Duplicación (necesita índice repo-wide → v1.1).
- Contratos / sesiones (capa 2; el enganche queda en la firma de los detectores).
- Daemon residente (solo si el presupuesto de latencia se rompe en tier 1).
- Impact analyzer, check planner, evidence store (se construyen cuando los datos de v1 lo justifiquen — ver roadmap).

## 6. Decisión de lenguaje: diferida, con criterio medible

El spec fija el presupuesto de latencia; el lenguaje se elige con un spike de ~30 minutos al inicio del plan, midiendo cold-start + parse de un archivo de 3 000 líneas en la máquina objetivo.

- Candidatos: Rust (cold-start ~5 ms, tree-sitter nativo), Go (~10 ms), Bun/TS (~30-80 ms, iteración más rápida).
- Bajo el presupuesto de Objective Guard (2-8 s), TypeScript sobra — su recomendación de stack es correcta *para su capa*.
- Bajo 200 ms por edit, el cold-start pesa: se mide, no se debate.
- Si más adelante aparece el daemon tier 1, la elección se revisa con los datos de v1.

## 7. Roadmap convergente

Vigia v1 es la Fase 0 que a Objective Guard le falta. La convergencia es explícita:

```text
Objective Guard (north star)
├── Fase 0        ← Vigia v1 (reflejo push < 200 ms, cero ceremonia, delta-only)   ← EMPEZAR ACÁ
├── Capa contratos ← "capa 2" de Vigia = Session + Contract Compiler de OG
├── Tier 1/2       ← Impact Analyzer + Check Planner + selección de tests
└── Evidencia      ← Evidence Store + estados proven/unknown (cuando existan sesiones)
```

Cada componente del PRD grande se construye **cuando los datos de v1 lo justifican**, no especulativamente. Los dos documentos deberían fusionarse en uno cuando v1 produzca sus primeras métricas reales.

## 8. Testing y criterios de éxito

- **Corpus golden**: pares `(antes, después) → hallazgos esperados` por detector, extraídos de patrones reales (`it.skip` en CI, `@Disabled`, assertions borradas).
- **Benchmark sembrado** (adoptado de Objective Guard §17): tareas con desviaciones plantadas; se mide detección temprana y tasa de bloqueos no accionables.
- **Test de latencia** con presupuesto asertado en CI.
- **Éxito tras una semana de uso real** (sesiones de Claude Code sobre repos de trabajo):
  1. p95 < 200 ms;
  2. ≥ 1 catch que ahorró un camino equivocado;
  3. `VIGIA_OFF` nunca seteado;
  4. tasa de falsos positivos que no entrene al modelo (ni al humano) a ignorar la salida.

## 9. Riesgos propios de este enfoque

| Riesgo | Mitigación |
| --- | --- |
| Los detectores universales resultan demasiado obvios y el modelo ya los evitaba | El benchmark sembrado lo mide en la semana 1; si es así, la capa de contratos sube de prioridad y v1 costó días, no meses |
| Falsos positivos que enseñan a ignorar la salida | Presupuesto de 5 líneas, delta-only, severidades separadas; medir tasa y podar detectores ruidosos |
| Estado delta corrupto o desincronizado | El estado es descartable: borrar `.vigia/state.json` solo causa un re-reporte, nunca un falso silencio |
| Fatiga de hook (latencia percibida en cada edit) | Presupuesto p95 como criterio de aceptación; fail-silent ante timeout |
| El reflejo no cubre desviación semántica profunda | Límite declarado (§4): eso es la capa de contratos + el oráculo de tests, no el tier 0 |

## 10. Pregunta abierta principal

¿Cuándo se fusionan los dos PRDs? Propuesta: después de la primera semana de métricas reales de v1 — con datos, la discusión "reflejo primero vs plano de control primero" deja de ser opinión.
