2323//! The fingerprint covers everything that can change the output:
2424//! - mtime of `report-cache.bin` (any new parse miss bumps it)
2525//! - mtime of `discovery.bin` (any new/deleted source bumps it)
26+ //! - session-files signature (hash of every known session file's
27+ //! mtime + size — catches appends to an
28+ //! existing session jsonl while the user
29+ //! keeps working in Claude / Cursor / etc.
30+ //! between runs, which don't bump the
31+ //! discovery-dir mtimes)
2632//! - today's local date (period windows shift at the day boundary)
2733//! - period + provider filter
2834//!
@@ -88,7 +94,11 @@ fn today_local_packed() -> u64 {
8894/// `extra` lets format-specific renderers fold things like terminal size
8995/// into the fingerprint — for the rich dashboard a different `cols` /
9096/// `rows` would produce different bytes, so the cache must invalidate.
91- fn fingerprint ( period : & str , provider : & str , format : & str , extra : u64 ) -> u64 {
97+ /// `session_sig` is the `u64` hash of every known session file's
98+ /// `(path, mtime, size)` triple (see `parser::stat_all_sources`) — this
99+ /// is what catches in-place appends to existing jsonl files when the
100+ /// user keeps working in Claude / Cursor / etc. between runs.
101+ fn fingerprint ( period : & str , provider : & str , format : & str , extra : u64 , session_sig : u64 ) -> u64 {
92102 let ( rep_mtime, rep_size) = meta ( & report_cache_path ( ) ) ;
93103 let ( disc_mtime, disc_size) = meta ( & discovery_cache_path ( ) ) ;
94104 let day = today_local_packed ( ) ;
@@ -105,18 +115,24 @@ fn fingerprint(period: &str, provider: &str, format: &str, extra: u64) -> u64 {
105115 h. write_u64 ( rep_size) ;
106116 h. write_u64 ( disc_mtime) ;
107117 h. write_u64 ( disc_size) ;
118+ h. write_u64 ( session_sig) ;
108119 // Bake the cache file format version into the fingerprint so a binary
109120 // with a changed renderer can never serve stale output from before
110121 // its upgrade.
111122 h. write_u32 ( VERSION ) ;
112123 h. finish ( )
113124}
114125
115- const VERSION : u32 = 2 ;
126+ // Bumped to 3: fingerprint now folds in a session-files signature so
127+ // in-place appends to existing jsonl files correctly invalidate.
128+ const VERSION : u32 = 3 ;
116129
117130/// Try to serve the report straight from a previous run's cached output.
118131/// Returns `true` if a hit was found and printed; the caller should exit.
119- pub fn try_serve ( period : & str , provider : & str , format : & str , extra : u64 ) -> bool {
132+ /// `session_sig` is the caller's freshly-computed session-files signature
133+ /// (see `parser::stat_all_sources`) — it's the only piece of the
134+ /// fingerprint that catches in-place appends to a session jsonl.
135+ pub fn try_serve ( period : & str , provider : & str , format : & str , extra : u64 , session_sig : u64 ) -> bool {
120136 let path = output_cache_path ( period, provider, format) ;
121137 let bytes = match std:: fs:: read ( & path) {
122138 Ok ( b) => b,
@@ -136,7 +152,7 @@ pub fn try_serve(period: &str, provider: &str, format: &str, extra: u64) -> bool
136152 if 12 + len > bytes. len ( ) {
137153 return false ;
138154 }
139- let want_fp = fingerprint ( period, provider, format, extra) ;
155+ let want_fp = fingerprint ( period, provider, format, extra, session_sig ) ;
140156 if stored_fp != want_fp {
141157 return false ;
142158 }
@@ -146,8 +162,8 @@ pub fn try_serve(period: &str, provider: &str, format: &str, extra: u64) -> bool
146162
147163/// Persist the rendered output keyed on the current fingerprint. Called
148164/// after a fresh compute so the next identical run can hit `try_serve`.
149- pub fn store ( period : & str , provider : & str , format : & str , extra : u64 , output : & [ u8 ] ) {
150- let fp = fingerprint ( period, provider, format, extra) ;
165+ pub fn store ( period : & str , provider : & str , format : & str , extra : u64 , session_sig : u64 , output : & [ u8 ] ) {
166+ let fp = fingerprint ( period, provider, format, extra, session_sig ) ;
151167 let path = output_cache_path ( period, provider, format) ;
152168 if let Some ( parent) = path. parent ( ) {
153169 let _ = std:: fs:: create_dir_all ( parent) ;
0 commit comments