@@ -17,6 +17,8 @@ func TestObserveProviderSourceMatchesClaudeLegacyParser(t *testing.T) {
1717 projectDir := "-Users-dev-code-demo"
1818 sourcePath := filepath .Join (root , projectDir , "claude-shadow.jsonl" )
1919 writeProviderShadowSourceFile (t , sourcePath , claudeShadowFixture ())
20+ usagePath := filepath .Join (root , projectDir , "usage-only.jsonl" )
21+ writeProviderShadowSourceFile (t , usagePath , claudeShadowUsageOnlyFixture ())
2022
2123 provider , ok := parser .NewProvider (parser .AgentClaude , parser.ProviderConfig {
2224 Roots : []string {root },
@@ -25,7 +27,15 @@ func TestObserveProviderSourceMatchesClaudeLegacyParser(t *testing.T) {
2527 require .True (t , ok )
2628 sources , err := provider .Discover (context .Background ())
2729 require .NoError (t , err )
28- require .Len (t , sources , 1 )
30+ require .Len (t , sources , 2 )
31+ sourcesByPath := make (map [string ]parser.SourceRef , len (sources ))
32+ for _ , source := range sources {
33+ sourcesByPath [source .DisplayPath ] = source
34+ }
35+ source , ok := sourcesByPath [sourcePath ]
36+ require .True (t , ok )
37+ usageSource , ok := sourcesByPath [usagePath ]
38+ require .True (t , ok )
2939
3040 legacyResults , legacyExcluded , err := parser .ParseClaudeSessionWithExclusions (
3141 sourcePath ,
@@ -34,13 +44,15 @@ func TestObserveProviderSourceMatchesClaudeLegacyParser(t *testing.T) {
3444 )
3545 require .NoError (t , err )
3646 require .Len (t , legacyResults , 1 )
47+ require .True (t , claudeShadowHasTokenUsage (legacyResults [0 ].Messages ))
3748
3849 observation , err := ObserveProviderSource (context .Background (), provider , ProviderObserveRequest {
39- Source : sources [ 0 ] ,
50+ Source : source ,
4051 Machine : "devbox" ,
4152 })
4253 require .NoError (t , err )
4354 require .Len (t , observation .Results , 1 )
55+ require .True (t , claudeShadowHasTokenUsage (observation .Results [0 ].Messages ))
4456 legacyResults [0 ].Session .File .Hash = observation .Fingerprint .Hash
4557
4658 assert .Equal (t , legacyResults [0 ].Session , observation .Results [0 ].Session )
@@ -49,11 +61,53 @@ func TestObserveProviderSourceMatchesClaudeLegacyParser(t *testing.T) {
4961 assert .Equal (t , legacyExcluded , observation .ExcludedSessionIDs )
5062 assert .Equal (t , []string {legacyResults [0 ].Session .ID }, observation .Planned .DataVersionSessionIDs ())
5163 assert .Empty (t , observation .Planned .Diagnostics )
64+
65+ _ , usageLegacyExcluded , err := parser .ParseClaudeSessionWithExclusions (
66+ usagePath ,
67+ "demo" ,
68+ "devbox" ,
69+ )
70+ require .NoError (t , err )
71+ require .Equal (t , []string {"usage-only" }, usageLegacyExcluded )
72+
73+ usageObservation , err := ObserveProviderSource (
74+ context .Background (),
75+ provider ,
76+ ProviderObserveRequest {
77+ Source : usageSource ,
78+ Machine : "devbox" ,
79+ },
80+ )
81+ require .NoError (t , err )
82+ assert .Empty (t , usageObservation .Results )
83+ assert .Equal (t , usageLegacyExcluded , usageObservation .ExcludedSessionIDs )
84+ assert .Empty (t , usageObservation .Planned .Diagnostics )
5285}
5386
5487func claudeShadowFixture () string {
5588 return testjsonl .JoinJSONL (
5689 testjsonl .ClaudeUserJSON ("provider question" , "2026-01-02T03:04:05Z" ),
57- testjsonl .ClaudeAssistantJSON ("Done." , "2026-01-02T03:04:06Z" ),
90+ `{"type":"assistant","timestamp":"2026-01-02T03:04:06Z","message":{"model":"claude-sonnet-4-20250514","content":[{"type":"text","text":"Done."}],"usage":{"input_tokens":100,"output_tokens":40,"cache_creation_input_tokens":10,"cache_read_input_tokens":5}}}` ,
91+ )
92+ }
93+
94+ func claudeShadowUsageOnlyFixture () string {
95+ return testjsonl .JoinJSONL (
96+ testjsonl .ClaudeUserJSON (
97+ "<command-name>/usage</command-name>\n " +
98+ "<command-message>usage</command-message>" ,
99+ "2026-01-02T03:04:07Z" ,
100+ ),
58101 )
59102}
103+
104+ func claudeShadowHasTokenUsage (messages []parser.ParsedMessage ) bool {
105+ for _ , message := range messages {
106+ if len (message .TokenUsage ) > 0 ||
107+ message .HasContextTokens ||
108+ message .HasOutputTokens {
109+ return true
110+ }
111+ }
112+ return false
113+ }
0 commit comments