# In what scenarios must an instance refuse capability negotiation when the requested tool grant exceeds the delegating agent's own residual authority after prior delegations **Date of Record:** 2026 November 13 **Scope:** Applies to multi-hop agent delegations where an orchestrator agent hands a scoped subset of its own tool capabilities to a subordinate agent via a negotiation handshake (offer/counter-offer). Does not apply to root-authority agents issuing first-tier grants from an un-delegated capability pool, nor to static capability manifests resolved at process start. ## Core Distinction The failure mode is not "the subordinate asked for a tool it should not have." That is the easy case, caught by a simple allowlist. The non-obvious case is this: the subordinate's request is *individually valid* against the orchestrator's original capability set, but the orchestrator has already delegated a portion of that same capability to sibling agents, and the residual authority remaining to it is smaller than what the new request would consume. Capability negotiation protocols almost universally treat the delegating agent's authority as a fixed ceiling ("can I grant X? does my manifest contain X?"). They rarely treat it as a *depletable budget*. When grants are exclusive (a lock, a rate-limited quota, a single-use credential, a fixed number of concurrent tool bindings), the ceiling is not fixed — it shrinks with every prior delegation. A negotiation that checks only the static ceiling will happily over-commit the orchestrator, and the over-commitment surfaces later as a runtime failure in a *sibling* agent that was already promised the capability. The sibling fails, not the negotiator, which is why the root cause is hard to localize. ## What Was Observed An orchestrator held a capability `db.write` with a concurrency budget of 4 exclusive leases (representing 4 connection slots). It delegated 3 leases to worker agents A, B, C over successive negotiations, each checking `db.write ∈ manifest` and each succeeding. A fourth worker, D, then negotiated for `db.write` with a request for 2 leases. The static check passed — `db.write` was in the manifest. The grant was issued. Total committed leases: 5 against a budget of 4. The failure did not appear at negotiation time. It appeared minutes later when worker A attempted to acquire its lease and found the pool exhausted by D's two allocations plus B and C. Worker A raised a lease-acquisition timeout. The orchestrator's logs showed four successful, individually-valid negotiations and one unrelated runtime error in A. Nothing in the negotiation trace pointed at the orchestrator as the over-committer. The invariant that was violated: **the sum of all outstanding grants for a depletable capability must never exceed the capability's total budget, evaluated at the moment of each new grant against residual — not original — authority.** The negotiation code evaluated against original authority. ## Condition of Validity The refusal is only correct when the capability is *depletable and exclusive*. Three conditions must all hold: 1. **Exclusivity** — two holders of the capability cannot exercise it simultaneously without contention (leases, locks, single-holder credentials, fixed worker pools). 2. **Prior commitment** — at least one grant for the same capability is outstanding and not yet released. 3. **Residual insufficiency** — `requested_amount > (total_budget - sum(outstanding_grants))`. If the capability is *shareable* (idempotent reads, stateless queries, capabilities with no concurrency ceiling), residual accounting is meaningless and refusing on residual grounds produces false denials that starve subordinates for no reason. If no prior grant is outstanding, residual equals total and the static check is sufficient. The refusal must be gated on all three, not on the mere presence of a budget field. ## Decision Boundary The boundary is between *static ceiling checking* and *residual budget checking*. Static checking answers "may this agent ever grant X?" Residual checking answers "may this agent grant X **right now, given what it has already promised**?" The correct action at the boundary is to **refuse and counter-offer the residual amount**, not to refuse outright and not to grant the full request. A subordinate asking for 2 leases when 1 remains should receive a counter-offer of 1, with the residual stated explicitly, so it can decide whether 1 is useful or whether it should escalate to the orchestrator's parent for a budget increase. An outright refusal discards a viable partial grant; a full grant violates the invariant. The negotiation must therefore carry the residual as a first-class field in the counter-offer, and the subordinate must be permitted to accept a partial grant. The subtle trap: an orchestrator that has delegated *all* of a capability (residual = 0) must refuse even a request for amount 1, and must refuse it as `CapabilityExhausted`, distinct from `CapabilityDenied`. These are different signals. `Denied` means "never yours to give." `Exhausted` means "yours to give, but already given away — escalate upward." Conflating them causes subordinates to retry against a permanently-denied capability or to escalate a merely-exhausted one to the wrong authority. ## Operational Effect When residual accounting is enforced, over-commitment is caught at negotiation time, the counter-offer carries actionable information, and the failure localizes to the negotiator rather than surfacing as a mystery timeout in a sibling. When it is absent, the system exhibits *deferred, misattributed failure*: grants succeed, siblings fail, and the orchestrator appears healthy in every trace. The operational cost of the bug is not the failed grant — it is the hours spent debugging the wrong agent. The residual must be recomputed at each negotiation from the live set of outstanding grants, not cached from manifest load, because grants are released asynchronously and a stale residual produces both false denials (residual too low) and over-commitment (residual too high). The accounting is a read-modify-write against shared grant state and must be serialized per capability, or two concurrent negotiations will each read the same residual and each grant against it. ```python from dataclasses import dataclass, field class CapabilityExhaustedError(RuntimeError): """Raised when residual authority is insufficient for the requested amount. Distinct from a plain denial: the capability *is* the orchestrator's to grant, but prior outstanding grants have consumed the budget. Callers should escalate upward or accept the counter-offer, not retry unchanged. """ @dataclass class GrantLedger: total_budget: int outstanding: dict = field(default_factory=dict) # agent_id -> amount def residual(self) -> int: return self.total_budget - sum(self.outstanding.values()) def negotiate_grant(ledger: GrantLedger, agent_id: str, requested: int) -> int: """Return the amount actually granted, or raise if residual is zero. Enforces the residual invariant: sum(outstanding) + granted <= total_budget. Counter-offers the residual amount when the full request cannot be met. """ if requested <= 0: raise ValueError("requested amount must be positive") remaining = ledger.residual() if remaining <= 0: # Capability is ours to give but fully delegated. Escalate, don't deny. raise CapabilityExhaustedError( f"capability exhausted: budget={ledger.total_budget} " f"outstanding={ledger.outstanding} residual=0; " f"agent {agent_id!r} must escalate to parent authority" ) granted = min(requested, remaining) # partial counter-offer ledger.outstanding[agent_id] = ledger.outstanding.get(agent_id, 0) + granted return granted def release_grant(ledger: GrantLedger, agent_id: str, amount: int) -> None: held = ledger.outstanding.get(agent_id, 0) if amount > held: raise ValueError(f"release of {amount} exceeds held {held} for {agent_id!r}") if amount == held: del ledger.outstanding[agent_id] else: ledger.outstanding[agent_id] = held - amount ```