Codex Handbook
core/src/turn_metadata_tests.rs 779 lines
use super::*;use crate::responses_metadata::CodexResponsesRequestKind;use crate::responses_metadata::CompactionTurnMetadata;use crate::responses_metadata::INSTALLATION_ID_KEY;use crate::responses_metadata::WINDOW_ID_KEY;use crate::sandbox_tags::permission_profile_sandbox_tag;use codex_analytics::CompactionImplementation;use codex_analytics::CompactionPhase;use codex_analytics::CompactionReason;use codex_analytics::CompactionTrigger;use codex_protocol::models::PermissionProfile;use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;use codex_protocol::protocol::SessionSource;use codex_protocol::protocol::SubAgentSource;use codex_utils_absolute_path::AbsolutePathBuf;use core_test_support::PathBufExt;use core_test_support::PathExt;use pretty_assertions::assert_eq;use serde_json::Value;use std::collections::HashMap;use std::time::Duration;use tempfile::TempDir;use tokio::process::Command;fn test_mcp_turn_metadata_context() -> McpTurnMetadataContext<'static> {    McpTurnMetadataContext {        model: "gpt-5.4",        reasoning_effort: Some(ReasoningEffortConfig::High),    }}fn test_responses_metadata_json(    state: &TurnMetadataState,    window_id: &str,    request_kind: CodexResponsesRequestKind,) -> String {    state        .to_responses_metadata(            "installation-a".to_string(),            window_id.to_string(),            request_kind,        )        .turn_metadata_json()        .expect("turn metadata json")}fn test_turn_responses_metadata_json(state: &TurnMetadataState, window_id: &str) -> String {    test_responses_metadata_json(state, window_id, CodexResponsesRequestKind::Turn)}fn test_compaction_responses_metadata_json(    state: &TurnMetadataState,    window_id: &str,    compaction: CompactionTurnMetadata,) -> String {    test_responses_metadata_json(        state,        window_id,        CodexResponsesRequestKind::Compaction(compaction),    )}fn test_turn_metadata_header(state: &TurnMetadataState) -> String {    state        .responses_metadata_template()        .turn_metadata_json()        .expect("header")}async fn create_clean_git_repo(repo_name: &str) -> (TempDir, AbsolutePathBuf) {    let temp_dir = TempDir::new().expect("temp dir");    let repo_path = temp_dir.path().join(repo_name).abs();    std::fs::create_dir_all(&repo_path).expect("create repo");    Command::new("git")        .args(["init"])        .current_dir(&repo_path)        .output()        .await        .expect("git init");    Command::new("git")        .args(["config", "user.name", "Test User"])        .current_dir(&repo_path)        .output()        .await        .expect("git config user.name");    Command::new("git")        .args(["config", "user.email", "test@example.com"])        .current_dir(&repo_path)        .output()        .await        .expect("git config user.email");    std::fs::write(repo_path.join("README.md"), "hello").expect("write file");    Command::new("git")        .args(["add", "."])        .current_dir(&repo_path)        .output()        .await        .expect("git add");    Command::new("git")        .args(["commit", "-m", "initial"])        .current_dir(&repo_path)        .output()        .await        .expect("git commit");    (temp_dir, repo_path)}#[tokio::test]async fn detached_memory_responses_metadata_omits_turn_identity() {    let (_temp_dir, repo_path) = create_clean_git_repo("repo-東京").await;    let header = detached_memory_responses_metadata(        String::new(),        String::new(),        String::new(),        String::new(),        &SessionSource::Unknown,        &repo_path,        Some("none"),    )    .await    .turn_metadata_json()    .expect("header");    assert!(header.is_ascii());    assert!(!header.contains("東京"));    let parsed: Value = serde_json::from_str(&header).expect("valid json");    assert_eq!(parsed["request_kind"].as_str(), Some("memory"));    assert!(parsed.get("session_id").is_none());    assert!(parsed.get("thread_id").is_none());    assert!(parsed.get("forked_from_thread_id").is_none());    assert!(parsed.get("turn_id").is_none());    assert!(parsed.get(WINDOW_ID_KEY).is_none());    let expected_repo_path = repo_path.to_string_lossy().into_owned();    let actual_repo_path = parsed        .get("workspaces")        .and_then(Value::as_object)        .and_then(|workspaces| workspaces.keys().next())        .expect("workspace path");    assert_eq!(actual_repo_path, &expected_repo_path);    let workspace = parsed        .get("workspaces")        .and_then(Value::as_object)        .and_then(|workspaces| workspaces.values().next())        .cloned()        .expect("workspace");    assert_eq!(        workspace.get("has_changes").and_then(Value::as_bool),        Some(false)    );}#[tokio::test]async fn detached_memory_responses_metadata_omits_empty_workspace_metadata() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let header = detached_memory_responses_metadata(        String::new(),        String::new(),        String::new(),        String::new(),        &SessionSource::Unknown,        &cwd,        /*sandbox*/ None,    )    .await    .turn_metadata_json()    .expect("detached memory should emit its request kind");    let parsed: Value = serde_json::from_str(&header).expect("valid json");    assert_eq!(parsed, serde_json::json!({"request_kind": "memory"}));}#[test]fn turn_metadata_state_uses_platform_sandbox_tag() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        /*forked_from_thread_id*/ None,        /*parent_thread_id*/ None,        &SessionSource::Exec,        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    let header = test_turn_metadata_header(&state);    let json: Value = serde_json::from_str(&header).expect("json");    let sandbox_name = json.get("sandbox").and_then(Value::as_str);    let session_id = json.get("session_id").and_then(Value::as_str);    let thread_id = json.get("thread_id").and_then(Value::as_str);    assert!(json.get("request_kind").is_none());    let expected_sandbox = permission_profile_sandbox_tag(        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    assert_eq!(sandbox_name, Some(expected_sandbox));    assert_eq!(session_id, Some("session-a"));    assert_eq!(thread_id, Some("thread-a"));    assert!(json.get("forked_from_thread_id").is_none());    assert!(json.get("parent_thread_id").is_none());    assert!(json.get("subagent_kind").is_none());    assert!(json.get("session_source").is_none());}#[test]fn turn_metadata_state_includes_root_fork_lineage() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let source_thread_id =        ThreadId::from_string("11111111-1111-4111-8111-111111111111").expect("thread id");    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        Some(source_thread_id),        /*parent_thread_id*/ None,        &SessionSource::Exec,        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    let header = test_turn_metadata_header(&state);    let json: Value = serde_json::from_str(&header).expect("json");    assert_eq!(        json["forked_from_thread_id"].as_str(),        Some("11111111-1111-4111-8111-111111111111")    );    assert!(json.get("parent_thread_id").is_none());    assert!(json.get("subagent_kind").is_none());}#[test]fn turn_metadata_state_includes_thread_spawn_subagent_parent_without_fork() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let parent_thread_id =        ThreadId::from_string("22222222-2222-4222-8222-222222222222").expect("thread id");    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        /*forked_from_thread_id*/ None,        Some(parent_thread_id),        &SessionSource::SubAgent(SubAgentSource::ThreadSpawn {            parent_thread_id,            depth: 1,            agent_path: None,            agent_nickname: None,            agent_role: None,        }),        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    let header = test_turn_metadata_header(&state);    let json: Value = serde_json::from_str(&header).expect("json");    assert!(json.get("forked_from_thread_id").is_none());    assert_eq!(        json["parent_thread_id"].as_str(),        Some("22222222-2222-4222-8222-222222222222")    );    assert_eq!(json["subagent_kind"].as_str(), Some("thread_spawn"));}#[test]fn turn_metadata_state_includes_forked_thread_spawn_subagent_lineage() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let parent_thread_id =        ThreadId::from_string("33333333-3333-4333-8333-333333333333").expect("thread id");    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        Some(parent_thread_id),        Some(parent_thread_id),        &SessionSource::SubAgent(SubAgentSource::ThreadSpawn {            parent_thread_id,            depth: 1,            agent_path: None,            agent_nickname: None,            agent_role: None,        }),        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    let header = test_turn_metadata_header(&state);    let json: Value = serde_json::from_str(&header).expect("json");    assert_eq!(        json["forked_from_thread_id"].as_str(),        Some("33333333-3333-4333-8333-333333333333")    );    assert_eq!(        json["parent_thread_id"].as_str(),        Some("33333333-3333-4333-8333-333333333333")    );    assert_eq!(json["subagent_kind"].as_str(), Some("thread_spawn"));}#[test]fn turn_metadata_state_includes_known_parent_for_non_thread_spawn_subagents_without_fork() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let parent_thread_id =        ThreadId::from_string("44444444-4444-4444-8444-444444444444").expect("thread id");    let sources = [        (SubAgentSource::Review, "review"),        (SubAgentSource::Other("guardian".to_string()), "guardian"),        (            SubAgentSource::Other("agent_job:job-1".to_string()),            "agent_job:job-1",        ),    ];    for (subagent_source, subagent_kind) in sources {        let state = TurnMetadataState::new(            "session-a".to_string(),            "thread-a".to_string(),            /*forked_from_thread_id*/ None,            Some(parent_thread_id),            &SessionSource::SubAgent(subagent_source),            "turn-a".to_string(),            cwd.clone(),            &permission_profile,            WindowsSandboxLevel::Disabled,            /*enforce_managed_network*/ false,        );        let header = test_turn_metadata_header(&state);        let json: Value = serde_json::from_str(&header).expect("json");        assert!(json.get("forked_from_thread_id").is_none());        assert_eq!(            json["parent_thread_id"].as_str(),            Some("44444444-4444-4444-8444-444444444444")        );        assert_eq!(json["subagent_kind"].as_str(), Some(subagent_kind));    }}#[test]fn turn_metadata_state_includes_turn_started_at_unix_ms_after_start() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        /*forked_from_thread_id*/ None,        /*parent_thread_id*/ None,        &SessionSource::Exec,        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    state.set_turn_started_at_unix_ms(/*turn_started_at_unix_ms*/ 1_700_000_000_123);    let header = test_turn_metadata_header(&state);    let json: Value = serde_json::from_str(&header).expect("json");    assert_eq!(        json["turn_started_at_unix_ms"].as_i64(),        Some(1_700_000_000_123)    );}#[test]fn turn_metadata_state_includes_model_and_reasoning_effort_only_in_request_meta() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        /*forked_from_thread_id*/ None,        /*parent_thread_id*/ None,        &SessionSource::Exec,        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    let header = test_turn_metadata_header(&state);    let header_json: Value = serde_json::from_str(&header).expect("json");    assert!(header_json.get("model").is_none());    assert!(header_json.get("reasoning_effort").is_none());    let meta = state        .current_meta_value_for_mcp_request(test_mcp_turn_metadata_context())        .expect("turn metadata should be present");    assert!(meta.get("request_kind").is_none());    assert_eq!(meta["model"].as_str(), Some("gpt-5.4"));    assert_eq!(meta["reasoning_effort"].as_str(), Some("high"));    let meta_without_reasoning_effort = state        .current_meta_value_for_mcp_request(McpTurnMetadataContext {            model: "gpt-5.4",            reasoning_effort: None,        })        .expect("turn metadata should be present");    assert_eq!(        meta_without_reasoning_effort["model"].as_str(),        Some("gpt-5.4")    );    assert!(        meta_without_reasoning_effort            .get("reasoning_effort")            .is_none()    );}#[test]fn turn_metadata_state_marks_user_input_requested_during_turn_only_for_mcp_request_meta() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        /*forked_from_thread_id*/ None,        /*parent_thread_id*/ None,        &SessionSource::Exec,        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    let header = test_turn_metadata_header(&state);    let header_json: Value = serde_json::from_str(&header).expect("json");    assert!(        header_json            .get(USER_INPUT_REQUESTED_DURING_TURN_KEY)            .is_none()    );    let meta = state        .current_meta_value_for_mcp_request(test_mcp_turn_metadata_context())        .expect("turn metadata should be present");    assert!(meta.get(USER_INPUT_REQUESTED_DURING_TURN_KEY).is_none());    state.mark_user_input_requested_during_turn();    let header = test_turn_metadata_header(&state);    let header_json: Value = serde_json::from_str(&header).expect("json");    assert!(        header_json            .get(USER_INPUT_REQUESTED_DURING_TURN_KEY)            .is_none()    );    let meta = state        .current_meta_value_for_mcp_request(test_mcp_turn_metadata_context())        .expect("turn metadata should be present");    assert_eq!(        meta.get(USER_INPUT_REQUESTED_DURING_TURN_KEY)            .and_then(Value::as_bool),        Some(true)    );}#[test]fn turn_metadata_state_ignores_client_reserved_metadata_before_start() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        /*forked_from_thread_id*/ None,        /*parent_thread_id*/ None,        &SessionSource::Exec,        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    state.set_responsesapi_client_metadata(HashMap::from([        (            "turn_started_at_unix_ms".to_string(),            "client-supplied".to_string(),        ),        (            "forked_from_thread_id".to_string(),            "client-supplied".to_string(),        ),        (            "parent_thread_id".to_string(),            "client-supplied".to_string(),        ),        ("subagent_kind".to_string(), "client-supplied".to_string()),    ]));    let header = test_turn_metadata_header(&state);    let json: Value = serde_json::from_str(&header).expect("json");    assert!(json.get("turn_started_at_unix_ms").is_none());    assert!(json.get("forked_from_thread_id").is_none());    assert!(json.get("parent_thread_id").is_none());    assert!(json.get("subagent_kind").is_none());}#[test]fn turn_metadata_state_merges_client_metadata_without_replacing_reserved_fields() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let source_thread_id =        ThreadId::from_string("44444444-4444-4444-8444-444444444444").expect("thread id");    let parent_thread_id =        ThreadId::from_string("55555555-5555-4555-8555-555555555555").expect("thread id");    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        Some(source_thread_id),        Some(parent_thread_id),        &SessionSource::SubAgent(SubAgentSource::ThreadSpawn {            parent_thread_id,            depth: 1,            agent_path: None,            agent_nickname: None,            agent_role: None,        }),        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    state.set_responsesapi_client_metadata(HashMap::from([        ("fiber_run_id".to_string(), "fiber-123".to_string()),        ("origin".to_string(), "東京".to_string()),        ("workspace_kind".to_string(), "projectless".to_string()),        ("model".to_string(), "client-supplied".to_string()),        (            "reasoning_effort".to_string(),            "client-supplied".to_string(),        ),        ("session_id".to_string(), "client-supplied".to_string()),        ("thread_id".to_string(), "client-supplied".to_string()),        ("installation_id".to_string(), "client-supplied".to_string()),        (            "x-codex-installation-id".to_string(),            "client-supplied".to_string(),        ),        (            "x-codex-parent-thread-id".to_string(),            "client-supplied".to_string(),        ),        (            "x-openai-subagent".to_string(),            "client-supplied".to_string(),        ),        (            "forked_from_thread_id".to_string(),            "client-supplied".to_string(),        ),        (            "parent_thread_id".to_string(),            "client-supplied".to_string(),        ),        ("subagent_kind".to_string(), "client-supplied".to_string()),        ("turn_id".to_string(), "client-supplied".to_string()),        (WINDOW_ID_KEY.to_string(), "client-supplied".to_string()),        ("thread_source".to_string(), "client-supplied".to_string()),        ("request_kind".to_string(), "client-supplied".to_string()),        (            "turn_started_at_unix_ms".to_string(),            "client-supplied".to_string(),        ),    ]));    state.set_turn_started_at_unix_ms(/*turn_started_at_unix_ms*/ 1_700_000_000_123);    let header = test_turn_metadata_header(&state);    assert!(header.is_ascii());    assert!(!header.contains("東京"));    let json: Value = serde_json::from_str(&header).expect("json");    assert_eq!(json["fiber_run_id"].as_str(), Some("fiber-123"));    assert_eq!(json["origin"].as_str(), Some("東京"));    assert_eq!(json["workspace_kind"].as_str(), Some("projectless"));    assert_eq!(json["model"].as_str(), Some("client-supplied"));    assert_eq!(json["reasoning_effort"].as_str(), Some("client-supplied"));    assert_eq!(json["session_id"].as_str(), Some("session-a"));    assert_eq!(json["thread_id"].as_str(), Some("thread-a"));    assert!(json.get(INSTALLATION_ID_KEY).is_none());    assert!(json.get("x-codex-installation-id").is_none());    assert!(json.get("x-codex-parent-thread-id").is_none());    assert!(json.get("x-openai-subagent").is_none());    assert_eq!(        json["forked_from_thread_id"].as_str(),        Some("44444444-4444-4444-8444-444444444444")    );    assert_eq!(        json["parent_thread_id"].as_str(),        Some("55555555-5555-4555-8555-555555555555")    );    assert_eq!(json["subagent_kind"].as_str(), Some("thread_spawn"));    assert_eq!(json["thread_source"].as_str(), Some("client-supplied"));    assert_eq!(json["turn_id"].as_str(), Some("turn-a"));    assert!(json.get("request_kind").is_none());    assert!(json.get(WINDOW_ID_KEY).is_none());    assert_eq!(        json["turn_started_at_unix_ms"].as_i64(),        Some(1_700_000_000_123)    );    let model_request_header = test_turn_responses_metadata_json(&state, "thread-a:1");    let model_request_json: Value =        serde_json::from_str(&model_request_header).expect("model request json");    assert_eq!(model_request_json["request_kind"].as_str(), Some("turn"));    assert_eq!(        model_request_json[INSTALLATION_ID_KEY].as_str(),        Some("installation-a")    );    assert_eq!(        model_request_json[WINDOW_ID_KEY].as_str(),        Some("thread-a:1")    );    let meta = state        .current_meta_value_for_mcp_request(test_mcp_turn_metadata_context())        .expect("turn metadata should be present");    assert_eq!(meta["model"].as_str(), Some("gpt-5.4"));    assert_eq!(meta["reasoning_effort"].as_str(), Some("high"));    assert!(meta.get(WINDOW_ID_KEY).is_none());    assert_eq!(state.workspace_kind().as_deref(), Some("projectless"));}#[test]fn turn_metadata_state_overlays_compaction_only_on_compaction_requests() {    let temp_dir = TempDir::new().expect("temp dir");    let cwd = temp_dir.path().abs();    let permission_profile = PermissionProfile::read_only();    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        /*forked_from_thread_id*/ None,        /*parent_thread_id*/ None,        &SessionSource::Exec,        "turn-a".to_string(),        cwd,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    state.set_responsesapi_client_metadata(HashMap::from([(        "compaction".to_string(),        "client-supplied".to_string(),    )]));    let compact_header = test_compaction_responses_metadata_json(        &state,        "thread-a:2",        CompactionTurnMetadata::new(            CompactionTrigger::Auto,            CompactionReason::ContextLimit,            CompactionImplementation::ResponsesCompactionV2,            CompactionPhase::MidTurn,        ),    );    let compact_json: Value = serde_json::from_str(&compact_header).expect("json");    assert_eq!(compact_json["request_kind"].as_str(), Some("compaction"));    assert_eq!(compact_json["turn_id"].as_str(), Some("turn-a"));    assert_eq!(compact_json[WINDOW_ID_KEY].as_str(), Some("thread-a:2"));    assert_eq!(        compact_json["compaction"],        serde_json::json!({            "trigger": "auto",            "reason": "context_limit",            "implementation": "responses_compaction_v2",            "phase": "mid_turn",            "strategy": "memento",        })    );    let regular_header = test_turn_responses_metadata_json(&state, "thread-a:3");    let regular_json: Value = serde_json::from_str(&regular_header).expect("json");    assert_eq!(regular_json["request_kind"].as_str(), Some("turn"));    assert_eq!(regular_json[WINDOW_ID_KEY].as_str(), Some("thread-a:3"));    assert!(regular_json.get("compaction").is_none());}#[tokio::test]async fn turn_metadata_state_preserves_lineage_after_git_enrichment() {    let (_temp_dir, repo_path) = create_clean_git_repo("repo").await;    let permission_profile = PermissionProfile::read_only();    let parent_thread_id =        ThreadId::from_string("66666666-6666-4666-8666-666666666666").expect("thread id");    let state = TurnMetadataState::new(        "session-a".to_string(),        "thread-a".to_string(),        Some(parent_thread_id),        Some(parent_thread_id),        &SessionSource::SubAgent(SubAgentSource::ThreadSpawn {            parent_thread_id,            depth: 1,            agent_path: None,            agent_nickname: None,            agent_role: None,        }),        "turn-a".to_string(),        repo_path,        &permission_profile,        WindowsSandboxLevel::Disabled,        /*enforce_managed_network*/ false,    );    state.spawn_git_enrichment_task();    let json = tokio::time::timeout(Duration::from_secs(2), async {        loop {            let header = test_turn_metadata_header(&state);            let json: Value = serde_json::from_str(&header).expect("json");            if json                .get("workspaces")                .and_then(Value::as_object)                .is_some_and(|workspaces| !workspaces.is_empty())            {                return json;            }            tokio::time::sleep(Duration::from_millis(10)).await;        }    })    .await    .expect("git enrichment should complete");    assert_eq!(        json["forked_from_thread_id"].as_str(),        Some("66666666-6666-4666-8666-666666666666")    );    assert_eq!(        json["parent_thread_id"].as_str(),        Some("66666666-6666-4666-8666-666666666666")    );    assert_eq!(json["subagent_kind"].as_str(), Some("thread_spawn"));}