vet: veredicto sobre código generado por IA
codafort vet da un veredicto sobre lo que cambió desde HEAD o desde la base del PR: lo que bloquea, lo que es consultivo y lo que no corrió.
Evalúa solo el delta, en cinco ejes. Lo probado va a blocking y reprueba; el resto va a advisory. El veredicto dice qué ejes no corrieron, y un eje omitido nunca cuenta como limpio.
Uso
codafort vet # el delta vs HEAD (cambios sin commit)
codafort vet --all # el proyecto entero
codafort vet --json # el veredicto completo, coda-vet/1 (agentes/CI)
codafort vet --base origin/main # en un PR: el delta es vs la BASE, no vs HEAD
codafort vet --ingest tsc.out # correlaciona lo que el CI ya corrió (tsc/junit-xml/lcov)
codafort vet --evidence-out vet.json # escribe coda-evidence/1 (modality: vet) para la declaración contrafirmada
Exit codes: 0 pasa · 1 veredicto bloqueante · 2 error de ejecución. Con el 2, una falla de ejecución nunca se lee como bloqueo.
Los ejes
| Eje | Pregunta | ¿Bloquea? |
|---|---|---|
V0-security-delta | ¿Hay un finding de seguridad nuevo en el cambio? (el mismo análisis de scan, solo en el diff) | confirmado sí; sospechoso es consultivo |
V1-ingested-diagnostics | ¿El tsc/junit/lcov que el CI ya corrió tiene error en línea nueva? | error sí; warning aconseja |
V2-vacuous-tests | ¿Prueba que pasa sin probar (assert True, sin aserción, mock que devuelve lo esperado)? | consultivo |
V3-public-contract | ¿Símbolo público eliminado/renombrado (contract_breaks)? | sí |
V4-blast-radius | Alcance: archivos, líneas, archivos críticos tocados, wide | consultivo; alimenta prioridad |
health | Complejidad ciclomática/cognitiva de las funciones tocadas | nunca |
V1 solo corre con --ingest; V3/V4 necesitan un delta. Cuando no corren, aparecen en axes.skipped, no en ran.
codafort no ejecuta tsc, pytest ni otra herramienta: lee el artefacto que el CI ya produjo y lo cruza con el diff.
La forma del veredicto (coda-vet/1)
{
"schema": "coda-vet/1",
"blocked": true,
"axes": { "ran": ["V0-security-delta", "V2-vacuous-tests", "V3-public-contract", "V4-blast-radius"],
"skipped": ["V1-ingested-diagnostics"] },
"blocking": [ { "id": "SF-1", "severity": "Critical", "tier": "confirmed", "rule": "TAINT-COMMAND-INJECTION",
"cwe": [78], "file": "./app.py", "line": 7, "is_new": true,
"taint": { "source": "os.environ", "sink": "os.system" } } ],
"advisory": [],
"ingested": [],
"contract_breaks": [ { "file": "./app.py", "symbol": "antiga_api", "kind": "function" } ],
"scope": { "files_changed": 1, "lines_added": 4, "critical_files": [], "wide": false },
"diff_only": true
}
Contrato publicado en /schemas; un veredicto real capturado en /schemas/fixtures/coda-vet.json.
En el CI (GitHub Action)
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # la BASE debe existir localmente
- uses: codafort/gate-action@v1
with:
source: '.'
vet: 'true' # default
vet-base: 'origin/main' # vacío = base del PR (github.base_ref)
vet-ingest: 'tsc.out' # opcional: artefacto ya producido por el CI
vet-enforce: 'true' # un veredicto BLOQUEANTE reprueba el job
En un checkout superficial el step intenta git fetch de la base. Si aún no resuelve, vet se omite con ::warning (report-only) o reprueba (vet-enforce: true); nunca pasa en silencio sin haber corrido. Detalles en CI y el gate.
Del veredicto a la declaración contrafirmada
--evidence-out vet.json escribe la contribución coda-evidence/1 con modality: "vet": blocking, advisory, scope, axes_ran, axes_skipped, verdict_digest. Adjunta con codafort attest create --evidence vet.json, entra al ledger que codafort contrafirma. Ver La cadena de evidencia y Declaración contrafirmada (el artefacto coda-attestation/1).