docs: addendum 6 — ActGov read in full, what shipped from it, and the provenance gap
Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WZb5A2kfVfjpdwSochkuHz
This commit is contained in:
co-authored by
Claude Opus 5
parent
1a244b7d32
commit
7b28950da1
@@ -472,3 +472,67 @@ missions before any promotion from shadow to selection; a local backend
|
|||||||
only if the vendor dependency bites (logit read-out over the 9B fleet
|
only if the vendor dependency bites (logit read-out over the 9B fleet
|
||||||
model, not a cross-encoder — measured); Slack/A2A intent routing when
|
model, not a cross-encoder — measured); Slack/A2A intent routing when
|
||||||
inbound volume justifies it.
|
inbound volume justifies it.
|
||||||
|
|
||||||
|
## Addendum 6 — 2026-09-22, ActGov and the role-scoped gate
|
||||||
|
|
||||||
|
**The paper** (arXiv 2609.24446, AAAI'27, read in full, not the abstract).
|
||||||
|
Two components. *ActGov-Policy*, offline: an LLM drafts rules from tool
|
||||||
|
specs, benign tasks and attack traces; a **Z3 solver** checks each candidate
|
||||||
|
bundle against predefined safety assertions and only an `unsat` on every
|
||||||
|
one is deployed; the bundle is then frozen. *ActGov-Runtime*, online:
|
||||||
|
intercepts each proposed tool call before any external effect, abstracts it
|
||||||
|
into a **finite record set** over a 2D schema — formation levels (L1
|
||||||
|
deterministic from system state, L2 an LLM used *only* as a parser into
|
||||||
|
predefined finite value domains, L3 Boolean/temporal compositions) × seven
|
||||||
|
semantic scopes (action, permission, parameter, history, binding, external,
|
||||||
|
domain) — and evaluates deterministic predicates over it.
|
||||||
|
|
||||||
|
Policies come in three layers: **Task-Permission** (bind the trusted user
|
||||||
|
task to the minimum tools it needs; outside is blocked), **Hard-Invariant**
|
||||||
|
(global properties regardless of task — e.g. no outbound network action
|
||||||
|
derived from untrusted content), and **Procedural-Obligation** (conditional
|
||||||
|
prerequisites before execution).
|
||||||
|
|
||||||
|
Measured: ASR 0.046–0.111 undefended → **0.004–0.007** (AgentDyn) and
|
||||||
|
**0.000** on three of four models (AgentDojo), across Qwen3.6-flash,
|
||||||
|
MiniMax-M2.5, DeepSeek-v4-pro and GPT-4o mini. The number that matters
|
||||||
|
more: **CaMeL and ACE score ASR 0.000 and utility 0.000** — they block
|
||||||
|
everything. The paper's own framing is that "a trivial defense can
|
||||||
|
eliminate attacks by blocking all executions, so efficacy must be
|
||||||
|
considered together with utility", which is this module's own rule
|
||||||
|
("denying ordinary reads to catch a rare POST is the trade this module
|
||||||
|
refuses to make") arrived at from the other direction.
|
||||||
|
|
||||||
|
**Where we already agree, independently.** The door governor is exactly
|
||||||
|
ActGov's L2 discipline: three bounded Nouls extract *facts* (is this
|
||||||
|
exfiltration, is there a credential, is this abusive) and the
|
||||||
|
authorization decision is deterministic band logic in code. The model is
|
||||||
|
a semantic parser, never the decider. Our `RULES` floor is P_HI. The
|
||||||
|
held-for-approval band is a P_PO obligation.
|
||||||
|
|
||||||
|
**What shipped from it** (`1a244b7`): the role dimension — the gate reads
|
||||||
|
the PreToolUse payload's `agent_type` and holds a role to its own limits
|
||||||
|
on top of the floor. First policy: **the verifier may not write.** A
|
||||||
|
verifier that edits what it is verifying turns a failed check into a
|
||||||
|
passing one and reports success. Enforced by us, deliberately as a *second*
|
||||||
|
enforcer: the CLI's own `--agents` tool list is the harness policing
|
||||||
|
itself and silently did nothing until 2.1.243 rejected the string form we
|
||||||
|
were sending. The enabling fact was measured before anything rested on it
|
||||||
|
— Claude Code 2.1.278 sets `agent_type` on a subagent's PreToolUse payload
|
||||||
|
and leaves it absent on the lead's (local probe: `agent_type: prober`).
|
||||||
|
Rendered into the same guest script as the floor, so the shell and the
|
||||||
|
Rust predicate cannot disagree; shell tests run the generated script and
|
||||||
|
check that the lead's identical write is still allowed.
|
||||||
|
|
||||||
|
**The real gap the paper names, and we do not have:** `arg_provenance`.
|
||||||
|
Our gate sees a command string and cannot tell a URL the operator supplied
|
||||||
|
from one a fetched web page supplied — so "no outbound action derived from
|
||||||
|
untrusted content", the invariant that actually stops indirect prompt
|
||||||
|
injection, is not expressible here. That needs taint on tool *outputs*
|
||||||
|
flowing into later tool *inputs*, which is a real piece of work and the
|
||||||
|
honest next step for this seam. Until it exists, our gate defends against
|
||||||
|
accidents and obvious cases, which is what its own header has always said.
|
||||||
|
|
||||||
|
**Also worth keeping:** measure efficacy and utility as a pair. Every gate
|
||||||
|
scenario already asserts the mission still delivered; that is the utility
|
||||||
|
half, and it should stay mandatory for any future rule.
|
||||||
|
|||||||
Reference in New Issue
Block a user