rigortype 0.4.6 → 0.4.7

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 (417) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
  4. data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
  5. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
  6. data/docs/handbook/01-getting-started.md +5 -2
  7. data/docs/handbook/11-sig-gen.md +50 -3
  8. data/docs/handbook/appendix-phpstan.md +4 -4
  9. data/docs/manual/02-cli-reference.md +152 -26
  10. data/docs/manual/03-configuration.md +36 -3
  11. data/docs/manual/06-baseline.md +6 -4
  12. data/docs/manual/09-editor-integration.md +13 -2
  13. data/docs/manual/10-mcp-server.md +38 -2
  14. data/docs/manual/12-caching.md +4 -3
  15. data/docs/manual/plugins/rigor-actionpack.md +19 -11
  16. data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
  17. data/lib/rigor/analysis/baseline.rb +24 -27
  18. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
  19. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
  20. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
  21. data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
  22. data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
  23. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -2
  24. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +0 -1
  25. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
  26. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
  27. data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
  28. data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
  29. data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
  30. data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
  31. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
  32. data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
  33. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  34. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
  35. data/lib/rigor/analysis/check_rules.rb +124 -44
  36. data/lib/rigor/analysis/crash_signature.rb +2 -2
  37. data/lib/rigor/analysis/dependency_recorder.rb +4 -4
  38. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
  39. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
  40. data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
  41. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  42. data/lib/rigor/analysis/edit_replay.rb +93 -6
  43. data/lib/rigor/analysis/fact_store.rb +3 -3
  44. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
  45. data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
  46. data/lib/rigor/analysis/incremental_session.rb +27 -16
  47. data/lib/rigor/analysis/rule_catalog.rb +2 -2
  48. data/lib/rigor/analysis/run_stats.rb +2 -2
  49. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +2 -2
  50. data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
  51. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +193 -93
  52. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
  53. data/lib/rigor/analysis/runner/pool_coordinator.rb +147 -323
  54. data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -3
  55. data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
  56. data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
  57. data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
  58. data/lib/rigor/analysis/runner.rb +77 -71
  59. data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
  60. data/lib/rigor/analysis/template_unit_positions.rb +2 -3
  61. data/lib/rigor/analysis/template_units.rb +9 -1
  62. data/lib/rigor/analysis/worker_session.rb +24 -31
  63. data/lib/rigor/bleeding_edge.rb +3 -6
  64. data/lib/rigor/builtins/imported_refinements.rb +6 -8
  65. data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
  66. data/lib/rigor/builtins/regex_refinement.rb +4 -6
  67. data/lib/rigor/cache/descriptor.rb +6 -5
  68. data/lib/rigor/cache/incremental_snapshot.rb +31 -12
  69. data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
  70. data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
  71. data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
  72. data/lib/rigor/cache/rbs_constant_table.rb +1 -1
  73. data/lib/rigor/cache/rbs_descriptor.rb +54 -3
  74. data/lib/rigor/cache/rbs_environment.rb +4 -1
  75. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
  76. data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
  77. data/lib/rigor/cache/store.rb +11 -10
  78. data/lib/rigor/ci_detector.rb +1 -1
  79. data/lib/rigor/cli/annotate_command.rb +24 -19
  80. data/lib/rigor/cli/baseline_command.rb +145 -55
  81. data/lib/rigor/cli/catalogue.rb +256 -0
  82. data/lib/rigor/cli/check_command.rb +108 -102
  83. data/lib/rigor/cli/check_runner_factory.rb +2 -2
  84. data/lib/rigor/cli/command.rb +15 -1
  85. data/lib/rigor/cli/coverage_command.rb +21 -8
  86. data/lib/rigor/cli/coverage_mutation.rb +5 -7
  87. data/lib/rigor/cli/coverage_renderer.rb +2 -1
  88. data/lib/rigor/cli/coverage_scan.rb +3 -5
  89. data/lib/rigor/cli/diff_command.rb +25 -20
  90. data/lib/rigor/cli/docs_command.rb +1 -2
  91. data/lib/rigor/cli/doctor_command.rb +12 -12
  92. data/lib/rigor/cli/effects_command.rb +50 -40
  93. data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
  94. data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
  95. data/lib/rigor/cli/effects_renderer.rb +2 -1
  96. data/lib/rigor/cli/effects_snapshot_command.rb +18 -17
  97. data/lib/rigor/cli/explain_command.rb +18 -8
  98. data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
  99. data/lib/rigor/cli/json_document.rb +41 -0
  100. data/lib/rigor/cli/lens_command.rb +12 -8
  101. data/lib/rigor/cli/lsp_command.rb +250 -23
  102. data/lib/rigor/cli/mcp_command.rb +12 -7
  103. data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
  104. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
  105. data/lib/rigor/cli/options.rb +40 -1
  106. data/lib/rigor/cli/plugin_command.rb +93 -46
  107. data/lib/rigor/cli/plugins_command.rb +17 -18
  108. data/lib/rigor/cli/plugins_renderer.rb +10 -6
  109. data/lib/rigor/cli/protection_fork_scan.rb +2 -2
  110. data/lib/rigor/cli/protection_renderer.rb +2 -1
  111. data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
  112. data/lib/rigor/cli/sig_gen_command.rb +84 -27
  113. data/lib/rigor/cli/skill_command.rb +32 -3
  114. data/lib/rigor/cli/skill_deep_probe.rb +1 -1
  115. data/lib/rigor/cli/skill_describe.rb +1 -1
  116. data/lib/rigor/cli/trace_command.rb +14 -9
  117. data/lib/rigor/cli/triage_command.rb +10 -11
  118. data/lib/rigor/cli/triage_renderer.rb +2 -1
  119. data/lib/rigor/cli/type_of_command.rb +16 -11
  120. data/lib/rigor/cli/type_of_project_scope.rb +1 -4
  121. data/lib/rigor/cli/type_of_renderer.rb +4 -1
  122. data/lib/rigor/cli/type_scan_command.rb +14 -9
  123. data/lib/rigor/cli/type_scan_renderer.rb +2 -1
  124. data/lib/rigor/cli/unused_command.rb +27 -17
  125. data/lib/rigor/cli/upgrade_command.rb +5 -0
  126. data/lib/rigor/cli.rb +209 -54
  127. data/lib/rigor/config_audit.rb +1 -1
  128. data/lib/rigor/configuration/severity_profile.rb +5 -5
  129. data/lib/rigor/configuration.rb +72 -30
  130. data/lib/rigor/effects/definition_context.rb +0 -2
  131. data/lib/rigor/effects/envelope.rb +1 -1
  132. data/lib/rigor/effects/framework_units.rb +1 -1
  133. data/lib/rigor/effects/mutation_classifier.rb +4 -7
  134. data/lib/rigor/effects/propagator.rb +1 -1
  135. data/lib/rigor/effects/registry.rb +2 -2
  136. data/lib/rigor/effects/snapshot.rb +1 -1
  137. data/lib/rigor/effects/taint_cause.rb +0 -2
  138. data/lib/rigor/effects/unit_scan.rb +9 -13
  139. data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
  140. data/lib/rigor/environment/class_registry.rb +3 -3
  141. data/lib/rigor/environment/core_root.rb +77 -0
  142. data/lib/rigor/environment/default_libraries.rb +1 -1
  143. data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
  144. data/lib/rigor/environment/gated_signature_guard.rb +1 -1
  145. data/lib/rigor/environment/gated_signature_plan.rb +5 -2
  146. data/lib/rigor/environment/lockfile_resolver.rb +28 -12
  147. data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
  148. data/lib/rigor/environment/member_consistency.rb +56 -7
  149. data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
  150. data/lib/rigor/environment/project_path_gems.rb +124 -0
  151. data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
  152. data/lib/rigor/environment/rbs_loader.rb +480 -67
  153. data/lib/rigor/environment/reflection.rb +7 -7
  154. data/lib/rigor/environment/required_features.rb +0 -3
  155. data/lib/rigor/environment/ruby_library_roots.rb +1 -1
  156. data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
  157. data/lib/rigor/environment.rb +41 -10
  158. data/lib/rigor/flow_contribution/merger.rb +4 -4
  159. data/lib/rigor/inference/acceptance.rb +93 -32
  160. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  161. data/lib/rigor/inference/block_run_count.rb +178 -0
  162. data/lib/rigor/inference/budget_trace.rb +8 -2
  163. data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
  164. data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
  165. data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
  166. data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
  167. data/lib/rigor/inference/callee_write_census.rb +815 -0
  168. data/lib/rigor/inference/construction_escape.rb +217 -0
  169. data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
  170. data/lib/rigor/inference/deep_erasure.rb +39 -7
  171. data/lib/rigor/inference/def_handle.rb +1 -1
  172. data/lib/rigor/inference/def_node_resolver.rb +1 -1
  173. data/lib/rigor/inference/def_return_typer.rb +46 -4
  174. data/lib/rigor/inference/definer_resolution.rb +4 -2
  175. data/lib/rigor/inference/definers_index.rb +106 -0
  176. data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
  177. data/lib/rigor/inference/element_read_widening.rb +49 -3
  178. data/lib/rigor/inference/expression_typer.rb +116 -49
  179. data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
  180. data/lib/rigor/inference/global_write_census.rb +76 -2
  181. data/lib/rigor/inference/global_writer_reach.rb +260 -0
  182. data/lib/rigor/inference/hkt_body.rb +1 -2
  183. data/lib/rigor/inference/hkt_body_parser.rb +3 -5
  184. data/lib/rigor/inference/hkt_reducer.rb +2 -2
  185. data/lib/rigor/inference/hkt_registry.rb +3 -5
  186. data/lib/rigor/inference/indexed_narrowing.rb +4 -7
  187. data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
  188. data/lib/rigor/inference/ivar_writer_census.rb +51 -0
  189. data/lib/rigor/inference/key_presence_guard.rb +2 -1
  190. data/lib/rigor/inference/loop_value_record.rb +8 -4
  191. data/lib/rigor/inference/match_rebinding/frame.rb +46 -0
  192. data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
  193. data/lib/rigor/inference/match_rebinding.rb +11 -1
  194. data/lib/rigor/inference/memo_audit.rb +55 -0
  195. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
  196. data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
  197. data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
  198. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
  199. data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
  200. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
  201. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
  202. data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
  203. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
  204. data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
  205. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
  206. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
  207. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
  208. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
  209. data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
  210. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
  211. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
  212. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
  213. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
  214. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
  215. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  216. data/lib/rigor/inference/method_dispatcher.rb +64 -14
  217. data/lib/rigor/inference/module_function_state.rb +11 -1
  218. data/lib/rigor/inference/multi_target_binder.rb +1 -1
  219. data/lib/rigor/inference/mutation_rejoin.rb +240 -4
  220. data/lib/rigor/inference/mutation_widening.rb +157 -27
  221. data/lib/rigor/inference/narrowing.rb +47 -33
  222. data/lib/rigor/inference/optimistic_origin.rb +1 -1
  223. data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
  224. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  225. data/lib/rigor/inference/pre_eval_constants.rb +1 -1
  226. data/lib/rigor/inference/project_method_ownership.rb +6 -3
  227. data/lib/rigor/inference/project_yielder.rb +11 -17
  228. data/lib/rigor/inference/receiver_alias.rb +20 -0
  229. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
  230. data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
  231. data/lib/rigor/inference/remover_holder_scan.rb +233 -0
  232. data/lib/rigor/inference/remover_narrowing.rb +224 -0
  233. data/lib/rigor/inference/rewrite_mutation.rb +53 -2
  234. data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
  235. data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
  236. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +58 -15
  237. data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
  238. data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
  239. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
  240. data/lib/rigor/inference/scope_indexer.rb +137 -46
  241. data/lib/rigor/inference/self_call_ivar_writes.rb +84 -12
  242. data/lib/rigor/inference/set_mutation.rb +189 -0
  243. data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
  244. data/lib/rigor/inference/statement_evaluator.rb +2328 -138
  245. data/lib/rigor/inference/struct_fold_safety.rb +4 -6
  246. data/lib/rigor/inference/synthetic_method_scanner.rb +5 -6
  247. data/lib/rigor/inference/toplevel_def_census.rb +59 -0
  248. data/lib/rigor/inference/unknown_store_widening.rb +103 -4
  249. data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
  250. data/lib/rigor/inference/void_tail_summary.rb +2 -1
  251. data/lib/rigor/language_server/buffer_table.rb +3 -3
  252. data/lib/rigor/language_server/completion_provider.rb +26 -20
  253. data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
  254. data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
  255. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  256. data/lib/rigor/language_server/folding_range_provider.rb +1 -2
  257. data/lib/rigor/language_server/hover_provider.rb +1 -7
  258. data/lib/rigor/language_server/hover_renderer.rb +6 -8
  259. data/lib/rigor/language_server/project_context.rb +39 -1
  260. data/lib/rigor/language_server/publish_batcher.rb +3 -4
  261. data/lib/rigor/language_server/selection_range_provider.rb +2 -4
  262. data/lib/rigor/language_server/server.rb +7 -3
  263. data/lib/rigor/language_server/signature_help_provider.rb +4 -4
  264. data/lib/rigor/lens/plugins.rb +1 -1
  265. data/lib/rigor/mcp/server.rb +73 -2
  266. data/lib/rigor/mcp.rb +1 -1
  267. data/lib/rigor/plugin/access_denied_error.rb +1 -2
  268. data/lib/rigor/plugin/bundled_catalog.rb +1 -12
  269. data/lib/rigor/plugin/io_boundary.rb +1 -1
  270. data/lib/rigor/plugin/loader.rb +25 -8
  271. data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
  272. data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
  273. data/lib/rigor/plugin/manifest.rb +9 -13
  274. data/lib/rigor/plugin/registry.rb +3 -3
  275. data/lib/rigor/plugin/services.rb +2 -3
  276. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
  277. data/lib/rigor/plugin/trust_policy.rb +2 -2
  278. data/lib/rigor/plugin/type_node_resolver.rb +2 -4
  279. data/lib/rigor/plugin.rb +6 -8
  280. data/lib/rigor/plugin_gap_advisory.rb +5 -1
  281. data/lib/rigor/project_environment.rb +16 -2
  282. data/lib/rigor/protection/analysis_guard.rb +1 -1
  283. data/lib/rigor/protection/discovery_seed.rb +44 -5
  284. data/lib/rigor/protection/mutator.rb +1 -1
  285. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  286. data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
  287. data/lib/rigor/rbs_extended/reporter.rb +27 -3
  288. data/lib/rigor/rbs_extended.rb +2 -3
  289. data/lib/rigor/reflection/constant_ancestors.rb +5 -3
  290. data/lib/rigor/rooted_glob.rb +15 -1
  291. data/lib/rigor/run_scoped_slots.rb +38 -0
  292. data/lib/rigor/runtime/jit.rb +1 -1
  293. data/lib/rigor/scope/discovery_index.rb +19 -6
  294. data/lib/rigor/scope/resolution_chain.rb +3 -1
  295. data/lib/rigor/scope.rb +365 -31
  296. data/lib/rigor/sig_gen/classification.rb +4 -0
  297. data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
  298. data/lib/rigor/sig_gen/generator.rb +371 -58
  299. data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
  300. data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
  301. data/lib/rigor/sig_gen/layout_index.rb +3 -3
  302. data/lib/rigor/sig_gen/method_candidate.rb +23 -4
  303. data/lib/rigor/sig_gen/observation_collector.rb +3 -5
  304. data/lib/rigor/sig_gen/observed_call.rb +1 -1
  305. data/lib/rigor/sig_gen/path_mapper.rb +6 -0
  306. data/lib/rigor/sig_gen/renderer.rb +15 -9
  307. data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
  308. data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
  309. data/lib/rigor/sig_gen/write_reporter.rb +63 -14
  310. data/lib/rigor/sig_gen/write_result.rb +23 -12
  311. data/lib/rigor/sig_gen/writer.rb +217 -37
  312. data/lib/rigor/type/acceptance_router.rb +2 -2
  313. data/lib/rigor/type/accepts_result.rb +1 -1
  314. data/lib/rigor/type/app.rb +4 -6
  315. data/lib/rigor/type/combinator.rb +9 -13
  316. data/lib/rigor/type/constant.rb +4 -5
  317. data/lib/rigor/type/hash_shape.rb +21 -40
  318. data/lib/rigor/type/union.rb +1 -1
  319. data/lib/rigor/type_node/identifier.rb +1 -2
  320. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  321. data/lib/rigor/type_node/string_literal.rb +1 -2
  322. data/lib/rigor/type_node/symbol_literal.rb +1 -2
  323. data/lib/rigor/type_node/union.rb +3 -6
  324. data/lib/rigor/value_semantics.rb +1 -10
  325. data/lib/rigor/version.rb +1 -1
  326. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
  327. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
  328. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
  329. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
  330. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
  331. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
  332. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
  333. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
  334. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
  335. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
  336. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
  337. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +66 -19
  338. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  339. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
  340. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
  341. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
  342. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
  343. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
  344. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
  345. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
  346. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
  347. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
  348. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
  349. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
  350. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
  351. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
  352. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
  353. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
  354. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
  355. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
  356. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
  357. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
  358. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
  359. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
  360. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
  361. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
  362. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
  363. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
  364. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
  365. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
  366. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
  367. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -5
  368. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
  369. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
  370. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
  371. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
  372. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
  373. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
  374. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
  375. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
  376. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
  377. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
  381. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
  382. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
  383. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
  384. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
  386. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
  387. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
  388. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
  389. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
  390. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
  391. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +1 -1
  392. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
  393. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
  394. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
  395. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
  396. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
  397. data/sig/rigor/analysis/baseline.rbs +0 -3
  398. data/sig/rigor/analysis/fact_store.rbs +1 -0
  399. data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
  400. data/sig/rigor/cli/diff_command.rbs +1 -1
  401. data/sig/rigor/cli/explain_command.rbs +1 -1
  402. data/sig/rigor/effects/registry.rbs +2 -2
  403. data/sig/rigor/inference.rbs +4 -1
  404. data/sig/rigor/plugin/additional_initializer.rbs +0 -5
  405. data/sig/rigor/plugin/base.rbs +1 -0
  406. data/sig/rigor/plugin/effect_attribution.rbs +3 -3
  407. data/sig/rigor/plugin/effect_edge.rbs +1 -1
  408. data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
  409. data/sig/rigor/plugin/io_boundary.rbs +2 -1
  410. data/sig/rigor/plugin/manifest.rbs +9 -9
  411. data/sig/rigor/plugin/protocol_contract.rbs +3 -7
  412. data/sig/rigor/scope.rbs +23 -3
  413. data/skills/rigor-plugin-author/SKILL.md +4 -4
  414. data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
  415. data/skills/rigor-plugin-review/SKILL.md +3 -3
  416. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
  417. metadata +36 -3
@@ -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,26 @@ 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
+ SCHEMA = 47
199
206
 
200
207
  # The persisted per-file state.
201
208
  # `cache` maps an analyzed file to its diagnostics.
202
- # `sources` maps a consumer to the Set of source files it read from.
209
+ # `sources` maps a consumer to the source files it read from.
203
210
  # `digests` maps a file to its content digest at analysis time.
204
211
  # `analyzed` is the ordered analyzed-file list.
205
212
  # 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).
213
+ # `symbol_sources` maps a consumer to { source_path → the "ClassName#method" names it read }.
214
+ # `ancestry_sources` maps a consumer to the source paths it read for class ancestry.
208
215
  # `symbol_fingerprints` maps a path to { "ClassName#method" => sha256_hex }.
209
216
  # 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.
217
+ # `missing` maps a consumer to the "kind:name" keys it looked up and missed.
218
+ # `class_decls` maps a path to the qualified class names it declares.
212
219
  # Issue #644:
213
220
  # `constant_decls` maps a path to that file's constant PUBLICATION CENSUS,
214
221
  # `{qualified name => [literal] | :unpublishable}`. The descriptor is what the recheck diffs: a name's
@@ -279,12 +286,14 @@ module Rigor
279
286
  end
280
287
  end
281
288
 
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)
289
+ # Issues #796 / #794 — the run-level rows the snapshot carries: one member per run-level fact declared
290
+ # `replay` ({Analysis::Runner::RunLevelFacts}, ADR-116 WD3). Today that is `definition_build_failures`,
291
+ # the reported per-class detail list ({Environment::RbsLoader#definition_build_failures}), and
292
+ # `hkt_scan_failure`, the `[error_class, first_line, frame, stage]` tuple
293
+ # ({Rigor::Environment#hkt_scan_failure}), nil when the scan succeeded — which is why the presence of
294
+ # this object, not the tuple, is what says a replay is available. Declaring another fact `replay`
295
+ # changes this shape, so it bumps {SCHEMA}.
296
+ RunLevelRows = Data.define(*Analysis::Runner::RunLevelFacts.replayable_names)
288
297
 
289
298
  # The global fingerprint that gates a snapshot load: a digest of the inputs whose change requires a full
290
299
  # rebuild — the engine version + schema, the engine's own SOURCE when the version does not pin it, the
@@ -324,6 +333,7 @@ module Rigor
324
333
  "sig:#{digest_signature_paths(project_sig_roots(configuration))}",
325
334
  "pre_eval:#{digest_pre_eval(configuration.pre_eval)}"
326
335
  ]
336
+ parts.concat(core_root_parts(configuration))
327
337
  parts.concat(dependency_source_parts(configuration))
328
338
  identity = EngineSource.process_identity
329
339
  parts << "engine-source:#{identity}" if identity
@@ -347,6 +357,15 @@ module Rigor
347
357
  nil
348
358
  end
349
359
 
360
+ # Issue #2517 — a selected core is read like the project's own `sig/`, so an edit in place to one of its files
361
+ # must drop the snapshot (and the language server's session, which keys on this fingerprint). No part for the
362
+ # default core, so every other project's fingerprint is as it was.
363
+ def self.core_root_parts(configuration)
364
+ core_root = configuration.core_root
365
+ core_root.nil? ? [] : ["core:#{digest_signature_paths([core_root])}"]
366
+ end
367
+ private_class_method :core_root_parts
368
+
350
369
  def self.required_features(configuration, roots)
351
370
  files = Analysis::PathExpansion.ruby_files(roots, configuration.exclude_patterns,
352
371
  base: configuration.exclude_base)
@@ -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,57 @@ 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)].compact
81
81
  end
82
82
 
83
+ # Issue #2517 — which directory the env loaded core from. The selected core's files are digested by
84
+ # {.file_entries} too, so this row is the identity of the choice and those rows its contents: switching
85
+ # between two roots with byte-identical files still re-keys. Absent for the default core, whose identity
86
+ # is the `rbs` gem row, so every project without the setting keeps its key.
87
+ def self.core_root_entry(loader)
88
+ core_root = selected_core_root(loader)
89
+ return nil if core_root.nil?
90
+
91
+ Descriptor::ConfigEntry.new(key: "rbs.core_root", value_hash: Digest::SHA256.hexdigest(core_root.to_s))
92
+ end
93
+
94
+ private_class_method :core_root_entry
95
+
96
+ # The loader's issue #2517 core root as a Pathname, or nil for the default core.
97
+ def self.selected_core_root(loader)
98
+ core_root = loader.respond_to?(:core_root) ? loader.core_root : nil
99
+ core_root.nil? ? nil : Pathname(core_root)
100
+ end
101
+
102
+ private_class_method :selected_core_root
103
+
104
+ # {.selected_core_root} as the signature roots it adds: none for the default core.
105
+ def self.core_root_dirs(loader)
106
+ core_root = selected_core_root(loader)
107
+ core_root.nil? ? [] : [core_root]
108
+ end
109
+
110
+ private_class_method :core_root_dirs
111
+
112
+ # Issue #2422 — which of the loader's `signature_paths:` are the project's own changes the env built from
113
+ # byte-identical files: a bundled plugin's method composes with another source's as an overload, but stands
114
+ # down to the project's own. Only read when a plugin defers signatures, so a project without one keeps its
115
+ # key.
116
+ def self.project_signature_paths_entry(loader)
117
+ return nil unless loader.respond_to?(:project_signature_paths) && loader.respond_to?(:deferred_signature_paths)
118
+ return nil if Array(loader.deferred_signature_paths).empty?
119
+
120
+ own = Array(loader.project_signature_paths).map(&:to_s).sort
121
+ return nil if own.empty?
122
+
123
+ Descriptor::ConfigEntry.new(
124
+ key: "rbs.project_signature_paths",
125
+ value_hash: Digest::SHA256.hexdigest(own.join("\n"))
126
+ )
127
+ end
128
+
129
+ private_class_method :project_signature_paths_entry
130
+
83
131
  def self.deferred_signature_paths_entry(loader)
84
132
  return nil unless loader.respond_to?(:deferred_signature_paths)
85
133
 
@@ -104,7 +152,9 @@ module Rigor
104
152
  # value must be deterministic; `:stat` (ADR-87 WD1) for the validation-only run-dependency descriptor
105
153
  # ({RunDescriptor#files}), where the stat tier short-circuits the SHA-256 on an unmoved file.
106
154
  def self.file_entries(loader, comparator: :digest)
107
- roots = loader.signature_paths +
155
+ # Issue #2517 — a selected core is project input like `signature_paths:`, not the engine-versioned `rbs`
156
+ # gem tree, so an edit in place must move the key.
157
+ roots = core_root_dirs(loader) + loader.signature_paths +
108
158
  Rigor::Environment::RbsLoader.vendored_gem_sig_paths +
109
159
  Rigor::Environment::RbsLoader.core_overlay_sig_paths +
110
160
  Rigor::Environment::RbsLoader.capability_role_sig_paths
@@ -149,7 +199,8 @@ module Rigor
149
199
  def self.glob_entries(loader)
150
200
  # Deduplicated by path: a root reachable twice (an explicit `signature_paths:` entry that is also a
151
201
  # 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|
202
+ # Issue #2517 — and the selected core's root, where a file appearing is as much a new input.
203
+ (core_root_dirs(loader) + loader.signature_paths).uniq(&:to_s).filter_map do |root|
153
204
  next unless root.directory?
154
205
 
155
206
  Descriptor::GlobEntry.compute(root: root.to_s, pattern: File.join("**", "*.rbs"), mode: :names)
@@ -33,7 +33,10 @@ 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
37
40
  )
38
41
  end
39
42
 
@@ -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
@@ -16,6 +16,7 @@ require_relative "../inference/statement_evaluator"
16
16
  require_relative "prism_colorizer"
17
17
  require_relative "command"
18
18
  require_relative "../project_environment"
19
+ require_relative "json_document"
19
20
 
20
21
  module Rigor
21
22
  class CLI
@@ -32,10 +33,12 @@ module Rigor
32
33
  class AnnotateCommand < Command
33
34
  USAGE = "Usage: rigor annotate [options] FILE"
34
35
 
35
- # Trailing `#=> …` annotation comment. Matched and stripped before re-annotating so re-running is idempotent —
36
- # this follows xmpfilter's convention of owning the `#=>` marker, and also absorbs the older `#=> dump_type:
37
- # <type>` spelling (idempotency across re-runs). The leading `\s` requirement keeps a `#=>` inside a string
38
- # literal (no preceding whitespace ambiguity aside) from matching mid-expression.
36
+ # Trailing `#=> …` annotation comment, matched and stripped before re-annotating so re-running is idempotent.
37
+ # Follows xmpfilter's convention of owning the `#=>` marker, and also absorbs the older `#=> dump_type: <type>`
38
+ # spelling.
39
+ # Strips the first `#=>` that is preceded by whitespace and followed by whitespace or the end of the line,
40
+ # together with that preceding whitespace, through the end of the line. A string literal is not special-cased,
41
+ # since the pattern does not parse Ruby; `a#=>b` and `x #=>y` are left alone.
39
42
  ANNOTATION_PATTERN = /\s+#=>(?:\s.*)?\z/
40
43
 
41
44
  # Arguments for highlighting through `bat`: the annotated text arrives on stdin, so the language must be explicit;
@@ -59,19 +62,13 @@ module Rigor
59
62
  execute(file, options)
60
63
  end
61
64
 
62
- private
63
-
64
- def parse_options
65
- # Default: colour a tty, unless `NO_COLOR` opts out. An explicit `--color` / `--no-color` overrides both.
66
- options = { config: nil, color: @out.tty? && !no_color_env?, bat: nil, format: :text }
67
-
68
- parser = OptionParser.new do |opts|
65
+ # The parser this command parses with, built without parsing anything: the catalogue reads the
66
+ # flags and formats from it (ADR-126 WD3). `options` is only written by the switches' blocks.
67
+ def option_parser(options = {})
68
+ OptionParser.new do |opts|
69
69
  opts.banner = USAGE
70
70
  Options.add_config(opts, options)
71
- opts.on("--format=FORMAT", %w[text json],
72
- "Output format: text (default) or json (a { line => type } map)") do |value|
73
- options[:format] = value.to_sym
74
- end
71
+ Options.add_format(opts, options, "Output format: text (default) or json (a { line => type } map)")
75
72
  opts.on("--[no-]color",
76
73
  "Force or disable ANSI colour (default: auto-detect a tty; honours NO_COLOR)") do |value|
77
74
  options[:color] = value
@@ -81,7 +78,15 @@ module Rigor
81
78
  options[:bat] = value
82
79
  end
83
80
  end
84
- parser.parse!(@argv)
81
+ end
82
+
83
+ private
84
+
85
+ def parse_options
86
+ # Default: colour a tty, unless `NO_COLOR` opts out. An explicit `--color` / `--no-color` overrides both.
87
+ options = { config: nil, color: @out.tty? && !no_color_env?, bat: nil, format: "text" }
88
+
89
+ option_parser(options).parse!(@argv)
85
90
 
86
91
  options
87
92
  end
@@ -118,7 +123,7 @@ module Rigor
118
123
  line_types = LineTypeCollector.new(scope_index).collect(parse_result.value)
119
124
 
120
125
  case options.fetch(:format)
121
- when :json
126
+ when "json"
122
127
  emit_json(line_types)
123
128
  else
124
129
  @out.puts(render(annotate(source, line_types), color: options.fetch(:color), bat: options.fetch(:bat)))
@@ -132,7 +137,7 @@ module Rigor
132
137
  # object keys).
133
138
  def emit_json(line_types)
134
139
  annotations = line_types.keys.sort.to_h { |line| [line.to_s, line_types[line].describe(:short)] }
135
- @out.puts(JSON.generate({ "annotations" => annotations }))
140
+ @out.puts(JSON.generate(JsonDocument.stamp({ "annotations" => annotations })))
136
141
  end
137
142
 
138
143
  # The plugin-aware environment for the annotated file, so a `#=> <type>` matches what `rigor check`
@@ -225,7 +230,7 @@ module Rigor
225
230
  @scope_index = scope_index
226
231
  end
227
232
 
228
- # @return 1-indexed line => type.
233
+ # @return map of 1-indexed line number to type.
229
234
  def collect(program)
230
235
  by_line = {}
231
236
  each_statement(program) do |statement|