rigortype 0.3.8 → 0.3.9

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 (447) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
  5. data/docs/handbook/02-everyday-types.md +17 -14
  6. data/docs/handbook/03-narrowing.md +37 -4
  7. data/docs/handbook/05-methods-and-blocks.md +1 -1
  8. data/docs/handbook/06-classes.md +2 -2
  9. data/docs/handbook/07-rbs-and-extended.md +14 -1
  10. data/docs/handbook/11-sig-gen.md +72 -1
  11. data/docs/handbook/README.md +1 -1
  12. data/docs/handbook/appendix-elixir.md +2 -2
  13. data/docs/handbook/appendix-go.md +2 -2
  14. data/docs/handbook/appendix-java-csharp.md +2 -2
  15. data/docs/handbook/appendix-mypy.md +2 -2
  16. data/docs/handbook/appendix-phpstan.md +1 -1
  17. data/docs/handbook/appendix-rust.md +1 -1
  18. data/docs/handbook/appendix-type-theory.md +4 -4
  19. data/docs/handbook/appendix-typescript.md +1 -1
  20. data/docs/llms.txt +2 -0
  21. data/docs/manual/02-cli-reference.md +32 -4
  22. data/docs/manual/03-configuration.md +11 -0
  23. data/docs/manual/04-diagnostics.md +11 -0
  24. data/docs/manual/07-plugins.md +15 -5
  25. data/docs/manual/08-skills.md +23 -2
  26. data/docs/manual/11-ci.md +9 -0
  27. data/docs/manual/15-type-protection-coverage.md +8 -0
  28. data/docs/manual/16-rbs-extended-annotations.md +6 -1
  29. data/docs/manual/18-removing-dead-code.md +10 -8
  30. data/docs/manual/plugins/rigor-actionpack.md +18 -0
  31. data/docs/manual/plugins/rigor-activerecord.md +28 -0
  32. data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
  33. data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
  34. data/lib/rigor/analysis/baseline.rb +2 -2
  35. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  36. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  37. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  38. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  40. data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
  41. data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
  42. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  43. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  44. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  45. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  46. data/lib/rigor/analysis/check_rules.rb +200 -32
  47. data/lib/rigor/analysis/crash_signature.rb +8 -24
  48. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  49. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  50. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  51. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  52. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  53. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  54. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  55. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  56. data/lib/rigor/analysis/fact_store.rb +6 -1
  57. data/lib/rigor/analysis/incremental.rb +16 -0
  58. data/lib/rigor/analysis/incremental_session.rb +81 -22
  59. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  60. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  61. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
  62. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  63. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  64. data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
  65. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  66. data/lib/rigor/analysis/result.rb +1 -3
  67. data/lib/rigor/analysis/rule_catalog.rb +4 -1
  68. data/lib/rigor/analysis/run_cache_key.rb +1 -1
  69. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  70. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  71. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  72. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +177 -26
  73. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  74. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
  75. data/lib/rigor/analysis/runner/pool_coordinator.rb +256 -88
  76. data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
  77. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  78. data/lib/rigor/analysis/runner.rb +133 -35
  79. data/lib/rigor/analysis/worker_session.rb +9 -10
  80. data/lib/rigor/bleeding_edge.rb +12 -24
  81. data/lib/rigor/builtins/hkt_builtins.rb +2 -2
  82. data/lib/rigor/builtins/imported_refinements.rb +155 -21
  83. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  84. data/lib/rigor/builtins/regex_refinement.rb +4 -4
  85. data/lib/rigor/builtins/static_return_refinements.rb +5 -6
  86. data/lib/rigor/cache/annotation_location.rb +2 -4
  87. data/lib/rigor/cache/descriptor.rb +58 -20
  88. data/lib/rigor/cache/engine_source.rb +4 -4
  89. data/lib/rigor/cache/file_digest.rb +9 -1
  90. data/lib/rigor/cache/incremental_snapshot.rb +40 -6
  91. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  92. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  93. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  94. data/lib/rigor/cache/rbs_descriptor.rb +79 -11
  95. data/lib/rigor/cache/rbs_environment.rb +8 -4
  96. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  97. data/lib/rigor/cache/store.rb +77 -32
  98. data/lib/rigor/cli/annotate_command.rb +5 -6
  99. data/lib/rigor/cli/check_command.rb +57 -10
  100. data/lib/rigor/cli/check_invocation.rb +6 -11
  101. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  102. data/lib/rigor/cli/coverage_command.rb +1 -1
  103. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  104. data/lib/rigor/cli/coverage_scan.rb +1 -5
  105. data/lib/rigor/cli/diff_command.rb +1 -1
  106. data/lib/rigor/cli/doc_links.rb +3 -3
  107. data/lib/rigor/cli/docs_command.rb +2 -2
  108. data/lib/rigor/cli/doctor_command.rb +85 -38
  109. data/lib/rigor/cli/effects_command.rb +2 -2
  110. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  111. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  112. data/lib/rigor/cli/explain_command.rb +34 -1
  113. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  114. data/lib/rigor/cli/lsp_command.rb +1 -1
  115. data/lib/rigor/cli/mcp_command.rb +1 -1
  116. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  117. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  118. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  119. data/lib/rigor/cli/plugin_command.rb +1 -1
  120. data/lib/rigor/cli/plugins_command.rb +2 -1
  121. data/lib/rigor/cli/prism_colorizer.rb +2 -2
  122. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  123. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  124. data/lib/rigor/cli/sig_gen_command.rb +2 -2
  125. data/lib/rigor/cli/skill_command.rb +1 -1
  126. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  127. data/lib/rigor/cli/skill_describe.rb +36 -35
  128. data/lib/rigor/cli/trace_command.rb +4 -4
  129. data/lib/rigor/cli/trace_renderer.rb +5 -6
  130. data/lib/rigor/cli/triage_command.rb +1 -1
  131. data/lib/rigor/cli/type_of_command.rb +10 -6
  132. data/lib/rigor/cli/type_scan_command.rb +4 -4
  133. data/lib/rigor/cli/unused_command.rb +11 -3
  134. data/lib/rigor/cli/upgrade_command.rb +1 -1
  135. data/lib/rigor/cli.rb +68 -6
  136. data/lib/rigor/config_audit.rb +30 -5
  137. data/lib/rigor/configuration/severity_profile.rb +6 -6
  138. data/lib/rigor/configuration.rb +43 -4
  139. data/lib/rigor/effects/attribution.rb +1 -3
  140. data/lib/rigor/effects/config_envelopes.rb +6 -8
  141. data/lib/rigor/effects/definition_lines.rb +25 -6
  142. data/lib/rigor/effects/effect_table.rb +0 -1
  143. data/lib/rigor/effects/entry_points.rb +0 -2
  144. data/lib/rigor/effects/envelope_check.rb +8 -8
  145. data/lib/rigor/effects/envelope_index.rb +5 -8
  146. data/lib/rigor/effects/framework_units.rb +4 -6
  147. data/lib/rigor/effects/identity.rb +4 -6
  148. data/lib/rigor/effects/inline_anchor.rb +7 -7
  149. data/lib/rigor/effects/label_intent.rb +3 -4
  150. data/lib/rigor/effects/liskov_check.rb +8 -8
  151. data/lib/rigor/effects/method_key.rb +1 -1
  152. data/lib/rigor/effects/plugin_facts.rb +4 -6
  153. data/lib/rigor/effects/propagator.rb +2 -3
  154. data/lib/rigor/effects/scanner.rb +1 -1
  155. data/lib/rigor/effects/signature_sources.rb +3 -5
  156. data/lib/rigor/effects/snapshot.rb +10 -10
  157. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  158. data/lib/rigor/effects/unit_scan.rb +10 -10
  159. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  160. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  161. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  162. data/lib/rigor/environment/class_registry.rb +3 -1
  163. data/lib/rigor/environment/failure_slot.rb +2 -2
  164. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  165. data/lib/rigor/environment/lockfile_resolver.rb +51 -4
  166. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  167. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  168. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  169. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  170. data/lib/rigor/environment/rbs_loader.rb +388 -59
  171. data/lib/rigor/environment.rb +105 -20
  172. data/lib/rigor/flow_contribution/merger.rb +0 -2
  173. data/lib/rigor/flow_contribution.rb +11 -13
  174. data/lib/rigor/inference/acceptance.rb +153 -8
  175. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  176. data/lib/rigor/inference/body_fixpoint.rb +5 -5
  177. data/lib/rigor/inference/budget_trace.rb +1 -5
  178. data/lib/rigor/inference/captured_locals.rb +2 -3
  179. data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
  180. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  181. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  182. data/lib/rigor/inference/element_read_widening.rb +168 -0
  183. data/lib/rigor/inference/expression_typer.rb +763 -143
  184. data/lib/rigor/inference/fork_map.rb +5 -7
  185. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  186. data/lib/rigor/inference/hkt_registry.rb +4 -7
  187. data/lib/rigor/inference/index_write_widening.rb +1 -5
  188. data/lib/rigor/inference/macro_block_self_type.rb +1 -4
  189. data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
  190. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  191. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  192. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  193. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  194. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
  195. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  196. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  197. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  198. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  199. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
  200. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  201. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
  202. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  203. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  204. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  205. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  206. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
  207. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  208. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  209. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
  210. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  211. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  212. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  213. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  214. data/lib/rigor/inference/method_dispatcher.rb +12 -13
  215. data/lib/rigor/inference/method_parameter_binder.rb +4 -6
  216. data/lib/rigor/inference/multi_target_binder.rb +1 -3
  217. data/lib/rigor/inference/mutation_rejoin.rb +168 -0
  218. data/lib/rigor/inference/mutation_widening.rb +100 -51
  219. data/lib/rigor/inference/narrowing.rb +299 -46
  220. data/lib/rigor/inference/optimistic_origin.rb +1 -7
  221. data/lib/rigor/inference/origin_lookup.rb +2 -3
  222. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  223. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  224. data/lib/rigor/inference/precision_scanner.rb +3 -4
  225. data/lib/rigor/inference/project_patched_methods.rb +2 -2
  226. data/lib/rigor/inference/project_patched_scanner.rb +3 -3
  227. data/lib/rigor/inference/protection_scanner.rb +1 -2
  228. data/lib/rigor/inference/range_constant.rb +57 -0
  229. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  230. data/lib/rigor/inference/receiver_alias.rb +3 -3
  231. data/lib/rigor/inference/refinement_mutation.rb +72 -0
  232. data/lib/rigor/inference/scope_indexer.rb +688 -151
  233. data/lib/rigor/inference/statement_evaluator.rb +221 -50
  234. data/lib/rigor/inference/string_mutation.rb +60 -0
  235. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  236. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  237. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  238. data/lib/rigor/inference/version_guard.rb +28 -21
  239. data/lib/rigor/inference/void_origin.rb +3 -3
  240. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  241. data/lib/rigor/language_server/buffer_table.rb +4 -4
  242. data/lib/rigor/language_server/completion_provider.rb +1 -1
  243. data/lib/rigor/language_server/debouncer.rb +1 -1
  244. data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
  245. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  246. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  247. data/lib/rigor/language_server/hover_provider.rb +1 -1
  248. data/lib/rigor/language_server/hover_renderer.rb +2 -2
  249. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  250. data/lib/rigor/language_server/project_context.rb +2 -5
  251. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  252. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  253. data/lib/rigor/language_server/server.rb +9 -9
  254. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  255. data/lib/rigor/language_server/uri.rb +1 -1
  256. data/lib/rigor/plugin/base.rb +90 -21
  257. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  258. data/lib/rigor/plugin/effect_attribution.rb +7 -7
  259. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  260. data/lib/rigor/plugin/fact_store.rb +6 -6
  261. data/lib/rigor/plugin/io_boundary.rb +93 -8
  262. data/lib/rigor/plugin/isolation.rb +29 -6
  263. data/lib/rigor/plugin/loader.rb +31 -12
  264. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  265. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  266. data/lib/rigor/plugin/manifest.rb +23 -1
  267. data/lib/rigor/plugin/registry.rb +42 -12
  268. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
  269. data/lib/rigor/plugin/trust_policy.rb +2 -4
  270. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  271. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  272. data/lib/rigor/project_environment.rb +138 -0
  273. data/lib/rigor/protection/analysis_guard.rb +93 -14
  274. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  275. data/lib/rigor/protection/dependency_closure.rb +5 -8
  276. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  277. data/lib/rigor/protection/discovery_seed.rb +11 -11
  278. data/lib/rigor/protection/kill_signature.rb +2 -4
  279. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  280. data/lib/rigor/protection/mutation_cache.rb +6 -9
  281. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  282. data/lib/rigor/protection/mutator.rb +2 -1
  283. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  284. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  285. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  286. data/lib/rigor/rbs_extended/reporter.rb +36 -10
  287. data/lib/rigor/rbs_extended.rb +61 -21
  288. data/lib/rigor/reflection/constant_path.rb +126 -0
  289. data/lib/rigor/reflection.rb +26 -10
  290. data/lib/rigor/runtime/jit.rb +6 -8
  291. data/lib/rigor/scope/discovery_index.rb +30 -0
  292. data/lib/rigor/scope.rb +203 -4
  293. data/lib/rigor/sig_gen/generator.rb +115 -22
  294. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  295. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  296. data/lib/rigor/sig_gen/observation_collector.rb +17 -15
  297. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  298. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  299. data/lib/rigor/sig_gen/renderer.rb +4 -5
  300. data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
  301. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  302. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  303. data/lib/rigor/sig_gen/writer.rb +23 -6
  304. data/lib/rigor/signature_path_audit.rb +153 -6
  305. data/lib/rigor/source/literals.rb +0 -23
  306. data/lib/rigor/source/node_children.rb +0 -2
  307. data/lib/rigor/source/node_locator.rb +5 -11
  308. data/lib/rigor/source/node_walker.rb +2 -5
  309. data/lib/rigor/triage/catalogue.rb +1 -3
  310. data/lib/rigor/triage.rb +3 -5
  311. data/lib/rigor/type/accepts_result.rb +24 -6
  312. data/lib/rigor/type/combinator.rb +68 -5
  313. data/lib/rigor/type/data_class.rb +2 -2
  314. data/lib/rigor/type/data_instance.rb +4 -4
  315. data/lib/rigor/type/float_range.rb +128 -0
  316. data/lib/rigor/type/hash_shape.rb +5 -5
  317. data/lib/rigor/type/integer_range.rb +13 -8
  318. data/lib/rigor/type/nominal.rb +8 -2
  319. data/lib/rigor/type/refined.rb +3 -3
  320. data/lib/rigor/type/struct_class.rb +3 -3
  321. data/lib/rigor/type/struct_instance.rb +4 -4
  322. data/lib/rigor/type.rb +1 -0
  323. data/lib/rigor/type_node/generic.rb +1 -1
  324. data/lib/rigor/type_node/range_literal.rb +25 -0
  325. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  326. data/lib/rigor/type_node.rb +1 -0
  327. data/lib/rigor/version.rb +1 -1
  328. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  329. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  330. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  331. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
  332. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  333. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  334. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  335. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
  336. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
  337. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  338. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  339. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
  340. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  341. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  342. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  343. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  344. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  345. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  346. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
  347. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
  348. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  349. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  350. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
  351. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  352. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
  353. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  354. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
  355. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  356. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
  357. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  358. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  359. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  360. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  361. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  362. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  363. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  364. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  365. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  366. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  367. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  368. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  369. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  370. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  371. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  372. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
  373. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  374. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  375. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
  376. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  377. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  378. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  379. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  380. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  381. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
  382. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  383. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  384. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  385. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
  386. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  387. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
  388. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
  389. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
  390. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  391. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  392. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  393. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  394. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
  395. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  396. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  397. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
  398. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  399. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  400. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  401. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  402. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  403. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  404. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  405. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  406. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  407. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  408. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  409. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  410. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
  411. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  412. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  413. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
  414. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  415. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  416. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  417. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  418. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  419. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  420. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
  421. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  422. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
  423. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  424. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  425. data/sig/rigor/cache.rbs +2 -3
  426. data/sig/rigor/environment.rbs +5 -0
  427. data/sig/rigor/inference.rbs +3 -10
  428. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  429. data/sig/rigor/plugin/manifest.rbs +2 -1
  430. data/sig/rigor/reflection.rbs +3 -1
  431. data/sig/rigor/scope.rbs +24 -3
  432. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
  433. data/sig/rigor/type.rbs +27 -1
  434. data/sig/rigor.rbs +1 -0
  435. data/skills/rigor-ask/SKILL.md +8 -6
  436. data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
  437. data/skills/rigor-project-init/SKILL.md +6 -3
  438. data/skills/rigor-project-init/references/02-configure.md +10 -6
  439. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  440. data/skills/rigor-type-oracle/SKILL.md +228 -0
  441. data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
  442. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  443. data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
  444. data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
  445. metadata +26 -3
  446. data/lib/rigor/cli/probe_environment.rb +0 -85
  447. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -3,6 +3,7 @@
3
3
  require "tmpdir"
4
4
 
5
5
  require_relative "../../environment"
6
+ require_relative "../../project_environment"
6
7
  require_relative "../diagnostic"
7
8
  require_relative "../worker_session"
8
9
  require_relative "../run_stats"
@@ -29,30 +30,21 @@ module Rigor
29
30
  # delegated back through an injected `analyze_file` callable so the CheckRules / recorder /
30
31
  # plugin-emission machinery stays on the {Runner}.
31
32
  class PoolCoordinator # rubocop:disable Metrics/ClassLength
32
- # @param configuration [Rigor::Configuration]
33
- # @param cache_store [Rigor::Cache::Store, nil]
34
- # @param explain [Boolean]
35
- # @param workers [Integer]
36
- # @param collect_stats [Boolean]
37
- # @param buffer [BufferBinding, nil]
38
- # @param environment_override [Rigor::Environment, nil]
39
- # @param rbs_extended_reporter [RbsExtended::Reporter]
40
- # @param boundary_cross_reporter [DependencySourceInference::BoundaryCrossReporter]
41
- # @param source_rbs_synthesis_reporter [Plugin::SourceRbsSynthesisReporter]
42
- # @param snapshots [RunSnapshots] shared end-of-pass snapshot sink.
43
- # @param plugin_registry [#call] reader for the current registry.
44
- # @param dependency_source_index [#call] reader.
45
- # @param synthetic_method_index [#call] reader.
46
- # @param project_patched_methods [#call] reader.
47
- # @param project_scope_seed [#call] reader for the cross-file pre-pass seed tables
33
+ # @param snapshots — shared end-of-pass snapshot sink.
34
+ # @param plugin_registry reader for the current registry.
35
+ # @param dependency_source_index — reader.
36
+ # @param synthetic_method_index — reader.
37
+ # @param project_patched_methods — reader.
38
+ # @param project_scope_seed reader for the cross-file pre-pass seed tables
48
39
  # (`Runner#project_scope_seed_tables`).
49
- # @param analyze_file [#call] `(path, environment) -> diagnostics`.
40
+ # @param analyze_file `(path, environment) -> diagnostics`.
50
41
  def initialize(configuration:, cache_store:, explain:, workers:, collect_stats:, # rubocop:disable Metrics/ParameterLists
51
42
  buffer:, environment_override:, rbs_extended_reporter:,
52
43
  boundary_cross_reporter:, source_rbs_synthesis_reporter:,
53
44
  snapshots:, plugin_registry:, dependency_source_index:,
54
45
  synthetic_method_index:, project_patched_methods:,
55
- analyze_file:, project_scope_seed: -> { {} }, record_dependencies: false)
46
+ analyze_file:, project_scope_seed: -> { {} }, record_dependencies: false,
47
+ restored_run_level_rows: nil)
56
48
  @configuration = configuration
57
49
  @cache_store = cache_store
58
50
  @explain = explain
@@ -80,6 +72,10 @@ module Rigor
80
72
  @project_patched_methods_reader = project_patched_methods
81
73
  @project_scope_seed_reader = project_scope_seed
82
74
  @analyze_file = analyze_file
75
+ # Issues #796 / #794 — the previous full run's run-level rows, handed down by
76
+ # {Analysis::IncrementalSession} on a NARROWED run only (a recheck's closure, a
77
+ # `--verify-incremental` partition). A full run leaves this nil and derives everything itself.
78
+ @restored_run_level_rows = restored_run_level_rows
83
79
  end
84
80
 
85
81
  # ADR-46 — the per-file cross-file read records the fork pool captured this run (empty unless
@@ -115,47 +111,12 @@ module Rigor
115
111
  # sources. The env stays a LOCAL variable (not an ivar) so it goes GC-eligible when the method
116
112
  # returns — holding it as long-lived state added memory pressure that surfaced as a Bus Error
117
113
  # during the spec suite under Ruby 4.0 + rbs 4.0.2.
118
- # @param project_files [Array<String>, nil] issue #784 — the WHOLE project's analyzed file set
119
- # (`expansion.fetch(:files)`), independent of any `analyze_only` narrowing of `files`. Read only
120
- # when `files` is empty, to decide whether anyone could have demanded the HKT registry at all.
114
+ # @param project_files issue #784 — the WHOLE project's analyzed file set
115
+ # (`expansion.fetch(:files)`), independent of any `analyze_only` narrowing of `files`; see
116
+ # {#serve_empty_closure} for what an empty `files` does with it.
121
117
  def analyze_files(files, environment: nil, project_files: nil)
122
118
  if files.empty?
123
- # Issue #784 — an EMPTY analyze set still owes the run its HKT-scan row: the per-file cache never
124
- # holds it (`IncrementalSession` caches only `Runner#per_file_diagnostics`, so every run-level row
125
- # is regenerated every run), so returning here without recording flips a red project green — and
126
- # the shipping `--incremental` path reaches this branch with NO environment in hand on every
127
- # warm recheck that changed nothing (`CheckCommand#run_incremental_check` builds its session
128
- # without one). So: an environment already in hand is consulted; otherwise one is resolved over
129
- # the project's OWN file list — not `[]`, which would drop every plugin-synthesized virtual RBS
130
- # (`Environment.collect_virtual_rbs` short-circuits on an empty list) and scan a different type
131
- # universe from the one a full run analyses — and only when the project HAS files: with none,
132
- # nobody could have demanded a registry, and an empty project keeps paying no env build. Keyed
133
- # on the project's files rather than on `analyze_only`, because a recheck over an EMPTY project
134
- # narrows to `Set[]`, which is non-nil.
135
- env = environment || @environment_override
136
- if project_files && !project_files.empty?
137
- env ||= resolve_sequential_environment(source_files: project_files)
138
- end
139
- # #788 rounds 6 and 9 — everything the run owes from its environment that no per-file analysis
140
- # produces is taken here, the way `analyze_files_sequentially` takes it: the project-signature
141
- # state (`synthesized-namespace`, `quarantined-signature`, `environment-build-failed`, the
142
- # conformance results), the effect-annotation carrier the residual pass reads, and the HKT-scan
143
- # outcome. Now that run-level rows are never served from the per-file cache, this branch is the
144
- # only producer on a warm recheck that changed nothing — leaving any of them out turned a red
145
- # project green on its second `--incremental` run (the inline-only `effect.annotations-unchecked`
146
- # went 1 → 0; a quarantined `signature_paths:` file went 1 → 0; a `conforms-to` class whose
147
- # definition build fails went 1 → 0). Same ORDER as the sequential path, because the order is
148
- # the contract: the conformance scan inside the signature-state snapshot demands the definition
149
- # of every `rigor:v1:conforms-to` class — a user-authored, invocation-independent demand that
150
- # #696 counts — so the definition-build failures are read AFTER it and BEFORE the HKT demand,
151
- # exactly where `analyze_files_sequentially` reads them relative to its own. What this branch
152
- # cannot regenerate is the part of that set the per-file ANALYSIS demanded (#796). This also
153
- # retires the #441 "`.rbs` lane only when the run analyses nothing" boundary: its cost premise
154
- # (no environment on this path) stopped holding the moment the branch above resolved one.
155
- snapshot_project_signature_state(env)
156
- snapshot_effect_annotation_carrier(env&.rbs_loader)
157
- record_definition_build_failures(env&.rbs_loader&.definition_build_failures)
158
- record_hkt_scan_failure(hkt_scan_outcome(env))
119
+ serve_empty_closure(environment, project_files)
159
120
  return []
160
121
  end
161
122
  # Issue #784 / #793 — `files` is what this run ANALYSES; `source_files` is what its environment
@@ -166,11 +127,152 @@ module Rigor
166
127
  # whose trigger lived in an excluded file's synthesized RBS fired on the full run and vanished on
167
128
  # the subset. Every environment and worker this run builds now takes the whole project.
168
129
  source_files = project_files || files
169
- return dispatch_pool(files, source_files: source_files) if pool_mode?
130
+ result =
131
+ if pool_mode?
132
+ dispatch_pool(files, source_files: source_files)
133
+ else
134
+ analyze_files_sequentially(
135
+ files, environment || resolve_sequential_environment(source_files: source_files)
136
+ )
137
+ end
138
+ # Issues #796 / #794 — a narrowed run's own demand set is a SUBSET of the full run's, so the two
139
+ # rows below are folded in after it, whichever backend produced the per-file stream.
140
+ replayed_restored_run_level_rows?(closure: files)
141
+ result
142
+ end
170
143
 
171
- analyze_files_sequentially(
172
- files, environment || resolve_sequential_environment(source_files: source_files)
144
+ # Issue #784 — an EMPTY analyze set still owes the run its run-level rows: the per-file cache never
145
+ # holds them (`IncrementalSession` caches only `Runner#per_file_diagnostics`), so returning here
146
+ # without recording flips a red project green — and the shipping `--incremental` path reaches this
147
+ # branch with NO environment in hand on every warm recheck that changed nothing
148
+ # (`CheckCommand#run_incremental_check` builds its session without one).
149
+ #
150
+ # #788 rounds 6 and 9 — everything the run owes from its environment that no per-file analysis
151
+ # produces is taken here, the way `analyze_files_sequentially` takes it: the project-signature state
152
+ # (`synthesized-namespace`, `quarantined-signature`, `environment-build-failed`, the conformance
153
+ # results), the effect-annotation carrier the residual pass reads, and the HKT-scan outcome. Leaving
154
+ # any of them out turned a red project green on its second `--incremental` run. Same ORDER as the
155
+ # sequential path, because the order is the contract: the conformance scan inside the
156
+ # signature-state snapshot demands the definition of every `rigor:v1:conforms-to` class — a
157
+ # user-authored, invocation-independent demand that #696 counts — so the definition-build failures
158
+ # are read AFTER it and BEFORE the HKT outcome.
159
+ #
160
+ # Issue #794 — the environment is resolved LAZILY, by the two snapshots that need one and only when
161
+ # their own gates say so: a project with no `signature_paths:`, no plugin-deferred signatures and no
162
+ # source-RBS synthesizer now reaches the end of this branch without building an environment at all,
163
+ # where #788 built one unconditionally to make the HKT demand. Issue #796 — what this branch cannot
164
+ # derive is the part of the definition-build set the per-file ANALYSIS demanded, and #696 forbids a
165
+ # Rigor-owned demand from standing in for it; the previous full run's set is replayed instead.
166
+ # @param project_files — issue #784 — the WHOLE project's analyzed file set
167
+ # (`expansion.fetch(:files)`), independent of any `analyze_only` narrowing. Read here to decide
168
+ # whether anyone could have demanded anything at all: with no files, nobody could have, and an
169
+ # empty project keeps paying no env build. Keyed on the project's files rather than on
170
+ # `analyze_only`, because a recheck over an EMPTY project narrows to `Set[]`, which is non-nil.
171
+ def serve_empty_closure(environment, project_files)
172
+ env = environment || @environment_override
173
+ # Resolved over the project's OWN file list — not `[]`, which would drop every plugin-synthesized
174
+ # virtual RBS (`Environment.collect_virtual_rbs` short-circuits on an empty list) and scan a
175
+ # different type universe from the one a full run analyses.
176
+ resolve = lambda do
177
+ next env if env
178
+ next nil if project_files.nil? || project_files.empty?
179
+
180
+ env = resolve_sequential_environment(source_files: project_files)
181
+ end
182
+ snapshot_project_signature_state(signature_state_environment(resolve))
183
+ snapshot_effect_annotation_carrier(
184
+ annotation_carrier_environment(resolve, in_hand: !env.nil?)&.rbs_loader
173
185
  )
186
+ # `env` rather than `resolve.call`: read the demand the snapshots above just made when they built
187
+ # an environment, and force nothing when they did not.
188
+ record_definition_build_failures(env&.rbs_loader&.definition_build_failures)
189
+ return if replayed_restored_run_level_rows?
190
+
191
+ record_hkt_scan_failure(hkt_scan_outcome(resolve.call))
192
+ end
193
+
194
+ # Issue #794 — the signature-state snapshot's own gates, asked BEFORE an environment exists. Both
195
+ # answers are loader-free (the configuration and the plugin registry), and with neither gate open
196
+ # {#snapshot_project_signature_state} writes the same empty state a nil environment gives it.
197
+ def signature_state_environment(resolve)
198
+ plugin_signature_paths? || project_signature_paths? ? resolve.call : nil
199
+ end
200
+
201
+ # Issue #794 — the same, for the #441 carrier. The carrier is a filter over the loader's
202
+ # `virtual_rbs`, and an environment whose loader can hold none of it answers the same empty carrier
203
+ # a nil environment does. Two ways it can hold some: a plugin registry that declares a synthesizer
204
+ # (`Environment.collect_virtual_rbs` short-circuits to `[]` without one — and ADR-93 auto-wires
205
+ # `rigor-rbs-inline`, so this is the shape almost every project is in), or an environment ALREADY in
206
+ # hand, whose loader is free to read and may carry virtual buffers from a route this coordinator
207
+ # never saw (a restored env cache, a caller-supplied override). Only the third shape — no
208
+ # synthesizer AND no environment yet — declines, which is the #794 saving: nothing is built to
209
+ # discover an emptiness the registry already proves.
210
+ def annotation_carrier_environment(resolve, in_hand: false)
211
+ return nil if @record_effects
212
+ return resolve.call if in_hand
213
+
214
+ registry = plugin_registry
215
+ return nil unless registry.respond_to?(:source_rbs_synthesizers)
216
+ return nil if registry.source_rbs_synthesizers.empty?
217
+
218
+ resolve.call
219
+ end
220
+
221
+ # Issues #796 / #794 — fold the previous full run's run-level rows into this narrowed run's
222
+ # snapshots. The definition-build set is UNIONed (`#record_definition_build_failures` dedupes by
223
+ # class), so a class this run demanded for itself and the replayed set agree; the HKT outcome is a
224
+ # single tuple whoever demanded it, so `||=` keeps this run's own answer when it has one. Returns
225
+ # true when a replay was available, which is what tells {#serve_empty_closure} it owes no HKT demand
226
+ # of its own — and so no environment.
227
+ #
228
+ # @param closure — the files this run analyses; see {#stale_replayed_failure?}.
229
+ def replayed_restored_run_level_rows?(closure: nil)
230
+ rows = @restored_run_level_rows
231
+ return false if rows.nil?
232
+
233
+ replayable = Array(rows.definition_build_failures).reject do |failure|
234
+ stale_replayed_failure?(failure, closure)
235
+ end
236
+ record_definition_build_failures(replayable)
237
+ record_hkt_scan_failure(rows.hkt_scan_failure)
238
+ true
239
+ end
240
+
241
+ # A replayed definition-build failure the snapshot's own fingerprint cannot vouch for. The
242
+ # fingerprint digests the three signature sources it can read WITHOUT an environment — `sig:`,
243
+ # `Gemfile.lock` / `rbs_collection.lock.yaml`, and the configuration — but NOT the virtual RBS a
244
+ # source-RBS synthesizer derives from `.rb` files (under ADR-93's auto-wired `rigor-rbs-inline`,
245
+ # every `#:` annotation). So a duplicate declaration can be REMOVED from a `.rb` file without moving
246
+ # the fingerprint, and the replayed row would outlive its own cause: the edited file enters the
247
+ # closure, its analysis demands nothing that would re-derive the row, and the run reports a
248
+ # collision between files that no longer collide.
249
+ #
250
+ # The narrowed run cannot re-derive the row either (#696 forbids Rigor demanding a definition the
251
+ # analysis did not), so the honest answer is to DROP it and under-report rather than assert a
252
+ # diagnostic on a program that may now be correct — false positives outrank worst-case reading. The
253
+ # cost is bounded to the row's own conflicting buffers: a collision between two files neither of
254
+ # which this run touches still replays.
255
+ def stale_replayed_failure?(failure, closure)
256
+ return false if closure.nil?
257
+
258
+ buffers = failure.is_a?(Array) ? failure[3] : nil
259
+ return false if buffers.nil?
260
+
261
+ closure_paths = closure.to_set { |path| File.expand_path(path.to_s) }
262
+ Array(buffers).any? do |name|
263
+ path = virtual_buffer_path(name)
264
+ !path.nil? && closure_paths.include?(File.expand_path(path))
265
+ end
266
+ end
267
+
268
+ # The source file behind a `virtual:<plugin id>:<path>` buffer name
269
+ # (`Environment.collect_virtual_rbs`), or nil for any other buffer — a `sig:` file, a bundled `.rbs`
270
+ # — which the fingerprint does cover and which therefore never goes stale under a replay.
271
+ def virtual_buffer_path(name)
272
+ parts = name.to_s.split(":", 3)
273
+ return nil unless parts.length == 3 && parts.first == "virtual"
274
+
275
+ parts.last
174
276
  end
175
277
 
176
278
  def analyze_files_sequentially(files, environment)
@@ -206,6 +308,13 @@ module Rigor
206
308
  # `.rigor/cache` on a bare `--no-stats` run. The loader has already memoised each answer, so reading
207
309
  # them here is free.
208
310
  def snapshot_project_signature_state(environment)
311
+ # Issue #610 — NOT behind the `signature_paths:` gate below: the source a bundled plugin's `sig/`
312
+ # stands down against is typically an `rbs collection install` the configuration never lists
313
+ # there. Gated instead on a loaded plugin contributing signatures at all — the registry answers
314
+ # that without touching the env, and it is exactly the set the loader defers — so a project with
315
+ # no such plugin reads nothing off its loader here, as before.
316
+ @snapshots.signature_standdowns =
317
+ plugin_signature_paths? ? signature_standdowns_for(environment&.rbs_loader) : []
209
318
  unless project_signature_paths?
210
319
  @snapshots.synthesized_namespaces = []
211
320
  @snapshots.quarantined_signatures = []
@@ -279,7 +388,7 @@ module Rigor
279
388
  # An effects run (ADR-103 WD13) is pinned for exactly the same reason — the Ractor messages carry
280
389
  # no side-table channel — and degrades the same way. The degrade is sound rather than merely safe:
281
390
  # the sequential fallback still collects, so the effect graph is complete either way.
282
- # @param source_files [Array<String>] the file list every worker's / the fallback's environment is
391
+ # @param source_files the file list every worker's / the fallback's environment is
283
392
  # built over — the whole project (issue #793), defaulting to `files` for direct callers.
284
393
  def dispatch_pool(files, source_files: files)
285
394
  if @record_dependencies || @record_effects
@@ -318,11 +427,7 @@ module Rigor
318
427
  rbs_extended_reporter: @rbs_extended_reporter,
319
428
  boundary_cross_reporter: @boundary_cross_reporter,
320
429
  source_rbs_synthesis_reporter: @source_rbs_synthesis_reporter,
321
- bundler_bundle_path: @configuration.bundler_bundle_path,
322
- bundler_auto_detect: @configuration.bundler_auto_detect,
323
- bundler_lockfile: @configuration.bundler_lockfile,
324
- rbs_collection_lockfile: @configuration.rbs_collection_lockfile,
325
- rbs_collection_auto_detect: @configuration.rbs_collection_auto_detect,
430
+ **ProjectEnvironment.dependency_discovery_options(@configuration),
326
431
  synthetic_method_index: synthetic_method_index,
327
432
  project_patched_methods: project_patched_methods,
328
433
  source_files: source_files
@@ -389,15 +494,22 @@ module Rigor
389
494
  # finds. `#prewarm_rbs_cache_for_pool` already builds and fully loads exactly this
390
495
  # coordinator-side environment (to warm the cache before any worker spawns); it now hands it
391
496
  # back so its RBS state can be read the same way the fork pool reads its pre-fork session
392
- # environment. The conformance scan inside the snapshot demands the definition of every
393
- # `rigor:v1:conforms-to` class a demand no Ractor worker shares memory with — so a resulting
394
- # definition-build failure is recorded explicitly right after, exactly as the empty-closure
395
- # branch reads its own environment's demand (#788). No matching explicit call for the HKT-scan
396
- # outcome: every worker already demands it from its OWN environment in `#drain_reporters`, and
397
- # the scan has one outcome whoever demands it, so a worker's report already says what the
398
- # coordinator's own demand would.
399
- warm_env = prewarm_rbs_cache_for_pool
497
+ # environment. The effect-annotation carrier (#441 the inline stratum of
498
+ # `effect.annotations-unchecked`, read by the residual pass after this environment is gone) is
499
+ # taken off the same environment, in the same ORDER as the fork pool and the sequential path
500
+ # take theirs: it had no producer here either, so an annotation living only in an rbs-inline
501
+ # comment lost its row under this backend alone. That read is only as good as the environment's
502
+ # virtual RBS, which is why the prewarm is built the way the workers build theirs (see
503
+ # {#prewarm_rbs_cache_for_pool}). The conformance scan inside the signature-state snapshot
504
+ # demands the definition of every `rigor:v1:conforms-to` class — a demand no Ractor worker
505
+ # shares memory with — so a resulting definition-build failure is recorded explicitly right
506
+ # after, exactly as the empty-closure branch reads its own environment's demand (#788). No
507
+ # matching explicit call for the HKT-scan outcome: every worker already demands it from its OWN
508
+ # environment in `#drain_reporters`, and the scan has one outcome whoever demands it, so a
509
+ # worker's report already says what the coordinator's own demand would.
510
+ warm_env = prewarm_rbs_cache_for_pool(source_files: source_files)
400
511
  snapshot_project_signature_state(warm_env)
512
+ snapshot_effect_annotation_carrier(warm_env&.rbs_loader)
401
513
  record_definition_build_failures(warm_env&.rbs_loader&.definition_build_failures)
402
514
 
403
515
  configuration = @configuration
@@ -633,7 +745,7 @@ module Rigor
633
745
  degraded
634
746
  end
635
747
 
636
- # @return [Hash, nil] the child's `{results:, reporters:}` payload, or nil when the child exited
748
+ # @return the child's `{results:, reporters:}` payload, or nil when the child exited
637
749
  # abnormally or wrote no readable payload. `Marshal.load` is safe here: the blob was written by
638
750
  # our own forked child to a temp file we created.
639
751
  def fork_worker_payload(status, out_path)
@@ -658,19 +770,42 @@ module Rigor
658
770
  # ADR-15 Phase 4b.x — drives every cached RBS producer on the main Ractor so each worker can serve
659
771
  # all reflection queries from disk (Marshal-load only). Builds a single coordinator-side
660
772
  # {Environment} for this purpose and returns it fully loaded — issue #798: the caller also reads
661
- # the project-signature state off it, since it is the only environment this backend's coordinator
662
- # ever holds. Workers still build their OWN `Environment.for_project` inside the Ractor body, which
663
- # then routes through `cached_env` instead of `RBS::EnvironmentLoader.new`.
664
- def prewarm_rbs_cache_for_pool
773
+ # the project-signature state and the effect-annotation carrier off it, since it is the only
774
+ # environment this backend's coordinator ever holds. Workers still build their OWN
775
+ # `Environment.for_project` inside the Ractor body, which then routes through `cached_env` instead
776
+ # of `RBS::EnvironmentLoader.new`.
777
+ #
778
+ # Built with the loaded plugin registry over the project's WHOLE file list (#793's rule), exactly
779
+ # as each worker builds its own. `Environment.for_project` collects plugin-synthesized virtual RBS
780
+ # only when handed BOTH (`collect_virtual_rbs` short-circuits on a nil registry and on an empty
781
+ # list), and this used to pass neither: its loader's `virtual_rbs` read empty on every project, so
782
+ # a carrier snapshotted off it would have been `[]` whatever the rbs-inline comments said — the
783
+ # method called, the row still missing. Two more things follow from building the environment the
784
+ # workers build: the env-cache descriptor digests the same signature roots (a plugin's
785
+ # `signature_paths:`) and the same virtual set, so the slot this warms is the slot the workers
786
+ # read; and each synthesizer runs once here and is memoised through the cache store (ADR-32 WD5)
787
+ # instead of once per worker.
788
+ #
789
+ # Deliberately NOT {#build_runner_environment}: that hands the environment the run's own reporter
790
+ # accumulators, and an environment BUILD writes to one of them — `collect_virtual_rbs` records a
791
+ # synthesizer's parse failures and unhonoured annotations on the `source_rbs_synthesis` reporter.
792
+ # Every Ractor worker builds this same environment with reporters of its own and drains them back
793
+ # through {#merge_worker_reporters}, whose replay of that stream appends without dedup, so a
794
+ # coordinator-side reporter here would count each entry once more than the drains already do. The
795
+ # other two are written only by demands this environment never receives (the HKT scan, per-file
796
+ # dispatch); all three are withheld together so the no-double-count holds by construction rather
797
+ # than by which reads happen to be made. The dispatch-only per-run state (the dependency-source
798
+ # index, the synthetic-method / project-patched indexes) is withheld for the same reason: nothing
799
+ # here dispatches.
800
+ # @param source_files — the WHOLE project's file list, never the analyzed subset.
801
+ def prewarm_rbs_cache_for_pool(source_files:)
665
802
  warm_env = Environment.for_project(
666
803
  libraries: @configuration.libraries,
667
804
  signature_paths: @configuration.signature_paths,
668
805
  cache_store: @cache_store,
669
- bundler_bundle_path: @configuration.bundler_bundle_path,
670
- bundler_auto_detect: @configuration.bundler_auto_detect,
671
- bundler_lockfile: @configuration.bundler_lockfile,
672
- rbs_collection_lockfile: @configuration.rbs_collection_lockfile,
673
- rbs_collection_auto_detect: @configuration.rbs_collection_auto_detect
806
+ plugin_registry: plugin_registry,
807
+ source_files: source_files,
808
+ **ProjectEnvironment.dependency_discovery_options(@configuration)
674
809
  )
675
810
  warm_env.rbs_loader&.prewarm
676
811
  warm_env
@@ -680,7 +815,7 @@ module Rigor
680
815
  # (currently: `--no-cache` would force workers through `EnvironmentLoader.new`), degrade to
681
816
  # sequential analysis with a `:warning` `pool-degraded` diagnostic at run start. The actual
682
817
  # per-file analysis runs on the coordinator, identical to the default sequential path.
683
- # @param source_files [Array<String>] issue #793 — the whole project, so this path's environment
818
+ # @param source_files issue #793 — the whole project, so this path's environment
684
819
  # carries the same plugin-synthesized RBS the pool workers' would. It used to build over `[]`,
685
820
  # i.e. with no synthesized RBS at all, even on a full run.
686
821
  def analyze_files_sequentially_fallback(files, reason:, source_files: files)
@@ -699,6 +834,7 @@ module Rigor
699
834
  record_hkt_scan_failure(hkt_scan_outcome(environment))
700
835
  @snapshots.class_decl_paths = loader&.class_decl_paths || {}.freeze
701
836
  @snapshots.signature_paths = loader&.signature_paths || [].freeze
837
+ @snapshots.signature_standdowns = plugin_signature_paths? ? signature_standdowns_for(loader) : []
702
838
  @snapshots.quarantined_signatures =
703
839
  project_signature_paths? ? (loader&.quarantined_signatures || []) : []
704
840
  @snapshots.env_build_failure = project_signature_paths? ? loader&.env_build_failure : nil
@@ -723,9 +859,15 @@ module Rigor
723
859
  end
724
860
  # ADR-32 WD6 — merge per-worker synthesizer failures back into the coordinator's reporter. Fetched
725
861
  # with a default empty array so older drains (pre-slice-2) remain compatible.
862
+ #
863
+ # `kind:` rides along (issue #824): without it every drained entry replayed as WD6's default
864
+ # `:failed`, so under `--workers N` a WD12 "parsed but not honoured" row came back as
865
+ # `source-rbs-synthesis-failed` — a different id, and a message telling the user the file
866
+ # contributed nothing when all but one of its annotations bound. Every worker reads the same
867
+ # sources and so hands over the same entries; the reporter's own dedup collapses them.
726
868
  Array(drained[:source_rbs_synthesis]).each do |entry|
727
869
  @source_rbs_synthesis_reporter.record(
728
- plugin_id: entry.plugin_id, path: entry.path, message: entry.message
870
+ plugin_id: entry.plugin_id, path: entry.path, message: entry.message, kind: entry.kind
729
871
  )
730
872
  end
731
873
  # Issue #696. Fetched with a default so an older drain stays compatible, exactly as the line above.
@@ -764,6 +906,18 @@ module Rigor
764
906
  message: entry.message, path: entry.path, line: entry.line, column: entry.column
765
907
  )
766
908
  end
909
+ replay_deprecated_forms(rbs)
910
+ end
911
+
912
+ # ADR-109 WD3 — the fourth stream, read with the nil default like the hkt one so an older drain
913
+ # shape records nothing.
914
+ def replay_deprecated_forms(rbs)
915
+ Array(rbs[:deprecated_forms]).each do |entry|
916
+ @rbs_extended_reporter.record_deprecated_form(
917
+ payload: entry.payload, replacement: entry.replacement,
918
+ path: entry.path, line: entry.line, column: entry.column
919
+ )
920
+ end
767
921
  end
768
922
 
769
923
  # Issue #696 — accumulate the per-class `RBS::DefinitionBuilder` failures this run observed, deduped
@@ -822,8 +976,7 @@ module Rigor
822
976
  # build on a project with no files (see {#analyze_files}). Memoised, so on a reused Environment
823
977
  # this is a hash read.
824
978
  #
825
- # @param environment [Rigor::Environment, nil]
826
- # @return [Array, nil] the recorded tuple, or nil (no environment, or the scan built)
979
+ # @return the recorded tuple, or nil (no environment, or the scan built)
827
980
  def hkt_scan_outcome(environment)
828
981
  return nil if environment.nil?
829
982
 
@@ -838,6 +991,21 @@ module Rigor
838
991
  !(paths.nil? || paths.empty?)
839
992
  end
840
993
 
994
+ # Issue #610 — see {#snapshot_project_signature_state}. The registry's `signature_paths` is what
995
+ # `Environment.for_project` defers, so this is the loader-free form of "is anything deferred?".
996
+ def plugin_signature_paths?
997
+ registry = plugin_registry
998
+ registry.respond_to?(:signature_paths) && !registry.signature_paths.empty?
999
+ end
1000
+
1001
+ # Empty for a loader with nothing deferred, without touching its env.
1002
+ def signature_standdowns_for(loader)
1003
+ return [] if loader.nil? || !loader.respond_to?(:deferred_signature_paths)
1004
+ return [] if loader.deferred_signature_paths.empty?
1005
+
1006
+ loader.signature_standdowns
1007
+ end
1008
+
841
1009
  def plugin_registry
842
1010
  @plugin_registry_reader.call
843
1011
  end
@@ -35,11 +35,9 @@ module Rigor
35
35
  :pre_eval_diagnostics_from_scanner
36
36
  )
37
37
 
38
- # @param configuration [Rigor::Configuration]
39
- # @param cache_store [Rigor::Cache::Store, nil]
40
- # @param buffer [BufferBinding, nil]
41
- # @param plugin_requirer [#call, nil]
42
- # @param pool_mode [#call] reader returning the pool-mode flag.
38
+ # @param plugin_requirer — how a plugin gem is brought into the process, called with the gem name; nil
39
+ # lets `Plugin::Loader` apply its own default.
40
+ # @param pool_mode reader returning the pool-mode flag.
43
41
  def initialize(configuration:, cache_store:, buffer:, plugin_requirer:, pool_mode:)
44
42
  @configuration = configuration
45
43
  @cache_store = cache_store
@@ -56,7 +54,7 @@ module Rigor
56
54
  :discovered_classes, :discovered_def_nodes, :discovered_def_nestings,
57
55
  :discovered_singleton_def_nodes, :discovered_def_sources, :discovered_singleton_def_sources,
58
56
  :discovered_superclasses, :discovered_header_nestings, :discovered_includes,
59
- :discovered_class_sources, :constant_values, :constant_sources, :constant_writes,
57
+ :discovered_extends, :discovered_class_sources, :constant_values, :constant_sources, :constant_writes,
60
58
  :discovered_method_visibilities, :discovered_methods, :data_member_layouts,
61
59
  :struct_member_layouts
62
60
  )
@@ -77,6 +75,11 @@ module Rigor
77
75
  bundle_path: @configuration.bundler_bundle_path,
78
76
  auto_detect: @configuration.bundler_auto_detect
79
77
  )&.to_s
78
+ # ADR-39 slice 5 / #911 — hand the project's `plugins_isolation:` to the invocation layer before
79
+ # any plugin `#prepare` runs, for the same reason the bundle root is resolved above: `#prepare`
80
+ # already calls through {Plugin::Isolation}, so a later assignment would leave prepare-time
81
+ # calls on a different strategy than the rest of the run.
82
+ Plugin::Isolation.configured_strategy = @configuration.plugins_isolation
80
83
  plugin_registry = load_plugins
81
84
  dependency_source_index = DependencySourceInference::Builder.build(@configuration.dependencies)
82
85
  # ADR-18 slice 3 — plugin prepare MUST run before the synthetic-method scanner so cross-plugin
@@ -161,6 +164,9 @@ module Rigor
161
164
  # the cref Ruby resolves it in rather than by peeling the subclass's own qualified name.
162
165
  discovered_header_nestings: def_index.fetch(:header_nestings),
163
166
  discovered_includes: def_index.fetch(:includes),
167
+ # Issue #898 — the singleton-side mixin table, kept beside the instance-side one so a class
168
+ # object's `extend`s reach `Narrowing` from a sibling file the way its `include`s already do.
169
+ discovered_extends: def_index.fetch(:extends),
164
170
  discovered_class_sources: def_index.fetch(:class_sources),
165
171
  # Issue #644 — the cross-file value-constant publication table and its write attribution.
166
172
  constant_values: def_index.fetch(:constant_values),
@@ -16,7 +16,7 @@ module Rigor
16
16
  # back-reference cycle.
17
17
  class RunSnapshots
18
18
  attr_accessor :class_decl_paths, :signature_paths,
19
- :synthesized_namespaces, :quarantined_signatures, :conformance_results,
19
+ :synthesized_namespaces, :quarantined_signatures, :signature_standdowns, :conformance_results,
20
20
  :env_build_failure, :definition_build_failures, :hkt_scan_failure,
21
21
  :effect_annotation_carrier
22
22
 
@@ -29,11 +29,15 @@ module Rigor
29
29
  # {PoolCoordinator#merge_worker_reporters}. `hkt_scan_failure` (#784) is nil-or-tuple like
30
30
  # `env_build_failure`, not a list like `definition_build_failures`: the scan is ONE build over the
31
31
  # whole `signature_paths:` overlay, not a per-class one, so it has exactly one outcome per run.
32
+ # `signature_standdowns` (#610) is the quarantine slot's twin for the plugin-contributed files that
33
+ # stood down against a colliding generic arity: a list, assigned once from the loader as
34
+ # `quarantined_signatures` is, and derived from the final env so a cache HIT carries it too.
32
35
  def initialize
33
36
  @class_decl_paths = {}.freeze
34
37
  @signature_paths = [].freeze
35
38
  @synthesized_namespaces = [].freeze
36
39
  @quarantined_signatures = [].freeze
40
+ @signature_standdowns = [].freeze
37
41
  @conformance_results = [].freeze
38
42
  @env_build_failure = nil
39
43
  @definition_build_failures = [].freeze
@@ -48,6 +52,7 @@ module Rigor
48
52
  @signature_paths = []
49
53
  @synthesized_namespaces = []
50
54
  @quarantined_signatures = []
55
+ @signature_standdowns = []
51
56
  @conformance_results = []
52
57
  @env_build_failure = nil
53
58
  @definition_build_failures = []