There is a class of mistake that only happens when the model thinks it is being helpful. The model finishes a task, sees the working tree is dirty, and decides “I will just commit this for the user.” Two minutes later there are six commits in a branch I have not reviewed, one of them includes a half-edited config file, and the remote already has the bad push.
I have hit that exact pattern more than once. Now my global rules forbid it.
The story behind the rule
This is the moment I wrote the first version of the rule down:
I was finishing a chunk of refactor work on an internal app, said “ok, looks good” to acknowledge the model’s summary, and went to make coffee. By the time I came back the model had run git commit, git push, and was waiting for the next instruction. The commit was fine in content, but it also rolled up an unrelated debug log I had left in src/utils/. The remote now had a commit nobody reviewed. The branch was shared with a teammate.
The cost was small that time. Reverting locally, force-pushing, sending the teammate a “ignore that last commit” message. But it was the third time in two weeks that “ok” or “done” had been interpreted as “go ahead and ship.” The fourth time was going to be on main.
The fix is not a smarter prompt or a hook. The fix is to tell the model, once, that “ok” means nothing. Only the literal word “commit” means commit. Only the literal word “push” means push.
The rule
This is the whole file. I keep it short on purpose. The shorter the rule, the less the model can wiggle around it.
# Safety Rules
- NEVER run `git commit` unless the user explicitly says "commit",
"git commit", or "please commit"
- NEVER run `git push` unless the user explicitly says "push",
"git push", or "please push"
- Words like "ok", "done", "sync", "deploy" are NOT commit or push
confirmations
- When task is done, show summary of changes and ASK: "Commit now?"
wait for explicit yes
- Do NOT auto-push after commit, user will request push separately
if needed
- Multiple tasks in one session = multiple separate commit
confirmations required
- NEVER access remote systems automatically; always ask the user
first
- NEVER make assumptions. Always read related documentation
carefully before proceeding, and ask the user if anything is
unclear
- NEVER assume without evidence. If context is missing, ambiguous,
or incomplete, explicitly ask clarifying questions before
providing an answer
## Sensitive Data Storage
- Store credentials, passwords, API keys, and sensitive configs
in `.local/` folder (gitignored)
- Always leave breadcrumbs when moving files to `.local/`,
create reference files in original location
I save it as ~/.claude/rules/safety.md. Claude Code auto-loads everything in ~/.claude/rules/ into every session, so I do not have to remember to include it.
Why it works
The trick is explicit word matching, not intent inference.
Earlier versions of this rule said things like “wait for user confirmation before committing.” That sounds reasonable until you watch how the model interprets confirmation. “ok”, “good”, “done”, “looks fine”, “let’s go”, “ship it”, “sync this up”, “save this”: each of these reads as a confirmation if you squint. The model squints a lot.
The current version forbids inference entirely. Three literal forms count as commit confirmation: commit, git commit, please commit. Three literal forms count as push confirmation: push, git push, please push. Anything else is not a confirmation. The rule names the most common false positives (“ok”, “done”, “sync”, “deploy”) so the model cannot pretend it was ambiguous.
The second part of the rule, “auto-push after commit is forbidden,” matters because the natural next step after committing is pushing. The model would chain them. Splitting commit and push into two separate confirmations doubles the friction by design. I want that friction. The cost of typing “push” after typing “commit” is two seconds. The cost of an unintended push is fifteen minutes of force-push cleanup, plus a possibly broken main branch, plus a teammate who now needs a heads-up.
The third part, “multiple tasks in one session = multiple separate commit confirmations,” kills the optimization where the model commits everything at once at the end. Every task ends with its own “Commit now?” prompt. If three tasks each need a commit, I confirm three times. I would rather type “commit” three times than discover one commit that bundles three unrelated changes.
When the rule does not apply
There is exactly one repository where this rule is overridden: my ~/.claude/ config repo itself. It is a private repo, branch-per-machine, low blast radius, and I commit and push constantly when I am editing skills or rules. In that repo, “auto-push after commit” is the convention. Everywhere else, the global rule wins.
I keep the override scoped tight. The override is in the ~/.claude/CLAUDE.md for that one directory, not a general loosening of the global rule. It says, in effect, “in this directory, after the user authorizes a commit, you may also push without a second confirmation.” The authorization is still required. The single signal still counts as commit-and-push for that one repo only.
This is the right shape for safety rules in general: write the global rule strict, override per-repo only when the override has a clear justification and the blast radius is low.
Variants you might want
The rule is mine but the shape is portable. A few adaptations to consider:
- Different trigger words. If you work in a non-English context, list the local words for “commit” and “push” too. Make sure they are equally unambiguous (no synonym that the model could match against a casual response).
- Add
mergeto the forbidden list. I do not, because I rarely ask the model to merge, but if you do, addgit mergeto the same forbidden-without-explicit-word list. - Add
gh pr mergeto the rule. Merging a PR is more destructive than pushing. If you let the model open PRs, also forbid it from merging them without the literal word “merge.” - Per-environment rules. A common pattern is “no commits to
mainever from any agent, even with the literal word.” This is a stricter variant. If you have a team workflow wheremainis protected anyway, the branch protection is the real fix and the rule is just for redundancy. - No remote access by default. I extended the same idea to SSH and remote API calls. “Never access remote systems automatically” lives in the same file. Same logic: explicit instruction or no remote action.
Closing note
The total cost of writing this rule was about ten minutes. The total benefit is that I have never had to undo an unintended commit since adding it. The model is, structurally, optimistic. It wants to finish your task. Telling it “no, finishing means showing me the diff and asking” is a one-line fix that changes the entire feel of how I interact with it.
For the spawning side of this same problem, see spawning patterns and worktree isolation in the claude-code from zero series. Worktree isolation is what protects you when an agent goes off-script. This rule is what protects you when the main session itself is the one going off-script.