feat(gate): task permission — the work surface is allowed, the platform is not
ActGov's second layer (arXiv 2609.24446), in the honest form our evidence supports. The paper binds each task to its minimum tools; 171 recorded tool calls cannot justify a per-task minimum, but they do justify the line this draws: files, commands, search, web, delegation and skills are the work surface and pass; ListAgents, ScheduleWakeup, CronCreate, SendMessage and the rest reach the platform itself and do not. That line is not theoretical. ListAgents and ScheduleWakeup were both called by microVM missions whose --allowedTools is Read Edit Write Bash Agent. Neither is on that list; both ran, because the flag governs permission prompting and not availability. Our gate is the only place this can be enforced. TaskPolicy is rendered into the same guest script as the floor and the role policies. A phase names its own set with "agent_tools" — NOT "tools", which security_scan already owns for its scanner list; both are now in phase_config::KNOWN_KEYS, adjacent, each saying what the other is. SHADOW BY DEFAULT. The gate records what it would have refused to would-deny.jsonl and allows the call; the host drains it into gate.would_deny on both tiers. CLAWMATES_TASK_PERMISSION=enforce flips it. A policy tightened on a guess and enforced on day one is how an agent learns to work around the gate, and a shadow mode nobody can read is an off switch with extra steps. The VM probe needed a sentinel: a refusal and a call that merely would have been refused are both JSON objects with the same keys, and telling them apart by content would confuse the one distinction shadow mode exists to make. Asymmetry, stated rather than hidden: a VM is per-phase and honours the phase's own agent_tools; a container serves every phase of its mission and gets the mission-wide default. Narrowing per phase there needs a re-install between phases and is not done. 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
2d1f3954e8
commit
79a6119f4e
@@ -649,7 +649,18 @@ impl MissionRuntimeProvisioner {
|
||||
// Re-install on reuse: the container outlives the server
|
||||
// process, and a hook that exists only on first creation is a
|
||||
// hook that quietly disappears after a redeploy.
|
||||
let hooks = crate::container_tool_hooks::install(&self.docker, &name).await;
|
||||
let hooks = crate::container_tool_hooks::install_with(
|
||||
&self.docker,
|
||||
&name,
|
||||
// A container serves every phase of the mission, so the
|
||||
// policy installed here is the mission-wide default. A
|
||||
// phase's own `agent_tools` is honoured on the microVM
|
||||
// tier, where the VM is per-phase; narrowing per phase
|
||||
// here would need a re-install between phases and is not
|
||||
// done.
|
||||
Some(&crate::vm_tool_gate::TaskPolicy::default_shadow()),
|
||||
)
|
||||
.await;
|
||||
let pairing_code = self.mint_pairing_code(&name).await;
|
||||
return Ok(EnsuredContainer {
|
||||
endpoint: endpoint_url(&name),
|
||||
@@ -865,7 +876,18 @@ impl MissionRuntimeProvisioner {
|
||||
// Gate and observe the tools claude runs inside its own subprocess.
|
||||
// Best-effort by design: a phase that runs unhooked still delivers, and
|
||||
// failing the launch to protect telemetry would be the wrong trade.
|
||||
let hooks = crate::container_tool_hooks::install(&self.docker, &name).await;
|
||||
let hooks = crate::container_tool_hooks::install_with(
|
||||
&self.docker,
|
||||
&name,
|
||||
// A container serves every phase of the mission, so the
|
||||
// policy installed here is the mission-wide default. A
|
||||
// phase's own `agent_tools` is honoured on the microVM
|
||||
// tier, where the VM is per-phase; narrowing per phase
|
||||
// here would need a re-install between phases and is not
|
||||
// done.
|
||||
Some(&crate::vm_tool_gate::TaskPolicy::default_shadow()),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(EnsuredContainer {
|
||||
endpoint: endpoint_url(&name),
|
||||
|
||||
Reference in New Issue
Block a user