grinder: the diff-noise pathspecs need :(glob), or top-level scratch slips through

`:(exclude)**/__pycache__/**` reads as "exclude any __pycache__" and is not. Without
`glob` magic git will not let `**` span ZERO directories, so the pattern hid a nested
__pycache__ and missed one at the repo root — which is exactly where pytest puts it for
a flat repo. Every grind diff has been carrying a .pyc; @reviewer flagged the symptom on
the modeltag grind, and `git diff --stat` against both spellings shows the mechanism.

The failure is invisible by inspection, since both spellings say the same thing in
English. So tests/test_grind_noise.py builds a real git repo with real scratch and diffs
it with the real pathspec list: red on the old spelling for the top-level case only,
green on the new one, ~30 ms, no model. NOISE moved to module level to be testable at all.

Also normalised pytest-of-* and .pytest_cache to the same **/…/** + glob form; they were
top-level-only patterns and would have missed a nested occurrence.
This commit is contained in:
2026-08-02 00:43:47 +02:00
parent eafa29a074
commit 2cf4ec3778
2 changed files with 82 additions and 5 deletions
+13 -5
View File
@@ -32,6 +32,19 @@ TIERS = os.environ.get("GRIND_TIERS", "[local] deepseek-v4-flash-dspark").split
# [free] openai/gpt-oss-120b which bills a fraction of a cent despite the [free] label) and
# previously verified to route + emit FILE: blocks correctly. A stuck floor model now actually
# has somewhere to escalate to instead of ESCALATE_AT being dead code.
# The coordinator is asked to APPLY a grind's diff, so test scratch must never appear in it.
# (The sic EPIPE grind returned 12 pytest tmp files alongside the 2 real source hunks.)
#
# `glob` magic is REQUIRED, not decoration: without it git treats `**` as an ordinary
# wildcard that will not span "zero directories", so `**/__pycache__/**` excluded a NESTED
# __pycache__ but not one at the repo root — which is exactly where pytest puts it for a
# flat repo. Every grind diff carried a .pyc because of it (@reviewer flagged the symptom
# 2026-08-02; measured with git diff --stat against both spellings).
# Module-level so tests/test_grind_noise.py can check it without running a grind.
NOISE = [":(exclude,glob)**/pytest-of-*/**",
":(exclude,glob)**/__pycache__/**",
":(exclude,glob)**/.pytest_cache/**"]
PY_TIERS = os.environ.get("PY_GRIND_TIERS", "[local] deepseek-v4-flash-dspark,[free] qwen/qwen3-coder")
MAX_ITERS = 10 # total model attempts across all tiers
ESCALATE_AT = 4 # attempts on a tier without progress before escalating
@@ -375,11 +388,6 @@ def grind(task, test_rel, targets):
# whether escalation was EARNED, not just on whether it reached green.
escalations = []
# The coordinator is asked to APPLY this diff, so test scratch must never appear in it.
# (The sic EPIPE grind returned 12 pytest tmp files alongside the 2 real source hunks.)
NOISE = [":(exclude)pytest-of-*", ":(exclude)**/__pycache__/**",
":(exclude).pytest_cache/**"]
def _model_tag(b):
# #118: name the COST TIER, not just the model. The gateway may fail over, and
# a same-model free->paid escalation used to be invisible here. bullpen_modeltag
+69
View File
@@ -0,0 +1,69 @@
"""A grind's diff is meant to be APPLIED, so build scratch must never appear in it.
The exclusion list looked right and did not work: `:(exclude)**/__pycache__/**` without
`glob` magic does not span "zero directories", so it hid a NESTED __pycache__ but not one
at the repo root — which is precisely where pytest puts it for a flat repo. Every grind
diff therefore carried a .pyc (@reviewer, 2026-08-02).
That is invisible by inspection: both spellings read as "exclude any __pycache__". So the
check is a real git repo with a real top-level __pycache__, diffed with the real pathspec
list — no model, no network, ~30 ms.
"""
import os
import subprocess
import sys
import pytest
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(REPO, "lib"))
def _noise():
"""The pathspec list as bullpen-grinder actually defines it (it has no .py suffix,
so it cannot simply be imported)."""
ns = {}
src = open(os.path.join(REPO, "bin", "bullpen-grinder"), encoding="utf-8").read()
for line in src.splitlines():
if line.startswith("NOISE = ["):
block = src[src.index(line):]
block = block[:block.index("]") + 1]
exec(block, ns) # noqa: S102 — a literal list from our own repo
return ns["NOISE"]
raise AssertionError("bin/bullpen-grinder no longer defines NOISE at module level")
def _git(cwd, *args):
return subprocess.run(["git", "-C", cwd, *args], capture_output=True, text=True)
@pytest.fixture
def repo(tmp_path):
d = str(tmp_path)
_git(d, "init", "-q")
_git(d, "config", "user.email", "t@example.invalid")
_git(d, "config", "user.name", "t")
open(os.path.join(d, "calc.py"), "w").write("def add(a, b):\n return a - b\n")
_git(d, "add", "-A")
_git(d, "commit", "-q", "-m", "baseline")
return d
@pytest.mark.parametrize("scratch", [
"__pycache__/calc.cpython-314.pyc", # top level — the case that regressed
"pkg/__pycache__/calc.cpython-314.pyc", # nested — the case that already worked
".pytest_cache/v/cache/nodeids",
"pytest-of-runner/pytest-0/junk.txt",
])
def test_scratch_never_reaches_the_diff(repo, scratch):
path = os.path.join(repo, scratch)
os.makedirs(os.path.dirname(path), exist_ok=True)
open(path, "wb").write(b"\x00binary-ish\n")
open(os.path.join(repo, "calc.py"), "w").write("def add(a, b):\n return a + b\n")
_git(repo, "add", "-A")
_git(repo, "commit", "-q", "-m", "grind")
out = _git(repo, "diff", "--stat", "HEAD~1", "HEAD", "--", ".", *_noise()).stdout
assert scratch.split("/")[0] not in out, (
f"{scratch} reached the diff — the pathspec list does not exclude it:\n{out}")
assert "calc.py" in out, f"the real change vanished along with the scratch:\n{out}"