Does trace-tests notice when its own conformance checks break?¶
Measured 2026-08-07 against agentrust-io/trace-tests @ 7fc189c (release 0.4.1), with the normative schema from agentrust-io/trace-spec @ dc7491c.
trace-tests is the suite an implementer runs to establish that their TRACE implementation conforms. Its output is a conformance claim. That makes a question one level up worth asking, and as far as the repository shows, unasked so far:
If a check inside a conformance module silently stopped working, would the repository's own test suite catch it?
When this was first measured, the answer was no for ten of the thirty-three failure paths. Those ten are now guarded, and the measurement is what established which ones needed it.
Result¶
modules 7 src/trace_tests/modules/tr_*.py
checks 18 distinct TR-xxx-nnn codes
sites 33 Finding(..., Status.FAIL, ...) constructions
by check 18 of 18 verified, 0 unverified
by site 33 of 33 verified, 0 unverified
Eight checks sit at margin 1, meaning exactly one test stands between them and silent regression. That is a floor, not a comfortable state, and it is the number to watch.
What the first measurement found¶
| Check | What it enforces | Sites then unverified |
|---|---|---|
TR-SIG-002 | cnf.jwk is an OKP/Ed25519 key and carries x | 2 of 2 |
TR-TXN-001 | tool_transcript is present at Level 2, is an object, and its hash is a well-formed digest | 3 of 3 |
TR-TXN-002 | tool_transcript.call_count is a non-negative integer | 1 of 1 |
TR-SCA-001 | build_provenance is an object | 1 site |
TR-SIG-004 | cnf.jwk.kty is a supported key type | 1 site |
TR-SIG-005 | a signature that cannot be checked fails rather than going unverified | 1 site |
TR-ANC-001 | a transparency URI that cannot be parsed fails cleanly | 1 site |
tr_txn and tr_sca had no unit tests at all, which is why every one of their failure paths measured zero. TR-TXN-001 carried the most weight: it is the Level 2 requirement that a tool transcript exist, and nothing else in the suite enforced it.
Counting by check understates the problem by more than threefold. A check code emitted from three places can have one failure path nothing verifies while the other two are covered, and still count as verified. The sites are where the checks actually live.
TR-TXN-001 carries the most weight. It is the Level 2 requirement that a tool transcript exist at all. If it regressed, implementations would continue to be stamped Level 2 conformant without that property being checked — and the transcript is the claim Level 2 is largely about.
TR-SIG-002 is the gate that rejects a key that is not Ed25519. With it inert, a record carrying another key type reaches signature verification rather than being refused at the key-type check.
Seven further failure paths inside otherwise-verified checks¶
TR-ANC-001, TR-SCA-001, TR-SIG-004 and TR-SIG-005 each have at least one site at margin 0 while other sites of the same code are verified. Those are the paths a by-code count cannot see.
Margin is thin where it exists¶
Twelve sites are held by exactly one test. That is the same no-margin property reported for the trace-spec fixture set in agentrust-io/trace-spec#124, in the repository whose output is the conformance stamp rather than in the informative fixture set.
A separate finding: a hand-written enum has drifted¶
tests/test_level0.py:7 defines VALID_PLATFORMS as a set literal restating the schema's runtime.platform enum. It holds nine values; the schema holds ten. software-only is missing.
The conformance module is correct — tr_rte.py:_VALID_PLATFORMS has all ten — so this is a defect in the self-test, not in what implementers run. Three-way comparison on a Level 0 record carrying software-only:
tr_rte.check(record, level=0) PASS (what an implementer runs)
normative schema accepts
tests/test_level0.py FAIL (contradicts both)
It is latent: no vector in tests/vectors/ uses software-only, so nothing triggers it today. It becomes active the moment one does — and it would then reject behaviour the project deliberately added, in PR #16/#17, which updated the conformance module and added tests/test_software_only_platform.py while leaving this copy behind.
Method¶
For every Finding(..., Status.FAIL, ...) construction in the conformance modules, rewrite that site so the check can never fail, run the entire suite, count how many tests notice, restore. The count is the margin. Zero means the check's failure path is unverified.
The cheap version gives the wrong answer¶
The obvious first approach is to grep the test files for TR-xxx-nnn codes and diff against what the modules emit. On this tree it reports 12 unverified checks. Mutation reports 3.
The proxy is wrong because tests exercise a module through tr_xxx.check(...) and assert on the returned findings without naming the codes. Naming is a convention; changing outcome is the property. The two are not related closely enough to substitute one for the other.
Guards, each of which produced a wrong answer during development¶
- A green baseline is a precondition. Mutating an already-red suite attributes nothing. The script runs the baseline first and aborts if it is not green.
- A mutation that did not apply is indistinguishable from a check nothing notices. Each rewrite is verified to have changed the file before the suite runs. An earlier hand-run of this measurement replaced a string that did not occur in the target, left the suite green, and would have been recorded as evidence for the opposite of the truth.
- An empty site list would report perfect coverage. Zero sites is a hard stop.
- Restoration is verified, not assumed, after every site.
- A suite that did not run against this code looks exactly like a suite that notices nothing. A stale editable install, or any
trace_testsearlier onsys.path, leaves the baseline green and every mutation unobserved: pytest never imports the file being rewritten, so the run reports zero verified. The import path is resolved and checked against this checkout before the baseline runs. - A stale
__pycache__reports a margin the code does not have.Status.FAILandStatus.PASSare the same length, so a rewrite changes no file size, and a run can end up measuring the previous iteration's bytecode. This is the worst of the guards to be missing, because it fails upward: the previous iteration's failures are attributed to the site under mutation, margins come out larger than they are, and a check held by exactly one test is reported as comfortably covered. Caches are purged before every suite run. Without the purge, four consecutive runs on one unchanged tree reported the margin-1 watch list as empty three times and as two entries once; the list has eight entries. A fourth run also leftpytestfailing on 14 tests against a treegit statusreported as clean. - An empty path is rejected before it can look valid.
Path("")is., which is a directory, so the obvious existence check passes and the script measures whichever tree it happens to be standing in. The enum comparison had exactly this hole and reported success having compared nothing — the failure it exists to find, in itself. It now refuses an empty argument, and refuses to report agreement when zero comparisons were made.
What this does not establish¶
- Not that implementations can skip these checks. The measurement is about whether a regression in the conformance module would be caught by
trace-tests' own suite. An implementation still faces whatever the module does today. - Not that the unverified checks are wrong. They may be perfectly correct. They are unguarded, which is a statement about the suite, not about them.
- Only
Status.FAILsites were mutated.PASSandSKIPpaths were not, so a check could pass for the wrong reason in a way this does not see. - The margin is a count of failing tests, not of independent tests. Five tests that fail together because they share a fixture are one test for this purpose, and this measurement does not distinguish that.
- Nothing here was reported upstream at the time of writing, and nothing in
trace-testswas modified. Every mutated source file is restored, and the restoration is verified, after every site. Restoring the file is not the same as leaving the checkout usable: until caches were purged between runs, a completed run could leavepytestfailing against a treegit statusreported as clean, because the interpreter was still loading bytecode compiled from a mutated source. The cache purge above is what makes "restored" mean what it reads as.
Reproducing¶
pip install -e ".[dev]" && pytest -q # expect 161 passed, 5 xpassed
python measurement/scripts/mutate_modules.py # exits 1 if any check is unguarded
python measurement/scripts/enum_drift.py # exits 1 on any drifted enum
Both default to the checkout they live in, derived from the script's own location rather than the working directory, and accept an explicit path as argv[1]. Both exit non-zero rather than reporting success over a corpus they could not find.