feat(compile): gate high-impact safe outputs behind manual review#1196
feat(compile): gate high-impact safe outputs behind manual review#1196jamesadevine wants to merge 1 commit into
Conversation
Wire the ManualValidation@1 builder into the canonical pipeline so safe
outputs can require human approval via a new agentless ManualReview job.
- safe-outputs.require-approval (section default) + per-tool override,
accepting a bool or { approvers, notify-users, timeout-minutes,
on-timeout, instructions }; reserved key filtered from tool enumeration
- Pool::Server agentless IR variant emitting `pool: server`
- mixed config splits Stage 3 into an automatic SafeOutputs job and a
gated SafeOutputs_Reviewed job (distinct safe_outputs_reviewed artifact)
- executor --only/--exclude tool filter
- ManualReview only pauses when Detection cleared the run and the agent
actually proposed a reviewed output (HasReviewedProposals); fail-closed
- audit aggregates execution records across both safe-output artifacts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Well-designed feature with one correctness issue that could silently bypass the approval gate on misconfiguration, plus a minor false-positive risk in the proposal-detection heuristic. Findings🐛 Bugs / Logic Issues
Both .and_then(|v| serde_json::from_value::<RequireApproval>(v.clone()).ok())If the user writes a syntactically legal YAML value that fails to deserialize as
The 🔒 Security ConcernsNone beyond the silent-failure issue above (which is correctness as much as security).
|
Summary
Wires the previously-unused
ManualValidation@1builder into the compiler so agents can propose higher-impact safe outputs that a human approves before Stage 3 applies them.Manual review is opt-in (default
false) and configured under the existingsafe-outputs:map, matching current conventions:require-approvalaccepts a bare boolean or an object for finer control:Resolution / defaults: per-tool
require-approval> section-level > unset (false). A barerequire-approval: truepauses the run, lets anyone with run permission approve/reject, sends no emails, and fails closed on timeout.Compiled pipeline shape
ManualReviewjob (pool: server,ManualValidation@1) inserted between Detection and SafeOutputs.SafeOutputsjob depends only on Agent + Detection (--excludethe reviewed tools) and runs in parallel with the review pause, independent of the approval outcome.SafeOutputs_Reviewedjob (--onlythe reviewed tools), gated behindManualReview, publishing a distinctsafe_outputs_reviewedartifact.HasReviewedProposals) — no pointless pauses.Implementation notes
RequireApproval/ApprovalConfigtypes + per-tool resolution (types.rs); the reservedrequire-approvalkey is filtered everywheresafe-outputs:keys are treated as tool names (common.rs,main.rs).Pool::Serveragentless IR variant emitting the scalarpool: server(ir/job.rs,ir/lower.rs,ir/summary.rs); 1ES path skips thetemplateContextwrap for server jobs (onees_ir.rs).ado-aw executegains--only/--excludetool filters (execute.rs,main.rs).audit/analyzers/safe_outputs.rs).standalone,1es,job,stage).Test plan
cargo buildcargo clippy --all-targets --all-features— cleancargo test— 2237 unit tests + all integration suites greencargo test --test bash_lint_tests— shellcheck passes on the new bash bodiesToolFilter, conditional pause (HasReviewedProposals), and audit aggregation across split artifactsrequire-approval)