fix(hygiene): untrack .coverage — the verdict command was rewriting a tracked binary - #119
Conversation
… tracked binary `.coverage`, a 90 KB coverage.py SQLite database, has been TRACKED on main since #109 — a typing PR whose every other changed file is about mypy. Nobody chose to commit it; it was in the working tree when the branch was staged, the same accident that put src/UNKNOWN.egg-info/ on #113. It is build output, and the churn is mechanical rather than occasional: verify.py's coverage_reset() UNLINKS ROOT/.coverage and ROOT/.coverage.* before every instrumented run and coverage_combine_and_report() writes them again. So while tracked, the repo's own verdict command showed up as a deletion of a tracked binary followed by a re-add of different bytes, and an uncommitted copy blocked a branch switch. BOTH patterns, because two different steps write them: `--parallel-mode` writes one `.coverage.<host>.<pid>.<random>` per instrumented child (~90 subprocesses per run) and `coverage combine` merges them into `.coverage`. Ignoring only the combined file leaves the per-child data files unignored. ROOT-ANCHORED per the rule #72 established for every debris pattern in .gitignore, and true of the producer here: verify.py globs and unlinks ROOT, so the checkout root is the only place these land. `/.coverage.*` and NOT `/.coverage*` — the second also swallows `.coveragerc`, coverage.py's own config file, which is source. And UNTRACKING is what makes the patterns bite: `git add -A` skips an ignored UNTRACKED path but stages an ignored TRACKED one, so the pattern alone would have changed nothing. test_repo_artifact_hygiene.py had NO case for this file — `grep -n coverage` on it returned nothing — so nothing would have caught a recurrence. Five added, matching the suite's three halves: two parametrized ignore cases, one untracked case, and two must-stay-committable cases. Break -> revert performed in all three directions the patterns can fail (too narrow, too broad by kind, ignored-but-re-tracked); each broke exactly one case, and the revert was byte-identical. Recorded in the file. The depth direction the langsmith patterns record has no in-tree near-miss here and is not claimed to; the anchoring is kept for the producer's reason instead. Verified: 458 passed, 0 failed, 0 skipped, 85/85 selftests, 5/5 gates. Floor 453 -> 458, measured on the merge result after fast-forwarding onto origin/main 5c769e0 and re-fetching to confirm main had not moved again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 48 minutes. View limit detailsLimit details: You’ve used the included review currently available. Your 70 included PR review attempts over the past 7 days set your current allowance at 1 review per hour. Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
Comment |
Workflow source neededPR #119 needs either a linked GitHub issue or one valid non-issue Workflow Source before PR metadata automation can manage it safely. Please do one of:
Once a valid source is present, this warning will not be reposted. |
Automated Status SummaryHead SHA: 27ce7a1
Coverage Overview
Coverage Trend
Top Coverage Hotspots (lowest coverage)
Low Coverage Files (<50.0%)
Updated automatically; will refresh on subsequent CI/Docker completions. Keepalive checklistScopeNo scope information available Tasks
Acceptance criteria
|
|
Workflow state fingerprint for Agents Gate Followups. Do not edit. |
…ported as 34.11% (#159) * ci: measure this repo's REAL coverage, which is 82.1% and has been reported as 34.11% pr-00-gate.yml measures coverage with pytest alone. Measured 2026-08-29 by running the command this job adds: 82.1%, over 44,740 statements with 8,026 missed, combined from 94 instrumented processes. The Gate reports 34.11%. Forty-eight points. WHY. 79 of 102 modules are exercised by a `--selftest` entry point rather than a pytest file -- the original design, from the initial commit -- and a selftest runs as a SUBPROCESS that pytest-cov cannot see. So the Gate has been measuring the instrument's blind spot and calling it a score: the same defect stranske/Workflows#3251 drained out of the fleet reporter, where a number nobody could measure rendered as a number that was measured. It mattered beyond cosmetics. Every other repo in the fleet now carries a config/coverage-baseline.json set at measured current; this one was deliberately SKIPPED, because a baseline against the wrong instrument would bake the blind spot in permanently. A SEPARATE JOB, NOT A FLAG ON THE VERDICT: * `--coverage` wraps ~94 child processes in `coverage run --parallel-mode`, so it is materially slower than the verdict, which is on the critical path for every PR. Measured in CI, the verify job is ~1m30s; this runs beside it rather than in front of it. (My first instinct was that this was too expensive to run at all -- from timing it locally at 7 minutes. That was the Dropbox tax CLAUDE.md warns about, not the real cost. CI is the number that decides.) * `--coverage` deliberately never touches the exit code, and verify.py pins that in a test, precisely so measuring cannot change a verdict. Its own job keeps that true by construction. continue-on-error, because a coverage MEASUREMENT must never redden a green verdict -- but the failure stays visible in the job list rather than being swallowed. Both summary branches self-describe: verify.py emits `coverage: NO DATA` and `coverage: FAILED` as well as the combined report, so an absent measurement prints as absent rather than as a low number. NOT YET WIRED TO THE GUARD, and the summary says so rather than implying enforcement. Maint Coverage Guard reads pr-00-gate.yml by a hardcoded workflowId and cannot see this job, so this repo still has no baseline. Publishing the honest figure is step one; teaching the guard to read it is a separate change -- the same hardcoded-workflowId limitation that makes stranske/Counter_Risk invisible to it. Verified: verify.py --coverage exits 0 and produces the report above. `.coverage` remains gitignored (#119), so uploading it as an artifact cannot reintroduce the tracked binary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: cite the CI figure, not the local one — they differ by 1.8 points and CI is authoritative The first version of this job's comment quoted 82.1%, measured on the owner's machine. This runner reports 80.3%: 44,741 statements, 8,828 missed, same 94 instrumented processes. The difference is real, not noise. 802 statements execute only where this instance's prerequisites exist -- agent CLIs, ~/.codex, a populated ledger -- and are missed on a bare runner. They are exactly the paths env_prereq guards, so the gap is the skip surface showing up in coverage rather than a measurement artefact. CLAUDE.md already says to take coverage from CI rather than a local run. Quoting the local number in the job that exists to establish the honest one would have overstated the repo by nearly two points, in a comment future readers would treat as the record. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(ci): address CodeRabbit review on combined-coverage job Pin checkout/setup-python/upload-artifact to verified SHAs, use shell: bash on the coverage pipeline so verify.py exit status survives tee, and publish the TOTAL row in the step summary. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Tim Stranske <tim@stranskemo.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
What
git rm --cached .coverage, root-anchored/.coverage+/.coverage.*in.gitignore, and five cases intests/test_repo_artifact_hygiene.pyso a recurrence is caught.Why
.coverage— a 90 KB coverage.py SQLite database — has been TRACKED on main since #109, a typing PR whose every other changed file is about mypy. It was in the working tree when the branch was staged, the same accident that putsrc/UNKNOWN.egg-info/on #113.It is build output, and the churn is mechanical rather than occasional:
verify.py'scoverage_reset()UNLINKSROOT/.coverageandROOT/.coverage.*before every instrumented run, thencoverage_combine_and_report()writes them again. So while tracked, this repo's own verdict command showed up as a deletion of a tracked binary followed by a re-add of different bytes — and an uncommitted copy blocked a branch switch.tests/test_repo_artifact_hygiene.pyexists precisely to assert that generated artifacts are gitignored and untracked, andgrep -n coverageon it returned nothing. The suite had no opinion about the one artifact this repo produces itself.The patterns, and why each detail
--parallel-modewrites one.coverage.<host>.<pid>.<random>per instrumented child (~90 subprocesses per run) andcoverage combinemerges them into.coverage. Ignoring only the combined file leaves the per-child data files unignored.verify.pyglobs and unlinksROOT, so the checkout root is the only place these land./.coverage.*and NOT/.coverage*— the second also swallows.coveragerc, coverage.py's own config file, which is source.git add -Askips an ignored UNTRACKED path but stages an ignored TRACKED one, so the pattern alone would have changed nothing.Deliberate break → revert, all three directions
/.coverage.*test_coverage_data_files_are_ignored[.coverage.a-host.12345.678901]/.coverage*test_coverage_sources_are_not_swallowed[.coveragerc]git add -f .coveragetest_no_coverage_data_file_is_trackedandtest_coverage_data_files_are_ignored[.coverage](check-ignore is index-aware)Each broke exactly one case and nothing else; the revert was byte-identical with all 19 green. Recorded in the test file.
The DEPTH direction the langsmith patterns record has no in-tree near-miss here and is not claimed to — nothing tracked in this repo has a basename beginning
.coverage. The anchoring is kept for the producer's reason instead.Floor
453 → 458. Exactly five new tests, all in
test_repo_artifact_hygiene.py. No ceiling moved and nothing new is skipped — all five ask GIT about a path in this checkout (check-ignore/ls-files), so none needs a populated ledger, an agent CLI or~/.codex, and all five run on a bare runner.Measured on the merge result: fast-forwarded onto
origin/main5c769e0 (recorded floor 453) and re-fetched to confirm main had not moved again before measuring.Verified
black --line-length 100clean on the one Python file touched;ruff checkclean.🤖 Generated with Claude Code