[observability] Add explicit field use markers

Add FieldUse metadata so fields declare the exact projections intended to consume them. Detail level and data class remain guardrails enforced by FieldPolicy, while uses drives selection and prevents broad policies from accidentally collecting every safe-looking field.

Support struct-level default uses in the Observation derive, with field-level uses overriding the default for mixed events. AppUsed now declares analytics once at the event level instead of repeating it on every field.

Keep analytics conformance focused on final TrackEventRequest equality and move marker/policy filtering coverage into codex-observability tests. Update the design doc to describe the intent-versus-guardrail split.
This commit is contained in:
Albin Cassirer
2026-04-19 15:17:38 -07:00
parent 590ed405d2
commit b615ce9291
6 changed files with 196 additions and 57 deletions
@@ -1560,6 +1560,14 @@ async fn app_used_observation_matches_legacy_analytics_fact() {
let mut observation_reducer = AnalyticsObservationReducer::default();
let mut legacy_events = Vec::new();
let mut observation_events = Vec::new();
let observation = codex_observability::events::AppUsed {
model_slug: "gpt-5",
thread_id: "thread-1",
turn_id: "turn-1",
connector_id: Some("drive"),
app_name: Some("Drive"),
invocation_type: Some(codex_observability::events::InvocationType::Implicit),
};
legacy_reducer
.ingest(
@@ -1580,17 +1588,7 @@ async fn app_used_observation_matches_legacy_analytics_fact() {
.await;
observation_reducer
.ingest_app_used(
codex_observability::events::AppUsed {
model_slug: "gpt-5",
thread_id: "thread-1",
turn_id: "turn-1",
connector_id: Some("drive"),
app_name: Some("Drive"),
invocation_type: Some(codex_observability::events::InvocationType::Implicit),
},
&mut observation_events,
)
.ingest_app_used(observation, &mut observation_events)
.await;
let legacy_payload = serde_json::to_value(&legacy_events).expect("serialize legacy events");