If two agent actions are identical in every respect except their identifier, can a human approval granted to one of them authorize the other?

For Authority the answer depends on which question is asked. For the INVOKED and RECORDED questions about one specific action, the answer is no: an approval covers exactly the action instance that requested it. One view, which answers a question about the action’s fingerprint rather than about an instance, deliberately still says yes. This article shows both, and why the difference matters.

The scenario

This is a post-audit regression scenario added in commit 46e1321. It was not one of the four findings recorded by the earlier adversarial audit. One canonical event history contains, in this order:

  1. A root delegation D1, granted by a human, lets an agent create purchase orders. Amounts between 100 EUR and 1,000 EUR require a human approval.
  2. The agent requests action A1: a 500 EUR purchase order, invoking D1.
  3. The agent requests action A2: a distinct action with the same requester, the same capability, the same parameters, therefore the same fingerprint, and the same invoked delegation D1. Only the action_id differs.
  4. An approval P1 is requested for A1.
  5. P1 is granted, for A1 only.
seq 1  DELEGATION_CREATED   D1  human → agent, approval band 100–1,000 EUR
seq 2  ACTION_REQUESTED     A1  500 EUR via D1
seq 3  ACTION_REQUESTED     A2  500 EUR via D1   (same fingerprint as A1)
seq 4  APPROVAL_REQUESTED   P1  for A1
seq 5  APPROVAL_GRANTED     P1  for A1

No execution exists yet, so P1 is not consumed.

What each view answers

The views below are described in What is AI delegation?. The first two rows are evaluated at sequence 5. The last row compares two alternative histories: in one, A1 is executed at sequence 6 citing P1; in the other, A2 is executed at sequence 6 citing P1.

Outcomes for A1 and A2 by view, at commit 8e37af7
Question askedViewA1A2Reason
Would some chain held by the agent authorize this capability with these parameters now?AVAILABLE
authorityAt, currentAuthority
AUTHORIZED, citing P1AUTHORIZED, citing P1The question is about a fingerprint, not an instance. A valid, unconsumed approval with the same fingerprint is sufficient by specification.
Would the delegation named by this action authorize this action instance now?INVOKED
invokedAuthorityNow
AUTHORIZED, citing P1REQUIRES_APPROVAL, via D1P1 is bound to the action_id of A1. Matching fingerprint, requester and delegation is not enough (I6).
Does the chain recorded by the execution validate this action at its decision point?RECORDED
execution.recordedValidation
AUTHORIZED, P1 consumedREQUIRES_APPROVAL, P1 cited but not consumedCiting P1 in the recorded chain does not bind it to A2; consumption also requires the same action_id.

So it would be wrong to summarize the result as “Authority refuses A2”. In the INVOKED and RECORDED views, A2 is REQUIRES_APPROVAL: it still needs its own approval. In the AVAILABLE view, A2’s fingerprint is AUTHORIZED, because that view asks a different question.

Why the fingerprint view still answers yes

authorityAt is prospective. It never reads a previous ACTION_REQUESTED, so it has no instance to bind to: by specification, a valid and unconsumed approval whose fingerprint matches the requested capability and parameters is sufficient to produce AUTHORIZED. The same fingerprint-scoped question is answered by currentAuthority and execution.authorityAtDecision in explainAction.

That answer is useful for one question, “is there authority available for this kind of action?”, and misleading if read as “this action was approved”. Since the fix, the authority explain command prints a note whenever the fingerprint-scoped line is AUTHORIZED while the invoked delegation line is not, so the first is never read as proof about the second.

What changed

The requirement existed before the fix

Invariant I6 in SPEC.md already required the binding before the fix: an APPROVAL_GRANTED “covers exactly the action that requested it, identified by its action_id and by its exact action_fingerprint”. The implementation did not apply it completely. Pull request #9 closed that gap: before it, a grant matching the fingerprint, the requester and the invoked delegation could authorize a different, fingerprint-identical action instance in the action-specific resolutions.

The fix

Commit 46e1321 passes the queried action_id to the approval search used by the three resolutions that concern one specific action: the invoked delegation validation, the recorded chain validation, and the grant selection used by capability issuance. A grant bound to another action_id is skipped. The fingerprint-scoped resolution deliberately does not receive an action_id.

The scenario and its documentation came afterwards

The detailed scenario and its explanation were added or reinforced after the fix: the “I6 — action-instance approval binding” section of SPEC.md does not exist in the specification before the fix (it appears later, in commit 95cabde), whereas the I6 requirement itself does. The links in this article point to merge commit 8e37af7, which carries the current, cleaned-up version of the code, tests and documentation.

Evidence

Mutation check

Removing the action_id comparison from the approval search in src/engine/evaluateConstraints.ts makes exactly the three tests above fail; the other 291 executed tests still pass. This was reproduced in a temporary copy of the repository at 8e37af7, without changing the repository itself.

Limits of this result

  • Authority is a V0 project.
  • Every event is ASSERTED_UNVERIFIED. V0 provides no signatures and no identity verification: the result says nothing about whether the events were really issued by the principals they name.
  • The result concerns the deterministic rules of the engine, evaluated on the event history it receives.
  • We know of no production system in which this gap was exploited.
  • No performance or load-testing claim is made.
  • I6 already required instance binding; pull request #9 corrected an implementation gap, not the specification.
  • The fingerprint-scoped view intentionally keeps a different meaning, described above.

Open questions

  • Is a fingerprint-scoped available view useful to expose, or is it dangerous because it can be misread as an approval of a specific action?
  • Which properties, in addition to the action instance, should an approval bind to?
  • Have you met a similar approval replay in a real system?
  • Is there an event sequence in which A2 can still obtain the benefit of A1’s approval?

Counterexamples that do not describe an unfixed exploitable flaw can be discussed in public on the repository. Anything exploitable should be reported privately: see the security page.