rigortype 0.4.6 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (550) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/data/core_overlay/bigdecimal.rbs +33 -0
  4. data/data/core_overlay/resolv.rbs +30 -0
  5. data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
  6. data/data/gem_overlay/addressable/addressable.rbs +40 -0
  7. data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
  8. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
  9. data/docs/handbook/01-getting-started.md +6 -3
  10. data/docs/handbook/11-sig-gen.md +85 -3
  11. data/docs/handbook/appendix-phpstan.md +4 -4
  12. data/docs/install.md +20 -0
  13. data/docs/manual/01-installation.md +67 -26
  14. data/docs/manual/02-cli-reference.md +455 -113
  15. data/docs/manual/03-configuration.md +76 -5
  16. data/docs/manual/04-diagnostics.md +5 -4
  17. data/docs/manual/06-baseline.md +6 -4
  18. data/docs/manual/08-skills.md +11 -0
  19. data/docs/manual/09-editor-integration.md +21 -2
  20. data/docs/manual/10-mcp-server.md +38 -2
  21. data/docs/manual/12-caching.md +4 -3
  22. data/docs/manual/14-rails-quickstart.md +1 -1
  23. data/docs/manual/19-effect-labels.md +3 -4
  24. data/docs/manual/plugins/rigor-actionpack.md +32 -15
  25. data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
  26. data/docs/manual/plugins/rigor-sorbet.md +17 -3
  27. data/lib/rigor/analysis/baseline.rb +24 -27
  28. data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
  29. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
  30. data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
  31. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
  32. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
  33. data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
  34. data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
  35. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
  36. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
  37. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
  38. data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
  40. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
  41. data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
  42. data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
  43. data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
  44. data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
  45. data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
  46. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
  47. data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
  48. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  49. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
  50. data/lib/rigor/analysis/check_rules.rb +720 -141
  51. data/lib/rigor/analysis/crash_signature.rb +45 -3
  52. data/lib/rigor/analysis/dependency_recorder.rb +237 -80
  53. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
  54. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
  55. data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
  56. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  57. data/lib/rigor/analysis/edit_replay.rb +93 -6
  58. data/lib/rigor/analysis/fact_store.rb +3 -3
  59. data/lib/rigor/analysis/incremental.rb +11 -1
  60. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
  61. data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
  62. data/lib/rigor/analysis/incremental_session.rb +306 -53
  63. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
  64. data/lib/rigor/analysis/rule_catalog.rb +39 -3
  65. data/lib/rigor/analysis/run_cache_key.rb +27 -3
  66. data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
  67. data/lib/rigor/analysis/run_stats.rb +2 -2
  68. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
  69. data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
  70. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
  71. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
  72. data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
  73. data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
  74. data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
  75. data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
  76. data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
  77. data/lib/rigor/analysis/runner.rb +298 -109
  78. data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
  79. data/lib/rigor/analysis/template_unit_positions.rb +2 -3
  80. data/lib/rigor/analysis/template_units.rb +9 -1
  81. data/lib/rigor/analysis/worker_session.rb +65 -71
  82. data/lib/rigor/bleeding_edge.rb +22 -10
  83. data/lib/rigor/builtins/imported_refinements.rb +6 -8
  84. data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
  85. data/lib/rigor/builtins/regex_refinement.rb +4 -6
  86. data/lib/rigor/cache/descriptor.rb +6 -5
  87. data/lib/rigor/cache/file_digest.rb +39 -31
  88. data/lib/rigor/cache/incremental_snapshot.rb +95 -17
  89. data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
  90. data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
  91. data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
  92. data/lib/rigor/cache/rbs_constant_table.rb +1 -1
  93. data/lib/rigor/cache/rbs_descriptor.rb +90 -4
  94. data/lib/rigor/cache/rbs_environment.rb +8 -1
  95. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
  96. data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
  97. data/lib/rigor/cache/store.rb +11 -10
  98. data/lib/rigor/ci_detector.rb +1 -1
  99. data/lib/rigor/cli/annotate_command.rb +24 -19
  100. data/lib/rigor/cli/baseline_command.rb +160 -57
  101. data/lib/rigor/cli/catalogue.rb +256 -0
  102. data/lib/rigor/cli/check_command.rb +112 -104
  103. data/lib/rigor/cli/check_runner_factory.rb +2 -2
  104. data/lib/rigor/cli/command.rb +15 -1
  105. data/lib/rigor/cli/coverage_command.rb +21 -8
  106. data/lib/rigor/cli/coverage_mutation.rb +5 -7
  107. data/lib/rigor/cli/coverage_renderer.rb +2 -1
  108. data/lib/rigor/cli/coverage_scan.rb +3 -5
  109. data/lib/rigor/cli/diff_command.rb +25 -20
  110. data/lib/rigor/cli/docs_command.rb +1 -2
  111. data/lib/rigor/cli/doctor_command.rb +12 -12
  112. data/lib/rigor/cli/effects_command.rb +50 -40
  113. data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
  114. data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
  115. data/lib/rigor/cli/effects_renderer.rb +2 -1
  116. data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
  117. data/lib/rigor/cli/explain_command.rb +18 -8
  118. data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
  119. data/lib/rigor/cli/json_document.rb +41 -0
  120. data/lib/rigor/cli/lens_command.rb +29 -10
  121. data/lib/rigor/cli/lsp_command.rb +250 -23
  122. data/lib/rigor/cli/mcp_command.rb +12 -7
  123. data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
  124. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
  125. data/lib/rigor/cli/options.rb +40 -1
  126. data/lib/rigor/cli/plugin_command.rb +93 -46
  127. data/lib/rigor/cli/plugins_command.rb +17 -18
  128. data/lib/rigor/cli/plugins_renderer.rb +10 -6
  129. data/lib/rigor/cli/protection_fork_scan.rb +2 -2
  130. data/lib/rigor/cli/protection_renderer.rb +2 -1
  131. data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
  132. data/lib/rigor/cli/sig_gen_command.rb +92 -27
  133. data/lib/rigor/cli/skill_command.rb +32 -3
  134. data/lib/rigor/cli/skill_deep_probe.rb +1 -1
  135. data/lib/rigor/cli/skill_describe.rb +2 -1
  136. data/lib/rigor/cli/trace_command.rb +14 -9
  137. data/lib/rigor/cli/triage_command.rb +10 -11
  138. data/lib/rigor/cli/triage_renderer.rb +2 -1
  139. data/lib/rigor/cli/type_of_command.rb +16 -11
  140. data/lib/rigor/cli/type_of_project_scope.rb +1 -4
  141. data/lib/rigor/cli/type_of_renderer.rb +4 -1
  142. data/lib/rigor/cli/type_scan_command.rb +14 -9
  143. data/lib/rigor/cli/type_scan_renderer.rb +2 -1
  144. data/lib/rigor/cli/unused_command.rb +27 -17
  145. data/lib/rigor/cli/upgrade_command.rb +5 -0
  146. data/lib/rigor/cli.rb +214 -56
  147. data/lib/rigor/config_audit.rb +33 -19
  148. data/lib/rigor/configuration/severity_profile.rb +14 -5
  149. data/lib/rigor/configuration.rb +104 -35
  150. data/lib/rigor/effects/collector.rb +53 -21
  151. data/lib/rigor/effects/definition_context.rb +0 -2
  152. data/lib/rigor/effects/envelope.rb +1 -1
  153. data/lib/rigor/effects/framework_units.rb +1 -1
  154. data/lib/rigor/effects/mutation_classifier.rb +4 -7
  155. data/lib/rigor/effects/propagator.rb +1 -1
  156. data/lib/rigor/effects/registry.rb +2 -2
  157. data/lib/rigor/effects/snapshot.rb +1 -1
  158. data/lib/rigor/effects/taint_cause.rb +0 -2
  159. data/lib/rigor/effects/unit_scan.rb +9 -13
  160. data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
  161. data/lib/rigor/environment/class_registry.rb +3 -3
  162. data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
  163. data/lib/rigor/environment/core_root.rb +77 -0
  164. data/lib/rigor/environment/default_libraries.rb +18 -1
  165. data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
  166. data/lib/rigor/environment/gated_signature_guard.rb +1 -1
  167. data/lib/rigor/environment/gated_signature_plan.rb +5 -2
  168. data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
  169. data/lib/rigor/environment/lockfile_resolver.rb +28 -12
  170. data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
  171. data/lib/rigor/environment/member_consistency.rb +56 -7
  172. data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
  173. data/lib/rigor/environment/project_path_gems.rb +124 -0
  174. data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
  175. data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
  176. data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
  177. data/lib/rigor/environment/rbs_loader.rb +772 -110
  178. data/lib/rigor/environment/reflection.rb +7 -7
  179. data/lib/rigor/environment/required_features.rb +0 -3
  180. data/lib/rigor/environment/ruby_library_roots.rb +1 -1
  181. data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
  182. data/lib/rigor/environment.rb +169 -42
  183. data/lib/rigor/flow_contribution/merger.rb +4 -4
  184. data/lib/rigor/inference/acceptance.rb +136 -40
  185. data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
  186. data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
  187. data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
  188. data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
  189. data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
  190. data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
  191. data/lib/rigor/inference/block_auto_splat.rb +1 -1
  192. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  193. data/lib/rigor/inference/block_run_count.rb +185 -0
  194. data/lib/rigor/inference/budget_trace.rb +8 -2
  195. data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
  196. data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
  197. data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
  198. data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
  199. data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
  200. data/lib/rigor/inference/callee_write_census.rb +815 -0
  201. data/lib/rigor/inference/captured_locals.rb +2 -0
  202. data/lib/rigor/inference/census_foreign_block.rb +354 -0
  203. data/lib/rigor/inference/clause_widening.rb +1 -1
  204. data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
  205. data/lib/rigor/inference/construction_escape.rb +258 -0
  206. data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
  207. data/lib/rigor/inference/declaration_walk.rb +4 -0
  208. data/lib/rigor/inference/deep_erasure.rb +39 -7
  209. data/lib/rigor/inference/def_handle.rb +1 -1
  210. data/lib/rigor/inference/def_node_resolver.rb +33 -15
  211. data/lib/rigor/inference/def_return_typer.rb +47 -5
  212. data/lib/rigor/inference/definer_resolution.rb +19 -9
  213. data/lib/rigor/inference/definers_index.rb +162 -0
  214. data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
  215. data/lib/rigor/inference/element_read_widening.rb +49 -3
  216. data/lib/rigor/inference/error_info.rb +16 -0
  217. data/lib/rigor/inference/expression_typer.rb +1559 -320
  218. data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
  219. data/lib/rigor/inference/flow_tracer.rb +31 -14
  220. data/lib/rigor/inference/fork_map.rb +3 -0
  221. data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
  222. data/lib/rigor/inference/global_write_census.rb +81 -5
  223. data/lib/rigor/inference/global_writer_reach.rb +260 -0
  224. data/lib/rigor/inference/hkt_body.rb +1 -2
  225. data/lib/rigor/inference/hkt_body_parser.rb +3 -5
  226. data/lib/rigor/inference/hkt_reducer.rb +2 -2
  227. data/lib/rigor/inference/hkt_registry.rb +3 -5
  228. data/lib/rigor/inference/in_effect_refinements.rb +7 -0
  229. data/lib/rigor/inference/indexed_narrowing.rb +61 -15
  230. data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
  231. data/lib/rigor/inference/ivar_writer_census.rb +154 -8
  232. data/lib/rigor/inference/key_presence_guard.rb +51 -24
  233. data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
  234. data/lib/rigor/inference/last_line.rb +5 -2
  235. data/lib/rigor/inference/loop_value_record.rb +66 -14
  236. data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
  237. data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
  238. data/lib/rigor/inference/match_rebinding.rb +28 -5
  239. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
  240. data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
  241. data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
  242. data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
  243. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
  244. data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
  245. data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
  246. data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
  247. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
  248. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
  249. data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
  250. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
  251. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
  252. data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
  253. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
  254. data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
  255. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
  256. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
  257. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
  258. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
  259. data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
  260. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
  261. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
  262. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
  263. data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
  264. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
  265. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
  266. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  267. data/lib/rigor/inference/method_dispatcher.rb +123 -25
  268. data/lib/rigor/inference/method_parameter_binder.rb +136 -56
  269. data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
  270. data/lib/rigor/inference/module_function_state.rb +24 -3
  271. data/lib/rigor/inference/multi_target_binder.rb +2 -2
  272. data/lib/rigor/inference/mutation_rejoin.rb +240 -4
  273. data/lib/rigor/inference/mutation_widening.rb +199 -29
  274. data/lib/rigor/inference/narrowing.rb +238 -50
  275. data/lib/rigor/inference/operand_effects.rb +43 -2
  276. data/lib/rigor/inference/optimistic_origin.rb +134 -9
  277. data/lib/rigor/inference/origin_lookup.rb +1 -1
  278. data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
  279. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  280. data/lib/rigor/inference/pre_eval_constants.rb +1 -1
  281. data/lib/rigor/inference/precision_scanner.rb +1 -1
  282. data/lib/rigor/inference/project_method_ownership.rb +30 -9
  283. data/lib/rigor/inference/project_patched_methods.rb +65 -13
  284. data/lib/rigor/inference/project_patched_scanner.rb +120 -25
  285. data/lib/rigor/inference/project_patched_visibility.rb +39 -3
  286. data/lib/rigor/inference/project_yielder.rb +11 -17
  287. data/lib/rigor/inference/rbs_type_translator.rb +68 -15
  288. data/lib/rigor/inference/receiver_alias.rb +20 -0
  289. data/lib/rigor/inference/receiver_writer_call.rb +67 -0
  290. data/lib/rigor/inference/recursion_rebinding.rb +157 -0
  291. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
  292. data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
  293. data/lib/rigor/inference/refined_dispatch.rb +13 -0
  294. data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
  295. data/lib/rigor/inference/remover_holder_scan.rb +287 -0
  296. data/lib/rigor/inference/remover_narrowing.rb +224 -0
  297. data/lib/rigor/inference/rewrite_mutation.rb +53 -2
  298. data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
  299. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
  300. data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
  301. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
  302. data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
  303. data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
  304. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
  305. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
  306. data/lib/rigor/inference/scope_indexer.rb +2213 -226
  307. data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
  308. data/lib/rigor/inference/self_preserving_block.rb +87 -0
  309. data/lib/rigor/inference/set_mutation.rb +189 -0
  310. data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
  311. data/lib/rigor/inference/singleton_object_constant.rb +32 -2
  312. data/lib/rigor/inference/statement_evaluator.rb +3268 -358
  313. data/lib/rigor/inference/string_mutation.rb +14 -0
  314. data/lib/rigor/inference/struct_fold_safety.rb +4 -6
  315. data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
  316. data/lib/rigor/inference/toplevel_def_census.rb +67 -0
  317. data/lib/rigor/inference/unknown_store_widening.rb +103 -4
  318. data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
  319. data/lib/rigor/inference/untyped_rejoin.rb +1 -1
  320. data/lib/rigor/inference/void_tail_summary.rb +29 -9
  321. data/lib/rigor/language_server/buffer_table.rb +3 -3
  322. data/lib/rigor/language_server/completion_provider.rb +26 -20
  323. data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
  324. data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
  325. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  326. data/lib/rigor/language_server/folding_range_provider.rb +1 -2
  327. data/lib/rigor/language_server/hover_provider.rb +1 -7
  328. data/lib/rigor/language_server/hover_renderer.rb +6 -8
  329. data/lib/rigor/language_server/project_context.rb +39 -1
  330. data/lib/rigor/language_server/publish_batcher.rb +3 -4
  331. data/lib/rigor/language_server/selection_range_provider.rb +2 -4
  332. data/lib/rigor/language_server/server.rb +7 -3
  333. data/lib/rigor/language_server/signature_help_provider.rb +4 -4
  334. data/lib/rigor/lens/claims/comparison.rb +78 -0
  335. data/lib/rigor/lens/claims/request.rb +131 -0
  336. data/lib/rigor/lens/claims/type_reader.rb +216 -0
  337. data/lib/rigor/lens/claims.rb +208 -0
  338. data/lib/rigor/lens/contract_reader.rb +5 -0
  339. data/lib/rigor/lens/document.rb +1 -1
  340. data/lib/rigor/lens/file_reader.rb +21 -8
  341. data/lib/rigor/lens/plugins.rb +1 -1
  342. data/lib/rigor/lens/run.rb +8 -0
  343. data/lib/rigor/lens.rb +1 -1
  344. data/lib/rigor/library_probe.rb +68 -0
  345. data/lib/rigor/mcp/server.rb +73 -2
  346. data/lib/rigor/mcp.rb +1 -1
  347. data/lib/rigor/memo_audit.rb +282 -0
  348. data/lib/rigor/plugin/access_denied_error.rb +1 -2
  349. data/lib/rigor/plugin/base.rb +15 -2
  350. data/lib/rigor/plugin/bundled_catalog.rb +1 -12
  351. data/lib/rigor/plugin/io_boundary.rb +26 -4
  352. data/lib/rigor/plugin/loader.rb +25 -8
  353. data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
  354. data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
  355. data/lib/rigor/plugin/manifest.rb +16 -16
  356. data/lib/rigor/plugin/registry.rb +16 -5
  357. data/lib/rigor/plugin/services.rb +2 -3
  358. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
  359. data/lib/rigor/plugin/trust_policy.rb +48 -2
  360. data/lib/rigor/plugin/type_node_resolver.rb +2 -4
  361. data/lib/rigor/plugin.rb +6 -8
  362. data/lib/rigor/plugin_gap_advisory.rb +5 -1
  363. data/lib/rigor/project_environment.rb +16 -2
  364. data/lib/rigor/protection/analysis_guard.rb +1 -1
  365. data/lib/rigor/protection/discovery_seed.rb +44 -5
  366. data/lib/rigor/protection/mutation_cache.rb +4 -1
  367. data/lib/rigor/protection/mutator.rb +1 -1
  368. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  369. data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
  370. data/lib/rigor/rbs_extended/reporter.rb +27 -3
  371. data/lib/rigor/rbs_extended.rb +2 -3
  372. data/lib/rigor/reflection/constant_ancestors.rb +81 -22
  373. data/lib/rigor/reflection/declaration_collectors.rb +55 -0
  374. data/lib/rigor/reflection/declarations.rb +58 -0
  375. data/lib/rigor/reflection/project_method.rb +177 -0
  376. data/lib/rigor/reflection.rb +2 -0
  377. data/lib/rigor/rooted_glob.rb +32 -1
  378. data/lib/rigor/run_scoped_slots.rb +38 -0
  379. data/lib/rigor/runtime/jit.rb +1 -1
  380. data/lib/rigor/scope/discovery_index.rb +168 -8
  381. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
  382. data/lib/rigor/scope/resolution_chain.rb +901 -54
  383. data/lib/rigor/scope.rb +644 -37
  384. data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
  385. data/lib/rigor/sig_gen/classification.rb +18 -1
  386. data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
  387. data/lib/rigor/sig_gen/generator.rb +588 -66
  388. data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
  389. data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
  390. data/lib/rigor/sig_gen/layout_index.rb +3 -3
  391. data/lib/rigor/sig_gen/method_candidate.rb +37 -4
  392. data/lib/rigor/sig_gen/observation_collector.rb +14 -7
  393. data/lib/rigor/sig_gen/observed_call.rb +1 -1
  394. data/lib/rigor/sig_gen/path_mapper.rb +6 -0
  395. data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
  396. data/lib/rigor/sig_gen/renderer.rb +15 -9
  397. data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
  398. data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
  399. data/lib/rigor/sig_gen/write_reporter.rb +63 -14
  400. data/lib/rigor/sig_gen/write_result.rb +23 -12
  401. data/lib/rigor/sig_gen/writer.rb +238 -37
  402. data/lib/rigor/sig_gen.rb +1 -0
  403. data/lib/rigor/source/forwardable_delegators.rb +122 -0
  404. data/lib/rigor/source/node_children.rb +13 -0
  405. data/lib/rigor/type/acceptance_router.rb +2 -2
  406. data/lib/rigor/type/accepts_result.rb +1 -1
  407. data/lib/rigor/type/app.rb +4 -6
  408. data/lib/rigor/type/combinator.rb +173 -21
  409. data/lib/rigor/type/constant.rb +4 -5
  410. data/lib/rigor/type/hash_shape.rb +21 -40
  411. data/lib/rigor/type/interface.rb +60 -0
  412. data/lib/rigor/type/union.rb +1 -1
  413. data/lib/rigor/type.rb +1 -0
  414. data/lib/rigor/type_node/identifier.rb +1 -2
  415. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  416. data/lib/rigor/type_node/string_literal.rb +1 -2
  417. data/lib/rigor/type_node/symbol_literal.rb +1 -2
  418. data/lib/rigor/type_node/union.rb +3 -6
  419. data/lib/rigor/type_of_memo/serving.rb +99 -0
  420. data/lib/rigor/type_of_memo/stats.rb +43 -0
  421. data/lib/rigor/type_of_memo.rb +337 -0
  422. data/lib/rigor/typing_state.rb +1094 -0
  423. data/lib/rigor/value_semantics.rb +1 -10
  424. data/lib/rigor/version.rb +1 -1
  425. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
  426. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
  427. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
  428. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
  429. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
  430. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
  431. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
  432. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
  433. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
  434. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
  435. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
  436. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
  437. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
  438. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
  439. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  440. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
  441. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
  442. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
  443. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
  444. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
  445. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
  446. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
  447. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
  448. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
  449. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
  450. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
  451. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
  452. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
  453. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
  454. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
  455. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
  456. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
  457. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
  458. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
  459. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
  460. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
  461. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
  462. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
  463. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
  464. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
  465. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
  466. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
  467. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
  468. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
  469. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
  470. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
  471. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
  472. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
  473. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
  474. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
  475. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
  476. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
  477. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
  478. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
  479. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
  480. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
  481. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
  482. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
  483. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
  484. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
  485. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
  486. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
  487. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
  488. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
  489. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
  490. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
  491. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
  492. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
  493. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
  494. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
  495. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
  496. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
  497. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
  498. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
  499. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
  500. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
  501. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
  502. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
  503. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
  504. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
  505. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
  506. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
  507. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
  508. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
  509. data/sig/rigor/analysis/baseline.rbs +0 -3
  510. data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
  511. data/sig/rigor/analysis/fact_store.rbs +2 -2
  512. data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
  513. data/sig/rigor/cache/descriptor.rbs +14 -0
  514. data/sig/rigor/cli/diff_command.rbs +1 -1
  515. data/sig/rigor/cli/explain_command.rbs +1 -1
  516. data/sig/rigor/effects/registry.rbs +2 -2
  517. data/sig/rigor/environment.rbs +4 -1
  518. data/sig/rigor/inference.rbs +4 -1
  519. data/sig/rigor/plugin/additional_initializer.rbs +0 -5
  520. data/sig/rigor/plugin/base.rbs +53 -18
  521. data/sig/rigor/plugin/effect_attribution.rbs +3 -3
  522. data/sig/rigor/plugin/effect_edge.rbs +1 -1
  523. data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
  524. data/sig/rigor/plugin/interfaces.rbs +51 -0
  525. data/sig/rigor/plugin/io_boundary.rbs +3 -3
  526. data/sig/rigor/plugin/manifest.rbs +9 -9
  527. data/sig/rigor/plugin/node_context.rbs +21 -0
  528. data/sig/rigor/plugin/protocol_contract.rbs +3 -7
  529. data/sig/rigor/reflection.rbs +37 -0
  530. data/sig/rigor/scope.rbs +43 -4
  531. data/sig/rigor/trinary.rbs +1 -0
  532. data/sig/rigor/type.rbs +9 -3
  533. data/sig/rigor.rbs +3 -2
  534. data/skills/rigor-ask/SKILL.md +1 -0
  535. data/skills/rigor-plugin-author/SKILL.md +4 -4
  536. data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
  537. data/skills/rigor-plugin-review/SKILL.md +3 -3
  538. data/skills/rigor-project-init/references/01-detect.md +1 -0
  539. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
  540. data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
  541. data/skills/rigor-rbs-contract/SKILL.md +147 -0
  542. data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
  543. data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
  544. data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
  545. data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
  546. data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
  547. data/skills/rigor-rbs-setup/SKILL.md +3 -1
  548. data/skills/rigor-type-oracle/SKILL.md +26 -1
  549. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
  550. metadata +82 -3
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "digest"
4
4
 
5
+ require_relative "../typing_state"
6
+
5
7
  module Rigor
6
8
  module Cache
7
9
  # Per-run content-digest memo AND the ADR-87 stat-then-digest freshness choke point. Within a single
@@ -20,20 +22,38 @@ module Rigor
20
22
  # zero SHA-256 bytes. The SHA-256 digest remains the sole change AUTHORITY — the stat tier only decides
21
23
  # whether the digest needs recomputing.
22
24
  #
23
- # Soundness: the memo + recording-instant + strict flag are process-local (thread-locals, so Ractor-safe
24
- # and never crossing a fork boundary), never persisted to disk, and scoped to exactly one run via
25
- # {.with_run} (which installs a fresh table and restores the previous one on exit — a second run, or a run
25
+ # Soundness: the memo + recording-instant + strict flag are process-local (a per-thread {TypingState}
26
+ # Frame, so Ractor-safe and never crossing a fork boundary), never persisted to disk, and scoped to exactly one
27
+ # run via {.with_run} (which installs a fresh table and restores the previous one on exit — a second run, or a run
26
28
  # that re-reads a file the user edited between runs, gets a clean table). Within a single run the analyzer
27
29
  # already assumes the filesystem is stable — {Descriptor#fresh?} validates the whole dependency set against
28
30
  # the live tree in one pass and never re-checks mid-run — so caching a coherent per-run snapshot changes
29
31
  # nothing about what a run observes. Outside a run scope (no active table) every call digests directly,
30
32
  # identical to a bare `Digest::SHA256.file`.
31
33
  module FileDigest
32
- MEMO_KEY = :rigor_cache_file_digest_memo
33
- STAT_KEY = :rigor_cache_file_stat_memo
34
- INSTANT_KEY = :rigor_cache_recording_instant
35
- STRICT_KEY = :rigor_cache_strict_validation
36
- private_constant :MEMO_KEY, :STAT_KEY, :INSTANT_KEY, :STRICT_KEY
34
+ # One run's state: the digest table, the stat table, the recording instant and the strict flag, installed
35
+ # together by {.with_run}.
36
+ Run = Data.define(:digests, :stats, :instant, :strict)
37
+ private_constant :Run
38
+
39
+ # ADR-129 WD1 — a Frame: {.with_run} installs the run and restores the previous one on the way out, and whether
40
+ # one is installed changes answers (outside a run every call digests or stats directly, the recording instant
41
+ # is the moment of the call, and validation is not strict). The installed {Run} is frozen; its two tables are
42
+ # extensional caches its owner fills in place without a touch (Memos by kind, whose entry is fixed once
43
+ # written): `digests` is keyed on the path and reads the file's bytes, `stats` is keyed on the path and reads
44
+ # its `File::Stat`, both under the run's premise that the filesystem is stable. The instant and the flag are
45
+ # read at install time.
46
+ RUN = TypingState.frame(
47
+ :file_digest_run,
48
+ memos: %i[digests stats],
49
+ reason: "installed per run by with_run; the digest and stat tables are extensional caches keyed on the path, " \
50
+ "filled in place, both reading the filesystem under the run's stable-filesystem premise, and the " \
51
+ "recording instant and strict flag are fixed at install",
52
+ # G1′: the run wraps the typing of every file, but the digest and stat lookups are the cache layer's, made while
53
+ # the RBS environment is prepared and its producers fetched, before anything types (#2883).
54
+ typing_never_reads: true
55
+ )
56
+ private_constant :RUN
37
57
 
38
58
  # Set in the environment to force the strict digest-always validation path for a single run, regardless
39
59
  # of the `cache.validation` config setting (the env wins). The escape hatch for a filesystem whose stat
@@ -46,21 +66,9 @@ module Rigor
46
66
  # captured at run start — a file whose recorded mtime is not strictly older than it is treated as racy
47
67
  # (always re-hashed), catching a file edited during the analysis window. `strict:` forces the
48
68
  # digest-always path for `cache.validation: digest`.
49
- def self.with_run(strict: false)
50
- previous_memo = Thread.current[MEMO_KEY]
51
- previous_stat = Thread.current[STAT_KEY]
52
- previous_instant = Thread.current[INSTANT_KEY]
53
- previous_strict = Thread.current[STRICT_KEY]
54
- Thread.current[MEMO_KEY] = {}
55
- Thread.current[STAT_KEY] = {}
56
- Thread.current[INSTANT_KEY] = now_ns
57
- Thread.current[STRICT_KEY] = strict
58
- yield
59
- ensure
60
- Thread.current[MEMO_KEY] = previous_memo
61
- Thread.current[STAT_KEY] = previous_stat
62
- Thread.current[INSTANT_KEY] = previous_instant
63
- Thread.current[STRICT_KEY] = previous_strict
69
+ def self.with_run(strict: false, &)
70
+ run = Run.new(digests: {}, stats: {}, instant: now_ns, strict: strict)
71
+ TypingState.current.with(RUN, run, &)
64
72
  end
65
73
 
66
74
  # The SHA-256 hex digest of `path`'s current content. Served from the active per-run table when one is
@@ -68,10 +76,10 @@ module Rigor
68
76
  # read failure (missing / unreadable file) propagates exactly as `Digest::SHA256.file` would and is NOT
69
77
  # memoised, so a caller's own rescue path is unchanged.
70
78
  def self.hexdigest(path)
71
- memo = Thread.current[MEMO_KEY]
72
- return Digest::SHA256.file(path).hexdigest if memo.nil?
79
+ run = TypingState.current[RUN]
80
+ return Digest::SHA256.file(path).hexdigest if run.nil?
73
81
 
74
- memo[path] ||= Digest::SHA256.file(path).hexdigest
82
+ run.digests[path] ||= Digest::SHA256.file(path).hexdigest
75
83
  end
76
84
 
77
85
  # @return whether digest-always validation is forced for this run. The env var wins over the
@@ -79,7 +87,7 @@ module Rigor
79
87
  def self.strict_validation?
80
88
  return true if ENV[STRICT_ENV] == "1"
81
89
 
82
- Thread.current[STRICT_KEY] == true
90
+ TypingState.current[RUN]&.strict == true
83
91
  end
84
92
 
85
93
  # ADR-87 WD1 — packs the `:stat` FileEntry value: `"<digest> <size> <mtime_ns> <ctime_ns> <inode>
@@ -156,14 +164,14 @@ module Rigor
156
164
  # describe that moment rather than an earlier probe's. A stat failure propagates un-memoised, exactly
157
165
  # as {.hexdigest} treats a read failure.
158
166
  def self.validation_stat(path)
159
- memo = Thread.current[STAT_KEY]
160
- return File.stat(path) if memo.nil?
167
+ run = TypingState.current[RUN]
168
+ return File.stat(path) if run.nil?
161
169
 
162
- memo[path] ||= File.stat(path)
170
+ run.stats[path] ||= File.stat(path)
163
171
  end
164
172
 
165
173
  def self.recording_instant_ns
166
- Thread.current[INSTANT_KEY] || now_ns
174
+ TypingState.current[RUN]&.instant || now_ns
167
175
  end
168
176
 
169
177
  # value layout: [digest, size, mtime_ns, ctime_ns, inode, recording_instant_ns]
@@ -8,6 +8,7 @@ require_relative "engine_source"
8
8
  require_relative "store"
9
9
  require_relative "../analysis/shadow_harness"
10
10
  require_relative "../analysis/run_cache_key"
11
+ require_relative "../analysis/runner/run_level_facts"
11
12
  require_relative "../environment"
12
13
  require_relative "../analysis/path_expansion"
13
14
  require_relative "../rooted_glob"
@@ -195,20 +196,43 @@ module Rigor
195
196
  # 45: issue #1154 — each seed bundle's `global_write_census` also records the ivar writers the declared-parameter
196
197
  # credit reads (`[:ivar_set, name]`, `[:ivar_set_class, class]`, `[:own_write, class, name, path]`), which a
197
198
  # pre-45 bundle carries none of, so a warm run would credit an ivar write a cold run declines.
198
- SCHEMA = 45
199
+ # 46: issue #2314 — each seed bundle's `global_write_census` also records the global writers
200
+ # (`[:global_writer, name, method]`) the statement walk reads past a call; a pre-46 bundle carries none, so a
201
+ # warm run would keep a global's binding a cold run forgets.
202
+ # 47: issues #2079 / #2170 — each seed bundle's `global_write_census` also records each instance-side ivar
203
+ # write's coarse value (`[:own_write_type, class, name, value, path]`), which the class-ivar census joins across
204
+ # files and along the chain; a pre-47 bundle carries none, so a warm run would keep a seed a cold run widens.
205
+ # 48: issue #1237 — a `return_summaries` key may carry a third element, `true`, for a call key a splat call
206
+ # observed, which the gate re-drives with no positional correspondence. A pre-48 summary holds such a call under a
207
+ # positional key the engine bound as one argument, so a warm run would re-drive it the old way.
208
+ # 49: issue #3116 — a `return_summaries` entry for a def observed under more keys than the per-def cap is
209
+ # `{ keys: [], returns: [], effects:, overflow: true }`. A pre-49 summary holds the first eight keys of such a
210
+ # def with no marker, so a warm run would skip the reader of a key it never recorded.
211
+ # 50: issue #3160 — a `return_summaries` entry holds every key under which a current reader observes the def,
212
+ # merged across the runs that observed them, and a def whose keys cannot all be listed has no entry (the
213
+ # `overflow` form of 49 is gone). A pre-50 summary holds only what the last run that analysed the def's file
214
+ # saw, so a warm run would skip a reader added, or left out of that run, since.
215
+ # 51: issue #3180 — a reader the ADR-89 WD2 gate skipped holds the dependency edges the gate's probe recorded
216
+ # for the def it passed, and the summaries hold the keys that probe observed. A pre-51 snapshot's skipped
217
+ # readers lack the edges and keys of whatever a gate-passed def came to reach, so a warm run would skip them.
218
+ # 52: issues #3186 / #3187 — a `return_summaries` entry also holds `guard_constants`, the published-constant
219
+ # spellings the truthiness rule's one hop reads in the def's body, and a gate-skipped reader holds the plugin
220
+ # reads and run-scoped registrations the gate's probe made. A pre-52 snapshot's skipped readers lack those
221
+ # plugin rows, so a warm run would serve them past an edit of what the plugin read.
222
+ SCHEMA = 52
199
223
 
200
224
  # The persisted per-file state.
201
225
  # `cache` maps an analyzed file to its diagnostics.
202
- # `sources` maps a consumer to the Set of source files it read from.
226
+ # `sources` maps a consumer to the source files it read from.
203
227
  # `digests` maps a file to its content digest at analysis time.
204
228
  # `analyzed` is the ordered analyzed-file list.
205
229
  # ADR-46 slice 4:
206
- # `symbol_sources` maps a consumer to { source_path → Set<"ClassName#method"> }.
207
- # `ancestry_sources` maps a consumer to Set<source_path> (class-ancestry deps).
230
+ # `symbol_sources` maps a consumer to { source_path → the "ClassName#method" names it read }.
231
+ # `ancestry_sources` maps a consumer to the source paths it read for class ancestry.
208
232
  # `symbol_fingerprints` maps a path to { "ClassName#method" => sha256_hex }.
209
233
  # ADR-46 slice 3:
210
- # `missing` maps a consumer to Set<"kind:name"> it looked up and missed.
211
- # `class_decls` maps a path to Set<qualified class name> it declares.
234
+ # `missing` maps a consumer to the "kind:name" keys it looked up and missed.
235
+ # `class_decls` maps a path to the qualified class names it declares.
212
236
  # Issue #644:
213
237
  # `constant_decls` maps a path to that file's constant PUBLICATION CENSUS,
214
238
  # `{qualified name => [literal] | :unpublishable}`. The descriptor is what the recheck diffs: a name's
@@ -226,10 +250,15 @@ module Rigor
226
250
  # fingerprint cannot see.
227
251
  # ADR-89 WD2:
228
252
  # `return_summaries` maps a `[path, "Class#method" | "Class.method"]` to the callee's persisted
229
- # behavioural surface `{ keys:, returns:, effects: }` — the observed `[receiver, arg_types]` call keys
230
- # (Marshal-clean type tuples), their `describe(:short)` return descriptors, and the content-mutated
231
- # parameter positions. A recheck re-evaluates a declaration-stable changed callee at these keys and,
232
- # when every return + the effects are unchanged, skips its symbol dependents.
253
+ # behavioural surface `{ keys:, returns:, effects:, guard_constants: }` — the observed `[receiver, arg_types]`
254
+ # call keys (issue #1237: `[receiver, arg_types, true]` for a key a splat call observed)
255
+ # (Marshal-clean type tuples), their `describe(:short)` return descriptors, the content-mutated
256
+ # parameter positions, and (issue #3186) the published-constant spellings the body reads.
257
+ # A recheck re-evaluates a declaration-stable changed callee at these keys and,
258
+ # when every return, the effects and the spellings are unchanged, skips its symbol dependents.
259
+ # Issues #3116 / #3160 — an entry
260
+ # holds every key under which a current symbol dependent observes the def; a def whose keys cannot all be
261
+ # listed (more than the per-def cap, among others) has no entry, and the recheck never skips its dependents.
233
262
  # ADR-67 WD6c lift:
234
263
  # `param_table` is the `parameter_inference:` seed table (`[class, method, kind] => {param => Type}`)
235
264
  # the run that wrote the snapshot seeded its analysis from — `{}` when the gate is off. A recheck
@@ -279,12 +308,14 @@ module Rigor
279
308
  end
280
309
  end
281
310
 
282
- # Issues #796 / #794 — the two run-level rows the snapshot carries. `definition_build_failures` is the
283
- # reported per-class detail list ({Environment::RbsLoader#definition_build_failures}); `hkt_scan_failure`
284
- # is the `[error_class, first_line, frame, stage]` tuple ({Rigor::Environment#hkt_scan_failure}), nil
285
- # when the scan succeeded — which is why the presence of this object, not the tuple, is what says a
286
- # replay is available.
287
- RunLevelRows = Data.define(:definition_build_failures, :hkt_scan_failure)
311
+ # Issues #796 / #794 — the run-level rows the snapshot carries: one member per run-level fact declared
312
+ # `replay` ({Analysis::Runner::RunLevelFacts}, ADR-116 WD3). Today that is `definition_build_failures`,
313
+ # the reported per-class detail list ({Environment::RbsLoader#definition_build_failures}), and
314
+ # `hkt_scan_failure`, the `[error_class, first_line, frame, stage]` tuple
315
+ # ({Rigor::Environment#hkt_scan_failure}), nil when the scan succeeded — which is why the presence of
316
+ # this object, not the tuple, is what says a replay is available. Declaring another fact `replay`
317
+ # changes this shape, so it bumps {SCHEMA}.
318
+ RunLevelRows = Data.define(*Analysis::Runner::RunLevelFacts.replayable_names)
288
319
 
289
320
  # The global fingerprint that gates a snapshot load: a digest of the inputs whose change requires a full
290
321
  # rebuild — the engine version + schema, the engine's own SOURCE when the version does not pin it, the
@@ -324,7 +355,7 @@ module Rigor
324
355
  "sig:#{digest_signature_paths(project_sig_roots(configuration))}",
325
356
  "pre_eval:#{digest_pre_eval(configuration.pre_eval)}"
326
357
  ]
327
- parts.concat(dependency_source_parts(configuration))
358
+ parts.concat(project_input_parts(configuration))
328
359
  identity = EngineSource.process_identity
329
360
  parts << "engine-source:#{identity}" if identity
330
361
  # Issue #1700 — the gated vendored signatures the run's source activates. A file that gains or drops a
@@ -347,6 +378,37 @@ module Rigor
347
378
  nil
348
379
  end
349
380
 
381
+ # Issue #2517 — a selected core is read like the project's own `sig/`, so an edit in place to one of its files
382
+ # must drop the snapshot (and the language server's session, which keys on this fingerprint). No part for the
383
+ # default core, so every other project's fingerprint is as it was.
384
+ def self.extra_signature_parts(configuration)
385
+ core_root_parts(configuration) + bundle_sig_parts(configuration)
386
+ end
387
+ private_class_method :extra_signature_parts
388
+
389
+ def self.core_root_parts(configuration)
390
+ core_root = configuration.core_root
391
+ core_root.nil? ? [] : ["core:#{digest_signature_paths([core_root])}"]
392
+ end
393
+ private_class_method :core_root_parts
394
+
395
+ # Issue #2987 — the bundle gems' own `sig/` directories the environment loads: which gems have one and what
396
+ # their `.rbs` files hold. The lockfile digest above covers a gem's version, not a `sig/` appearing under an
397
+ # unchanged version, nor an edit in place to one. Shared with the language server's session key. No part for a
398
+ # project whose bundle holds none, so its fingerprint is as it was.
399
+ def self.bundle_sig_parts(configuration)
400
+ locked = Environment::LockfileResolver.locked_gems(
401
+ lockfile_path: configuration.bundler_lockfile, project_root: Dir.pwd,
402
+ auto_detect: configuration.bundler_auto_detect
403
+ )
404
+ dirs = Environment::BundleSigDiscovery.discover(
405
+ bundle_path: configuration.bundler_bundle_path, project_root: Dir.pwd,
406
+ auto_detect: configuration.bundler_auto_detect, locked_gems: locked.empty? ? nil : locked
407
+ ).map(&:to_s).sort
408
+ dirs.empty? ? [] : ["bundle-sig:#{dirs.join("\n")}:#{digest_signature_paths(dirs)}"]
409
+ end
410
+ private_class_method :bundle_sig_parts
411
+
350
412
  def self.required_features(configuration, roots)
351
413
  files = Analysis::PathExpansion.ruby_files(roots, configuration.exclude_patterns,
352
414
  base: configuration.exclude_base)
@@ -364,6 +426,22 @@ module Rigor
364
426
  end
365
427
  private_class_method :dependency_source_parts
366
428
 
429
+ # The parts read off the project beside its configuration, in the order the fingerprint has always joined them:
430
+ # the extra signature roots, the `dependencies.source_inference` gems, and (#3030) the collection setup hint.
431
+ def self.project_input_parts(configuration)
432
+ extra_signature_parts(configuration) + dependency_source_parts(configuration) +
433
+ collection_setup_hint_parts(configuration)
434
+ end
435
+ private_class_method :project_input_parts
436
+
437
+ # Issue #3030 — the `rbs.coverage.collection-not-installed` hint, a run-level row read off the working directory
438
+ # that no other part covers. No part without a hint, so every other project's fingerprint is as it was.
439
+ def self.collection_setup_hint_parts(configuration)
440
+ hint = Analysis::RunCacheKey.collection_setup_hint(configuration)
441
+ hint ? ["rbs-collection-hint:#{hint}"] : []
442
+ end
443
+ private_class_method :collection_setup_hint_parts
444
+
367
445
  def self.digest_file_if_present(path)
368
446
  path = path&.to_s
369
447
  path && File.file?(path) ? Digest::SHA256.file(path).hexdigest : "absent"
@@ -6,8 +6,8 @@ module Rigor
6
6
  module Cache
7
7
  # Base for the RBS-derived cache producers.
8
8
  #
9
- # Every producer (`RbsKnownClassNames`, `RbsConstantTable`, `RbsEnvironment`, the ancestor / type-param /
10
- # definition tables, …) repeated the identical `fetch` wiring: build the RBS descriptor, then
9
+ # Every producer (`RbsKnownClassNames`, `RbsConstantTable`, `RbsEnvironment`, the ancestor / type-param
10
+ # tables, …) repeated the identical `fetch` wiring: build the RBS descriptor, then
11
11
  # `store.fetch_or_compute` under the producer's id, yielding to the producer's `compute`. Only the
12
12
  # `PRODUCER_ID` constant and the `compute(loader)` body actually differ between producers.
13
13
  #
@@ -6,7 +6,7 @@ require_relative "rbs_cache_producer"
6
6
  module Rigor
7
7
  module Cache
8
8
  # Cache producer that materialises the RBS-declared ancestor chain of every loaded class / module into a
9
- # Marshal-clean `Hash<String, Array<String>>`. Ancestor names are top-level-stripped (e.g. `"Integer"` not
9
+ # Marshal-clean table. Ancestor names are top-level-stripped (e.g. `"Integer"` not
10
10
  # `"::Integer"`) to match `Environment::RbsHierarchy#normalize_name`.
11
11
  #
12
12
  # The hierarchy is the substrate behind every `class_ordering` query, which is itself a hot path on the
@@ -6,8 +6,8 @@ require_relative "rbs_cache_producer"
6
6
  module Rigor
7
7
  module Cache
8
8
  # Cache producer that materialises every loaded class's RBS-declared type-parameter names as a
9
- # Marshal-clean `Hash<String, Array<Symbol>>` keyed by top-level-stripped class name (e.g. `"Array"` →
10
- # `[:Elem]`, `"Hash"` → `[:K, :V]`). Producer id `"rbs.class_type_param_names"`.
9
+ # Marshal-clean table keyed by top-level-stripped class name (e.g. `"Array"` →
10
+ # `[:Elem]`, `"Hash"` → `[:K, :V]`).
11
11
  #
12
12
  # The dispatcher reads type-parameter names every time it builds a substitution map from a receiver's
13
13
  # `type_args` into a method's return type — it is one of the hottest reflection lookups during analysis.
@@ -6,7 +6,7 @@ require_relative "rbs_cache_producer"
6
6
  module Rigor
7
7
  module Cache
8
8
  # Cache producer that materialises every RBS-declared constant to its translated `Rigor::Type` form and
9
- # stores the result as a `Hash<String, Rigor::Type>` keyed by canonical constant name. This is the v0.0.8
9
+ # stores the result as a table keyed by canonical constant name. This is the v0.0.8
10
10
  # first cached producer per ADR-6 § 7; it caches a post-translation artefact so the cache value is
11
11
  # `Marshal`-clean (RBS-native objects carry `RBS::Location`, which lacks `_dump_data`).
12
12
  #
@@ -77,9 +77,58 @@ module Rigor
77
77
  # reconstructs {.config_entries} without loading a plugin, so an entry there that it cannot rebuild
78
78
  # would turn the warm fast lane into a permanent miss on every project that enables a bundled plugin.
79
79
  def self.env_only_config_entries(loader)
80
- [deferred_signature_paths_entry(loader)].compact
80
+ [deferred_signature_paths_entry(loader), project_signature_paths_entry(loader), core_root_entry(loader),
81
+ subordinate_signature_paths_entry(loader), collection_signature_paths_entry(loader)].compact
81
82
  end
82
83
 
84
+ # Issue #2517 — which directory the env loaded core from. The selected core's files are digested by
85
+ # {.file_entries} too, so this row is the identity of the choice and those rows its contents: switching
86
+ # between two roots with byte-identical files still re-keys. Absent for the default core, whose identity
87
+ # is the `rbs` gem row, so every project without the setting keeps its key.
88
+ def self.core_root_entry(loader)
89
+ core_root = selected_core_root(loader)
90
+ return nil if core_root.nil?
91
+
92
+ Descriptor::ConfigEntry.new(key: "rbs.core_root", value_hash: Digest::SHA256.hexdigest(core_root.to_s))
93
+ end
94
+
95
+ private_class_method :core_root_entry
96
+
97
+ # The loader's issue #2517 core root as a Pathname, or nil for the default core.
98
+ def self.selected_core_root(loader)
99
+ core_root = loader.respond_to?(:core_root) ? loader.core_root : nil
100
+ core_root.nil? ? nil : Pathname(core_root)
101
+ end
102
+
103
+ private_class_method :selected_core_root
104
+
105
+ # {.selected_core_root} as the signature roots it adds: none for the default core.
106
+ def self.core_root_dirs(loader)
107
+ core_root = selected_core_root(loader)
108
+ core_root.nil? ? [] : [core_root]
109
+ end
110
+
111
+ private_class_method :core_root_dirs
112
+
113
+ # Issue #2422 — which of the loader's `signature_paths:` are the project's own changes the env built from
114
+ # byte-identical files: a bundled plugin's method composes with another source's as an overload, but stands
115
+ # down to the project's own. Only read when a plugin defers signatures, so a project without one keeps its
116
+ # key.
117
+ def self.project_signature_paths_entry(loader)
118
+ return nil unless loader.respond_to?(:project_signature_paths) && loader.respond_to?(:deferred_signature_paths)
119
+ return nil if Array(loader.deferred_signature_paths).empty?
120
+
121
+ own = Array(loader.project_signature_paths).map(&:to_s).sort
122
+ return nil if own.empty?
123
+
124
+ Descriptor::ConfigEntry.new(
125
+ key: "rbs.project_signature_paths",
126
+ value_hash: Digest::SHA256.hexdigest(own.join("\n"))
127
+ )
128
+ end
129
+
130
+ private_class_method :project_signature_paths_entry
131
+
83
132
  def self.deferred_signature_paths_entry(loader)
84
133
  return nil unless loader.respond_to?(:deferred_signature_paths)
85
134
 
@@ -94,6 +143,40 @@ module Rigor
94
143
 
95
144
  private_class_method :deferred_signature_paths_entry
96
145
 
146
+ # Issue #2978 — which of the loader's `signature_paths:` stand down member by member to the rest (a bundle gem's
147
+ # collection copy behind its own `sig/`) changes the env built from byte-identical files, as the deferred
148
+ # partition does. Absent when there is none, so every other project keeps its key.
149
+ def self.subordinate_signature_paths_entry(loader)
150
+ return nil unless loader.respond_to?(:subordinate_signature_paths)
151
+
152
+ subordinate = Array(loader.subordinate_signature_paths).map(&:to_s).sort
153
+ return nil if subordinate.empty?
154
+
155
+ Descriptor::ConfigEntry.new(
156
+ key: "rbs.subordinate_signature_paths",
157
+ value_hash: Digest::SHA256.hexdigest(subordinate.join("\n"))
158
+ )
159
+ end
160
+
161
+ private_class_method :subordinate_signature_paths_entry
162
+
163
+ # Issue #3030 — which of the loader's `signature_paths:` are `rbs collection` gem directories decides whether
164
+ # their `_`-prefixed directories load, so it changes the env built from byte-identical files. Absent when
165
+ # there is none, so every project without a collection lockfile keeps its key.
166
+ def self.collection_signature_paths_entry(loader)
167
+ return nil unless loader.respond_to?(:collection_signature_paths)
168
+
169
+ collection = Array(loader.collection_signature_paths).map(&:to_s).sort
170
+ return nil if collection.empty?
171
+
172
+ Descriptor::ConfigEntry.new(
173
+ key: "rbs.collection_signature_paths",
174
+ value_hash: Digest::SHA256.hexdigest(collection.join("\n"))
175
+ )
176
+ end
177
+
178
+ private_class_method :collection_signature_paths_entry
179
+
97
180
  # Public (ADR-87 WD4) so the boot-slimming run-cache probe reconstructs the identical `gems` +
98
181
  # `rbs.libraries` key slots the runner writes, from the config-derived library list, without a loader.
99
182
  def self.rbs_gem_entry
@@ -104,7 +187,9 @@ module Rigor
104
187
  # value must be deterministic; `:stat` (ADR-87 WD1) for the validation-only run-dependency descriptor
105
188
  # ({RunDescriptor#files}), where the stat tier short-circuits the SHA-256 on an unmoved file.
106
189
  def self.file_entries(loader, comparator: :digest)
107
- roots = loader.signature_paths +
190
+ # Issue #2517 — a selected core is project input like `signature_paths:`, not the engine-versioned `rbs`
191
+ # gem tree, so an edit in place must move the key.
192
+ roots = core_root_dirs(loader) + loader.signature_paths +
108
193
  Rigor::Environment::RbsLoader.vendored_gem_sig_paths +
109
194
  Rigor::Environment::RbsLoader.core_overlay_sig_paths +
110
195
  Rigor::Environment::RbsLoader.capability_role_sig_paths
@@ -149,7 +234,8 @@ module Rigor
149
234
  def self.glob_entries(loader)
150
235
  # Deduplicated by path: a root reachable twice (an explicit `signature_paths:` entry that is also a
151
236
  # bundled plugin's `sig/`, #610) would otherwise contribute two rows whose stat signatures race.
152
- loader.signature_paths.uniq(&:to_s).filter_map do |root|
237
+ # Issue #2517 — and the selected core's root, where a file appearing is as much a new input.
238
+ (core_root_dirs(loader) + loader.signature_paths).uniq(&:to_s).filter_map do |root|
153
239
  next unless root.directory?
154
240
 
155
241
  Descriptor::GlobEntry.compute(root: root.to_s, pattern: File.join("**", "*.rbs"), mode: :names)
@@ -157,7 +243,7 @@ module Rigor
157
243
  end
158
244
 
159
245
  # @param library_names — the loader's merged library list (or, on the WD4 probe
160
- # path, `Environment::DEFAULT_LIBRARIES + config.libraries` reconstructed without a loader).
246
+ # path, `Environment::BaseLibraries.resolve` reconstructed without a loader).
161
247
  def self.libraries_entry(library_names)
162
248
  sorted = library_names.map(&:to_s).sort
163
249
  Descriptor::ConfigEntry.new(
@@ -33,7 +33,14 @@ module Rigor
33
33
  signature_paths: loader.signature_paths,
34
34
  virtual_rbs: loader.respond_to?(:virtual_rbs) ? loader.virtual_rbs : [],
35
35
  deferred_signature_paths:
36
- loader.respond_to?(:deferred_signature_paths) ? loader.deferred_signature_paths : []
36
+ loader.respond_to?(:deferred_signature_paths) ? loader.deferred_signature_paths : [],
37
+ project_signature_paths:
38
+ loader.respond_to?(:project_signature_paths) ? loader.project_signature_paths : [],
39
+ core_root: loader.respond_to?(:core_root) ? loader.core_root : nil,
40
+ subordinate_signature_paths:
41
+ loader.respond_to?(:subordinate_signature_paths) ? loader.subordinate_signature_paths : [],
42
+ collection_signature_paths:
43
+ loader.respond_to?(:collection_signature_paths) ? loader.collection_signature_paths : []
37
44
  )
38
45
  end
39
46
 
@@ -53,8 +53,8 @@ require_relative "annotation_location"
53
53
  # differently, and only the latter reaches `marshal_load`), so without the bump a stale blob would keep
54
54
  # reporting the moved position indefinitely.
55
55
  #
56
- # Idempotent: the guard checks `method_defined?(:_dump)` so requiring this file twice (or against an upstream
57
- # rbs that adds Marshal hooks itself) is a no-op.
56
+ # Idempotent: each hook is defined only when `method_defined?` reports it missing, so requiring this file twice
57
+ # (or against an upstream rbs that adds Marshal hooks itself) is a no-op.
58
58
  #
59
59
  # `RBS::TypeName` / `RBS::Namespace` carry a second, subtler Marshal hazard, introduced by rbs 4.1's
60
60
  # flyweight interning (ruby/rbs#2957): both memoise their `#hash` into an `@hash` ivar, and that value is
@@ -7,7 +7,7 @@ module Rigor
7
7
  module Cache
8
8
  # Cache producer that materialises the set of every RBS-declared class / module / alias name (top-level
9
9
  # prefixed, e.g. `"::Math"`) currently loaded into the environment. Marshal-clean — the cache value is a
10
- # `Set<String>`.
10
+ # Set of names.
11
11
  #
12
12
  # The set lets `RbsLoader#class_known?` answer point lookups in O(1) without re-parsing the name on each
13
13
  # call, and lets a warm process skip the env walk entirely (the env still has to be built to enumerate
@@ -135,14 +135,6 @@ module Rigor
135
135
  end
136
136
  end
137
137
 
138
- # Walks the on-disk cache rooted at `root` and reports a producer-level inventory. Used by
139
- # `rigor check --cache-stats` to surface cache size and per-producer entry counts without depending on
140
- # in-process counters (which only reflect the current run).
141
- #
142
- # @return `{ root:, schema_version:, total_entries:, total_bytes:, producers: [{ id:, entries:,
143
- # bytes: }, ...] }`. When the root does not exist or has no schema-version marker, `schema_version` is
144
- # nil and the producer list is empty.
145
- #
146
138
  # The `schema_version.txt` marker content. Covers BOTH invalidation axes: the descriptor schema and the
147
139
  # on-disk byte layout ({FORMAT_VERSION}, ADR-54). A format bump leaves the old entries permanently
148
140
  # unreadable (header mismatch → miss) but, alone, would never reclaim their bytes — they can sit below
@@ -152,6 +144,13 @@ module Rigor
152
144
  "#{PAYLOAD_ABI_VERSION}.#{Descriptor::SCHEMA_VERSION}.#{FORMAT_VERSION}"
153
145
  end
154
146
 
147
+ # Walks the on-disk cache rooted at `root` and reports a producer-level inventory. Used by
148
+ # `rigor check --cache-stats` to surface cache size and per-producer entry counts without depending on
149
+ # in-process counters (which only reflect the current run).
150
+ #
151
+ # @return `{ root:, schema_version:, total_entries:, total_bytes:, producers: [{ id:, entries:,
152
+ # bytes: }, ...] }`. When the root does not exist or has no schema-version marker, `schema_version` is
153
+ # nil and the producer list is empty.
155
154
  def self.disk_inventory(root:)
156
155
  root_s = root.to_s
157
156
  marker = File.join(root_s, "schema_version.txt")
@@ -360,7 +359,8 @@ module Rigor
360
359
  # size-based LRU pass below stays gated on `max_bytes:` being configured: it walks all remaining
361
360
  # `.entry` files, sorts by mtime ascending (oldest = least recently used), and unlinks from the oldest
362
361
  # until the total is at or below the cap. Touch-on-disk-read ({read_entry}) is the cross-process LRU
363
- # signal: every disk hit (not in-process-memo hit) updates the mtime so recently-read entries survive
362
+ # signal: a disk hit (not an in-process-memo hit) updates the mtime when `max_bytes:` is set, so
363
+ # recently-read entries survive
364
364
  # the eviction pass. Any FS error is swallowed — eviction must never break a run.
365
365
  def evict!
366
366
  return if @read_only
@@ -591,7 +591,8 @@ module Rigor
591
591
  # — one check per Store is enough; a benign double-check under a thread race would just repeat
592
592
  # idempotent work.
593
593
  #
594
- # A writable store clears the cache root and rewrites the marker on a stale/missing marker, then reports
594
+ # A writable store clears the cache root and rewrites the marker on a stale marker (a missing or empty
595
+ # one is rewritten without clearing), then reports
595
596
  # available. A read-only store never touches the root — no mkdir, no marker write, no destructive clear
596
597
  # — so it reports available ONLY when the on-disk marker already matches current exactly; a stale or
597
598
  # missing marker (e.g. a Rigor upgrade with no writable run yet, as in LSP / editor mode) reports
@@ -20,7 +20,7 @@ module Rigor
20
20
  # non-CLI consumers exist: {Configuration#cache_validation_strict?} resolves the `auto` cache-validation default
21
21
  # through it (a fresh CI checkout regenerates every stat tuple, so ADR-87's stat tier cannot work there).
22
22
  # Detection is fully testable; callers pass `ENV`.
23
- # `RIGOR_CI_DETECT=0` (or `false`/`no`) disables it globally — the seam the spec suite uses for determinism.
23
+ # `RIGOR_CI_DETECT=0` (or `false`/`no`/`off`) disables it globally — the seam the spec suite uses for determinism.
24
24
  module CiDetector
25
25
  Platform = Struct.new(:id, :name, :format, :tier, keyword_init: true) do
26
26
  def native_stdout? = tier == :native_stdout