Class: Rigor::Analysis::RunCacheProbe

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/analysis/run_cache_probe.rb

Overview

ADR-87 WD4 — the boot-slimming run-cache hit probe. Serves an ordinary rigor check's diagnostics straight from the ADR-45 analysis.run-diagnostics cache WITHOUT loading the inference engine, its plugin gems, or building the RBS environment: a warm HIT boots only CLI + config + cache + digest code (the recon's $LOADED_FEATURES never gains a rigor/inference entry). The full plugin/prepass + env-build tax (~0.8s on a gitlab null) is paid ONLY on a miss.

Soundness is inherited wholesale from ADR-45: the stored dependency descriptor records every file the prior run read — analyzed sources, the RBS signature tree, AND every file each plugin read mid-analysis (the Pundit-policy case) — and Cache::Store#peek_validated re-checks all of it against the live tree (ADR-87 :stat-validated). A hit therefore means every input the prior run observed is unchanged, so its cached diagnostics are exactly what a fresh full run would produce; skipping the plugin prepasses is sound because their inputs are in that same validated set. The key is built through the shared RunCacheKey — a project whose plugins synthesise virtual RBS produces a probe key that omits that entry, so it simply misses and the full path takes over (never a wrong hit).

What the slot does NOT contain (#428)

ADR-103 puts the two effect diagnostics OUTSIDE the cached run assembly on purpose: the effects: block is deliberately absent from the diagnostics cache identity, so a finding written into that entry would outlive the configuration that produced it. Runner#run_analysis therefore appends them after #compute_run_diagnostics — which is exactly the code a served hit skips. Serving the slot verbatim silently dropped both of them on every warm run, and a check that only ever fires on a cold cache is worse than one that never fires at all.

A probe that serves a slot has to answer for what the slot omits, so this one does, per pass:

  • effect.annotations-unchecked is reproduced here. It was built to be free (a glob and a regex over the project's own signature tree — Effects::SignatureSources), so the probe simply runs it, with no virtual RBS: the inline stratum is the documented fail-quiet direction of that pass, and it is the stratum a run without an environment never had. #441 — that omission is unreachable rather than merely tolerated, because a project whose plugins synthesise virtual RBS is exactly the one whose probe key omits the rbs.virtual_rbs entry (§ above), so it misses here and the full path — which carries the stratum in every run mode — answers instead.
  • effect.envelope-exceeded / effect.liskov-widened / effect.unknown-label cannot be: they read the propagated effect graph and the cross-file discovery tables, i.e. the engine this path exists to skip. So the probe declines for a project that could earn one (#envelope_lane_live?) and the full path — which caches both halves in its own two slots, and re-judges them every run — serves it instead. The decline is measured against the declarations alone, never against what they would judge to, so it costs one glob and never a wrong answer.

Instance Method Summary collapse

Constructor Details

#initialize(configuration:, cache_root:, explain:) ⇒ RunCacheProbe

Returns a new instance of RunCacheProbe.

Parameters:

  • configuration (Rigor::Configuration)
  • cache_root (String)
  • explain (Boolean)

    the --explain flag (folded into the key, as the runner does).



57
58
59
60
61
# File 'lib/rigor/analysis/run_cache_probe.rb', line 57

def initialize(configuration:, cache_root:, explain:)
  @configuration = configuration
  @cache_root = cache_root
  @explain = explain
end

Instance Method Details

#serve(paths) ⇒ Analysis::Result?

Returns the cached run result with the severity profile applied and no stats (matching a cache-served Runner#run), or nil to DECLINE — a miss / stale / unavailable cache — so the caller loads the engine and runs the full path. Any failure declines rather than raising: the probe must never turn a servable run into a crash.

Parameters:

  • paths (Array<String>)

    the analysis roots (@argv or configuration.paths).

Returns:

  • (Analysis::Result, nil)

    the cached run result with the severity profile applied and no stats (matching a cache-served Runner#run), or nil to DECLINE — a miss / stale / unavailable cache — so the caller loads the engine and runs the full path. Any failure declines rather than raising: the probe must never turn a servable run into a crash.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/rigor/analysis/run_cache_probe.rb', line 68

def serve(paths)
  files = PathExpansion.ruby_files(paths, @configuration.exclude_patterns)
  key = RunCacheKey.descriptor(
    configuration: @configuration, files: files, explain: @explain,
    rbs_config_entries: RunCacheKey.libraries_config_entries(@configuration)
  )
  return nil if key.nil?

  diagnostics = validated_diagnostics(key)
  return nil if diagnostics.nil?
  # #428 — asked only after the peek, so a run that was going to miss anyway never pays the walk.
  return nil if envelope_lane_live?

  Result.new(
    diagnostics: SeverityStamp.apply(diagnostics + residual_diagnostics, @configuration), stats: nil
  )
rescue StandardError
  nil
end