gabriel / muse public
perf BREAKING dev
AI Agent gabriel · 159 days ago · Apr 19, 2026 · Diff

perf: add ImplicitEdgeCache to eliminate redundant framework plugin runs

build_implicit_edge_graph re-read every Python blob and re-ran all framework plugins (FastAPI, Flask, Celery) on every invocation — ~10s for the muse repo.

ImplicitEdgeCache persists the per-file edge list keyed by the SHA-256 object_id from the manifest. On a warm cache, read_object, parse_symbols, and all plugin detect_entry_points calls are skipped.

Cold: 10,744 ms → Warm: 150 ms (72× speedup)

Also fixes pre-existing test failures in test_callgraph.py, test_framework_plugins.py, and test_core_symbol_cache.py where _write_snapshot / _make_manifest helpers used bare hex digests instead of blob_id() — rejected by validate_object_id.

sha256:d6ac48300babe0f2424dcd727be8f8cab7474380373778e3d52d0e42edc5c538 sha
+98 ~8 symbols
sha256:713166d8cacc6a5c94fc07fd281718eed1e8eac35c3327fa5d54092cc7559399 snapshot
+98
symbols added
~8
symbols modified
0
dead code introduced
Semantic Changes 106 symbols
~ muse/core/implicit_edge_cache.py .py 21 symbols added
+ ImplicitEdgeCache class class ImplicitEdgeCache L110–241
+ __init__ method method __init__ L130–137
+ empty method method empty L184–186
+ get method method get L192–194
+ load method method load L144–181
+ prune method method prune L201–208
+ put method method put L196–199
+ save method method save L219–241
+ size method method size L211–213
+ _CACHE_FILENAME variable variable _CACHE_FILENAME L71–71
+ _CACHE_VERSION variable variable _CACHE_VERSION L70–70
+ _REQUIRED_STR_FIELDS variable variable _REQUIRED_STR_FIELDS L74–74
+ _edge_from_dict function function _edge_from_dict L77–97
+ _edge_to_dict function function _edge_to_dict L100–107
+ annotations import import annotations L59–59
+ logging import import logging L61–61
+ msgpack import import msgpack L64–64
+ pathlib import import pathlib L62–62
+ read_msgpack_file import import read_msgpack_file L66–66
+ load_implicit_edge_cache function function load_implicit_edge_cache L244–252
+ logger variable variable logger L68–68
~ tests/test_implicit_edge_cache.py .py 71 symbols added
+ TestBuildImplicitEdgeGraphWithCache class class TestBuildImplicitEdgeGraphWithCache L453–596
+ _repo method method _repo L456–458
+ test_build_does_not_call_cache_save method method test_build_does_not_call_cache_save L568–580
+ test_cache_miss_calls_read_object method method test_cache_miss_calls_read_object L486–501
+ test_cache_populated_after_build method method test_cache_populated_after_build L503–514
+ test_cold_cache_none_correct_graph method method test_cold_cache_none_correct_graph L460–467
+ test_explicit_cache_hit_skips_read_object method method test_explicit_cache_hit_skips_read_object L469–484
+ test_no_framework_file_cached_as_empty_list method method test_no_framework_file_cached_as_empty_list L516–528
+ test_non_python_files_not_cached method method test_non_python_files_not_cached L552–566
+ test_second_call_skips_read_object method method test_second_call_skips_read_object L582–596
+ test_warm_graph_equals_cold_graph method method test_warm_graph_equals_cold_graph L530–550
+ TestImplicitEdgeCacheGracefulLoad class class TestImplicitEdgeCacheGracefulLoad L325–396
+ test_absent_file_returns_empty method method test_absent_file_returns_empty L328–330
+ test_corrupt_file_returns_empty method method test_corrupt_file_returns_empty L332–336
+ test_edge_missing_required_field_skipped method method test_edge_missing_required_field_skipped L377–383
+ test_edge_non_str_field_skipped method method test_edge_non_str_field_skipped L385–396
+ test_invalid_entry_skipped_valid_survive method method test_invalid_entry_skipped_valid_survive L352–375
+ test_non_dict_entries_returns_empty method method test_non_dict_entries_returns_empty L345–350
+ test_wrong_version_returns_empty method method test_wrong_version_returns_empty L338–343
+ TestImplicitEdgeCacheMemory class class TestImplicitEdgeCacheMemory L96–201
+ test_different_ids_independent method method test_different_ids_independent L118–126
+ test_empty_get_miss method method test_empty_get_miss L99–101
+ test_empty_list_is_valid method method test_empty_list_is_valid L148–153
+ test_empty_save_is_noop method method test_empty_save_is_noop L180–185
+ test_get_returns_implicit_entry_edge_instances method method test_get_returns_implicit_entry_edge_instances L187–193
+ test_metadata_preserved_in_memory method method test_metadata_preserved_in_memory L195–201
+ test_prune_marks_dirty_when_stale method method test_prune_marks_dirty_when_stale L164–170
+ test_prune_no_stale_not_dirty method method test_prune_no_stale_not_dirty L172–178
+ test_prune_removes_stale method method test_prune_removes_stale L155–162
+ test_put_marks_dirty method method test_put_marks_dirty L111–116
+ test_put_same_id_overwrites method method test_put_same_id_overwrites L128–135
+ test_put_then_get_hit method method test_put_then_get_hit L103–109
+ test_size_grows_with_put method method test_size_grows_with_put L141–146
+ test_size_starts_zero method method test_size_starts_zero L137–139
+ TestImplicitEdgeCachePerformance class class TestImplicitEdgeCachePerformance L604–684
+ _build_repo method method _build_repo L607–630
+ test_graph_correctness_not_degraded method method test_graph_correctness_not_degraded L666–684
+ test_warm_cache_at_least_5x_faster method method test_warm_cache_at_least_5x_faster L632–649
+ test_warm_under_200ms_for_30_files method method test_warm_under_200ms_for_30_files L651–664
+ TestImplicitEdgeCachePersistence class class TestImplicitEdgeCachePersistence L209–317
+ test_atomic_write_no_tmp_leftover method method test_atomic_write_no_tmp_leftover L294–300
+ test_empty_list_round_trips method method test_empty_list_round_trips L260–267
+ test_multiple_entries_survive_round_trip method method test_multiple_entries_survive_round_trip L302–317
+ test_round_trip_preserves_all_fields method method test_round_trip_preserves_all_fields L240–258
+ test_save_clears_dirty_flag method method test_save_clears_dirty_flag L275–281
+ test_save_creates_file method method test_save_creates_file L212–218
+ test_save_no_dirty_skips_write method method test_save_no_dirty_skips_write L269–273
+ test_save_then_load_round_trip method method test_save_then_load_round_trip L220–238
+ test_second_save_is_noop method method test_second_save_is_noop L283–292
+ TestLoadImplicitEdgeCache class class TestLoadImplicitEdgeCache L404–423
+ test_no_muse_dir_returns_empty method method test_no_muse_dir_returns_empty L406–408
+ test_with_empty_muse_dir_returns_empty method method test_with_empty_muse_dir_returns_empty L420–423
+ test_with_muse_dir_loads_existing method method test_with_muse_dir_loads_existing L410–418
+ _FASTAPI_SOURCE variable variable _FASTAPI_SOURCE L431–442
+ _NO_FRAMEWORK_SOURCE variable variable _NO_FRAMEWORK_SOURCE L444–450
+ _edge function function _edge L77–88
+ _make_manifest function function _make_manifest L73–74
+ _muse_dir function function _muse_dir L59–62
+ _write_blob function function _write_blob L65–70
+ ImplicitEntryEdge import import ImplicitEntryEdge L51–51
+ Manifest import import Manifest L50–50
+ annotations import import annotations L37–37
+ asdict import import asdict L43–43
+ hashlib import import hashlib L39–39
+ msgpack import import msgpack L46–46
+ patch import import patch L44–44
+ pathlib import import pathlib L40–40
+ pytest import import pytest L47–47
+ textwrap import import textwrap L41–41
+ time import import time L42–42
+ write_object import import write_object L49–49
~ muse/cli/commands/impact.py .py 1 symbol added, 1 symbol modified
+ load_implicit_edge_cache import import load_implicit_edge_cache L103–103
~ run
~ muse/plugins/code/_framework.py .py 1 symbol added, 1 symbol modified
+ ImplicitEdgeCache import import ImplicitEdgeCache L70–70
~ tests/test_callgraph.py .py 1 symbol added, 2 symbols modified
+ blob_id import import blob_id L41–41
~ tests/test_core_symbol_cache.py .py 2 symbols added, 2 symbols modified
+ blob_id import import blob_id L35–35
+ write_object import import write_object L36–36
~ tests/test_framework_plugins.py .py 1 symbol added, 2 symbols modified
+ blob_id import import blob_id L79–79
← Older Oldest on dev
All commits
Newer → Latest on dev

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:d6ac48300babe0f2424dcd727be8f8cab7474380373778e3d52d0e42edc5c538 --body "your comment"