fix: make the self-repo git guard Windows-only

The live-checkout git mutation guard blocked history-rewriting git ops
(checkout, reset --hard, rebase, cherry-pick, ...) in the running source
checkout and its worktrees on every platform. The hazard it protects
against is only real on Windows, where NTFS locks loaded module files and
an in-place rewrite can corrupt the running process. On POSIX, open file
handles pin the old inodes, so a checkout swap under a running process is
safe, and the guard mostly taxed normal dev/salvage workflows with clone
workarounds.

- tools/self_repo_guard.py: add guard_active() -> os.name == "nt"
- tools/terminal_tool.py: consult guard_active() before running the
  detector; detector logic and block message unchanged for Windows
- tests: wiring tests force the guard on; new tests cover the POSIX
  pass-through and the platform predicate
This commit is contained in:
Teknium
2026-08-17 09:46:23 -07:00
parent 0ea430ab84
commit c86197e607
3 changed files with 44 additions and 6 deletions
+19 -1
View File
@@ -32,10 +32,12 @@ def repo(tmp_path):
return root.resolve()
def _run(command, config, monkeypatch, repo_root, session_cwds=None, **kwargs):
def _run(command, config, monkeypatch, repo_root, session_cwds=None,
guard_on=True, **kwargs):
from tools.terminal_tool import terminal_tool
monkeypatch.setattr(self_repo_guard, "get_running_source_root", lambda: repo_root)
monkeypatch.setattr(self_repo_guard, "guard_active", lambda: guard_on)
mock_env = MagicMock()
mock_env.execute.return_value = {"output": "ok", "returncode": 0}
mock_env.cwd = config["cwd"]
@@ -116,3 +118,19 @@ class TestSelfRepoGuardWiring:
result, env = _run("git checkout main", config, monkeypatch, None)
assert result.get("status") != "blocked"
env.execute.assert_called_once()
def test_guard_inactive_passes_through(self, repo, monkeypatch):
"""POSIX (guard_active() False): mutations in the source repo run."""
config = _make_env_config(cwd=str(repo))
result, env = _run(
"git reset --hard origin/main", config, monkeypatch, repo,
guard_on=False,
)
assert result.get("status") != "blocked"
env.execute.assert_called_once()
def test_guard_active_matches_platform(self):
"""guard_active() is True exactly on Windows."""
import os
assert self_repo_guard.guard_active() == (os.name == "nt")
+13
View File
@@ -695,6 +695,19 @@ def _find_mutation(command: str, cwd: Path, root: Path, depth: int = 0) -> str |
return None
def guard_active() -> bool:
"""Whether the self-repo git guard applies on this platform.
Windows-only: NTFS locks loaded .py/.pyd files and an in-place overwrite
of the live checkout can corrupt the running process. On POSIX, open file
handles keep the old inode alive, so a checkout swap under a running
process is safe — already-imported modules keep executing the old code
and the mixed-module hazard is limited to later lazy imports, which is
not worth blocking every git workflow for.
"""
return os.name == "nt"
def detect_self_repo_git_mutation(
command: str,
cwd: str | None,
+12 -5
View File
@@ -2967,18 +2967,25 @@ def terminal_tool(
"status": "blocked"
}, ensure_ascii=False)
# Non-bypassable: rewriting the local checkout backing this interpreter
# can mix module versions. Remote backends cannot reach that checkout.
# Windows-only: NTFS locks loaded module files, so rewriting the local
# checkout backing this interpreter can corrupt the running process.
# POSIX keeps old inodes alive for open handles, so the guard is off
# there. Remote backends cannot reach that checkout.
if env_type == "local":
from tools.self_repo_guard import detect_self_repo_git_mutation
from tools.self_repo_guard import (
detect_self_repo_git_mutation,
guard_active,
)
guard_cwd = _resolve_command_cwd(
workdir=workdir,
default_cwd=cwd,
session_key=session_key,
)
_self_repo_hit, _self_repo_msg = detect_self_repo_git_mutation(
command, guard_cwd
_self_repo_hit, _self_repo_msg = (
detect_self_repo_git_mutation(command, guard_cwd)
if guard_active()
else (False, None)
)
if _self_repo_hit:
logger.warning(