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:
+13
-5
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
Reference in New Issue
Block a user