[codex-analytics] guardian review analytics events emission (#17693)

## Why

Guardian approvals now run as review sessions, but Codex analytics did
not have a terminal event for those reviews. That made it hard to
measure approval outcomes, failure modes, Guardian session reuse, model
metadata, token usage, and timing separately from the parent turn.

## What changed

Adds `codex_guardian_review` analytics emission for Guardian approval
reviews. The event is emitted from the Guardian review path with review
identity, target item id, approval request source, a PII-minimized
reviewed-action shape, terminal decision/status, failure reason,
Guardian assessment fields, Guardian session metadata, token usage, and
timing metadata.

The reviewed-action payload intentionally omits high-risk fields such as
shell commands, working directories, argv, file paths, network
targets/hosts, rationale, retry reason, and permission justifications.
It also classifies prompt-build failures separately from Guardian
session/runtime failures so fail-closed cases are distinguishable in
analytics.

## Verification

- Guardian review analytics tests cover terminal success,
timeout/cancel/fail-closed paths, session metadata, and token usage
plumbing.
- `cargo clippy -p codex-core --lib --tests -- -D warnings`

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/17693).
* #17696
* #17695
* __->__ #17693
This commit is contained in:
rhan-oai
2026-04-22 01:02:47 -07:00
committed by GitHub
parent 5eab9ff8ca
commit 4e7399c6b9
9 changed files with 730 additions and 138 deletions
+4
View File
@@ -3,6 +3,7 @@ use std::sync::Arc;
use async_channel::Receiver;
use async_channel::Sender;
use codex_analytics::GuardianApprovalRequestSource;
use codex_async_utils::OrCancelExt;
use codex_protocol::protocol::ApplyPatchApprovalRequestEvent;
use codex_protocol::protocol::Event;
@@ -471,6 +472,7 @@ async fn handle_exec_approval(
justification: None,
},
reason,
GuardianApprovalRequestSource::DelegatedSubagent,
review_cancel.clone(),
);
await_approval_with_cancel(
@@ -573,6 +575,7 @@ async fn handle_patch_approval(
patch,
},
reason.clone(),
GuardianApprovalRequestSource::DelegatedSubagent,
review_cancel.clone(),
);
Some(
@@ -689,6 +692,7 @@ async fn maybe_auto_review_mcp_request_user_input(
new_guardian_review_id(),
build_guardian_mcp_tool_review_request(&event.call_id, &invocation, metadata.as_ref()),
/*retry_reason*/ None,
GuardianApprovalRequestSource::DelegatedSubagent,
review_cancel.clone(),
);
let decision = await_approval_with_cancel(