Stances
A rule does one of three things, and the middle one is the point:
| stance | effect |
|---|---|
observe | records the firing and says nothing at all |
advise | puts the reason into the model's context; refuses nothing |
deny | refuses the tool call, with the reason and the remedy |
observe and advise are not two ways of saying "not blocking yet".
additionalContext enters the model's context and therefore changes its
behaviour, which contaminates the rate the observation exists to measure. A
rule that talks is intervening. That is why the two are named differently
and why backtest numbers from an advise rule are not comparable with the
numbers that justified promoting it.
That contamination is also what makes it measurable. amont-agent backtest --compliance reads the two stances against each other: what a model did
after an advise fired, beside what it did after an observe fired and
said nothing. The second is the rate the habit corrects on its own, and an
advise that does not beat it is costing context tokens for nothing. See
measuring and graduating.
Changing one
Takes effect on the next command; nothing to restart.
git config --global amont.agent.pipe-to-tail.stance observe
git config --global amont.agent.stance observe # every rule
The ladder, most specific first:
rule.default_stance < amont.agent.stance < amont.agent.<id>.stance
then capped at the rule's own ceiling, and clamped to observe if the guard
is switched off.
Ceilings
Every rule declares the loudest stance it may ever take. For most rules that
is deny, and the ceiling changes nothing. A rule whose finding is an
estimate — "this loop may make hundreds of requests" — is capped at
advise: refusing a command on an estimate would claim a certainty the
analysis does not have.
The cap is applied after every configured key, so neither
amont.agent.stance deny nor the rule's own key can pass it, and
graduate --to deny refuses a capped rule with the reason. amont-agent rules prints each rule's ceiling beside its stance.
Why git config and not a committed file
Promotion power stays on the machine, with the person. A rule that a committed
file could promote to deny would mean cloning a repository hands it the
power to refuse your shell commands.
This is enforced three times, because neither of the first two was enough.
amont.conf's parser will not name these rules, so a committed manifest cannot reach them.- amont's config reader lets a repository's committed policy
setlines outrank system and global git config — right for a hook manager, wrong for a guard — so this project reads git config itself, without that ladder. - Git's own search order ends at
--localand--worktree, and the last file wins. A.git/configtherefore outranked your--globalanswer without any of amont's machinery being involved — and the agent whose command was just refused can write one, sincegit configis not a command any rule here objects to. So the reader takes--global, then--system, and nothing else.
A stance answers to your own git config and to nothing a repository carries or
a process standing in one can write. A file your global config includes or
includeIfs counts as your own: git skips those for a scoped read unless asked,
and this reader asks. graduate and demote write --global
for the same reason.
One consequence worth stating plainly: git config amont.agent.<rule>.stance
run inside a repository writes --local by default, and this tool will not
read it. Pass --global, which is what every example here does.