@@ -158,10 +158,16 @@ struct CodexSubagentAccountingIntegrationTests {
158158 . replacingOccurrences (
159159 of: " \" type \" : \" inter_agent_communication_metadata \" " ,
160160 with: " \" ty \\ u0070e \" : \" inter_agent_communication_metadata \" " ) )
161+ let escapedTimestampFileURL = try env. writeCodexSessionFile (
162+ day: day,
163+ filename: " rollout- \( forkTimestamp) -marker-child-escaped-timestamp.jsonl " ,
164+ contents: fastContents
165+ . replacingOccurrences ( of: " marker-child " , with: " marker-child-escaped-timestamp " )
166+ . replacingOccurrences ( of: " \" timestamp \" : " , with: " \" time \\ u0073tamp \" : " ) )
161167
162168 let dayKey = CostUsageScanner . CostUsageDayRange. dayKey ( from: day)
163169 let normalizedLeafModel = CostUsagePricing . normalizeCodexModel ( leafModel)
164- for fileURL in [ fastFileURL, fallbackFileURL] {
170+ for fileURL in [ fastFileURL, fallbackFileURL, escapedTimestampFileURL ] {
165171 var resolvedParentBaseline = false
166172 let parsed = CostUsageScanner . parseCodexFile (
167173 fileURL: fileURL,
@@ -187,11 +193,11 @@ struct CodexSubagentAccountingIntegrationTests {
187193 until: day,
188194 now: day,
189195 options: options)
190- #expect( report. data. first? . totalTokens == 110 )
196+ #expect( report. data. first? . totalTokens == 165 )
191197
192198 let cache = CostUsageCacheIO . load ( provider: . codex, cacheRoot: env. cacheRoot)
193199 let childUsages = cache. files. values. filter { $0. sessionId? . hasPrefix ( " marker-child " ) == true }
194- #expect( childUsages. count == 2 )
200+ #expect( childUsages. count == 3 )
195201 #expect( childUsages. allSatisfy {
196202 $0. forkBaselineDependencyKey == CostUsageScanner . codexForkDependencyNotRequiredKey
197203 } )
@@ -261,6 +267,133 @@ struct CodexSubagentAccountingIntegrationTests {
261267 #expect( resolvedParentBaseline)
262268 }
263269
270+ @Test
271+ func `oversized ancestor metadata remains conservative copied- prefix evidence`() throws {
272+ let env = try CostUsageTestEnvironment ( )
273+ defer { env. cleanup ( ) }
274+
275+ let day = try env. makeLocalNoon ( year: 2026 , month: 7 , day: 16 )
276+ let timestamp = env. isoString ( for: day)
277+ let opening = try env. jsonl ( [
278+ [
279+ " type " : " session_meta " ,
280+ " timestamp " : timestamp,
281+ " payload " : [
282+ " id " : " oversized-child " ,
283+ " source " : [ " subagent " : [ " thread_spawn " : [ : ] ] ] ,
284+ ] ,
285+ ] ,
286+ self . tokenCount (
287+ timestamp: env. isoString ( for: day. addingTimeInterval ( 1 ) ) ,
288+ model: " openai/gpt-5.3 " ,
289+ total: ( input: 1000 , cached: 900 , output: 100 ) ,
290+ last: ( input: 50 , cached: 10 , output: 5 ) ) ,
291+ ] )
292+ let oversizedAncestor = " { \" type \" : \" session_meta \" , \" timestamp \" : \" \( timestamp) \" , " +
293+ " \" payload \" :{ \" id \" : \" oversized-parent \" , \" padding \" : \" " +
294+ String( repeating: " x " , count: 300_000 ) + " \" }} \n "
295+ let tail = try env. jsonl ( [
296+ self . tokenCount (
297+ timestamp: env. isoString ( for: day. addingTimeInterval ( 2 ) ) ,
298+ model: " openai/gpt-5.4 " ,
299+ total: ( input: 1050 , cached: 910 , output: 105 ) ,
300+ last: ( input: 50 , cached: 10 , output: 5 ) ) ,
301+ ] )
302+ let fileURL = try env. writeCodexSessionFile (
303+ day: day,
304+ filename: " rollout- \( timestamp) -oversized-ancestor.jsonl " ,
305+ contents: opening + oversizedAncestor + tail)
306+
307+ var resolvedParentBaseline = false
308+ let parsed = CostUsageScanner . parseCodexFile (
309+ fileURL: fileURL,
310+ range: CostUsageScanner . CostUsageDayRange ( since: day, until: day) ,
311+ inheritedTotalsResolver: { parentSessionID, _ in
312+ resolvedParentBaseline = true
313+ #expect( parentSessionID == " oversized-parent " )
314+ return . resolved( . init( input: 1000 , cached: 900 , output: 100 ) )
315+ } )
316+
317+ let dayKey = CostUsageScanner . CostUsageDayRange. dayKey ( from: day)
318+ let model = CostUsagePricing . normalizeCodexModel ( " openai/gpt-5.4 " )
319+ #expect( parsed. days [ dayKey] ? [ model] == [ 50 , 10 , 5 ] )
320+ #expect( parsed. forkedFromId == " oversized-parent " )
321+ #expect( parsed. dependsOnParentTotals)
322+ #expect( resolvedParentBaseline)
323+ }
324+
325+ @Test
326+ func `invalid timestamp suffix markers preserve parent dependency on both parser paths`() throws {
327+ let env = try CostUsageTestEnvironment ( )
328+ defer { env. cleanup ( ) }
329+
330+ let day = try env. makeLocalNoon ( year: 2026 , month: 7 , day: 16 )
331+ let timestamp = env. isoString ( for: day)
332+ let contents = try env. jsonl ( [
333+ [
334+ " type " : " session_meta " ,
335+ " timestamp " : timestamp,
336+ " payload " : [
337+ " id " : " invalid-marker-child " ,
338+ " source " : [ " subagent " : [ " thread_spawn " : [ : ] ] ] ,
339+ ] ,
340+ ] ,
341+ self . tokenCount (
342+ timestamp: env. isoString ( for: day. addingTimeInterval ( 1 ) ) ,
343+ model: " openai/gpt-5.3 " ,
344+ total: ( input: 1000 , cached: 900 , output: 100 ) ,
345+ last: ( input: 50 , cached: 10 , output: 5 ) ) ,
346+ [
347+ " type " : " session_meta " ,
348+ " timestamp " : timestamp,
349+ " payload " : [ " id " : " invalid-marker-parent " ] ,
350+ ] ,
351+ [
352+ " type " : " turn_context " ,
353+ " payload " : [ " model " : " openai/gpt-5.4 " ] ,
354+ ] ,
355+ [
356+ " type " : " inter_agent_communication_metadata " ,
357+ " payload " : [ " trigger_turn " : true ] ,
358+ ] ,
359+ self . tokenCount (
360+ timestamp: env. isoString ( for: day. addingTimeInterval ( 2 ) ) ,
361+ model: " openai/gpt-5.4 " ,
362+ total: ( input: 1050 , cached: 910 , output: 105 ) ,
363+ last: ( input: 50 , cached: 10 , output: 5 ) ) ,
364+ ] )
365+ let fastFileURL = try env. writeCodexSessionFile (
366+ day: day,
367+ filename: " rollout- \( timestamp) -invalid-marker.jsonl " ,
368+ contents: contents)
369+ let fallbackFileURL = try env. writeCodexSessionFile (
370+ day: day,
371+ filename: " rollout- \( timestamp) -invalid-marker-fallback.jsonl " ,
372+ contents: contents
373+ . replacingOccurrences ( of: " invalid-marker-child " , with: " invalid-marker-child-fallback " )
374+ . replacingOccurrences ( of: " \" type \" : \" turn_context \" " , with: " \" ty \\ u0070e \" : \" turn_context \" " )
375+ . replacingOccurrences (
376+ of: " \" type \" : \" inter_agent_communication_metadata \" " ,
377+ with: " \" ty \\ u0070e \" : \" inter_agent_communication_metadata \" " ) )
378+
379+ let dayKey = CostUsageScanner . CostUsageDayRange. dayKey ( from: day)
380+ let model = CostUsagePricing . normalizeCodexModel ( " openai/gpt-5.4 " )
381+ for fileURL in [ fastFileURL, fallbackFileURL] {
382+ var resolvedParentBaseline = false
383+ let parsed = CostUsageScanner . parseCodexFile (
384+ fileURL: fileURL,
385+ range: CostUsageScanner . CostUsageDayRange ( since: day, until: day) ,
386+ inheritedTotalsResolver: { parentSessionID, _ in
387+ resolvedParentBaseline = true
388+ #expect( parentSessionID == " invalid-marker-parent " )
389+ return . resolved( . init( input: 1000 , cached: 900 , output: 100 ) )
390+ } )
391+ #expect( parsed. days [ dayKey] ? [ model] == [ 50 , 10 , 5 ] )
392+ #expect( parsed. dependsOnParentTotals)
393+ #expect( resolvedParentBaseline)
394+ }
395+ }
396+
264397 @Test
265398 func `idless copied prefix without a parent or local marker is suppressed`() throws {
266399 let env = try CostUsageTestEnvironment ( )
0 commit comments