[codex-analytics] Emit structured compaction codex errors (#27082)

## Summary
- replace raw compaction `error` analytics with `codex_error_kind` and
`codex_error_http_status_code`
- derive compaction error telemetry from `CodexErr` using the same
`CodexErrKind` mapping and HTTP status helper used by turn events
- remove the pre-compact hook stop reason from the internal compaction
outcome now that it is no longer emitted as raw analytics text

## Why
Compaction `error` was a raw `CodexErr::to_string()` value, which can
carry free-form provider or user-derived text. Structured Codex error
fields preserve useful low-cardinality telemetry without sending the raw
string.

## Validation
- `just fmt`
- `just test -p codex-analytics`
- `just test -p codex-core
compact::tests::build_token_limited_compacted_history_appends_summary_message`

Attempted `just test -p codex-core`; the changed crate compiled, but the
full target failed in unrelated environment-dependent tests such as
missing helper binaries and shell snapshot timeouts.
This commit is contained in:
rhan-oai authored and GitHub committed 2026-06-11 06:07:06 +00:00
1 parent b5a718ef67
commit 8d9f33c87c
8 files changed
+52 -35

No files matched your search

@@ -43,6 +43,7 @@ use crate::facts::AppInvocation;
use crate::facts::AppMentionedInput;
use crate::facts::AppUsedInput;
use crate::facts::CodexCompactionEvent;
use crate::facts::CodexErrKind;
use crate::facts::CompactionImplementation;
use crate::facts::CompactionPhase;
use crate::facts::CompactionReason;
@@ -1278,7 +1279,8 @@ fn compaction_event_serializes_expected_shape() {
phase: CompactionPhase::MidTurn,
strategy: CompactionStrategy::Memento,
status: CompactionStatus::Completed,
error: None,
codex_error_kind: None,
codex_error_http_status_code: None,
active_context_tokens_before: 120_000,
active_context_tokens_after: 18_000,
retained_image_count: None,
@@ -1329,7 +1331,8 @@ fn compaction_event_serializes_expected_shape() {
"phase": "mid_turn",
"strategy": "memento",
"status": "completed",
"error": null,
"codex_error_kind": null,
"codex_error_http_status_code": null,
"active_context_tokens_before": 120000,
"active_context_tokens_after": 18000,
"retained_image_count": null,
@@ -1852,7 +1855,8 @@ async fn compaction_event_ingests_custom_fact() {
phase: CompactionPhase::StandaloneTurn,
strategy: CompactionStrategy::Memento,
status: CompactionStatus::Failed,
error: Some("context limit exceeded".to_string()),
codex_error_kind: Some(CodexErrKind::ContextWindowExceeded),
codex_error_http_status_code: None,
active_context_tokens_before: 131_000,
active_context_tokens_after: 131_000,
retained_image_count: None,
@@ -1873,6 +1877,14 @@ async fn compaction_event_ingests_custom_fact() {
assert_eq!(payload[0]["event_params"]["session_id"], "session-thread-1");
assert_eq!(payload[0]["event_params"]["thread_id"], "thread-1");
assert_eq!(payload[0]["event_params"]["turn_id"], "turn-compact");
assert_eq!(
payload[0]["event_params"]["codex_error_kind"],
json!("context_window_exceeded")
);
assert_eq!(
payload[0]["event_params"]["codex_error_http_status_code"],
json!(null)
);
assert_eq!(
payload[0]["event_params"]["app_server_client"]["product_client_id"],
DEFAULT_ORIGINATOR
@@ -2779,7 +2791,8 @@ async fn subagent_events_use_inherited_connection_unless_turn_connection_is_expl
phase: CompactionPhase::StandaloneTurn,
strategy: CompactionStrategy::Memento,
status: CompactionStatus::Completed,
error: None,
codex_error_kind: None,
codex_error_http_status_code: None,
active_context_tokens_before: 131_000,
active_context_tokens_after: 64_000,
retained_image_count: None,