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

AxisQuestionBlocks?
V0-security-deltaIs there a new security finding in the change? (the same analysis as scan, on the diff only)confirmed yes; suspected is advisory
V1-ingested-diagnosticsDoes the tsc/junit/lcov CI already ran have an error on a new line?error yes; warning advises
V2-vacuous-testsA test that passes without testing (assert True, no assertion, mock returning the expected value)?advisory
V3-public-contractPublic symbol removed/renamed (contract_breaks)?yes
V4-blast-radiusScope: files, lines, critical files touched, wide?advisory; feeds priority
healthCyclomatic/cognitive complexity of the touched functionsnever

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).