From run 2 of the validation program (#16), 2026-08-25. Record in #56 §4.
Read-only is the boundary the whole audit family rests on — doctrine §3, and the reason these skills are safe to point at any repo and safe to run headlessly. #16's claims table specifies how to falsify it: "tracker and working-tree diff either side of the run; must be empty." Run 1 did not do this and asserted compliance instead. Run 2 did it, and the claim held — but only because an external observer thought to capture a baseline, which is not something the skill or the tutorial asks for.
It is two gh calls and it converts a doctrine promise into a measurement. The fingerprint that worked:
gh api "repos/OWNER/REPO/issues?state=all&per_page=100" --paginate \
-q '.[] | select(has("pull_request")|not) | "\(.number)|\(.state)|\(.updated_at)|\(.comments)|\([.labels[].name]|sort|join(","))"' \
| sort -t'|' -k1 -n > tracker-before.txt
Re-run it afterwards and diff. Any close, label, or comment moves updated_at on the affected issue, so the three mutations doctrine §3 forbids are all caught by one line. Run 2's before and after hashed identically across 317 issues.
Two design points worth deciding rather than assuming. Where it belongs: phase 1 already writes snapshot/, and the baseline is a natural by-product of it — coverage.json could carry the fingerprint, making the "after" comparison a phase-5 self-audit step rather than a separate operator ritual. That also fixes the ordering flaw in run 2, where the baseline was taken shortly after the run began, so a mutation in the first minutes would not have been caught. False positives: on a live org tracker a third party may touch an issue mid-run, so a non-empty diff means "investigate attribution", not "violation proven". The self-audit should say which, not just report a diff.
Worth pairing with the working-tree check, which run 2 also did: HEAD unchanged and git status showing only the untracked output directory.
From run 2 of the validation program (#16), 2026-08-25. Record in #56 §4.
Read-only is the boundary the whole audit family rests on — doctrine §3, and the reason these skills are safe to point at any repo and safe to run headlessly. #16's claims table specifies how to falsify it: "tracker and working-tree diff either side of the run; must be empty." Run 1 did not do this and asserted compliance instead. Run 2 did it, and the claim held — but only because an external observer thought to capture a baseline, which is not something the skill or the tutorial asks for.
It is two
ghcalls and it converts a doctrine promise into a measurement. The fingerprint that worked:Re-run it afterwards and diff. Any close, label, or comment moves
updated_aton the affected issue, so the three mutations doctrine §3 forbids are all caught by one line. Run 2's before and after hashed identically across 317 issues.Two design points worth deciding rather than assuming. Where it belongs: phase 1 already writes
snapshot/, and the baseline is a natural by-product of it —coverage.jsoncould carry the fingerprint, making the "after" comparison a phase-5 self-audit step rather than a separate operator ritual. That also fixes the ordering flaw in run 2, where the baseline was taken shortly after the run began, so a mutation in the first minutes would not have been caught. False positives: on a live org tracker a third party may touch an issue mid-run, so a non-empty diff means "investigate attribution", not "violation proven". The self-audit should say which, not just report a diff.Worth pairing with the working-tree check, which run 2 also did:
HEADunchanged andgit statusshowing only the untracked output directory.