<!-- Live enforcement: real agent applications denied mid-run, permissions narrowing across a handoff chain — Attenu. Source: https://github.com/attenu-io/attenu-derive/blob/main/docs/LIVE-ENFORCE.md · Rendered: https://attenu.io/docs/live-enforcement/ · Updated 2026-08-25 -->

# Live enforce evidence — customer-service (T30 Haiku, T31 Sonnet)

*The last unproven leap: a real agent, a real model, actually blocked by the shim mid-run in ENFORCE mode.
google/adk-samples customer-service, the `retail-support` curated pack, derived authority → `meet` → shim
enforce. Raw evidence JSON in `data/reports/enforce-live/`. Reproduce: `python -m
attenu_derive.sample.run_adk_enforce --app <cs> --prompt "..." --domain retail-support --grant crm.write
--grant data.write [--grant mail.send] --model <anthropic/claude-haiku-4-5-... | anthropic/claude-sonnet-4-5>`.*

The operator installs the app with the `retail-support` pack and grants its everyday scopes
(`data.read`, `data.write`, `crm.write`) but leaves the two `send_*` tools **held** (`mail.send`,
`requires_grant`). Three prompts, two models:

| run | model | prompt | tool the model called | outcome | ledger | anchor |
|---|---|---|---|---|---|---|
| A held | Haiku | "email me the care instructions" | `send_care_instructions` (mail.send) | **DENIED live** (`scope_not_granted`), denial returned to the model | `deny` event | verified |
| B granted | Haiku | same, with `--grant mail.send` | `send_care_instructions` | **passes** (0 denies) | — | verified |
| C benign | Haiku | cart + recommendation + CRM update | `access_cart_information`, `get_product_recommendations`, `update_salesforce_crm` | **0 benign blocks** | — | verified |
| A held | Sonnet | "email me the care instructions" | `send_care_instructions` (mail.send) | **DENIED live**, denial returned to the model | `deny` event | verified |
| C benign | Sonnet | cart + recommendation + CRM update | `access_cart_information`, `get_product_recommendations`, `update_salesforce_crm` | **0 benign blocks** | — | verified |

**What this proves, live:**
- **G2 clause 1 (does not break):** the app's own workload runs untouched — 0 benign blocks — including
  `update_salesforce_crm` (`crm.write`), which passes only because the pack curated it correctly (it was a
  name heuristic before T25).
- **G2 clause 2 (does stop):** a call outside the granted authority is denied *before the tool body runs*,
  the machine-readable denial is handed back to the model (the denial contract), and the denial is on the
  hash-chained ledger, which is anchored and verifies (T27).
- **The `requires_grant` mechanism, end to end:** the same `send_care_instructions` call is denied when
  `mail.send` is held (A) and passes when the operator grants it (B) — one config flip, live. That is the
  day-0 "held pending curation → operator grants → passes" flow, on a real agent.
- **No model divergence:** Haiku and Sonnet behave identically across all runs — the model-monoculture
  residual is retired for this app; enforcement does not depend on model class.

**Bounds (honest):** the runs in THIS section are single-agent; the delegation-chain section below adds a
live multi-agent enforce run on the same app (financial-advisor), so "no live delegation chain" is no longer
a bound. Remaining bounds: one framework (ADK), and no live *payment* denial (the deriver holding
`process_payment` until granted is pinned offline in `tests/test_onboarding.py`).


## Live enforce ACROSS A DELEGATION CHAIN — financial-advisor (T34, Haiku)

The single-agent runs above prove a tool denial; they do not prove *monotonic attenuation across a chain*,
which is Attenu's actual claim. This run does, live: the `financial_coordinator` delegates to the
`data_analyst_agent` (a real spawn), and the analyst is minted `meet(parent, request)` — strictly narrower
than its coordinator — on the same chain ledger.

| run | prompt | chain | analyst authority vs coordinator | analyst's `google_search` | ledger |
|---|---|---|---|---|---|
| A held (`--hold web.search`) | "Analyze market data for GOOGL" | coordinator → data_analyst_agent | analyst `{web.fetch}` ⊂ coordinator `{4× agent.delegate.*, web.fetch}` | **DENIED live** (`web.search` the coordinator never held) — mid-chain | `spawn` + `deny`, anchored across the chain, verified |
| B benign (`--grant web.search`) | same | coordinator → data_analyst_agent | analyst `{web.fetch, web.search}` ⊂ coordinator `{…, web.fetch, web.search}` | passes | 0 benign blocks, anchored, verified |

**What this proves, live:** `child ⊆ parent` is enforced *across the delegation boundary*, not just at a
single agent. In run A the analyst cannot search because its coordinator was never granted `web.search`, so
`meet` never gave it to the child — the denial happens inside the sub-agent's run, and the `deny` lands on
the parent's chain ledger, which anchors over the whole chain. The delegation graph in the evidence JSON
(`data/reports/enforce-live/chain-*.json`) shows both authorities and `narrower_than_root: true` for the
child. This is the mechanism the company is about, enforced on a live multi-agent app.

Reproduce: `run_adk_enforce --app <financial_advisor> --prompt "Analyze market data for GOOGL" --domain
finance-advisory [--hold web.search | --grant web.search] --model anthropic/claude-haiku-4-5-...`.

## Offline-verifiable evidence bundle (T33a)

Every enforce run exports an evidence bundle (the hash-chained ledger + a signed anchor) and re-verifies it
from the bundle ALONE — no engine — via `attenu_guard.evidence.verify_bundle`. On the live customer-service
benign run above the offline verifier returned `{integrity: true, monotonicity: true, containment: true}` with
**3 authorized actions re-checked** against the acting node's authority; a deliberately altered bundle fails
each check independently (`tests/test_core_v02.py`). `delegation_graph(bundle)` renders the chain (agents,
authorities, allow/deny counts, edges) for a reviewer. This is the engine's offline-verifiable audit trail: an
auditor confirms every guarantee without trusting the engine that produced the log.
