DEV Community

hao li
hao li

Posted on Edited on Originally published at github.com

Two Claude Code sessions, one repo, one corrupted edit. A 60-line fix.

The failure mode, quoted from a real report: "With several agent sessions on one repository, session A reads createSession, session B changes it, and A then edits on the old assumption."

I run multiple Claude Code sessions against the same repo all the time. The stale-edit problem is silent — no error, just a quietly wrong merge of intentions.

edit-guard is a PreToolUse hook that fixes it in ~60 lines of stdlib Python. It watches every Read/Edit/Write through the hook, remembers what each session last saw, and blocks the write when the file moved on without you — telling the agent to re-read first.

Design choices worth noting:

  • Fail-open. If the hook itself errors, your edit proceeds. A guard that breaks your workflow is worse than no guard.
  • Backs up your settings. edit-guard install merges three hook entries into ~/.claude/settings.json and backs it up first. Never clobbers.
  • Single file. Copy it anywhere, read it in one sitting, audit it yourself.
git clone https://github.com/hahahahahahahahah6/edit-guard.git
cd edit-guard
pip install .
edit-guard install
Enter fullscreen mode Exit fullscreen mode

Repo: https://github.com/hahahahahahahahah6/edit-guard

MIT licensed. The whole thing is small enough to read before you trust it — I'd actually recommend that.

v2: the offensive half — session-start inject

The pre-edit hook is defense: it stops a session from acting on stale assumptions. But I kept hitting the other direction of the same problem: a new session starts, knows nothing about decisions made in previous sessions, and confidently re-litigates settled architecture. The guard only catches that if the new session writes a file someone else touched. If it just builds the wrong thing in a fresh file, nothing fires.

So v2 adds offense: edit-guard inject --session-start. It scans the project for recorded decisions — docs/adr/*.md, adr/, or a DECISIONS.md — and prints a compact summary: one bullet per decision, title plus status plus the gist of the ## Decision section. The idea is borrowed from uehara's ADR hook: re-read the architecture decisions at session start instead of re-litigating them.

Wire it as a Claude Code SessionStart hook — hook stdout lands in the session context automatically:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          { "type": "command", "command": "edit-guard inject --session-start" }
        ]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Each new session then opens with something like:

## Recorded project decisions (edit-guard session-start inject)

These are decisions this project already recorded. Do not contradict
them in a new session unless there is an explicit reason to revisit one:

- ADR-0001: Use stdlib only (status: accepted)
  Ship zero third-party dependencies. Everything uses the Python stdlib.
Enter fullscreen mode Exit fullscreen mode

Tuning flags: --adr-dir (custom ADR directory), --decisions-file (custom decisions file), --max-chars (output cap, default 4000). No decision files means empty output and exit 0 — a repo without ADRs gets zero noise in its session context.

How this differs from the consultative approach: tools like a worktree claim guard are Mod-shaped and ask-style — they track who owns what and never refuse a write. edit-guard is the opposite on both axes: the pre-edit hook refuses stale writes outright, and the session-start inject pushes decisions into the session before it acts. Block at write time, inject at start time. One tool, attack and defense.


Try it yourself: pip install edit-guard — GitHub repo

Top comments (1)

Collapse
 
maddy30445r profile image
Madhur Mittal •

A git worktree per session pairs well with this. Each session gets its own checkout, so collisions only surface at merge time, where you can actually review them. Curious how the hook handles edits that skip the Edit tool, like a sed one-liner or a codegen script run from Bash. That seems like where two sessions could still slip past each other. Also, does the blocked session get told what the other one changed, or does it just get stopped?