fix(measurement): purge __pycache__, so a run measures the tree it just rewrote - #60
Conversation
|
❔ Contributor Check: UNKNOWN
Automated check by AgenTrust Contributor Check. |
|
The cache purge is the important half of this and I want it. Eight runs on one unchanged tree returning 8, 5, 2, 0, 5, 2, 5, 5 is a harness that cannot be cited, and the reason is exactly as you say: The part that matters most is your point that it fails upward. A guard that inflates margins reports unguarded checks as verified, and this repo's whole output is a conformance claim, so that is the one direction it must never fail in. The REPORT.md note about restoration is the same insight and worth keeping: One thing to change. I merged #59 a few minutes ago, which adds the same import guard as Could you rebase onto main and drop One difference worth knowing: #59 tests |
…st rewrote agentrust-io#59 landed the import-path guard from this branch. This is the other half, and it is the half that fails upward. Status.FAIL and Status.PASS are the same length, so rewriting a site leaves the file size unchanged and a run can end up measuring the previous iteration's bytecode. The failures that iteration caused are then attributed to the site currently under mutation. Margins come out larger than they are, and a check held by exactly one test is reported as comfortably covered - which is the one direction this instrument must never fail in. Measured on two fresh clones, one at 424c2f8 and one at this commit, each run starting from a purged cache, nothing else touching either directory: 424c2f8 margin-1 watch list: 0, 0, 0, 2 this commit margin-1 watch list: 8, 8, 8, 8 The list has eight entries; REPORT.md names them and calls them the number to watch. On main that watch list came out empty three times in four. The second symptom is visible without reading any numbers. After four runs on 424c2f8, pytest reports 14 failures against a tree git status calls clean: the source is restored, the bytecode is not. On this commit pytest is green after every run. REPORT.md is corrected on that point - it said the checkout was "restored and re-verified green after every mutation", true of the files and not of what Python then executed - and its guard list now covers both the guard agentrust-io#59 added and this one. The module docstring says five guards and lists five; agentrust-io#59 added a fourth without updating the count. The expected baseline in the reproduction block is updated from 118 to 161, which is what this tree produces. Signed-off-by: lywinged <48041247+lywinged@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
d8a9e48 to
ddeffa2
Compare
|
Force-pushed onto 424c2f8 and narrowed. #59 took the import-path guard, so this is now only the cache purge, and the title and description are updated to match. Worth flagging one thing that changed with it: The A/B numbers in the description are freshly measured on two clones at 424c2f8 and at this commit. They are not the numbers that were here before — the earlier run was taken on the pre-#59 tree, and a second attempt was invalidated when I ran an unrelated |
The audit that prompted this: revert each fix we have landed and count what fails. Ten of eleven have a test that notices. The one that does not is agentrust-io#59 - delete assert_suite_imports_this_checkout() from main() and the suite stays green at 161 passed. measurement/ had no tests at all, so every guard in the script was in exactly the state the script exists to detect. Thirteen tests, one broken precondition each, run against a synthetic checkout - the smallest tree with the shape the script requires, carrying one guarded check and one unguarded one so the reporting path is covered and not only the aborts. Run as a subprocess rather than imported, because the script resolves TRACE_TESTS at module scope and importing it would bind that to this repository. Exit codes are not asserted alone: a refusal exits 1 and so does a completed run that found an unverified check, so every test asserts on the message. Load-bearing, measured rather than claimed. Removing each guard and re-running: checkout not found 3 tests fail no FAIL sites 2 refusing to guess the line 1 import path 2 green baseline 1 __pycache__ purge 2 restore after each site 3 Every one of the thirteen fails under at least one removal. Two are covered by the purge alone, one of them the reporting control - the synthetic tree reproduces the cache contamination rather than only asserting the directory is gone. Two things this turned up while writing it. A test named for the "refusing to guess" guard was reaching "no FAIL sites" instead: its fixture bound Status.FAIL to a name, so the walk never collected the site. Renamed and given a fixture that reaches the guard, with the original behaviour kept as its own test - a Finding whose status arrives through a name is not collected, the run reports on what it found without mentioning the omission, and nothing here writes them that way. Recorded rather than fixed, and the test fails if that stops being true. 174 pass, and identical across two consecutive runs. ruff clean on the new file, and the same 68 pre-existing errors either side of this change. Signed-off-by: lywinged <48041247+lywinged@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
imran-siddique
left a comment
There was a problem hiding this comment.
The cache purge occurs before every subprocess suite run, directly addressing same-size source rewrites and pytest bytecode caches. The synthetic harness covers the positive reporting path, each refusal guard, restoration, and cache removal; the earlier import-guard conflict is resolved.
…ema (#87) tests/test_enum_parity.py holds the hand-written enums to the schema. Nothing holds the digest-format pattern, and there are more copies of it than there are of any enum. The string ^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$ sits in eleven places: six pattern values in schemas/trace-claim.json (model.weights_digest, runtime.measurement, policy.bundle_hash, tool_transcript.hash, delegation.parent_record_hash, build_provenance.digest) and five compiled constants (_DIGEST_RE in tr_pol, tr_rte, tr_sca and tr_txn, plus test_level0.DIGEST_RE). All eleven are byte-identical today. The count is measured rather than assumed: a full-depth walk of the schema finds six digest-shaped patterns and no seventh, and git grep -F for the string finds eleven lines in six files. src/trace_tests/inclusion.py and tests/test_report.py each pin a sha256-only pattern, which is a narrower rule and not a twelfth copy; the module docstring says so, so the next reader does not have to re-derive it. measurement/scripts/enum_drift.py cannot find these. It discovers copies by walking the AST for set literals of string constants, so a compiled regex is invisible to it by construction. That is why the five enum copies were guarded and these eleven were not. Each site was shown load-bearing before this was opened. A one-character drift was planted at each of the eleven in turn and run through the full suite. All eleven red, and every failure names the site that moved. Under a sha384 length drift, {96} to {97}, seven of the eleven are caught by no other test in the suite; under a sha256 length drift, {64} to {65}, every site but one is caught elsewhere. model.weights_digest has no other guard under either shape. The drift runs were executed with __pycache__ purged before each, per #60. The compiled copies are compared against model.weights_digest, the first listed schema site. Any of the six would serve, and test_every_schema_digest_site_holds_one_pattern is what makes that choice arbitrary rather than load-bearing. Drift at that one site therefore reds six cases instead of one, which is accurate rather than noisy: the string the copies are all held to is the one that moved. The schema sites are named rather than discovered by walking, so a seventh digest field appearing later fails test_every_known_site_is_listed instead of joining silently. Signed-off-by: opento-suggestions <opentosuggestionsofficial@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two commits. #59 took the import-guard half of this branch; what remains is the cache purge, plus tests for the guards themselves — which nothing was checking, including the one #59 landed.
1. Purge
__pycache__, so a run measures the tree it just rewroteStatus.FAILandStatus.PASSare the same length, so rewriting a site leaves the file size unchanged and a run can end up measuring the previous iteration's bytecode. The failures that iteration caused are then attributed to the site currently under mutation: margins come out larger than they are, and a check held by exactly one test is reported as comfortably covered.Two fresh clones, one at
424c2f8and one at this commit, each run starting from a purged cache, nothing else touching either directory:The list has eight entries —
REPORT.mdnames them and calls them "the number to watch". On main that watch list came out empty three times in four. That is the wrong direction to be wrong in: the run reports a healthier suite than the one that exists.The second symptom needs no numbers. After four runs on
424c2f8:Every mutated source file was restored, and the restoration is verified. The interpreter was still loading bytecode compiled from a mutated one.
REPORT.mdsaid the checkout was "restored and re-verified green after every mutation" — true of the files, not of what Python then executed. Corrected here.2. Tests for the guards
This came out of an audit: revert each fix landed in these two repositories and count what fails. Ten of eleven have a test that notices. The one that does not is #59 — delete
assert_suite_imports_this_checkout()frommain()and the suite stays green at 161 passed.measurement/had no tests at all, so every guard in the script was in exactly the state the script exists to detect.Thirteen tests, one broken precondition each, against a synthetic checkout — the smallest tree the script accepts, carrying one guarded check and one unguarded one so the reporting path is covered and not only the aborts.
Load-bearing, measured rather than claimed. Removing each guard and re-running:
__pycache__purgeEvery one of the thirteen fails under at least one removal. The purge accounts for two, one of them the reporting control — the synthetic tree reproduces the cache contamination rather than only asserting the directory is gone.
Two things this turned up while writing it
A test named for the "refusing to guess" guard was reaching "no FAIL sites" instead: its fixture bound
Status.FAILto a name, so the walk never collected the site. Renamed, given a fixture that reaches the guard, and the original behaviour kept as its own test — aFindingwhose status arrives through a name is not collected, and the run reports on what it found without mentioning the omission. Nothing in this repository writes them that way and the site count is checkable againstgrep -c Status.FAIL, so it is recorded rather than fixed, and the test fails if that stops being true.Run as a subprocess rather than imported, because the script resolves
TRACE_TESTSat module scope and importing it would bind that to this repository. Exit codes are not asserted alone: a refusal exits 1, and so does a completed run that found an unverified check.Also in this diff
REPORT.md's guard list now covers both the guard measurement: refuse to report when the suite imports a different tree #59 added and the purge; it had neither.# expect 118 passed, 5 xpassed. This tree produces 174.Verification
Fresh clone, in a directory that has never held the repository: 174 pass, 5 xpassed, identical across two consecutive runs.
ruffclean on the new file, and the same 68 pre-existing errors either side of this change.The A/B measurement above was re-run from scratch after an earlier attempt was invalidated — I had run an unrelated
git checkoutinside the directory the experiment was using, which swapped the script under test mid-run. Given what this PR is about, that seemed worth saying rather than quietly re-reporting.