@@ -887,3 +887,78 @@ func TestExtractAllModifiedFiles_SubagentOnlyChanges(t *testing.T) {
887887 t .Errorf ("missing expected file %q" , f )
888888 }
889889}
890+
891+ // Regression for #329: a subagent spawned BEFORE the checkpoint's startLine
892+ // must still be discovered, because it can keep modifying files in later turns.
893+ // The Task spawn/result live in lines before startLine; only the full transcript
894+ // scan finds them.
895+ func TestExtractAllModifiedFiles_FindsSubagentSpawnedBeforeStartLine (t * testing.T ) {
896+ t .Parallel ()
897+
898+ tmpDir := t .TempDir ()
899+ subagentsDir := tmpDir + "/tasks/toolu_task1"
900+ c := & ClaudeCodeAgent {}
901+ if err := os .MkdirAll (subagentsDir , 0o755 ); err != nil {
902+ t .Fatalf ("failed to create subagents dir: %v" , err )
903+ }
904+
905+ transcriptData := buildJSONL (
906+ makeTaskToolUseLine (t , "a1" , "toolu_taskA" ), // line 0 (before startLine)
907+ makeTaskResultLine (t , "uA" , "toolu_taskA" , "subA" ), // line 1 (before startLine)
908+ makeWriteToolLine (t , "a2" , "/repo/main.go" ), // line 2 (>= startLine)
909+ )
910+ writeJSONLFile (t , subagentsDir + "/agent-subA.jsonl" ,
911+ makeWriteToolLine (t , "sa1" , "/repo/helper.go" ),
912+ )
913+
914+ files , err := c .ExtractAllModifiedFiles (transcriptData , 2 , subagentsDir )
915+ if err != nil {
916+ t .Fatalf ("ExtractAllModifiedFiles() error: %v" , err )
917+ }
918+
919+ got := make (map [string ]bool , len (files ))
920+ for _ , f := range files {
921+ got [f ] = true
922+ }
923+ if ! got ["/repo/main.go" ] {
924+ t .Errorf ("missing main-agent file /repo/main.go: %v" , files )
925+ }
926+ if ! got ["/repo/helper.go" ] {
927+ t .Errorf ("subagent spawned before startLine was not discovered; missing /repo/helper.go: %v" , files )
928+ }
929+ }
930+
931+ // Regression for #329: subagent token usage must be counted even when the
932+ // subagent was spawned before the checkpoint's startLine.
933+ func TestCalculateTotalTokenUsage_CountsSubagentSpawnedBeforeStartLine (t * testing.T ) {
934+ t .Parallel ()
935+
936+ tmpDir := t .TempDir ()
937+ subagentsDir := tmpDir + "/tasks/toolu_task1"
938+ c := & ClaudeCodeAgent {}
939+ if err := os .MkdirAll (subagentsDir , 0o755 ); err != nil {
940+ t .Fatalf ("failed to create subagents dir: %v" , err )
941+ }
942+
943+ // Subagent spawned in lines 0-1 (before startLine=2); main usage on line 2.
944+ transcriptData := buildJSONL (
945+ makeTaskToolUseLine (t , "a1" , "toolu_taskB" ),
946+ makeTaskResultLine (t , "uB" , "toolu_taskB" , "subB" ),
947+ `{"type":"assistant","uuid":"a2","message":{"id":"m2","usage":{"input_tokens":300,"output_tokens":150}}}` ,
948+ )
949+ writeJSONLFile (t , subagentsDir + "/agent-subB.jsonl" ,
950+ `{"type":"assistant","uuid":"sa1","message":{"id":"sm1","usage":{"input_tokens":50,"output_tokens":25}}}` ,
951+ )
952+
953+ usage , err := c .CalculateTotalTokenUsage (transcriptData , 2 , subagentsDir )
954+ if err != nil {
955+ t .Fatalf ("CalculateTotalTokenUsage() error: %v" , err )
956+ }
957+ if usage .SubagentTokens == nil {
958+ t .Fatal ("subagent spawned before startLine was not counted (SubagentTokens is nil)" )
959+ }
960+ if usage .SubagentTokens .InputTokens != 50 || usage .SubagentTokens .OutputTokens != 25 {
961+ t .Errorf ("subagent tokens = input %d output %d, want input 50 output 25" ,
962+ usage .SubagentTokens .InputTokens , usage .SubagentTokens .OutputTokens )
963+ }
964+ }
0 commit comments