Skip to content

fix(cache): prevent stale same-tick stat-index hits - #2385

Open
kavioavio wants to merge 1 commit into
Graphify-Labs:v8from
kavioavio:fix/stat-index-same-tick
Open

fix(cache): prevent stale same-tick stat-index hits#2385
kavioavio wants to merge 1 commit into
Graphify-Labs:v8from
kavioavio:fix/stat-index-same-tick

Conversation

@kavioavio

Copy link
Copy Markdown

Problem

The persisted stat index could reuse a stale file hash or word count when a rapid rewrite kept the same size and mtime_ns. That could replay semantic cache data for the previous contents.

Changes

  • treat recent and flagless stat-index values as requiring revalidation
  • track hash and word-count volatility independently
  • confirm warm-hit stats and require two equal byte snapshots before caching a hash
  • preserve stable portable cache hits and handle future-dated restored files
  • add deterministic same-stat, reload, migration, race, and portability regressions

Verification

  • Python 3.12, uv run --frozen --all-extras pytest -q: 3984 passed, 3 skipped
  • focused cache/stat-index tests: 80 passed
  • seven race/volatile regressions: 20 repeated runs
  • Ruff: pass
  • Pyright for graphify/cache.py: 0 errors
  • git diff --check: pass

Revalidate recent and legacy stat-index values, confirm warm-hit stats, and require consistent content snapshots before caching file hashes.

Keep hash and word-count volatility independent and cover reload, promotion, portability, future-mtime, and race paths.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

This PR reworks the stat-based fastpath in graphify/cache.py for file_hash and cached_word_count. It introduces a timestamp "stability window" (2 seconds) so recently modified files bypass the stat-only cache hit until their timestamps settle, plus new helpers for validating stat signatures, checking stability, reading content confirmed by consecutive equal stat snapshots, and dropping stale entries. It also adds per-field "volatile" flags (hashes_volatile, word_count_volatile) so a refreshed hash doesn't promote a stale sibling word-count and vice versa. The test surface is broad, touching test_cache.py, test_stat_index_portability.py, and test_word_count_cache.py — covering cache roundtrips, invalidation, portability across roots, future-mtime/ctime fallback behavior, and same-stat rewrite scenarios.

No blocking issues surfaced. 10 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 963 functions depend on the 261 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: file_hash() — 43 callers, 8 callees
  • worse: cached_word_count() — 6 callers, 6 callees

Verification — 963 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 631 function(s) in the blast radius were not formally verified this run

· 1 grounded finding(s) anchored inline below; 1 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/cache.py
raw, st = confirm, confirm_st
return raw, None

def file_hash(path: Path, root: Path = Path("."), cache_root: "Path | None" = None) -> str:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regressionfile_hash()

fans out to 8 callees (efferent coupling); 43 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant