Veefore docs

ADR 0004: Delegation semantics

Refuse an escalating grant rather than clamping it, intersect every hop, and cascade revocation immediately.

#Context

A human delegates to an agent. The agent delegates to a sub-agent. The sub-agent delegates to a worker. Three questions have to be answered the same way every time:

  1. What happens when a grant asks for more than its issuer holds?
  2. When a chain narrows at one hop, what is the leaf's effective authority?
  3. When a grant is revoked, what happens to everything derived from it?

Each has an obvious convenient answer and a correct one, and they differ.

#Decision

A child can never exceed its parent, and an attempt is refused.

A delegation asking for a capability the parent does not hold, an amount above the parent's ceiling, an expiry past the parent's expiry, or a scope wider than the parent's is refused at creation with DELEGATION_ESCALATION. Widening an existing delegation through PATCH is refused the same way.

The effective value at each dimension is the narrowest in the chain.

Resolution walks from the acting agent to the root, checks every hop is still valid, and intersects the constraints. If the root grants ₹100,000 and an intermediate hop narrowed it to ₹50,000, the leaf's ceiling is ₹50,000 — even if the leaf's own grant says ₹100,000.

Revocation cascades immediately, at every depth.

No grace period, no draining. Everything derived from a revoked grant dies with it.

Depth is bounded by max_depth, and 0 at the leaf means no onward delegation.

Resolution runs at stage 6 of every pipeline run, against live state. There is no cached authority.

#Alternatives

Clamp instead of refusing. Silently narrow an over-broad grant to what the parent actually holds. Convenient, and it means the caller believes it has authority it does not have.

The failure mode is specific and bad: a delegation requests a ₹100,000 ceiling, is silently clamped to ₹50,000, and the worker discovers it mid-task when a ₹60,000 action is refused — at which point the operator looks at the delegation record, reads max_amount: 100000, and concludes Veefore is broken.

An explicit failure at grant time is strictly better than a surprise at use time. Rejected.

Nearest-wins instead of narrowest-wins. The leaf's own grant decides, on the theory that it is the most specific. This makes narrowing anywhere in the chain reversible by the leaf, which means an intermediate hop cannot actually constrain anything below it. Delegation would be advisory.

Revoke with a grace period. Lets in-flight work finish. A cascade that waits is a cascade that does not work when you need it — and the moment you need it is precisely the moment in-flight work is the problem.

Soft revocation: mark revoked, let existing sessions continue. Same objection, more subtle. Sessions are up to 15 minutes, so a compromised chain would stay usable for 15 minutes after revocation.

Unbounded depth. Simpler model. Every hop is another set of constraints to intersect and another record to check for revocation — and, more importantly, a human reviewing authority four hops deep cannot realistically say what the leaf can do. An unbounded chain is a reasoning problem before it is a performance problem.

Cache resolved authority for the session. Would remove a database read per action. It would also mean revocation takes effect when the cache expires rather than when you revoke, which is the one property that has to be immediate.

#Consequences

Creating a delegation can fail, and callers must handle it. POST /v1/delegations/simulate exists so the failure can be anticipated rather than discovered — it reports which bound would be violated, and it is what the console calls as you fill in the form.

"The delegation says 100000 but we get AUTHORITY_EXCEEDED at 60000" is a recurring question. The answer is always an intermediate hop, and the tool is GET /v1/agents/{id}/authority, which returns the resolved result. Documented in Delegation and the FAQ.

Revoking a supervisor can stop a lot of work at once. The CLI and the emergency control both show the full blast radius before asking, and the blast radius is recomputed at execution time and stored with the record.

Resolution costs a database read per action. Acceptable: the pipeline is 20–50 ms excluding execution, and the alternative is cached authority with delayed revocation.

Delegations accumulate. Grants outlive their purpose, chains get deeper than they need to be, and ceilings get set higher than anything that has actually been used. GET /v1/delegations/graph exists because this is hard to see in a list and obvious in a graph, and a monthly review is in Operate it.

Policy can test the shape, not just the bounds. delegation.chain_length, delegation.depth, authority.max_amount and authority.capabilities are all policy fields — so "dangerous tools require direct authority" is expressible as a rule rather than as a convention.

Identity, authority, policy and enforcement for autonomous AI agents.