The rules

ruleships aswhat it catches
pipe-to-taildenya mutating command whose status is swallowed by a pipe
bare-stash-popobservegit stash pop with no ref, where refs/stash is shared across worktrees
gh-pr-merge-autoobserve--auto on a repository with no required checks, which merges immediately
publish-without-skilladvisea v* tag pushed or a pull request merged with no call of the tag-release / merge-when-green skill in this turn or the one before, so the procedure ran from memory
forge-merge-by-handadvisemerging a pull request by POSTing to the forge's merge endpoint, which answers 200 whether the checks passed, failed or never started
forge-status-stale-rowobservekeeping the first row of a commit's append-only /statuses list, so a stale pending reads as the present and the wait never ends
no-verifyobserveturning the whole commit gate off rather than one check
git-add-broadobservestaging the tree instead of the change
stale-baseadvisea branch or worktree started from a checkout the remote has moved past
push-preflightadvisea git push whose slow pre-push test gate has not been rehearsed with amont rehearse --wait
push-previewadvisea push that would publish interface changes no approved localhost preview covers, unless the plan the person approved declares the evidence is enough (preview approval)
plan-review-paneldenya plan presented at ExitPlanMode before its expert review panel ran (the review panel)
plan-phases-opendenya turn ending while the plan this branch carries still has an open phase that is not a 🧑 decision: — the agent is sent on to it
implementation-reviewadvisea push of a branch that carries a plan, whose diff no independent reviewer has read for the tree being pushed (the implementation review)
foreground-polladvisea polling loop or gh run watch in the foreground, where the tool's ten-minute clock will kill it one poll short
unbounded-background-pushadvisea git push sent to the background with its output kept in a file and no timeout, so the pre-push gate inside it can run for as long as it likes with nothing ending it or reporting it
sed-in-placeadvisesed -i spelled for the other sed (-i '' on GNU, bare -i on BSD)
kubectl-gitopsadvisean imperative kubectl write in a repository Flux or Argo reconciles
tag-after-commitadvisegit tag chained onto a git commit that a hook may have refused
release-tag-pushobservepushing a v* tag, which publishes: the tag may name the wrong commit, and a green workflow does not mean the artefact is right
worktree-remove-forceadvisegit worktree remove --force on a worktree that still holds uncommitted work
amend-pushedadvisegit commit --amend on a commit the remote already has
branch-force-deleteobservegit branch -D on a branch whose commits are on no remote and not merged
poll-blank-verdictobservea wait that stops on any value but the one it names, so a failed lookup's empty string reads as the answer
worktree-isolationobservea branch created, or git reset --hard, in the primary checkout of a repository that already has linked worktrees
stdin-hangobservea command that will read standard input with nothing on it — cat > file, a bare interpreter, tee outside a pipe — which blocks silently until the tool's clock runs out
glob-in-flag-valueadvisean unquoted glob inside a flag value (--include=*.ts), which zsh expands — or fails on — before the program sees it
glob-no-matchadvisean unquoted glob operand that matches nothing, which under zsh aborts the clause before it starts while a later clause reports success
equals-separatorobservea bare word beginning with = (echo ===, [ x == y ]), which zsh reads as a command lookup and whose failure aborts the whole command list
unsplit-expansionobservean unquoted $var under set -- or for … in, which zsh leaves as one word: the positionals or the loop get the whole string and the command runs wrong while reporting success
path-operand-missingadvisea read of a path that is not there — under the grep shim a warning in mid-stream, for the coreutils one line and carry on — which the chain then reports as success
stat-bsd-formatadvisestat -f '%…' on GNU stat (or -c on BSD), which prints a filesystem report where a timestamp was wanted
whole-file-dumpadvisea file poured whole into the tool result by cat/sed -n/head — 31% of all result bytes measured — where the Read tool would have windowed it
persisted-output-dumpadvisereading back whole a tool result the harness saved to a file for being too large, paying for it twice
file-rereadadvisea Read, or a cat, of a file this session already has in context and that is unchanged on disk since — answered from the session's own record, not the command
read-unbounded-largeadvise (ceiling deny)a Read with no offset/limit of a file over 16 KB, where the whole file lands in the context and every later turn carries it; plan files and diffs a reviewer is handed are exempt
lint-suppression-addedadvise (ceiling deny)an Edit, MultiEdit or Write that adds a lint suppression (# type: ignore, # noqa, eslint-disable, @ts-ignore, #[allow], //nolint) or loosens a lint configuration (tsconfig, eslint, pyright, ruff, Cargo [lints], golangci), where general.no-disabled-safety says to fix the finding instead; the file is compared before and after by marker, not by line
request-fanoutobserve (ceiling advise)one command that may make more than 50 explicit network transfers to one destination — a loop following Link: next, gh api --paginate, a curl URL range — counted by the shell analysis (analysis.md) with the loops and calls that multiply them

Two more checks run after a command rather than before it, on PostToolUse: push-landed and push-published verify what a push that reported success actually did. See the assertions.

amont-agent rules prints this with each rule's measured firing rate, and with the stance in force on this machine rather than the shipped one: a rule you promoted in ~/.gitconfig reads deny (ships as observe), and a rule with a ceiling names it, (max advise).

Why only three of them deny

pipe-to-tail blocks because seven consecutive weeks of measurement showed no downward trend while every other habit halved. That is the bar: a rule earns deny from your own transcripts, not from an argument about how bad the mistake is. A habit the model is already correcting does not need a deny. See measuring and graduating.

plan-review-panel is the other kind of deny, and the person's choice rather than a measurement: it fires on ExitPlanMode, not on a shell command, and checks a fact — whether the review panel the plan calls for ran on the plan being presented — naming the missing roles when it did not. After two refusals of the same plan, or whenever it cannot check, it hands the call to the person as ask instead. See the review panel.

plan-phases-open is the third, also the person's choice. It fires on Stop, the moment the agent ends its turn. ADR-0022 makes a plan one branch and one pull request with its phases as commits, so a turn that ends with a phase still open is a stall, not a handoff. The rule reads only the active plans changed on the current branch (against the merge-base with the remote default branch), never one already on main, and sends the agent on to the first open - [ ] under ## Phases. It is silent when that phase is a 🧑 decision:, in plan mode, while background tasks are still running, and when the agent's last message has a line starting with WAITING: <reason> (a preview awaiting approval, a deferred phase, a blocker). After three continuations on the same phase in one session it lets the turn end and tells the person, with a note only they see; their next prompt resets the count. Anything it cannot establish — no repository, a git failure, an unreadable plan, a session id it will not use as a file name — lets the turn end. To turn it off without uninstalling: git config --global amont.agent.plan-phases-open.stance observe.

stale-base advises from the start because it refuses nothing, speaks only after measuring a real gap, and names a failure no correcting loop can see — nothing fails when you build on stale code. The work is correct against the code it can see, and the conflict arrives later, from somewhere else. So a session opening in a checkout the remote has moved past is told — see the session notice for why it fetches and never pulls.

push-preflight advises for the same reason. git opens its connection to the remote before it runs pre-push and holds it idle for as long as the test gate takes; a remote that closes idle sessions kills the push after the gate has already passed, and the model reads "the network" where the cause was the gate's placement. With amont ≥ 1.28, amont rehearse --wait runs the same gate on a snapshot of HEAD with no connection open — or follows the rehearsal amont.rehearseOnCommit already started — and stamps the tree, so the push that follows skips the suite (amont run pre-push on 1.27). The rule speaks only when confirm finds all three facts: amont guards this repository's pushes, a test gate would run for this push, and HEAD's tree carries no stamp yet.

Shape, then the world

Most rules after the first handful came out of the transcripts the same way — tens of thousands of Bash calls, sorted by what failed, was killed, or drew a correction (see mine). Each fires on shape and, where the fact lives in the world, confirms it first: whether the call already runs in the background, which sed is on PATH, whether the repository holds a Flux or Argo resource, whether the worktree is dirty, whether the remote has the commit, whether any other branch has the commits. A rule that fires on shape alone, like tag-after-commit, names a failure every command in the chain reports as success.

pipe-to-tail in full

git push origin main 2>&1 | tail -5

A pipeline's exit status is its last command's. tail succeeds at tailing an error message, so a rejected push, a push killed by a timeout, and a push that never left the machine all report success — and the trimming discards the error text, so the failure is silent in both channels.

The remedy the rule prints is not "don't use tail": it is to run the mutating command on its own, read its output afterwards, and then verify the effect (git ls-remote origin refs/heads/<branch>) rather than the exit code.

set -o pipefail first, or writing to a file and tailing the file, are also accepted — the rule fires on the shape, and the reason names all three ways out.

Asking about one command

amont-agent check 'git push | tail -1'

No stdin, no session, no journal entry — just the rules over one string, with whatever they would have said.