pub fn probe_read_freshness(msgs: &[ChatMessage]) -> ReadFreshnessExpand description
The A8 disk-probe pre-pass, deliberately kept OUTSIDE project_messages
so the pure projection core never touches the filesystem itself (SPEC.md
A8’s purity requirement). Call this with the exact same msgs slice about
to be projected — indices must line up — and thread the result through
ReductionPolicy::read_freshness before calling project_messages (or
project_messages); it is only ever consulted when policy.elide_stale_reads is
set, so callers that never enable A8 can skip calling this entirely.
Staleness check. For each detect_reads hit, this re-reads the file
and compares content_hash(“hash of the current bytes, lossy-UTF8-decoded”)
against the hash of the tool result’s recorded content — exactly the
transform read_file itself applies for a plain whole-file read
(tools/builtins.rs:70-97, no offset/limit, file under
MAX_READ_BYTES). This one comparison also naturally covers the two
harder cases without duplicating read_file’s own decoration/slicing
logic (which lives in a file this change does not touch):
- a sliced read (
offset/limitgiven): the recorded content is a line slice, never byte-identical to a raw whole-file re-read, so the hash mismatches and the read is (correctly, conservatively) never fresh; - a read whose original result already carried
read_file’s own oversize-truncation notice: same reasoning, the recorded content is not raw file bytes, so it never matches a raw re-read.
Both are the documented SPEC.md A8 fallback (“mtime+len only, document”) taken to its simplest safe form: this implementation’s fallback for anything it cannot cheaply verify is “treat as changed” (never elide), which only ever under-elides, never over-elides — the safe direction.
Unreadable/deleted files are likewise never fresh. mtime is recorded for
ReadLogEntry::mtime whenever the file’s metadata is readable, even
when the freshness verdict itself is false.