vet: verdict on AI-written code
codafort vet gives a verdict on what changed since HEAD or since the PR base: what blocks, what is advisory and what did not run.
It judges only the delta, on five axes. What is proven goes to blocking and fails; the rest goes to advisory. The verdict states which axes did not run, and a skipped axis never counts as clean.
Usage
codafort vet # the delta vs HEAD (uncommitted changes)
codafort vet --all # the whole project
codafort vet --json # the full verdict, coda-vet/1 (agents/CI)
codafort vet --base origin/main # in a PR: the delta is vs the BASE, not HEAD
codafort vet --ingest tsc.out # correlate what CI already ran (tsc/junit-xml/lcov)
codafort vet --evidence-out vet.json # write coda-evidence/1 (modality: vet) for the counter-signed declaration
Exit codes: 0 pass · 1 blocking verdict · 2 execution error. With 2, an execution failure never reads as a block.
The axes
| Axis | Question | Blocks? |
|---|---|---|
V0-security-delta | Is there a new security finding in the change? (the same analysis as scan, on the diff only) | confirmed yes; suspected is advisory |
V1-ingested-diagnostics | Does the tsc/junit/lcov CI already ran have an error on a new line? | error yes; warning advises |
V2-vacuous-tests | A test that passes without testing (assert True, no assertion, mock returning the expected value)? | advisory |
V3-public-contract | Public symbol removed/renamed (contract_breaks)? | yes |
V4-blast-radius | Scope: files, lines, critical files touched, wide? | advisory; feeds priority |
health | Cyclomatic/cognitive complexity of the touched functions | never |
V1 only runs with --ingest; V3/V4 need a delta. When they do not run, they appear in axes.skipped, not in ran.
codafort does not execute tsc, pytest or any other tool: it reads the artifact CI already produced and matches it against the diff.
The verdict shape (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
}
Contract published at /schemas; a real captured verdict at /schemas/fixtures/coda-vet.json.
In CI (GitHub Action)
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # the BASE must exist locally
- uses: codafort/gate-action@v1
with:
source: '.'
vet: 'true' # default
vet-base: 'origin/main' # empty = PR base (github.base_ref)
vet-ingest: 'tsc.out' # optional: artifact CI already produced
vet-enforce: 'true' # a BLOCKING verdict fails the job
On a shallow checkout the step tries a git fetch of the base. If it still does not resolve, vet is skipped with a ::warning (report-only) or fails (vet-enforce: true); it never passes silently without having run. Details in CI and the gate.
From verdict to counter-signed declaration
--evidence-out vet.json writes the coda-evidence/1 contribution with modality: "vet": blocking, advisory, scope, axes_ran, axes_skipped, verdict_digest. Attached with codafort attest create --evidence vet.json, it enters the ledger codafort counter-signs. See The evidence chain and Counter-signed declaration (the coda-attestation/1 artifact).