Skip to content

v0.10.0 — the contract release

Choose a tag to compare

@Mormolykos Mormolykos released this 29 Jul 07:00

Nothing about how trainproof judges a run changed in this release. No rule, no threshold, and no verdict moved — every gallery verdict is locked in tests/golden/ and byte-identical to v0.9. What changed is what trainproof promises, now written down in CONTRACTS.md.

New: CONTRACTS.md

An explicit statement of what this tool guarantees: exit codes, JSON schema policy, rule-ID stability, SARIF mapping, the verdict-stability guarantee, and the pre-1.0 breaking-change policy.

Writing it found a real bug that had already shipped. See below.

New: SARIF 2.1.0 output

--sarif PATH on data, tokenizer, epoch, doctor, compare and preflight. Findings become GitHub PR annotations, so a doomed fine-tune is flagged inline on the diff that caused it instead of buried in a CI log nobody opens. Works independently of --json.

--json is now available on data, tokenizer and preflight as well (previously epoch, doctor and compare only).

Breaking: exit codes

2 now means "trainproof could not judge" — unreadable log, missing file, no parsed records, missing optional dependency. Several of these previously exited 1, which is reserved for a FAIL verdict about your run.

CI that treats any non-zero code as failure is unaffected. Anything that distinguishes 1 from 2 should be reviewed.

An unreadable log no longer reports worst_verdict: "FAIL" in JSON. It reports worst_verdict: null with a populated error key, and exits 2.

Breaking: schema_version is now 2

doctor no longer emits a separate compare_findings key. Single-run and baseline findings live in one findings array, each tagged with source (single_run or compare). The envelope gained an error key.

Fixed: doctor --baseline printed FAIL and exited 0

The exit code was computed from single-run verdicts alone and never consulted the comparison, so doctor --baseline could print [FAIL] comparison findings and still exit 0. The printed output had been telling the truth; the exit code had been lying. bad_labels against healthy is the reproducing case.

Found by the contract work — writing down which of the two signals was authoritative is what exposed that no test had ever checked they agreed.

Also fixed:

  • Uncaught internal errors previously fell through to Python's default exit code 1 and were indistinguishable from a FAIL verdict. A top-level handler now reports them as 2.
  • A missing transformers install is no longer a FAIL verdict on your dataset (rule TP-PRE-TRANSFORMERS-MISSING removed); it is a tool error, exit 2.
  • "Cannot judge" messages now go to stderr, leaving stdout parseable.
  • RULES.md no longer carries a stale version stamp.
  • The v0.5.0 entry in the changelog described a coroner command that was never implemented; corrected to epoch.

Regression locking

Gallery snapshots in tests/golden/ now pin the verdict and the complete rule ID set for all six runs and seven baseline comparisons. A rule that stops firing and one that starts firing spuriously both fail the build.

Two new tests enforce that rule IDs in the source and in RULES.md match in both directions, and that the two declared version strings agree.


85 tests passing. 57 stable rule IDs. pip install trainproof