Skip to content

feat(tools): block git mutations of the running source checkout#65791

Open
erosika wants to merge 4 commits into
NousResearch:mainfrom
erosika:feat/self-repo-git-guard
Open

feat(tools): block git mutations of the running source checkout#65791
erosika wants to merge 4 commits into
NousResearch:mainfrom
erosika:feat/self-repo-git-guard

Conversation

@erosika

@erosika erosika commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Problem

In a source/editable install, changing Hermes's own checkout underneath the live interpreter can mix module versions: already-imported modules stay old while later imports load new code. The resulting failures surface well after the Git command and can discard a completed turn.

The observed failure happened after the agent checked out a PR branch in its running source repo. At turn end, the lazy finalize_turn import loaded the branch version under the old caller and raised a signature TypeError.

Fix

Live source-checkout guard

Local terminal commands now hard-block Git operations that can rewrite the checkout backing the running process. The guard:

  • uses the existing quote-aware approval scanner rather than splitting raw shell text;
  • resolves the session's actual cwd, git -C, GIT_WORK_TREE/--work-tree, cd chains, wrappers, nested shells, and Git aliases;
  • catches gh pr checkout and hub pr checkout as well as direct Git commands;
  • keeps subshell/pipeline cwd isolated and distinguishes executable shell heredocs from heredocs used as data;
  • remains inert for packaged installs and remote/sandboxed backends.

Read-only and non-worktree operations remain available, including normal status/log/diff workflows, commits, fetch/push, worktree creation, soft/mixed reset, staged-only restore, stash metadata operations, and clean dry-runs. The block cannot be bypassed with force=True; approval cannot make an in-process code swap safe.

Scope: this is a guardrail against the agent accidentally rewriting its own code, not a security boundary. A determined adversarial command can construct shell text the scanner cannot statically resolve; the failure mode it targets — and the one observed — is the model reaching for a plain git checkout/gh pr checkout in the wrong repo, and the block message redirects it to git worktree add or a temp clone.

Shared scanner improvement

tools/approval.py's _iter_shell_command_starts now recurses into nested $( ) and backtick command substitutions instead of only recording the outermost start. The guard reuses this scanner, and existing dangerous-pattern detection benefits from the same nested coverage. No pattern or approval-flow behavior changes otherwise; covered by the approval-parser suite.

Stable turn finalization

finalize_turn is bound when agent.conversation_loop loads instead of being imported for the first time after the agent loop. An out-of-band source change (e.g. the user running git pull in a separate shell mid-session, which no command guard can see) can therefore no longer load a skewed finalizer at the persistence seam.

Validation

  • 85 self-repo guard and terminal wiring tests
  • 311 approval-parser tests
  • 104 terminal cwd/approval and gateway lifecycle tests
  • 28 turn-finalizer tests
  • Ruff and ty clean for the changed guard code

erosika added 3 commits July 16, 2026 12:35
When hermes runs from a source/editable install, a git checkout/reset/pull
in its own repo swaps code on disk under the live interpreter. Modules
imported before the switch stay old while later lazy imports load new code,
producing delayed signature TypeErrors and tracebacks that don't match the
source, typically losing the in-flight turn.

New tools/self_repo_guard.py detects working-tree/ref mutations (checkout,
switch, reset, rebase, merge, pull, restore, stash, clean, cherry-pick,
revert) whose target repo is the source root the process runs from, via
cwd, git -C, cd chains, and subshell segments. Read-only git, commits,
fetch, and git worktree add stay allowed; packaged installs (no .git) are
inert.
Wire the self-repo guard in next to the gateway lifecycle hard-block,
before the force check — force=True cannot make the command safe, only
delay the crash. Local backend only: sandboxed backends cannot reach the
host checkout. The block message explains the version-skew mechanism and
redirects to git worktree add / a temp clone, or running the command
outside hermes with a restart after.
The function-scoped import at the end of run_conversation loads
agent.turn_finalizer fresh from disk on the first turn that reaches it.
On a source/editable install whose checkout changed mid-session, that
pairs an old caller with a new callee at the exact seam where every turn's
work is persisted — the turn crashes on a signature mismatch after the
work is done. The lazy import was never cycle-forced: turn_finalizer
defers its own conversation_loop import.
@alt-glitch alt-glitch added type/security Security vulnerability or hardening P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint tool/terminal Terminal execution and process management backend/local Local shell execution labels Jul 16, 2026

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Approved (read-only token — formal approval requires write access)

Blocks git mutations (checkout, pull, branch, stash, etc.) when the running source checkout is detected. Prevents accidental corruption of the running hermes-agent installation.

  • New tools/self_repo_guard.py module with path resolution and git command detection
  • Hoists the finalize_turn import to module level to avoid stale-module reads after git operations
  • Good test coverage in tests/tools/test_self_repo_guard.py
  • No security issues found

Reviewed by Hermes Agent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend/local Local shell execution comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have tool/terminal Terminal execution and process management type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants