Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ docs/superpowers/*
# logs, and per-session caches are never artifacts of the codebase.
.hermes/

# Desktop/bootstrap install marker written into the managed checkout root by the
# bootstrap installer. It is Hermes-managed runtime state, never a code change —
# ignore it so `hermes update`'s `git stash push --include-untracked` does not
# treat it as a local edit and autostash it on every run (#38529).
.hermes-bootstrap-complete

# Tool Search live-test harness output — non-deterministic model transcripts,
# regenerated by scripts/tool_search_livetest.py. Never an artifact of the repo.
scripts/out/
Expand Down
47 changes: 47 additions & 0 deletions tests/hermes_cli/test_update_autostash.py
Original file line number Diff line number Diff line change
Expand Up @@ -784,3 +784,50 @@ def test_cmd_update_skips_stash_restore_when_reset_fails(monkeypatch, tmp_path,

out = capsys.readouterr().out
assert "preserved in stash" in out


def test_bootstrap_marker_not_autostashed_by_update(tmp_path):
"""#38529: the Desktop bootstrap marker must be git-ignored so that
``hermes update``'s ``git stash push --include-untracked`` does not sweep it
into an autostash on every run.

Behavioral + hermetic: build a throwaway repo that adopts the project's real
``.gitignore`` (the contract under test), drop the marker, and confirm the
same stash invocation the updater uses leaves it untouched.
"""
import shutil
import subprocess

if shutil.which("git") is None:
pytest.skip("git not available")

repo_gitignore = Path(hermes_main.__file__).resolve().parents[1] / ".gitignore"

def git(*args):
return subprocess.run(
["git", *args], cwd=tmp_path, capture_output=True, text=True, check=True
)

git("init", "-q")
git("config", "user.email", "t@example.com")
git("config", "user.name", "t")
(tmp_path / ".gitignore").write_text(repo_gitignore.read_text())
(tmp_path / "tracked.txt").write_text("x\n")
git("add", "-A")
git("commit", "-qm", "init")

marker = tmp_path / ".hermes-bootstrap-complete"
marker.write_text("")

# Exact flags used by hermes update (hermes_cli/main.py).
git("stash", "push", "--include-untracked", "-m", "hermes-update-autostash")

assert marker.exists(), (
".hermes-bootstrap-complete was swept into the update autostash — it must "
"be listed in .gitignore so `git stash -u` skips it (#38529)."
)
# It must not even register as a dirty/untracked change.
status = subprocess.run(
["git", "status", "--porcelain"], cwd=tmp_path, capture_output=True, text=True
).stdout
assert ".hermes-bootstrap-complete" not in status