Replace Guardian tickets with parent response IDs (#43002)

## What changed

Capture the ID from `response.created` and pass it as `parent_response_id` in Guardian review and classifier request metadata. Clear the current response ID before sampling retries so tool reviews cannot inherit a failed response's ID.

Request Guardian credits with `guardian_credits_requested` on eligible parent requests. Remove the ticket protocol, transport attachment, and ticket-specific body redaction. Keep ordinary app-server client `parent_response_id` values inside `x-codex-turn-metadata` rather than promoting them to Guardian request metadata.

## Testing

Update coverage for HTTP fallback, WebSocket review reuse, missing response IDs, classifier transport retries, and code-mode yield/resume attribution. Extend app-server tests to verify client metadata placement over HTTP and WebSocket.

GitOrigin-RevId: c1665e56382ecec8d260fc2ca0ecd5e20b0b2391
This commit is contained in:
jif
2026-09-05 13:02:49 +00:00
committed by copyberry
parent a7a4321593
commit dee21ec1bc
32 changed files with 178 additions and 362 deletions
+3 -15
View File
@@ -331,8 +331,7 @@ async fn responses_client_uses_guardian_path() -> Result<()> {
async fn responses_client_stream_request_preserves_item_ids() -> Result<()> {
let state = RecordingState::default();
let transport = RecordingTransport::new(state.clone());
let client = ResponsesClient::new(transport, provider("openai"), Arc::new(NoAuth))
.with_endpoint(ResponsesEndpoint::Guardian);
let client = ResponsesClient::new(transport, provider("openai"), Arc::new(NoAuth));
let request = ResponsesApiRequest {
model: "gpt-test".into(),
instructions: "Say hi".into(),
@@ -357,24 +356,14 @@ async fn responses_client_stream_request_preserves_item_ids() -> Result<()> {
client_metadata: None,
access_programs: None,
};
let raw_ticket = "t".repeat(43);
let ticket = codex_protocol::guardian_ticket::GuardianTicket::from_server(&raw_ticket).unwrap();
let mut expected = serde_json::to_value(&request)?;
expected["client_metadata"] = serde_json::json!({"guardian_ticket": raw_ticket});
let expected = serde_json::to_value(&request)?;
let _stream = client
.stream_request(
request,
ResponsesOptions {
guardian_ticket: Some(ticket),
..Default::default()
},
)
.stream_request(request, ResponsesOptions::default())
.await?;
let requests = state.take_stream_requests();
assert_eq!(requests.len(), 1);
assert!(!format!("{:?}", requests[0]).contains(&raw_ticket));
let prepared = requests[0]
.prepare_body_for_send()
.expect("body should prepare");
@@ -589,7 +578,6 @@ async fn azure_store_sends_ids_and_headers() -> Result<()> {
extra_headers,
compression: Compression::None,
turn_state: None,
guardian_ticket: None,
},
)
.await?;