@@ -367,3 +367,54 @@ func TestBillingReconciliationFlag(t *testing.T) {
367367 t .Errorf ("overshoot must surface as positive input_delta, got %v" , recon )
368368 }
369369}
370+
371+ // Claude Code writes locally fabricated assistant entries (model
372+ // "<synthetic>", isApiErrorMessage) with an all-zero usage object when an
373+ // API call errors. They were never billed: treating one as a real call
374+ // reconciles the whole history against a zero-token prompt and dumps the
375+ // usage-derived pieces into the fresh-input tier.
376+ func TestBillingSkipsSyntheticZeroUsageCalls (t * testing.T ) {
377+ u1 := & Usage {InputTokens : 100 , CacheCreationInputTokens : 8_000 , OutputTokens : 60_000 }
378+ entries := []TranscriptEntry {userPromptEntry ("do the thing" )}
379+ entries = append (entries , assistantCall (t , "m1" , u1 ,
380+ Content {Type : "thinking" , Thinking : "redacted" },
381+ Content {Type : "text" , Text : "working on it" },
382+ )... )
383+
384+ // The synthetic error entry: zero usage, full history would be "its
385+ // request" — must be ignored entirely.
386+ entries = append (entries , assistantCall (t , "synthetic-1" , & Usage {},
387+ Content {Type : "text" , Text : "API error: request interrupted" },
388+ )... )
389+
390+ u2 := & Usage {InputTokens : 50 , CacheReadInputTokens : 70_000 ,
391+ CacheCreationInputTokens : 2_000 , OutputTokens : 40 }
392+ entries = append (entries , assistantCall (t , "m2" , u2 , Content {Type : "text" , Text : "done" })... )
393+
394+ snap := computeBillingSnapshot (entries , entries )
395+ if snap == nil {
396+ t .Fatal ("expected billing snapshot" )
397+ }
398+ if got := snap ["llm_calls" ].(int ); got != 2 {
399+ t .Fatalf ("llm_calls = %d, want 2 (synthetic call must be skipped)" , got )
400+ }
401+
402+ wantRead := u1 .CacheReadInputTokens + u2 .CacheReadInputTokens
403+ wantWrite := u1 .CacheCreationInputTokens + u2 .CacheCreationInputTokens
404+ wantFresh := u1 .InputTokens + u2 .InputTokens
405+ wantOut := u1 .OutputTokens + u2 .OutputTokens
406+
407+ read , write , fresh , output , rows := billingColumnSums (snap )
408+ closeEnough := func (got , want int ) bool {
409+ d := got - want
410+ if d < 0 {
411+ d = - d
412+ }
413+ return d <= rows
414+ }
415+ if ! closeEnough (read , wantRead ) || ! closeEnough (write , wantWrite ) ||
416+ ! closeEnough (fresh , wantFresh ) || ! closeEnough (output , wantOut ) {
417+ t .Errorf ("Σ lanes = read %d / write %d / fresh %d / output %d, want %d/%d/%d/%d (±%d)" ,
418+ read , write , fresh , output , wantRead , wantWrite , wantFresh , wantOut , rows )
419+ }
420+ }
0 commit comments