Skip to content

feat(test-linker): add optional disk cache for test edges (TAP-4080)#193

Merged
wtthornton merged 1 commit into
masterfrom
tap-4080-test-edge-cache
Jul 2, 2026
Merged

feat(test-linker): add optional disk cache for test edges (TAP-4080)#193
wtthornton merged 1 commit into
masterfrom
tap-4080-test-edge-cache

Conversation

@wtthornton

Copy link
Copy Markdown
Owner

Summary

Implement optional disk caching for test edges using AtomicJsonCache from the shared cache substrate (ADR-0029). The cache is transparently loaded on hit, with automatic fallback to rebuild on miss. Force rebuild bypasses the cache entirely.

Changes

  • test_linker_cache.py: New module with atomic save/load primitives

    • save_test_edges_cache(project_root, edges) — writes atomically
    • load_test_edges_cache(project_root) — reads or returns None on miss
    • Stats tracking via register_cache_stats("test_edges", ...)
  • test_linker.py: Integrate cache into load_or_build_test_edges()

    • Cache hit path: load and return
    • Cache miss: build from call graph, save cache, return
    • force_rebuild=True bypasses cache entirely
  • test_test_linker.py: Four new test cases

    • test_test_edges_cache_save_and_load — verify atomic write/read cycle
    • test_load_or_build_uses_cache — cache hit on second call
    • test_load_or_build_force_rebuild_bypasses_cache — force_rebuild skips cache

Acceptance Criteria

  • Reuses existing AtomicJsonCache and cache substrate
  • Optional (no impact if cache directory absent)
  • Root-cause implementation (smallest correct change)
  • No scope creep (only add cache layer)
  • All tests pass (5/5)
  • All quality gates pass (3/3 files 100% score)

Test Results

packages/tapps-mcp/tests/unit/test_test_linker.py::TestTestLinker::test_tests_edge_from_test_call PASSED
packages/tapps-mcp/tests/unit/test_test_linker.py::TestTestLinker::test_get_tests_for_symbol PASSED
packages/tapps-mcp/tests/unit/test_test_linker.py::TestTestLinker::test_test_edges_cache_save_and_load PASSED
packages/tapps-mcp/tests/unit/test_test_linker.py::TestTestLinker::test_load_or_build_uses_cache PASSED
packages/tapps-mcp/tests/unit/test_test_linker.py::TestTestLinker::test_load_or_build_force_rebuild_bypasses_cache PASSED

======================== 5 passed in 0.85s =========================

🤖 Generated with Claude Code

Implement disk caching for test edges using AtomicJsonCache (ADR-0029). The
optional cache is loaded on-disk when available, with automatic fallback to
rebuild on miss. Force rebuild bypasses the cache.

- Create test_linker_cache.py with save/load primitives
- Integrate cache into load_or_build_test_edges()
- Add four test cases for save, load, and rebuild scenarios
- Stats tracking via unified cache-stats interface

All acceptance criteria met:
- Reuses existing AtomicJsonCache/cache substrate
- Optional (no impact if absent)
- Root-cause implementation (smallest correct change)
- No scope creep

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@wtthornton wtthornton merged commit 3851a14 into master Jul 2, 2026
4 checks passed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a34fcea49e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +119 to +121
cached = load_test_edges_cache(project_root)
if cached is not None:
return cached

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Invalidate test-edge cache before returning it

When load_or_build_test_edges() is called after a source or test file changes, this branch returns the old .tapps-mcp/test-edges.json without comparing it to the call-graph fingerprint or any source state. build_call_graph_index() already detects stale graph caches, but this early return skips that path entirely, so affected-test results can stay stale until callers pass force_rebuild=True or delete the cache.

Useful? React with 👍 / 👎.

wtthornton added a commit that referenced this pull request Jul 2, 2026
…oute (TAP-4080) (#194)

The reduced-scope #193 shipped an UNCONDITIONAL fixed-path cache that was
never fingerprint-keyed, never checked staleness (serving edges from an old
call graph as a hit — a correctness bug), and was dead code: the three
diff_impact call sites still called build_test_edges directly, and the
"test_edges" stats provider only registered on a lazy import inside
load_or_build. This finishes the spec on the ADR-0029 substrate.

- Cache at .tapps-mcp/test-edges-index.json now embeds the call-graph
  fingerprint (index.fingerprint) and validates it with FingerprintStaleness
  on load: same fingerprint => hit, changed => miss + rebuild + rewrite. No
  stale edges are ever served.
- New load_or_build_test_edges_for_index(project_root, index) is the fast path
  for callers already holding the index (no redundant graph rebuild).
- All three diff_impact call sites (analyze_diff_impact, per-symbol lookup,
  export_test_map) route through the cached path; force_rebuild threaded
  through export_test_map.
- register_cache_stats("test_edges", ...) fires eagerly via a side-effect
  import at the end of test_linker.py, which is imported on the diff_impact /
  call-graph paths a normal tapps_stats run exercises — so "test_edges" now
  appears in tapps_stats.caches without a prior load_or_build call.
- Added _reset_test_edges_stats() to conftest cache reset.
- Tests: cache save/load and load_or_build updated to the new path/signature;
  added test_cache_invalidates_on_fingerprint_change (old fp => hit, changed
  fp => miss + fresh rebuild) and test_stats_registered_in_collect_cache_stats.

Reuses AtomicJsonCache + FingerprintStaleness (ADR-0029); no parallel
atomic-write or staleness code.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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