fix(code-health): --staged/--commit run the ratchet from the judged snapshot
F20 (andrexibiza): in --staged/--commit mode the guard jobs ran inside the snapshot root with the snapshot's git env, but the health job ran in the real worktree. Its source was pinned to the index tree, yet its engine and policy were not: Measurer reads scripts/ci/profile_scope_patterns.json from the repo it runs in, so with an identical `git write-tree`, editing only the unstaged P05 path_regex turned PS-P05 / exit 1 into "ok" / exit 0 and back. The health job now runs like every other job: cwd = the snapshot root with the snapshot env, so `python -m scripts.code_health` imports the snapshot's own engine and reads the snapshot's policy files and ruff pin. The ruff job resolves its pin from the same root. Unstaged edits to the policy file or the engine no longer move the verdict, and they stay untouched in the worktree; plain runs are unchanged (root is the worktree).
This commit is contained in:
@@ -122,13 +122,15 @@ def _jobs(names: list[str], base: str | None, head: str | None,
|
||||
if name in _GUARDS:
|
||||
jobs[name] = _Job([sys.executable, *_GUARDS[name]], root, env)
|
||||
elif name == "ruff":
|
||||
jobs[name] = _Job([*resolve_ruff(REPO), "check", "."], root, env)
|
||||
jobs[name] = _Job([*resolve_ruff(root), "check", "."], root, env)
|
||||
else:
|
||||
# The ratchet reads both sides from git: real repo, judged against the head tree.
|
||||
# The ratchet reads both sides from git. It runs from `root` like every other job, so
|
||||
# in a snapshot its engine, rules and policy files (profile_scope_patterns.json, the
|
||||
# ruff pin) are the judged artifact's own, never unstaged working-tree bytes.
|
||||
health = [sys.executable, "-m", "scripts.code_health"]
|
||||
health += ["--base", base] if base else []
|
||||
health += ["--head", head] if head else []
|
||||
jobs[name] = _Job(health)
|
||||
jobs[name] = _Job(health, root, env)
|
||||
return jobs
|
||||
|
||||
|
||||
|
||||
@@ -114,6 +114,39 @@ def _ratchet_repo(tmp_path: Path, switch: str | None = None) -> tuple[Path, str]
|
||||
return repo, _git(repo, "rev-parse", "HEAD")
|
||||
|
||||
|
||||
# --- F20: the staged/commit health verdict comes from the judged artifact only -----------------
|
||||
|
||||
|
||||
@pytest.mark.parametrize("unstaged", [
|
||||
# the policy file read by the PS-P05 rule, narrowed so it no longer covers pkg/
|
||||
("scripts/ci/profile_scope_patterns.json", lambda t: t.replace('"id": "P05",', '"id": "P05",\n'
|
||||
' "path_regex": "^nowhere/",')),
|
||||
# the engine itself, edited to exempt pkg/ from PS-P05
|
||||
("scripts/code_health/config.py", lambda t: t.replace(
|
||||
'exclude=SINGLE_PROFILE, pattern_id="P05"',
|
||||
'exclude=SINGLE_PROFILE + ("pkg/*",), pattern_id="P05"')),
|
||||
])
|
||||
def test_staged_health_ignores_unstaged_policy_and_engine(tmp_path, unstaged):
|
||||
repo = _engine_repo(tmp_path)
|
||||
_write(repo, {"pkg/c.py": _ENV_COPY})
|
||||
_git(repo, "add", "--", "pkg/c.py")
|
||||
tree = _git(repo, "write-tree")
|
||||
first = _check(repo, "--staged", "--only", "health", "--base", "HEAD")
|
||||
assert first.returncode == 1 and "PS-P05" in first.stdout, first.stdout + first.stderr
|
||||
|
||||
rel, edit = unstaged
|
||||
original = (repo / rel).read_text(encoding="utf-8")
|
||||
edited = edit(original)
|
||||
assert edited != original
|
||||
(repo / rel).write_text(edited, encoding="utf-8")
|
||||
again = _check(repo, "--staged", "--only", "health", "--base", "HEAD")
|
||||
assert again.returncode == 1 and "PS-P05" in again.stdout, again.stdout + again.stderr
|
||||
# the judged artifact and the user's unstaged work are both untouched
|
||||
assert _git(repo, "write-tree") == tree
|
||||
assert (repo / rel).read_text(encoding="utf-8") == edited
|
||||
assert _git(repo, "diff", "--name-only") == rel
|
||||
|
||||
|
||||
# --- F22: selectors are validated before anything runs ----------------------------------------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user