From c5cf62bc6107b418aadf003b1ad8ce57f42c05d4 Mon Sep 17 00:00:00 2001 From: minislively Date: Mon, 1 Jun 2026 10:02:54 +0900 Subject: [PATCH] Preserve fork replay coverage for submit-cap failures A high-fanout Codex subagent day can make published submit payloads exceed server daily caps when replayed inherited cache rows are counted. Add a focused regression fixture that keeps the latest fork replay behavior anchored to the submit failure shape without changing parser semantics. Constraint: Public issue #650 reports validation failures from sanitized local Codex fork-rollout evidence. Rejected: Change parser logic in this PR | current main already contains fork replay skipping; this PR locks the submit-cap shape as coverage. Confidence: medium Scope-risk: narrow Tested: cargo fmt --check Tested: cargo test -p tokscale-core sessions::codex::tests::test_forked_child_submit_cap_regression_skips_large_inherited_cache_replays -- --exact Tested: cargo test -p tokscale-core sessions::codex::tests::test_forked_child -- --nocapture --- crates/tokscale-core/src/sessions/codex.rs | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/tokscale-core/src/sessions/codex.rs b/crates/tokscale-core/src/sessions/codex.rs index 6a71276fe..b5ab86bfa 100644 --- a/crates/tokscale-core/src/sessions/codex.rs +++ b/crates/tokscale-core/src/sessions/codex.rs @@ -1652,6 +1652,33 @@ mod tests { assert_eq!(messages[0].tokens.output, 2); } + #[test] + fn test_forked_child_submit_cap_regression_skips_large_inherited_cache_replays() { + let file = create_test_file(concat!( + r#"{"timestamp":"2026-05-05T21:51:57.991Z","type":"session_meta","payload":{"id":"child-session","forked_from_id":"parent-session","source":{"subagent":{"thread_spawn":{"parent_thread_id":"parent-session","depth":1,"agent_role":"architect"}}},"model_provider":"openai","agent_nickname":"architect","cwd":"/repo-child"}}"#, + "\n", + r#"{"timestamp":"2026-05-05T21:51:57.994Z","type":"event_msg","payload":{"type":"token_count","info":{"total_token_usage":{"input_tokens":1200000000,"cached_input_tokens":1180000000,"output_tokens":1000000,"reasoning_output_tokens":100000,"total_tokens":1201100000},"last_token_usage":{"input_tokens":750000000,"cached_input_tokens":740000000,"output_tokens":500000,"reasoning_output_tokens":50000,"total_tokens":750550000}}}}"#, + "\n", + r#"{"timestamp":"2026-05-05T21:51:58.947Z","type":"turn_context","payload":{"model":"gpt-5.5","cwd":"/repo-child"}}"#, + "\n", + r#"{"timestamp":"2026-05-05T21:51:58.948Z","type":"event_msg","payload":{"type":"token_count","info":{"total_token_usage":{"input_tokens":1180000000,"cached_input_tokens":1160000000,"output_tokens":900000,"reasoning_output_tokens":90000,"total_tokens":1180990000},"last_token_usage":{"input_tokens":20000000,"cached_input_tokens":20000000,"output_tokens":0,"reasoning_output_tokens":0,"total_tokens":20000000}}}}"#, + "\n", + r#"{"timestamp":"2026-05-05T21:51:58.949Z","type":"event_msg","payload":{"type":"token_count","info":{"total_token_usage":{"input_tokens":1200000000,"cached_input_tokens":1180000000,"output_tokens":1000000,"reasoning_output_tokens":100000,"total_tokens":1201100000},"last_token_usage":{"input_tokens":20000000,"cached_input_tokens":20000000,"output_tokens":100000,"reasoning_output_tokens":10000,"total_tokens":20110000}}}}"#, + "\n", + r#"{"timestamp":"2026-05-05T21:51:59.253Z","type":"event_msg","payload":{"type":"token_count","info":{"total_token_usage":{"input_tokens":1200001500,"cached_input_tokens":1180001000,"output_tokens":1000200,"reasoning_output_tokens":100050,"total_tokens":1201101750},"last_token_usage":{"input_tokens":1500,"cached_input_tokens":1000,"output_tokens":200,"reasoning_output_tokens":50,"total_tokens":1750}}}}"#, + "\n" + )); + + let messages = parse_codex_file(file.path()); + + assert_eq!(messages.len(), 1); + assert_eq!(messages[0].model_id, "gpt-5.5"); + assert_eq!(messages[0].tokens.input, 500); + assert_eq!(messages[0].tokens.cache_read, 1000); + assert_eq!(messages[0].tokens.output, 200); + assert_eq!(messages[0].tokens.reasoning, 50); + } + #[test] fn test_forked_child_detects_thread_spawn_source_without_top_level_fork_id() { let file = create_test_file(concat!(