rigortype 0.3.7 → 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 (450) 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 +96 -15
  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 +39 -4
  22. data/docs/manual/03-configuration.md +11 -0
  23. data/docs/manual/04-diagnostics.md +12 -1
  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 +69 -13
  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 +100 -29
  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 +316 -40
  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 +430 -77
  76. data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
  77. data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
  78. data/lib/rigor/analysis/runner.rb +200 -43
  79. data/lib/rigor/analysis/worker_session.rb +36 -12
  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 +166 -30
  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 +70 -0
  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_environment_marshal_patch.rb +39 -0
  97. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  98. data/lib/rigor/cache/store.rb +85 -33
  99. data/lib/rigor/cli/annotate_command.rb +5 -6
  100. data/lib/rigor/cli/check_command.rb +57 -10
  101. data/lib/rigor/cli/check_invocation.rb +6 -11
  102. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  103. data/lib/rigor/cli/coverage_command.rb +1 -1
  104. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  105. data/lib/rigor/cli/coverage_scan.rb +1 -5
  106. data/lib/rigor/cli/diff_command.rb +1 -1
  107. data/lib/rigor/cli/doc_links.rb +3 -3
  108. data/lib/rigor/cli/docs_command.rb +2 -2
  109. data/lib/rigor/cli/doctor_command.rb +85 -38
  110. data/lib/rigor/cli/effects_command.rb +2 -2
  111. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  112. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  113. data/lib/rigor/cli/explain_command.rb +34 -1
  114. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  115. data/lib/rigor/cli/lsp_command.rb +1 -1
  116. data/lib/rigor/cli/mcp_command.rb +1 -1
  117. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  118. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  119. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  120. data/lib/rigor/cli/plugin_command.rb +1 -1
  121. data/lib/rigor/cli/plugins_command.rb +2 -1
  122. data/lib/rigor/cli/prism_colorizer.rb +2 -2
  123. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  124. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  125. data/lib/rigor/cli/sig_gen_command.rb +31 -2
  126. data/lib/rigor/cli/skill_command.rb +1 -1
  127. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  128. data/lib/rigor/cli/skill_describe.rb +36 -35
  129. data/lib/rigor/cli/trace_command.rb +4 -4
  130. data/lib/rigor/cli/trace_renderer.rb +5 -6
  131. data/lib/rigor/cli/triage_command.rb +1 -1
  132. data/lib/rigor/cli/type_of_command.rb +10 -6
  133. data/lib/rigor/cli/type_scan_command.rb +4 -4
  134. data/lib/rigor/cli/unused_command.rb +11 -3
  135. data/lib/rigor/cli/upgrade_command.rb +1 -1
  136. data/lib/rigor/cli.rb +68 -6
  137. data/lib/rigor/config_audit.rb +30 -5
  138. data/lib/rigor/configuration/severity_profile.rb +6 -6
  139. data/lib/rigor/configuration.rb +43 -4
  140. data/lib/rigor/effects/attribution.rb +1 -3
  141. data/lib/rigor/effects/config_envelopes.rb +6 -8
  142. data/lib/rigor/effects/definition_lines.rb +25 -6
  143. data/lib/rigor/effects/effect_table.rb +0 -1
  144. data/lib/rigor/effects/entry_points.rb +0 -2
  145. data/lib/rigor/effects/envelope_check.rb +8 -8
  146. data/lib/rigor/effects/envelope_index.rb +5 -8
  147. data/lib/rigor/effects/framework_units.rb +4 -6
  148. data/lib/rigor/effects/identity.rb +4 -6
  149. data/lib/rigor/effects/inline_anchor.rb +7 -7
  150. data/lib/rigor/effects/label_intent.rb +3 -4
  151. data/lib/rigor/effects/liskov_check.rb +8 -8
  152. data/lib/rigor/effects/method_key.rb +1 -1
  153. data/lib/rigor/effects/plugin_facts.rb +4 -6
  154. data/lib/rigor/effects/propagator.rb +2 -3
  155. data/lib/rigor/effects/scanner.rb +1 -1
  156. data/lib/rigor/effects/signature_sources.rb +3 -5
  157. data/lib/rigor/effects/snapshot.rb +10 -10
  158. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  159. data/lib/rigor/effects/unit_scan.rb +10 -10
  160. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  161. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  162. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  163. data/lib/rigor/environment/class_registry.rb +3 -1
  164. data/lib/rigor/environment/failure_slot.rb +28 -0
  165. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  166. data/lib/rigor/environment/lockfile_resolver.rb +51 -4
  167. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  168. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  169. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  170. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  171. data/lib/rigor/environment/rbs_loader.rb +613 -113
  172. data/lib/rigor/environment.rb +189 -27
  173. data/lib/rigor/flow_contribution/merger.rb +0 -2
  174. data/lib/rigor/flow_contribution.rb +11 -13
  175. data/lib/rigor/inference/acceptance.rb +153 -8
  176. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  177. data/lib/rigor/inference/body_fixpoint.rb +5 -5
  178. data/lib/rigor/inference/budget_trace.rb +1 -5
  179. data/lib/rigor/inference/captured_locals.rb +2 -3
  180. data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
  181. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  182. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  183. data/lib/rigor/inference/element_read_widening.rb +168 -0
  184. data/lib/rigor/inference/expression_typer.rb +763 -143
  185. data/lib/rigor/inference/fork_map.rb +5 -7
  186. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  187. data/lib/rigor/inference/hkt_registry.rb +19 -10
  188. data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
  189. data/lib/rigor/inference/index_write_widening.rb +1 -5
  190. data/lib/rigor/inference/macro_block_self_type.rb +1 -4
  191. data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
  192. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  193. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  194. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  195. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  196. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
  197. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  198. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  199. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  200. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  201. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
  202. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  203. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
  204. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  205. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  206. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  207. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  208. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
  209. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  210. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  211. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
  212. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  213. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  214. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  215. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  216. data/lib/rigor/inference/method_dispatcher.rb +12 -13
  217. data/lib/rigor/inference/method_parameter_binder.rb +4 -6
  218. data/lib/rigor/inference/multi_target_binder.rb +1 -3
  219. data/lib/rigor/inference/mutation_rejoin.rb +168 -0
  220. data/lib/rigor/inference/mutation_widening.rb +100 -51
  221. data/lib/rigor/inference/narrowing.rb +299 -46
  222. data/lib/rigor/inference/optimistic_origin.rb +1 -7
  223. data/lib/rigor/inference/origin_lookup.rb +2 -3
  224. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  225. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  226. data/lib/rigor/inference/precision_scanner.rb +3 -4
  227. data/lib/rigor/inference/project_patched_methods.rb +2 -2
  228. data/lib/rigor/inference/project_patched_scanner.rb +3 -3
  229. data/lib/rigor/inference/protection_scanner.rb +1 -2
  230. data/lib/rigor/inference/range_constant.rb +57 -0
  231. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  232. data/lib/rigor/inference/receiver_alias.rb +3 -3
  233. data/lib/rigor/inference/refinement_mutation.rb +72 -0
  234. data/lib/rigor/inference/scope_indexer.rb +688 -151
  235. data/lib/rigor/inference/statement_evaluator.rb +221 -50
  236. data/lib/rigor/inference/string_mutation.rb +60 -0
  237. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  238. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  239. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  240. data/lib/rigor/inference/version_guard.rb +28 -21
  241. data/lib/rigor/inference/void_origin.rb +3 -3
  242. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  243. data/lib/rigor/language_server/buffer_table.rb +4 -4
  244. data/lib/rigor/language_server/completion_provider.rb +1 -1
  245. data/lib/rigor/language_server/debouncer.rb +1 -1
  246. data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
  247. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  248. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  249. data/lib/rigor/language_server/hover_provider.rb +1 -1
  250. data/lib/rigor/language_server/hover_renderer.rb +2 -2
  251. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  252. data/lib/rigor/language_server/project_context.rb +2 -5
  253. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  254. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  255. data/lib/rigor/language_server/server.rb +9 -9
  256. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  257. data/lib/rigor/language_server/uri.rb +1 -1
  258. data/lib/rigor/plugin/base.rb +90 -21
  259. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  260. data/lib/rigor/plugin/effect_attribution.rb +7 -7
  261. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  262. data/lib/rigor/plugin/fact_store.rb +6 -6
  263. data/lib/rigor/plugin/io_boundary.rb +93 -8
  264. data/lib/rigor/plugin/isolation.rb +29 -6
  265. data/lib/rigor/plugin/loader.rb +31 -12
  266. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  267. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  268. data/lib/rigor/plugin/manifest.rb +23 -1
  269. data/lib/rigor/plugin/registry.rb +67 -14
  270. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
  271. data/lib/rigor/plugin/trust_policy.rb +2 -4
  272. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  273. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  274. data/lib/rigor/project_environment.rb +138 -0
  275. data/lib/rigor/protection/analysis_guard.rb +93 -14
  276. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  277. data/lib/rigor/protection/dependency_closure.rb +5 -8
  278. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  279. data/lib/rigor/protection/discovery_seed.rb +11 -11
  280. data/lib/rigor/protection/kill_signature.rb +2 -4
  281. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  282. data/lib/rigor/protection/mutation_cache.rb +6 -9
  283. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  284. data/lib/rigor/protection/mutator.rb +2 -1
  285. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  286. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  287. data/lib/rigor/rbs_extended/envelope_scanner.rb +12 -13
  288. data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
  289. data/lib/rigor/rbs_extended/reporter.rb +122 -16
  290. data/lib/rigor/rbs_extended.rb +67 -22
  291. data/lib/rigor/reflection/constant_path.rb +126 -0
  292. data/lib/rigor/reflection.rb +26 -10
  293. data/lib/rigor/runtime/jit.rb +6 -8
  294. data/lib/rigor/scope/discovery_index.rb +30 -0
  295. data/lib/rigor/scope.rb +203 -4
  296. data/lib/rigor/sig_gen/generator.rb +181 -102
  297. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  298. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  299. data/lib/rigor/sig_gen/observation_collector.rb +17 -15
  300. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  301. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  302. data/lib/rigor/sig_gen/renderer.rb +17 -11
  303. data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
  304. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  305. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  306. data/lib/rigor/sig_gen/writer.rb +23 -6
  307. data/lib/rigor/signature_path_audit.rb +153 -6
  308. data/lib/rigor/source/literals.rb +0 -23
  309. data/lib/rigor/source/node_children.rb +0 -2
  310. data/lib/rigor/source/node_locator.rb +5 -11
  311. data/lib/rigor/source/node_walker.rb +2 -5
  312. data/lib/rigor/triage/catalogue.rb +1 -3
  313. data/lib/rigor/triage.rb +3 -5
  314. data/lib/rigor/type/accepts_result.rb +24 -6
  315. data/lib/rigor/type/combinator.rb +68 -5
  316. data/lib/rigor/type/data_class.rb +2 -2
  317. data/lib/rigor/type/data_instance.rb +4 -4
  318. data/lib/rigor/type/float_range.rb +128 -0
  319. data/lib/rigor/type/hash_shape.rb +5 -5
  320. data/lib/rigor/type/integer_range.rb +13 -8
  321. data/lib/rigor/type/nominal.rb +8 -2
  322. data/lib/rigor/type/refined.rb +3 -3
  323. data/lib/rigor/type/struct_class.rb +3 -3
  324. data/lib/rigor/type/struct_instance.rb +4 -4
  325. data/lib/rigor/type.rb +1 -0
  326. data/lib/rigor/type_node/generic.rb +1 -1
  327. data/lib/rigor/type_node/range_literal.rb +25 -0
  328. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  329. data/lib/rigor/type_node.rb +1 -0
  330. data/lib/rigor/version.rb +1 -1
  331. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  332. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  333. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  334. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
  335. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  336. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  337. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  338. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
  339. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
  340. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  341. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  342. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
  343. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  344. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  345. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  346. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  347. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  348. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  349. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
  350. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
  351. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  352. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  353. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
  354. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  355. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
  356. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  357. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
  358. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  359. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
  360. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  361. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  362. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  363. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  364. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  365. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  366. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  367. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  368. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  369. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  370. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  371. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  372. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  373. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  374. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  375. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
  376. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  377. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  378. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
  379. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  380. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  381. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  382. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  383. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  384. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
  385. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  386. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  387. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  388. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
  389. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  390. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
  391. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
  392. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
  393. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  394. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  395. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  396. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  397. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
  398. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  399. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  400. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
  401. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  402. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  403. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  404. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  405. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  406. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  407. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  408. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  409. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  410. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  411. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  412. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  413. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
  414. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  415. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  416. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
  417. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  418. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  419. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  420. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  421. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  422. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  423. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
  424. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  425. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
  426. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  427. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  428. data/sig/rigor/cache.rbs +2 -3
  429. data/sig/rigor/environment.rbs +11 -0
  430. data/sig/rigor/inference.rbs +3 -10
  431. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  432. data/sig/rigor/plugin/manifest.rbs +2 -1
  433. data/sig/rigor/reflection.rbs +3 -1
  434. data/sig/rigor/scope.rbs +24 -3
  435. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
  436. data/sig/rigor/type.rbs +27 -1
  437. data/sig/rigor.rbs +1 -0
  438. data/skills/rigor-ask/SKILL.md +8 -6
  439. data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
  440. data/skills/rigor-project-init/SKILL.md +6 -3
  441. data/skills/rigor-project-init/references/02-configure.md +10 -6
  442. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  443. data/skills/rigor-type-oracle/SKILL.md +228 -0
  444. data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
  445. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  446. data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
  447. data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
  448. metadata +28 -3
  449. data/lib/rigor/cli/probe_environment.rb +0 -85
  450. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -55,35 +55,36 @@ module Rigor
55
55
  # knowledge the single-file run does not have) must not mask a kill the shipped oracle would report.
56
56
  Baseline = Data.define(:own, :dependents)
57
57
 
58
- # @param configuration [Rigor::Configuration]
59
- # @param environment [Rigor::Environment] built once by the caller.
60
- # @param project_scan [Rigor::Analysis::ProjectScan] built once by the caller; adopted per analysis
58
+ # @param environment — built once by the caller.
59
+ # @param project_scan — built once by the caller; adopted per analysis
61
60
  # through `prebuilt:`, exactly as {DiagnosticOracle} does.
62
- # @param paths [Array<String>] the measured file set, in canonical order (the seed's span: a class
61
+ # @param paths — the measured file set, in canonical order (the seed's span: a class
63
62
  # declared outside it stays unknown, as it does for Tier 1's seed and for {DiscoverySeed}).
64
- # @param dependents [Hash{String => Array<String>}] {DependencyClosure} map, restricted to `paths`.
65
- # @param seed_bundles [Hash{String => Hash}] {DiscoverySeed.bundles} over the same `paths`.
66
- # @param discovery_seed [Hash, nil] the `discovery-seeded-mutation-sites` seed when that feature is also
63
+ # @param dependents — {DependencyClosure} map, restricted to `paths`.
64
+ # @param seed_bundles — {DiscoverySeed.bundles} over the same `paths`.
65
+ # @param discovery_seed — the `discovery-seeded-mutation-sites` seed when that feature is also
67
66
  # adopted, nil otherwise. It goes to the delegated {DiagnosticOracle} verbatim, so the mutated file's
68
67
  # verdict is byte-for-byte the verdict that feature combination produces without this one; its
69
68
  # `param_inferred_types` slot additionally rides the per-mutant closure seed, so an admitted site is
70
69
  # judged with the knowledge that admitted it (issue #260's amended decision). The table is not
71
70
  # refreshed per mutant — the collector is a whole-project pre-pass, and one mutated method body does
72
71
  # not justify re-running it thousands of times.
72
+ # @param rebuild_session — issue #790 — a callable returning a FRESH `[environment, project_scan]`
73
+ # pair, invoked after a run this oracle refused for an analyzer defect. nil means the caller has no
74
+ # way to rebuild; see {#recovering} for what the oracle then does instead.
75
+ # rubocop:disable Metrics/ParameterLists -- the eight are independent collaborators the caller builds
76
+ # once each; an options object would only put a name in front of the same list.
73
77
  def initialize(configuration:, environment:, project_scan:, paths:, dependents:, seed_bundles:,
74
- discovery_seed: nil)
78
+ discovery_seed: nil, rebuild_session: nil)
79
+ # rubocop:enable Metrics/ParameterLists
75
80
  @configuration = configuration
76
- @environment = environment
77
- @project_scan = project_scan
78
81
  @paths = paths
79
82
  @dependents = dependents
80
83
  @seed_bundles = seed_bundles
81
84
  @discovery_seed = discovery_seed
85
+ @rebuild_session = rebuild_session
82
86
  @param_inferred_types = discovery_seed && discovery_seed[:param_inferred_types]
83
- @single = DiagnosticOracle.new(
84
- configuration: configuration, environment: environment, project_scan: project_scan,
85
- discovery_seed: discovery_seed
86
- )
87
+ adopt_session(environment, project_scan)
87
88
  end
88
89
 
89
90
  # The clean baselines a mutant must add a diagnostic to: the mutated file's (the shipped oracle's own,
@@ -91,21 +92,25 @@ module Rigor
91
92
  # never per mutant. The dependents' half is bound through the same buffer machinery a mutant is, so the
92
93
  # clean and mutant runs of the closure differ in exactly one input — the bytes.
93
94
  def baseline(source:, path:)
94
- Baseline.new(
95
- own: @single.baseline(source: source, path: path),
96
- dependents: dependents_signatures(source, path)
97
- )
95
+ recovering do
96
+ Baseline.new(
97
+ own: @single.baseline(source: source, path: path),
98
+ dependents: dependents_signatures(source, path)
99
+ )
100
+ end
98
101
  end
99
102
 
100
103
  # Killed iff the mutant introduces a diagnostic the baseline did not carry — in the mutated file (the
101
104
  # shipped verdict), or, failing that, in any dependent of it (what this feature adds).
102
105
  def killed?(mutant_source:, path:, baseline:)
103
- return true if @single.killed?(mutant_source: mutant_source, path: path, baseline: baseline.own)
106
+ recovering do
107
+ next true if @single.killed?(mutant_source: mutant_source, path: path, baseline: baseline.own)
104
108
 
105
- dependents = @dependents[path] || []
106
- return false if dependents.empty?
109
+ dependents = @dependents[path] || []
110
+ next false if dependents.empty?
107
111
 
108
- dependents_signatures(mutant_source, path).any? { |sig| !baseline.dependents.include?(sig) }
112
+ dependents_signatures(mutant_source, path).any? { |sig| !baseline.dependents.include?(sig) }
113
+ end
109
114
  end
110
115
 
111
116
  # The file set a kill is looked for in: the mutated file plus its measured dependents.
@@ -115,6 +120,45 @@ module Rigor
115
120
 
116
121
  private
117
122
 
123
+ # Issue #790 — a refused mutant is one mutant; a POISONED Environment is every mutant after it.
124
+ #
125
+ # The #784 seam records its failure on the Environment and {Environment::HktRegistryHolder} memoises
126
+ # the degraded registry, while {Environment::FailureSlot} is first-write-wins. So the one mutant that
127
+ # made the scan raise leaves an Environment that reports `rbs.coverage.hkt-scan-failed` on every later
128
+ # run and never re-attempts the scan — the row lands on BOTH sides of the next mutant's set
129
+ # difference, and every later mutant is measured over the degraded universe. Pre-#788 the raise
130
+ # propagated instead of being memoised, so the next demand retried the build and the poisoning did not
131
+ # exist; recovering it is this method.
132
+ #
133
+ # REBUILD rather than reset the slots. A reset would be cheaper, but it has to enumerate every holder
134
+ # the degraded registry fed — the memo itself, the failure slot, and the constant-type cache whose
135
+ # entries were folded under it — and a holder added to {Rigor::Environment} later would silently not
136
+ # be reset, with nothing to go red. The cost objection a reset answers is a rebuild PER MUTANT; this
137
+ # one is paid only after a defect is actually observed, which on a healthy sweep is never.
138
+ #
139
+ # With no `rebuild_session:` the oracle keeps the poisoned Environment and every later mutant is
140
+ # refused by the same guard. That is the deliberate fallback: a sweep that reports `harness_errors`
141
+ # says it could not measure, where the pre-#790 behaviour said "survived" about a universe it had
142
+ # silently degraded.
143
+ def recovering
144
+ yield
145
+ rescue AnalyzerCrashed => e
146
+ adopt_session(*@rebuild_session.call) if e.analyzer_defect? && @rebuild_session
147
+ raise
148
+ end
149
+
150
+ # The delegated single-file oracle is rebuilt with the pair, not handed the new Environment after the
151
+ # fact: it holds its own reference, and an oracle whose two halves disagree about which Environment
152
+ # they measure over is the same split-universe bug one layer down.
153
+ def adopt_session(environment, project_scan)
154
+ @environment = environment
155
+ @project_scan = project_scan
156
+ @single = DiagnosticOracle.new(
157
+ configuration: @configuration, environment: environment, project_scan: project_scan,
158
+ discovery_seed: @discovery_seed
159
+ )
160
+ end
161
+
118
162
  # The diagnostic signatures the dependents of `path` report while `source` stands in for it. Empty (and
119
163
  # analysis-free) when nothing depends on the file — 120 of Rigor's own 349 `lib` files.
120
164
  def dependents_signatures(source, path)
@@ -187,7 +231,7 @@ module Rigor
187
231
  configuration: @configuration, environment: @environment, prebuilt: @project_scan,
188
232
  cache_store: nil, collect_stats: false, buffer: buffer, discovery_seed: seed, analyze_only: paths
189
233
  ).run(paths),
190
- context: "ClosureKillOracle closure analysis of #{paths.join(', ')}"
234
+ context: "ClosureKillOracle closure analysis of #{paths.join(', ')}", environment: @environment
191
235
  )
192
236
  end
193
237
  end
@@ -24,14 +24,13 @@ module Rigor
24
24
  module DependencyClosure
25
25
  module_function
26
26
 
27
- # @param paths [Array<String>] the measured file set, in caller order.
28
- # @param configuration [Rigor::Configuration]
29
- # @param environment [Rigor::Environment] built once by the caller.
30
- # @param cache_store [Rigor::Cache::Store, nil] threaded to the recording run only (its RBS-env and
27
+ # @param paths — the measured file set, in caller order.
28
+ # @param environment — built once by the caller.
29
+ # @param cache_store — threaded to the recording run only (its RBS-env and
31
30
  # plugin-producer tiers); the per-mutant analyses stay `cache_store: nil` regardless.
32
- # @param workers [Integer] fork-pool workers for the recording pass (the pool records per worker and
31
+ # @param workers — fork-pool workers for the recording pass (the pool records per worker and
33
32
  # marshals the records back, so a pooled graph equals the sequential one).
34
- # @return [Hash{String => Array<String>}] frozen `path => sorted dependents`, restricted to `paths`.
33
+ # @return frozen `path => sorted dependents`, restricted to `paths`.
35
34
  def build(paths:, configuration:, environment:, cache_store: nil, workers: 0)
36
35
  runner = Analysis::Runner.new(
37
36
  configuration: configuration, environment: environment, cache_store: cache_store,
@@ -45,8 +44,6 @@ module Rigor
45
44
  # not being measured, so re-analysing it would report a diagnostic against a file the run never
46
45
  # baselined. Self-edges are dropped (the mutated file is the closure's own head), and each list is
47
46
  # sorted so a `--threshold` gate reads the same number whatever order the recording pass finished in.
48
- #
49
- # @return [Hash{String => Array<String>}]
50
47
  def index(dependents, paths)
51
48
  measured = Set.new(paths)
52
49
  paths.to_h do |path|
@@ -17,7 +17,7 @@ module Rigor
17
17
  # disk write). Passing `prebuilt:` disables the run-result cache (whose key digests the *disk* file), so a
18
18
  # mutant is never served a stale clean hit.
19
19
  class DiagnosticOracle
20
- # @param discovery_seed [Hash, nil] issue #260 — the cross-file discovery tables (see {DiscoverySeed})
20
+ # @param discovery_seed — issue #260 — the cross-file discovery tables (see {DiscoverySeed})
21
21
  # the per-mutant analysis is seeded with, threaded through to `Runner.new(discovery_seed:)`. Without
22
22
  # it the runner's `prebuilt:` path carries frozen-empty discovery tables, so a receiver whose class is
23
23
  # declared in a *sibling* file reads `Dynamic` and NO mutation at that site can produce a diagnostic —
@@ -53,7 +53,7 @@ module Rigor
53
53
  configuration: @configuration, environment: @environment, prebuilt: @project_scan,
54
54
  cache_store: nil, collect_stats: false, discovery_seed: @discovery_seed
55
55
  ).run_source(source: source, path: path),
56
- context: "DiagnosticOracle re-analysis of #{path}"
56
+ context: "DiagnosticOracle re-analysis of #{path}", environment: @environment
57
57
  )
58
58
  end
59
59
  end
@@ -30,12 +30,12 @@ module Rigor
30
30
  module DiscoverySeed
31
31
  module_function
32
32
 
33
- # @param paths [Array<String>] the measured file set; the seed spans these files only, exactly as Tier 1's
33
+ # @param paths — the measured file set; the seed spans these files only, exactly as Tier 1's
34
34
  # seed does. A class declared outside them stays unknown.
35
- # @param environment [Rigor::Environment] the plugin-aware environment, built once by the caller.
36
- # @param target_ruby [String] Prism parse version for the parameter-inference pre-pass.
37
- # @param workers [Integer] worker count for that pre-pass (0 keeps it sequential).
38
- # @return [Hash{Symbol => Object}] frozen seed tables; empty when the paths yield nothing.
35
+ # @param environment — the plugin-aware environment, built once by the caller.
36
+ # @param target_ruby — Prism parse version for the parameter-inference pre-pass.
37
+ # @param workers — worker count for that pre-pass (0 keeps it sequential).
38
+ # @return frozen seed tables; empty when the paths yield nothing.
39
39
  def build(paths:, environment:, target_ruby:, workers: 0)
40
40
  tables = discovery_tables(paths)
41
41
  params = Inference::ParameterInferenceCollector.collect(
@@ -51,8 +51,8 @@ module Rigor
51
51
  # builds. Built ONCE on the parent (before {CLI::MutationForkScan} forks, so children copy-on-write
52
52
  # inherit it), and cheap: ≈0.36s over Rigor's own 349-file `lib`.
53
53
  #
54
- # @param paths [Array<String>] the measured file set, in canonical (caller) order.
55
- # @return [Hash{String => Hash}] per-path bundles, the input {#tables_for_buffer} re-folds.
54
+ # @param paths — the measured file set, in canonical (caller) order.
55
+ # @return per-path bundles, the input {#tables_for_buffer} re-folds.
56
56
  def bundles(paths:)
57
57
  Inference::ScopeIndexer.discovered_project_index_incremental(paths, seed_bundles: {}).fetch(:bundles)
58
58
  end
@@ -69,10 +69,10 @@ module Rigor
69
69
  #
70
70
  # ≈15ms over 349 files (one re-walk plus a whole-set fold), against ≈210ms for one mutant's analysis.
71
71
  #
72
- # @param paths [Array<String>] the measured file set, in the same order {#bundles} was built from.
73
- # @param bundles [Hash{String => Hash}] that bundle set.
74
- # @param buffer [Rigor::Analysis::BufferBinding] the mutant binding (logical path → mutant bytes).
75
- # @return [Hash{Symbol => Object}] frozen seed tables.
72
+ # @param paths — the measured file set, in the same order {#bundles} was built from.
73
+ # @param bundles — that bundle set.
74
+ # @param buffer — the mutant binding (logical path → mutant bytes).
75
+ # @return frozen seed tables.
76
76
  def tables_for_buffer(paths:, bundles:, buffer:)
77
77
  index = Inference::ScopeIndexer.discovered_project_index_incremental(
78
78
  paths, seed_bundles: bundles, buffer: buffer
@@ -15,14 +15,12 @@ module Rigor
15
15
  module KillSignature
16
16
  module_function
17
17
 
18
- # @param diagnostic [Rigor::Analysis::Diagnostic]
19
- # @return [Array] the comparison key.
18
+ # @return the comparison key.
20
19
  def of(diagnostic)
21
20
  [diagnostic.rule, diagnostic.path, diagnostic.line, diagnostic.column, diagnostic.message]
22
21
  end
23
22
 
24
- # @param diagnostics [Enumerable<Rigor::Analysis::Diagnostic>]
25
- # @return [Set<Array>] the signature set of `diagnostics`.
23
+ # @return the signature set of `diagnostics`.
26
24
  def signatures_of(diagnostics)
27
25
  diagnostics.to_set { |diagnostic| of(diagnostic) }
28
26
  end
@@ -19,15 +19,13 @@ module Rigor
19
19
  module MeasurementIntegrity
20
20
  module_function
21
21
 
22
- # @return [Boolean] false only when nothing could be measured AND something failed trying.
22
+ # @return false only when nothing could be measured AND something failed trying.
23
23
  def measured?(total:, harness_errors:)
24
24
  !(total.zero? && harness_errors.positive?)
25
25
  end
26
26
 
27
27
  # The report-level companion: a project ratio computed over nothing, where something went wrong.
28
28
  # Distinct from an empty project, which is legitimately vacuous.
29
- #
30
- # @return [Boolean]
31
29
  def ratio_unmeasurable?(grand_total:, unmeasured_files:)
32
30
  grand_total.zero? && unmeasured_files.positive?
33
31
  end
@@ -89,17 +89,15 @@ module Rigor
89
89
  UNIDENTIFIED_ENGINE = "the engine's own source tree could not be digested"
90
90
 
91
91
  class << self
92
- # @param configuration [Rigor::Configuration]
93
- # @param roots [Array<String>] the analysis roots to look for a snapshot under, most-specific first
92
+ # @param roots — the analysis roots to look for a snapshot under, most-specific first
94
93
  # (the command's own path arguments, then the configured `paths:`).
95
- # @param project_scan [Rigor::Analysis::ProjectScan] the prepared whole-project scan.
96
- # @param sampling [Sampling]
97
- # @param feature_ids [Array<String>] the ADOPTED bleeding-edge ids that change this measurement.
98
- # @param seed_inputs [Array<String>, nil] the files the {DiscoverySeed} was built over when it is
94
+ # @param project_scan — the prepared whole-project scan.
95
+ # @param feature_ids — the ADOPTED bleeding-edge ids that change this measurement.
96
+ # @param seed_inputs — the files the {DiscoverySeed} was built over when it is
99
97
  # active, nil when it is not. The CLI stays the only place that knows a feature id exists.
100
- # @param bypass_reason [String, nil] a caller-side reason to run uncached (`--no-cache`, the closure
98
+ # @param bypass_reason — a caller-side reason to run uncached (`--no-cache`, the closure
101
99
  # oracle). Reported verbatim.
102
- # @return [MutationCache] enabled, or a disabled instance carrying `#reason`.
100
+ # @return enabled, or a disabled instance carrying `#reason`.
103
101
  def build(configuration:, roots:, project_scan:, sampling:, feature_ids:, seed_inputs: nil,
104
102
  bypass_reason: nil)
105
103
  return disabled(bypass_reason) if bypass_reason
@@ -263,7 +261,6 @@ module Rigor
263
261
  end
264
262
 
265
263
  # The cached result for `path`, or nil on any miss (including "this cache is disabled").
266
- # @return [MutationScanner::FileResult, nil]
267
264
  def fetch(path)
268
265
  return nil unless enabled?
269
266
  # A path the snapshot never analysed has no recorded `deps[A]`, which means "depends on every project
@@ -87,25 +87,25 @@ module Rigor
87
87
  end
88
88
  end
89
89
 
90
- # @param configuration [Rigor::Configuration]
91
- # @param environment [Rigor::Environment] pre-built once by the caller
92
- # @param project_scan [Rigor::Analysis::ProjectScan] pre-built once
93
- # @param limit [Integer, nil] optional per-file mutation cap (sampled with
90
+ # @param environment — pre-built once by the caller
91
+ # @param project_scan — pre-built once
92
+ # @param limit — optional per-file mutation cap (sampled with
94
93
  # `seed`); nil analyses every type-relevant mutation (deterministic).
95
- # @param seed [Integer] RNG seed for the optional sample.
96
- # @param oracle [#baseline, #killed?, nil] the kill oracle (ADR-69 Seam 1);
97
- # defaults to the {DiagnosticOracle} (the ADR-62/63 behaviour).
98
- # @param site_selector [:biteable, :all] which sites to mutate (ADR-69
94
+ # @param seed — RNG seed for the optional sample.
95
+ # @param oracle — the kill oracle (ADR-69 Seam 1) — anything answering
96
+ # `baseline` and `killed?`; defaults to the {DiagnosticOracle} (the
97
+ # ADR-62/63 behaviour).
98
+ # @param site_selector — which sites to mutate (ADR-69
99
99
  # Seam 2). `:biteable` (default) keeps only concrete-type sites Rigor can
100
100
  # bite; `:all` also mutates Dynamic-receiver dispatch sites — use only
101
101
  # with a {TestSuiteOracle} (the fused overlay), never the diagnostic path.
102
- # @param base_scope [Rigor::Scope, nil] the scope site selection judges
102
+ # @param base_scope — the scope site selection judges
103
103
  # anchor types against, built once by the caller (see
104
104
  # {Mutator#anchor_base_scope}). `nil` — the default — keeps the bare
105
105
  # single-file empty scope. A caller that seeds cross-file discovery
106
106
  # passes it here; this class stays free of {Rigor::Configuration} and of
107
107
  # bleeding-edge feature ids, which live one layer up in the CLI.
108
- # @param discovery_seed [Hash, nil] issue #260 — the SAME cross-file table
108
+ # @param discovery_seed — issue #260 — the SAME cross-file table
109
109
  # set `base_scope` was built from, threaded to the default
110
110
  # {DiagnosticOracle} so an admitted cross-file site is one the oracle can
111
111
  # also kill at. Pass both or neither: a `base_scope` without it measures
@@ -127,9 +127,8 @@ module Rigor
127
127
  )
128
128
  end
129
129
 
130
- # @param path [String] the file to measure (used as the in-memory bind path)
131
- # @param source [String, nil] the file's source; read from disk when nil
132
- # @return [FileResult]
130
+ # @param path — the file to measure (used as the in-memory bind path)
131
+ # @param source — the file's source; read from disk when nil
133
132
  def scan_file(path, source: nil)
134
133
  source ||= File.read(path, encoding: Encoding::UTF_8)
135
134
  kept = kept_mutations(source, path)
@@ -156,8 +155,6 @@ module Rigor
156
155
  # mutant the type checker did **not** kill, asks `test_oracle` whether the project's test suite catches it.
157
156
  # The expensive suite run is paid only for type-survivors (the gradual short-circuit), so the cost is
158
157
  # proportional to the protection hole.
159
- # @param test_oracle [TestSuiteOracle]
160
- # @return [FusedFileResult]
161
158
  def scan_file_fused(path, test_oracle:, source: nil)
162
159
  source ||= File.read(path, encoding: Encoding::UTF_8)
163
160
  kept = kept_mutations(source, path)
@@ -127,6 +127,7 @@ module Rigor
127
127
  when Type::Constant then CONSTANT_CLASSES[type.value.class] || type.value.class.name
128
128
  when Type::Refined, Type::Difference then concrete_class_name(type.base)
129
129
  when Type::IntegerRange then "Integer"
130
+ when Type::FloatRange then "Float"
130
131
  when Type::App then concrete_class_name(type.bound)
131
132
  end
132
133
  end
@@ -171,7 +172,7 @@ module Rigor
171
172
  # only removes provably-Dynamic sites. Returns [kept, dropped_count]. Builds the scope index from THIS
172
173
  # mutator's parse so anchor node identity matches the keys.
173
174
  #
174
- # @param base_scope [Rigor::Scope, nil] a pre-seeded scope to judge anchors against (see
175
+ # @param base_scope — a pre-seeded scope to judge anchors against (see
175
176
  # {#anchor_base_scope}); nil builds the bare empty scope, which is the shipped default.
176
177
  def filter_by_type(mutations, environment:, path:, base_scope: nil)
177
178
  base = anchor_base_scope(environment, path, base_scope)
@@ -16,8 +16,8 @@ module Rigor
16
16
  # standard mutation-testing hazard the `ensure` cannot cover; callers running this in CI accept that, as
17
17
  # `mutant` / Stryker do.)
18
18
  class TestSuiteOracle
19
- # @param command [Array<String>] the test command (the runner hook)
20
- # @param runner [#call, nil] `runner.call(command) -> true iff the suite
19
+ # @param command — the test command (the runner hook)
20
+ # @param runner — `runner.call(command) -> true iff the suite
21
21
  # passed`. Defaults to shelling out via `system`.
22
22
  def initialize(command:, runner: nil)
23
23
  @command = command
@@ -31,9 +31,9 @@ module Rigor
31
31
  end
32
32
 
33
33
  # Killed iff the mutant turns the suite red. Restores `original` afterward.
34
- # @param path [String] the file to (temporarily) overwrite with the mutant
35
- # @param original [String] the clean bytes to restore
36
- # @param mutant_source [String] the mutated bytes to test against
34
+ # @param path — the file to (temporarily) overwrite with the mutant
35
+ # @param original — the clean bytes to restore
36
+ # @param mutant_source — the mutated bytes to test against
37
37
  def killed?(path:, original:, mutant_source:)
38
38
  File.write(path, mutant_source)
39
39
  !@runner.call(@command)
@@ -31,8 +31,9 @@ module Rigor
31
31
  # - `IncompatibleSignature` — a provided method's signature violates the interface contract (return widened,
32
32
  # or a parameter narrowed). Same rule, signature-specific message.
33
33
  # - `UnresolvedInterface` — the named interface is not loaded (a typo, or the defining library / `sig` set is
34
- # not on the RBS load path). Surfaces as `dynamic.rbs-extended.unresolved` `:info`, the fail-soft channel
35
- # the other directive parsers use, so a bad name never silently disables the author's assertion.
34
+ # not on the RBS load path). Surfaces as `dynamic.rbs-extended.unresolved` at `:warning` (#928): the
35
+ # other directive parsers fail soft to `:info`, but a name the author asserted against must not pass
36
+ # silently — the catalog ships, so an unresolved role is a typo or a missing `sig` set.
36
37
  #
37
38
  # Fail-soft throughout: a class whose own definition cannot be built (RBS error) is skipped rather than
38
39
  # reported.
@@ -60,7 +61,7 @@ module Rigor
60
61
  results
61
62
  end
62
63
 
63
- # @return [Array] zero or more records for one (class, interface) pair.
64
+ # @return zero or more records for one (class, interface) pair.
64
65
  def check_one(rbs_loader, class_name, interface_name, location)
65
66
  interface_def = resolve_interface(rbs_loader, class_name, interface_name)
66
67
  if interface_def.nil?
@@ -22,14 +22,15 @@ module Rigor
22
22
  #
23
23
  # ## Why it parses rather than reading the built environment
24
24
  #
25
- # {ConformanceChecker}, the obvious model, walks `RbsLoader`'s built env. An envelope cannot: the
25
+ # {ConformanceChecker}, the obvious model, walks `RbsLoader`'s built env. An envelope did not: the
26
26
  # diagnostic has to name **where the bound was written** (`sig/foo.rbs:12`), and the ADR-54 env
27
- # cache dumps every `RBS::Location` to a zero-range `<cached>` sentinel, so a warm run would lose
28
- # both the position and the only evidence of which file a declaration came from. Parsing the
29
- # project's own signature sources is a few milliseconds over a tree Rigor already globs, it is
30
- # identical warm and cold, and it enforces ADR-103 WD6's trust rule structurally: a `%a{pure}` in
31
- # rbs core or in a gem's shipped RBS is never read, so it cannot bound a project method that
32
- # happens to share its key.
27
+ # cache reduced every `RBS::Location` to a zero-range `<cached>` sentinel, so a warm run lost both
28
+ # the position and the only evidence of which file a declaration came from. That particular loss is
29
+ # closed now — #696 restored the buffer name, #799 an annotation's position — so what still decides
30
+ # it is the rest: parsing the project's own signature sources is a few milliseconds over a tree
31
+ # Rigor already globs, it is identical warm and cold, and it enforces ADR-103 WD6's trust rule
32
+ # structurally: a `%a{pure}` in rbs core or in a gem's shipped RBS is never read, so it cannot bound
33
+ # a project method that happens to share its key.
33
34
  #
34
35
  # Malformed payloads and `pure`-versus-`effect` contradictions are recorded on a {Reporter} the
35
36
  # scanner owns and ride out on {Result#unresolved}. They surface no diagnostic in this slice:
@@ -47,10 +48,9 @@ module Rigor
47
48
 
48
49
  module_function
49
50
 
50
- # @param sources [Array<Array(String, String)>] `[buffer name, RBS source]` pairs — the project's
51
+ # @param sources — `[buffer name, RBS source]` pairs — the project's
51
52
  # `.rbs` files, plus the virtual entries rbs-inline and plugin `source_rbs` synthesis contribute.
52
- # @param registry [Rigor::Effects::Registry] the vocabulary an unknown label is judged against.
53
- # @return [Result]
53
+ # @param registry — the vocabulary an unknown label is judged against.
54
54
  #
55
55
  # The `ANNOTATION_HINT` routing test runs here, before any parse: a source with no honoured
56
56
  # payload can contribute neither an envelope nor an unresolved report, so a signature tree with
@@ -93,11 +93,10 @@ module Rigor
93
93
  # class discovery knows", which is a project fact; a gem's class-level tag would have to
94
94
  # distribute over a definition set this reader does not have.
95
95
  #
96
- # @param loader [Rigor::Environment::RbsLoader] the run's loader; it owns the env walk
96
+ # @param loader — the run's loader; it owns the env walk
97
97
  # ({Rigor::Environment::RbsLoader#each_annotated_method_member}), so the environment itself never
98
98
  # leaves it.
99
- # @param registry [Rigor::Effects::Registry]
100
- # @return [Hash{String => Rigor::Effects::Envelope}] keyed `Class#m` / `Class.m`
99
+ # @return keyed `Class#m` / `Class.m`
101
100
  def from_loader(loader:, registry:)
102
101
  out = {}
103
102
  loader.each_annotated_method_member do |class_name, member|
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "../inference/hkt_registry"
4
4
  require_relative "../inference/hkt_body_parser"
5
+ require_relative "reporter"
5
6
 
6
7
  module Rigor
7
8
  module RbsExtended
@@ -286,10 +287,24 @@ module Rigor
286
287
  source_location.respond_to?(:start_line) ? source_location.start_line : nil
287
288
  end
288
289
 
290
+ # Issue #785 — routes a declined directive to the per-run reporter. The FIRST arm is the one that
291
+ # matters: the production reporter is {RbsExtended::Reporter}, whose surface is `record_unresolved` /
292
+ # `record_lossy_projection` / `record_hkt_error` — it has neither `#record` nor `#<<`, so before this
293
+ # arm existed every call here fell off the end of the `if` and every malformed HKT directive was
294
+ # dropped in every real run, while the doc comments claimed an `:info` entry was recorded. The
295
+ # position is flattened at record time through {RbsExtended::Reporter.position_of}, the one place
296
+ # every stream flattens through since #805, because the entry has to reach the pool drain channel as
297
+ # primitives (see {RbsExtended::Reporter}).
298
+ #
299
+ # The `#record` / `#<<` arms stay for the collecting doubles the directive specs and plugin authors
300
+ # use, which is the contract `scan_rbs_loader`'s `@param reporter [#record, nil]` documents.
289
301
  def record_hkt_error(reporter, message, source_location)
290
302
  return if reporter.nil?
291
303
 
292
- if reporter.respond_to?(:record)
304
+ if reporter.respond_to?(:record_hkt_error)
305
+ path, line, column = Reporter.position_of(source_location)
306
+ reporter.record_hkt_error(message: message, path: path, line: line, column: column)
307
+ elsif reporter.respond_to?(:record)
293
308
  reporter.record(directive: "hkt", message: message, source_location: source_location)
294
309
  elsif reporter.respond_to?(:<<)
295
310
  reporter << { directive: "hkt", message: message, source_location: source_location }