What it will not do

It never emits allow. That would short-circuit your own permission prompt, so a guard approving everything it has no objection to would have switched off the permission system it was installed beside. Silence is how it says "no objection".

Every failure path is silence. An unreadable payload, an unknown event, a command it cannot parse, a rule that panics, a journal it cannot write — all of them exit 0 having written nothing.

A hook that fails toward refusing gets in the way of work you knew was correct, and the fix people reach for at that moment is to delete it from settings.json, which switches off every rule at once. One that fails toward silence loses a single firing. That trade is the whole posture, and it is why the hook payload is parsed with serde_json::Value and hand-written accessors rather than a derived struct: a field that is missing or has changed type becomes "no opinion", not a parse error somebody would be tempted to treat as an opinion. What the hook writes back is emitted by a hand-rolled escaper (src/json.rs), so the reading and the writing share no representation.

It does not judge what it cannot read. Heredocs without terminators, unbalanced quotes, eval — all opaque, and opaque never fires.

The unit of that is the pipeline, not the line. | chains one command's output into the next, so a stage we cannot read makes the whole run unreadable. &&, || and ; do not: a command there is as independent of an unreadable neighbour as of any other clause, and treating the whole line as opaque cost every rule on it — measured over 33,774 real commands, 218 had a readable pipeline thrown away. Those are judged now, and check names the run it could not read beneath the verdict.

Two things keep that honest. eval, source and . run in THIS shell and can move it, so one of them still hides the whole line — otherwise a later confirm would resolve a path against a directory we can no longer vouch for. And a finding from a partly-read command never refuses: it advises, whatever stance the rule carries. Total opacity would have let the command run, so blocking on half a reading is the worst outcome available.

It does not phone home. No telemetry, no update checks, no fetches — with one exception, which is git fetch against your own remote for the session notice, and amont.agent.fetch false switches that off.

No repository can change a stance. Stances are read from --global and --system git config only — never from a committed file, and never from the .git/config of the repository the agent is standing in, which is a file that agent could write. See stances.

The journal

Every firing is recorded at ~/.claude/amont-agent/journal.log, redacted, and never transmitted anywhere.

It only counts. Nothing in it may participate in a decision — the rules read the command in front of them and nothing else. A guard whose verdict depended on its own history would be one you could not reason about from the command alone, and could not test from a corpus.