7.7 KiB
proveit.py — a runner that re-checks every claim it prints
Lane: Codeforge. Purpose: make the evidence law mechanical instead of rhetorical.
The law this artifact serves: "the executor said done" is not "a test passed", and
"prepared" is not "observed". So this runner has no prose-only claim path. A status
string is only ever written by Suite.make_claim(), which invokes the predicate,
records the outcome, and derives PASS/FAIL from what actually happened. There is
no other place in the source that assigns to status, and no code path that can
emit UNVERIFIED — that word is not assignable anywhere, which is checked
structurally (AST), not by substring.
What is measured, not asserted
| claim | how it is measured |
|---|---|
| file exists and is non-empty | open().read() length |
| interpreter is 3.11+ | sys.version_info at runtime |
sqrt(2)**2 is 2 to 1e-12 |
math |
| predicate really ran 3 times | repeat=3, call counter |
| a false claim is reported FAIL | negative control, 2+2==5 |
| a raising predicate is FAIL | negative control, ZeroDivisionError |
| a controlled exception is PASS | expect_exception= |
| a truthy non-bool is NOT a pass | contract 12, lambda: "yes" |
a raise outside expect_exception stays FAIL |
contract 13 |
| a subclass of the expected exception is expected | contract 14 |
| an incomplete run is harness-fault exit 2 | contract 15 |
Commands
python3 proveit.py # demo report; exits 1 (contains known-false controls)
python3 proveit.py --json # same report, machine-readable
python3 proveit.py --demo-fail# a single-claim failure, so you see what failure prints
python3 proveit.py --selfcheck# meta-test: plants bad claims, asserts each is caught
python3 proveit.py --scan-status # the status literals this source can assign
Exit codes: 0 all claims PASS, 1 at least one claim not PASS, 2 harness broke.
The judge (external, does not import proveit)
_judge.py runs the artifact as a subprocess and checks 33 observable checks
across 15 contracts: exit codes, report contents, JSON coherence, the honesty
properties, path robustness (the artifact copied elsewhere must still find and scan
itself), the negative control that a raising predicate is never reported PASS, that
repeat=N really invokes the predicate N times, that an incomplete run (a
predicate that raises before repeat is met) reports measured=False rather than
True, that only a strict True is a PASS, that expect_exception is not a blanket
pass, that a subclass of the expected exception still counts as expected, and that a
harness fault is exit 2 rather than a silently downgraded 1.
python3 _judge.py
Teeth (the verdict is not a tautology)
_probe_teeth.py plants a real defect — rewriting the runner's PASS to
UNVERIFIED — and asserts the judge exits nonzero.
_probe_mutations.py is the stronger check: it plants 19 plausible regressions
(swapped PASS/FAIL derivation, a neutered run loop, a hardcoded measured = True,
a lying summary count, a capped repeat, a truthy non-bool accepted as PASS, ...)
and requires the judge to exit nonzero on every one. A judge that always exits 0
cannot pass it.
python3 _probe_mutations.py
The hunt (the open question)
_hunt_blindspots.py asks what the mutation list does not contain: it applies
further candidate regressions to a throwaway copy and reports which ones the judge
cannot see. Four in-scope holes were found this way (see bugs 6-9 below) and are now
pinned by contracts 12-15 and by matching mutation entries. Four candidates remain
unpinned and are reported honestly rather than faked:
| candidate | why it is not pinned |
|---|---|
detail drops the error text |
detail is not a documented claim; no contract asserts it |
seconds always zero |
timing is not a documented claim |
status ignores measured |
equivalent: measured is False only when a raise already set ok=False |
| selfcheck always returns OK | unmeasurable from outside — it is the meta-test's own verdict |
python3 _hunt_blindspots.py
The battery (one command, one exit code)
verify.py runs all five stages and exits 0 only if each passed and the judge
demonstrated teeth:
python3 verify.py
Its stage 3 success token pins the count ("19/19"), so a newly planted defect that slips past the judge fails the battery instead of silently widening the blind spot.
Measured result
proveit.py --selfcheck→ exit 0, "8/8 meta-assertions hold"proveit.py→ exit 1,5/7 claims PASS, 2 FAIL(the two FAILs are the intentional negative controls — the runner is honest, not broken)_judge.py→ exit 0, 33/33 observable checks hold (15 contracts)_probe_mutations.py→ exit 0, 19/19 planted defects caught_probe_teeth.py→ exit 0, mutated artifact rejected with judge exit 1_hunt_blindspots.py→ exit 0, every in-scope candidate caughtverify.py→ exit 0, 5/5 stages passed
Bugs this process actually caught (evidence, not narrative)
- The first self-check asserted "no
status="UNVERIFIED"substring" — but that literal appears in the file's own docstring. The check was unsound; it was rewritten as a structural AST scan. HERE = __file__.rsplit("/", 1)[0]broke when the artifact was imported from another directory, so the self-scan silently found nothing. Fixed toos.path.dirname(os.path.abspath(__file__)); the judge now covers this with a copy-elsewhere test.- The AST scan only looked for bare
ast.Constant, so it missed theFAILbranch ofstatus="PASS" if ok else "FAIL"and reported only{"PASS"}. The judge caught the divergence between--scan-statusand the selfcheck. Fixed by resolvingIfExpbranches; the scan now reports{"FAIL", "PASS"}. - The judge could not see
measured = Truehardcoded. Contracts 8-10 all run the loop to completion, whereran >= max(1, repeat)is True either way, so the mutation scored 14/15 and read as a coverage hole. The observable difference lives in an incomplete run: a predicate that raises on the first ofrepeat=3invocations leavesran=1 < 3, so correct code reportsmeasured=Falseand the mutation reportsTrue. Contract 11 pins that; sensitivity went to 15/15. _probe_teeth.pydid its "point the judge at the mutated artifact" step with a string replace against a hardcoded absolute path that no longer existed in_judge.py. The replace matched nothing, so the probe's premise was never checked and it could have printedTEETH OKover an unmutated artifact. It now copies both files into a scratch dir and asserts the mutation actually applied.ok = bool(value)— a predicate returning a truthy non-bool ("yes",1,[1]) was reported PASS. The runner's whole claim is that PASS means an observedTrue, so this is a real lie, and no contract ever handed the runner a non-bool. Measured before the fix: judge exit 0 on the mutated tree. Contract 12 pins it.ok = True if expect_exception else False— naming an expected exception excused any raise. AZeroDivisionErrorwithexpect_exception=ValueErrorwas PASS. Measured before the fix: judge exit 0. Contract 13 pins it.ok = type(exc) is expect_exception— stricter-looking and wrong: a subclass of the named exception was no longer treated as expected. Measured before the fix: judge exit 0. Contract 14 pins it.- Removing
FAILURES += 1— the artifact documents "2 == harness broke" and claims a broken harness cannot silently pass, but every claim-level contract looks only atstatus/measured, so the fault was downgraded to exit 1 unnoticed. Measured before the fix: judge exit 0. Contract 15 pins it, and sensitivity went to 19/19.