Module: Rigor::Analysis::RunCacheKey
- Defined in:
- lib/rigor/analysis/run_cache_key.rb
Overview
ADR-45 / ADR-87 WD4 — the stable run-result cache KEY, built in ONE place so the miss path (the
Runner) and the boot-slimming hit path (the RunCacheProbe) can never drift out of key
agreement. The key reads the stable inputs known before analysis: the rbs gem version, the resolved
RBS library list, a digest of the whole resolved configuration, the engine + schema + --explain
triple, and the analyzed-path SET.
The ONLY difference between the two callers is the rbs_config_entries slot: the Runner passes the
loader's RbsDescriptor.config_entries (which include a rbs.virtual_rbs entry when a plugin's
source_rbs_synthesizer contributed one), while the probe passes #libraries_config_entries —
reconstructed from config alone, WITHOUT building the RBS environment or loading any plugin. A project
whose plugins DO synthesise virtual RBS therefore produces a probe key that omits that entry, so the
probe simply misses and the full path takes over (sound: never a wrong hit, only a forgone fast lane).
Slot order is irrelevant — Cache::Descriptor#to_canonical_hash sorts configs by key.
Constant Summary collapse
- RUN_DIAGNOSTICS_PRODUCER_ID =
"analysis.run-diagnostics"- RUN_EFFECTS_PRODUCER_ID =
ADR-103 WD13 / issue #382 — the whole-run effects sidecar: the run's per-file effect collections, keyed by Effects::Identity.descriptor (this key descriptor plus the vocabulary version, the catalogue identity and the
effects:digest) rather than by the descriptor above.A separate producer id, not a second section of the diagnostics entry, and that is the whole of "the diagnostics slot is never invalidated by effects": the two slots cannot share a fate when they do not share a file. It also keeps the ADR-87 boot-slim probe reading exactly the bytes it reads today — it peeks
analysis.run-diagnosticsand finds a plain diagnostics array, whatever a collecting run wrote elsewhere. "analysis.run-effects"- GENERATION_CAP =
The run-result producer's declared compaction budget (
Cache::Store#evict!pass 2). Whole-project, but unlike therbs.*producers several generations can be live at once: thepathskey slot means one entry per analyzed-path SET, sorigor checkover the whole project, overlib, and over a single file are three separate live generations. 16 is a judgement call sized for that churn; it is a cap on GENERATIONS, not on correctness — over-evicting here costs a recompute, never a wrong answer. Measured 2026-07-25 against a real project's.rigor/cache: the cap does bind (60 distinct path-set generations observed, differing in thepathsslot alone), with no evidence yet on how often an evicted generation is asked for again — see issue #151. 16- EFFECTS_GENERATION_CAP =
The effects sidecar's own compaction budget. One generation per (path set × effects identity), and only a project that opted in writes any at all, so it is sized as the diagnostics cap's shadow: a collecting project's path-set churn is the same churn, and over-evicting costs one recompute.
GENERATION_CAP
Class Method Summary collapse
- .config_entry(key, payload) ⇒ Object
- .descriptor(configuration:, files:, explain:, rbs_config_entries:) ⇒ Object
-
.engine_source_entries ⇒ Object
Issue #285 — the
engineslot above pins the engine by VERSION, which identifies the source only for a released gem. -
.libraries_config_entries(configuration) ⇒ Object
The
rbs.librariesconfig slot reconstructed from configuration alone — byte-identical to the loader'sRbsDescriptor.libraries_entry(loader.libraries)becauseEnvironment.for_projectmerges exactlyDEFAULT_LIBRARIES + config.libraries(uniq) intoloader.libraries.
Class Method Details
.config_entry(key, payload) ⇒ Object
97 98 99 |
# File 'lib/rigor/analysis/run_cache_key.rb', line 97 def config_entry(key, payload) Cache::Descriptor::ConfigEntry.new(key: key, value_hash: Digest::SHA256.hexdigest(payload)) end |
.descriptor(configuration:, files:, explain:, rbs_config_entries:) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/rigor/analysis/run_cache_key.rb', line 68 def descriptor(configuration:, files:, explain:, rbs_config_entries:) Cache::Descriptor.new( gems: [Cache::RbsDescriptor.rbs_gem_entry], configs: rbs_config_entries + engine_source_entries + [ config_entry("configuration", Marshal.dump(configuration.to_h)), config_entry("engine", "#{Rigor::VERSION}:#{Cache::Descriptor::SCHEMA_VERSION}:#{explain}"), config_entry("paths", files.sort.join("\n")) ] ) rescue StandardError nil end |
.engine_source_entries ⇒ Object
Issue #285 — the engine slot above pins the engine by VERSION, which identifies the source only
for a released gem. A checkout (a contributor's, or a bundle add rigor, github: clone) gets one
extra slot carrying a digest of the engine's own source, so editing lib/rigor/inference/*.rb no
longer replays the pre-edit diagnostics out of a warm cache. A released install adds NO entry, so
its key — and its hit rate — are exactly what they were.
Cache::EngineSource::Unavailable is left to propagate into descriptor's rescue, which disables
the cache for the run: an engine we cannot identify must not be keyed by its version alone.
90 91 92 93 94 95 |
# File 'lib/rigor/analysis/run_cache_key.rb', line 90 def engine_source_entries identity = Cache::EngineSource.process_identity return [] if identity.nil? [config_entry("engine-source", identity)] end |
.libraries_config_entries(configuration) ⇒ Object
The rbs.libraries config slot reconstructed from configuration alone — byte-identical to the
loader's RbsDescriptor.libraries_entry(loader.libraries) because Environment.for_project merges
exactly DEFAULT_LIBRARIES + config.libraries (uniq) into loader.libraries.
104 105 106 107 |
# File 'lib/rigor/analysis/run_cache_key.rb', line 104 def libraries_config_entries(configuration) merged = (Environment::DEFAULT_LIBRARIES + configuration.libraries.map(&:to_s)).uniq [Cache::RbsDescriptor.libraries_entry(merged)] end |