Repository navigation
Conversation
A decision that needed a person could only be refused, with a name attached. That is workable for an assistant somebody is watching and useless for anything running on its own, which is most of what a guard is for. Now a refused call leaves a request behind, somebody answers it out of band, and the same call goes through next time. The guard writes the request and refuses with a reference; agent-passport approvals shows what is waiting; approve or decline answers it. What ties an approval to a call is the decision digest, which is taken over the subject, the authority and the request and over nothing else. It is the same digest that already stopped an action being swapped after approval, reused. So an approval buys one exact call: change the amount by a penny, the customer, or any argument, and it stops matching. It is spent when the call actually runs, and it lapses with the policy's response window. A decline is final. A waiting request carries the arguments in full, which a receipt deliberately does not. Nobody can approve what they cannot see, and this file is the operator's rather than something handed to a counterparty. Then the part worth saying plainly: an agent that can run a shell can also run the approve command. A policy that lists `approvers` takes only answers signed by those keys, and refuses an unsigned one, one signed by a key it does not name, and one edited after signing. Without that list anything in the file counts, which is safe only where the agent cannot write to it, and the docs say so rather than implying the feature is stronger than it is. The Claude Code hook honours an approval that already exists but opens none of its own, because Claude Code can put the question to the person directly and asking twice helps nobody. Verified with 8 tests driving the real CLI, including that an approval for one call does not cover the same call with a different amount, customer or argument. Mutation-tested seven ways: skipping the digest comparison, spending an approval twice, reopening a decline, accepting an unsigned answer, never verifying the signature, accepting an unnamed key, and never marking an approval used. Each reddens exactly its case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A decision that needed a person could only be refused, with a name attached. Workable for an assistant somebody is watching, useless for anything running on its own, which is most of what a guard is for.
The loop
The agent gets a refusal with a reference:
Answer it, and the same call goes through next time.
What an approval is worth
One exact call. What ties an approval to a call is the decision digest, taken over the subject, the authority and the request and over nothing else. It is the same digest that already stopped an action being swapped after approval, reused here. Change the amount by a penny, the customer, or any argument, and the approval stops matching.
Once. Spent when the call actually runs.
Until the window closes. The policy's
slaHours. A decline is final.A waiting request carries the arguments in full, which a receipt deliberately does not. Nobody can approve what they cannot see, and this file is the operator's rather than something handed to a counterparty.
Who may answer
Worth being blunt: an agent that can run a shell can also run
agent-passport approve. A policy that listsapproverstakes only answers signed by those keys:An unsigned answer, one signed by a key the policy does not name, and one edited after signing are all refused. Without that list anything in the file counts, which is safe only where the agent cannot write to it, and the docs say that rather than implying the feature is stronger than it is.
The Claude Code hook honours an approval that already exists but opens none of its own, because Claude Code can put the question to the person directly.
Verification
8 new tests driving the real CLI, 129 in total. Harness and end-to-end scenario green. Mutation-tested seven ways, each reddening exactly its case:
🤖 Generated with Claude Code