fix(guard): steer live-checkout block message to disk-backed scratch clones

The guard's "use a separate worktree or temporary clone" advice sent
agents to /tmp by default. /tmp is RAM-backed tmpfs on most distros, and
parallel salvage clones each running npm ci (~1.6GB per clone) filled a
32GB tmpfs to 97% during a 15-subagent campaign, ENOSPC-ing sibling test
runs. The message now recommends `git clone --shared <root> ~/.hermes/scratch/<task>`
(honoring HERMES_HOME), warns that dependency installs belong on real
disk, and tells the agent to delete the clone once the branch is pushed.
This commit is contained in:
Teknium
2026-08-14 21:59:21 -07:00
parent 3af56c2203
commit 62014d8dd3
2 changed files with 40 additions and 2 deletions
+25
View File
@@ -354,3 +354,28 @@ class TestUnparseableCommands:
def test_subshell_syntax_does_not_crash(self, repo):
hit, _ = _detect("VAL=$(git rev-parse HEAD) git checkout main", repo, repo)
assert hit is True
class TestBlockMessageGuidance:
"""The block message must steer agents to a disk-backed scratch clone,
not a bare "temporary clone" (agents defaulted to /tmp, which is tmpfs
on most distros — parallel salvage clones running npm ci filled a 32GB
tmpfs to 97% in one campaign)."""
def test_message_recommends_shared_clone_on_disk(self, repo):
hit, msg = _detect("git rebase origin/main", repo, repo)
assert hit is True
assert "git clone --shared" in msg
assert "scratch" in msg
def test_message_warns_against_tmp_for_dep_installs(self, repo):
hit, msg = _detect("git rebase origin/main", repo, repo)
assert hit is True
assert "tmpfs" in msg
assert "Delete the clone" in msg
def test_scratch_hint_honors_hermes_home(self, repo, monkeypatch):
monkeypatch.setenv("HERMES_HOME", "/custom/hermes-home")
hit, msg = _detect("git rebase origin/main", repo, repo)
assert hit is True
assert "/custom/hermes-home/scratch" in msg
+15 -2
View File
@@ -714,9 +714,22 @@ def detect_self_repo_git_mutation(
def _block_message(operation: str, root: Path) -> str:
scratch = _scratch_dir_hint()
return (
f"Blocked: `{operation}` would rewrite Hermes's live source checkout "
f"({root}) and can mix module versions in this running process. "
"Use a separate worktree or temporary clone. To change this checkout, "
"stop Hermes, run the command externally, then restart Hermes."
f"Use a separate worktree or a shared clone on real disk, e.g. "
f"`git clone --shared {root} {scratch}/<task>` — avoid /tmp for "
"clones that install node/python deps: /tmp is usually RAM-backed "
"tmpfs and a few dependency installs can fill it and ENOSPC other "
"work. Delete the clone when the branch is pushed. To change this "
"checkout, stop Hermes, run the command externally, then restart "
"Hermes."
)
def _scratch_dir_hint() -> str:
"""Disk-backed scratch location suggested to agents for temporary clones."""
hermes_home = os.environ.get("HERMES_HOME", "").strip()
base = Path(hermes_home).expanduser() if hermes_home else Path.home() / ".hermes"
return str(base / "scratch")