-
Notifications
You must be signed in to change notification settings - Fork 2
fix(tools): AdversarialPolicyGateExecutor.write_audit() does not propagate claim_source from ToolOutput #2535
Description
Summary
AdversarialPolicyGateExecutor.write_audit() in crates/zeph-tools/src/adversarial_gate.rs writes audit entries for all tool calls but hardcodes claim_source: None, even though the tool executor sets it in ToolOutput.
Root cause
write_audit() is called before or without access to the ToolOutput — it builds AuditEntry directly from ToolCall parameters with no reference to the execution result. Individual executors (shell, scrape, file, search_code) set claim_source in ToolOutput, but this is never copied into the adversarial gate's audit entry.
Observed in CI-355 (2026-03-31)
Audit entries for search_code and read always show no claim_source field:
{"tool": "search_code", "adversarial_policy_decision": "allow"}
{"tool": "read", "adversarial_policy_decision": "allow"}Expected: "claim_source": "code_search" for search_code, "claim_source": "file_system" for read.
Fix sketch
Refactor write_audit() to be called after tool execution completes (or add a post-execution variant that accepts the ToolOutput) so it can copy claim_source from the result. For blocked/denied calls where no ToolOutput exists, claim_source remains None.
Note: ShellExecutor writes its own audit entries separately (via shell/mod.rs:674) and correctly sets claim_source: Some(ClaimSource::Shell). The adversarial gate entries would duplicate shell entries if both are active — consider deduplicating or merging them.
Related
- fix(tools): propagate ClaimSource and ErrorDomain to AuditEntry #2319 — partially fixed
claim_sourcepropagation for shell/scrape internal audit entries - fix(tools): wire is_reasoning_model() and error_phase into agent tool execution path #2357 —
error_phasealwaysNonein audit (same structural gap)