A read-only session parse (served when the cache refresh lock times out or
is unavailable) reported itself as a complete hydration, so the daily
backfill published `complete: true` and advanced `lastComputedDate` to
yesterday over days the parse never covered. Because gapStart is
lastComputedDate + 1, those days were never looked at again — observed as a
cache marked complete at 2026-07-28 whose newest entry was 2026-07-25.
- parser: a read-only run reports a complete hydration only when nothing
changed under the snapshot it served (a skipped or staled file makes it
partial).
- daily-cache: only a complete parse may advance `lastComputedDate`, on both
the gap and the full re-derive paths.
- daily-cache: a cache whose watermark outruns its newest populated day has
its watermark pulled back to that day, so the ordinary gap parse re-derives
the tail instead of trusting the marker.
Days are only ever added or re-derived, never dropped; the preservation bias
is unchanged and covered by test.
* cache: strict cross-process gate for the warm session-cache refresh transaction (#645)
* lock: serialize the fence against the owner's own heartbeat
verifyStillOwner and the heartbeat tick both take the takeover guard;
without in-process serialization the fence could observe its own
heartbeat's guard file, read it as displacement, and abort a legitimate
publication. Fail-safe, but it discarded the parse the lock exists to
protect (~6% of verifies at a 1ms heartbeat in the repro). Owner-side
guard operations now run through one serializer; cross-process guard
semantics are unchanged. Regression test at heartbeatMs 1, mutation-
verified against the unserialized code.
---------
Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
Co-authored-by: reviewer <review@local>