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
@@ -28,11 +28,6 @@ module Rigor
28
28
  # The violations for a single entry call (`FactoryBot.create(...)` etc.), or `[]` when the node
29
29
  # is not an entry call or its first argument is not a literal factory name. ADR-37: the engine
30
30
  # owns the walk.
31
- #
32
- # @param call_node [Prism::Node]
33
- # @param factory_index [FactoryIndex]
34
- # @param model_index [Hash, nil]
35
- # @return [Array<Violation>]
36
31
  def violations_for(call_node:, factory_index:, model_index: nil)
37
32
  return [] unless entry_call?(call_node)
38
33
 
@@ -37,7 +37,6 @@ module Rigor
37
37
  @search_paths = search_paths
38
38
  end
39
39
 
40
- # @return [FactoryIndex]
41
40
  def discover
42
41
  entries = {}
43
42
  ruby_files_under(@search_paths).each do |path|
@@ -29,7 +29,6 @@ module Rigor
29
29
  freeze
30
30
  end
31
31
 
32
- # @return [Entry, nil]
33
32
  def find(factory_name)
34
33
  @entries[factory_name.to_s]
35
34
  end
@@ -58,6 +58,7 @@ module Rigor
58
58
  class Factorybot < Rigor::Plugin::Base
59
59
  manifest(
60
60
  id: "factorybot",
61
+ target_gems: ["factory_bot"],
61
62
  # Bumped 2026-08-16 — publishes `:reachability_references` for `rigor unused` (ADR-102 WD3 / #350).
62
63
  version: "0.3.0",
63
64
  description: "Validates FactoryBot.create / build / attributes_for call shapes; " \
@@ -69,7 +70,7 @@ module Rigor
69
70
  consumes: [
70
71
  { plugin_id: "activerecord", name: :model_index, optional: true }
71
72
  ],
72
- produces: [:reachability_references]
73
+ produces: %i[reachability_references factory_index]
73
74
  )
74
75
 
75
76
  producer :factory_index, watch: -> { [[@factory_search_paths, "**/*.rb"]] } do |_params|
@@ -101,6 +102,12 @@ module Rigor
101
102
  index = producer_value(:factory_index)
102
103
  return if index.nil? || index.empty?
103
104
 
105
+ # The producer value and the ADR-9 fact are different channels (#921) — `producer_value` is this
106
+ # plugin's own cached computation, read back via `producer_value` above; `fact_store.publish` is
107
+ # what makes it visible to another plugin's `read_fact`, which is how rigor-rspec binds
108
+ # `create(:name)` to the factory's model class.
109
+ services.fact_store.publish(plugin_id: manifest.id, name: :factory_index, value: index)
110
+
104
111
  references = index.entries.values.filter_map do |entry|
105
112
  { name: entry.model_class, role: :test } if entry.model_class
106
113
  end.uniq
@@ -13,9 +13,21 @@ module Rigor
13
13
  class FFI < Base
14
14
  manifest(
15
15
  id: "ffi",
16
- version: "0.1.0",
16
+ target_gems: ["ffi"],
17
+ # Bumped 2026-09-10 (#918) — declares `config_schema` for the two `.rigor.yml` surfaces ADR-30
18
+ # WD4 (`exceptions`) and WD6 (`target`) already read off `config` but the manifest never
19
+ # published, so both keys were rejected by `Manifest#validate_config` as unknown.
20
+ version: "0.2.0",
17
21
  description: "Models FFI library bindings, struct layouts, callbacks, carrier types, and ffx target compatibility.",
18
- signature_paths: ["sig"]
22
+ signature_paths: ["sig"],
23
+ config_schema: {
24
+ # WD4 — typedef alias names the nominal-opaque-pointer heuristic should treat as a transparent
25
+ # `:pointer` alias even though they match the `_ptr$` / `_handle$` naming pattern.
26
+ "exceptions" => { kind: :array, default: [] },
27
+ # WD6 — forces ffx-target detection instead of the `extconf.rb` / `Gemfile.lock` cascade.
28
+ # `"auto"` (the default) keeps the cascade; `"ffi"` / `"ffx"` pin the target outright.
29
+ "target" => { kind: :string, default: "auto" }
30
+ }
19
31
  )
20
32
 
21
33
  producer :ffi_catalog do
@@ -25,7 +37,13 @@ module Rigor
25
37
 
26
38
  # Dynamic return rule for attached FFI functions
27
39
  # Gated on known FFI library receivers and attached function names
28
- dynamic_return receivers: -> { producer_value(:ffi_catalog)&.libraries || [] },
40
+ #
41
+ # Issue #701 — both receiver kinds are declared because the block below already resolved
42
+ # `Nominal` and `Singleton` alike: `attach_function` installs the binding on the library module
43
+ # itself (`MyLib.strlen`) and `include`ing that module makes the same name an instance call, so
44
+ # neither kind is an accident. The kind-aware gate makes that intent explicit rather than relying
45
+ # on a `receivers:` entry answering for both.
46
+ dynamic_return receivers: -> { both_receiver_kinds(producer_value(:ffi_catalog)&.libraries) },
29
47
  methods: -> { producer_value(:ffi_catalog)&.function_method_names || [] } do |call_node, scope|
30
48
  catalog = producer_value(:ffi_catalog)
31
49
  next nil if catalog.nil?
@@ -51,7 +69,11 @@ module Rigor
51
69
 
52
70
  # Dynamic return rule for FFI struct field accessors
53
71
  # Gated on known FFI struct class names and field names
54
- dynamic_return receivers: -> { producer_value(:ffi_catalog)&.struct_names || [] },
72
+ #
73
+ # Issue #701 — both kinds again, for the same reason: the block resolves the receiver itself and
74
+ # answers only for a name the discovered layout carries, so restricting the gate to instances
75
+ # would narrow a shipped rule on a shape nothing in the corpus has adjudicated.
76
+ dynamic_return receivers: -> { both_receiver_kinds(producer_value(:ffi_catalog)&.struct_names) },
55
77
  methods: -> { producer_value(:ffi_catalog)&.struct_field_names || [] } do |call_node, scope|
56
78
  catalog = producer_value(:ffi_catalog)
57
79
  next nil if catalog.nil?
@@ -114,6 +136,16 @@ module Rigor
114
136
  @target = TargetDetector.detect(root: root, config: config)
115
137
  @exceptions = config["exceptions"] || []
116
138
  end
139
+
140
+ private
141
+
142
+ # Every discovered name in both `dynamic_return receivers:` kinds (#701), for the two rules whose
143
+ # blocks accept an instance and a class receiver alike.
144
+ #
145
+ # @param names — discovered class / module names, or nil when the catalog producer did not run
146
+ def both_receiver_kinds(names)
147
+ Array(names).flat_map { |name| [name, "singleton(#{name})"] }
148
+ end
117
149
  end
118
150
  end
119
151
  end
@@ -8,6 +8,7 @@ module Rigor
8
8
  class FFIRZMQ < Base
9
9
  manifest(
10
10
  id: "ffi-rzmq",
11
+ target_gems: ["ffi-rzmq"],
11
12
  version: "0.1.0",
12
13
  description: "Models ffi-rzmq / LibZMQ wrappers, socket operations, and cross-gem bindings.",
13
14
  signature_paths: ["sig"]
@@ -40,16 +40,18 @@ module Rigor
40
40
 
41
41
  module_function
42
42
 
43
- # @param paths [Array<String>] absolute paths to `.rb` files the project's `paths:` resolves to.
44
- # @return [Hash{Symbol => Hash}] frozen 4-key result: `:types` (per-`Schema::Object` field
43
+ # @param paths absolute paths to `.rb` files the project's `paths:` resolves to.
44
+ # @param io_boundary the plugin's {Rigor::Plugin::IoBoundary}; every source read goes
45
+ # through it (#630) so each scanned file is a recorded cache dependency.
46
+ # @return frozen 4-key result: `:types` (per-`Schema::Object` field
45
47
  # table), `:enums` (per-`Schema::Enum` value list), `:input_objects`
46
48
  # (per-`Schema::InputObject` argument table), `:mutations` (per-`Schema::Mutation`
47
49
  # arguments+fields table). Any subset may be empty when no recognisable declaration of that
48
50
  # kind is found.
49
- def scan(paths:)
51
+ def scan(paths:, io_boundary:)
50
52
  acc = empty_accumulator
51
53
  paths.each do |path|
52
- merge_accumulator(acc, scan_file(path))
54
+ merge_accumulator(acc, scan_file(path, io_boundary))
53
55
  end
54
56
  freeze_accumulator(acc)
55
57
  end
@@ -75,8 +77,8 @@ module Rigor
75
77
  end
76
78
  private_class_method :freeze_accumulator
77
79
 
78
- def scan_file(path)
79
- source = File.read(path)
80
+ def scan_file(path, io_boundary)
81
+ source = io_boundary.read_file(path)
80
82
  parse_result = Prism.parse(source, filepath: path)
81
83
  return empty_accumulator unless parse_result.errors.empty?
82
84
 
@@ -50,6 +50,7 @@ module Rigor
50
50
  class Graphql < Rigor::Plugin::Base
51
51
  manifest(
52
52
  id: "graphql",
53
+ target_gems: ["graphql"],
53
54
  version: "0.1.0",
54
55
  description: "Recognises `class T < GraphQL::Schema::{Object,Enum,InputObject,Mutation}` " \
55
56
  "subclasses; publishes the per-type field-type table, the per-enum value " \
@@ -59,7 +60,7 @@ module Rigor
59
60
  )
60
61
 
61
62
  def prepare(services)
62
- scanned = TypeScanner.scan(paths: scannable_paths(services))
63
+ scanned = TypeScanner.scan(paths: scannable_paths(services), io_boundary: io_boundary)
63
64
  publish_if_present(services, :graphql_type_table, scanned.fetch(:types))
64
65
  publish_if_present(services, :graphql_enum_table, scanned.fetch(:enums))
65
66
  publish_if_present(services, :graphql_input_object_table, scanned.fetch(:input_objects))
@@ -78,11 +79,13 @@ module Rigor
78
79
  services.fact_store.publish(plugin_id: manifest.id, name: name, value: value)
79
80
  end
80
81
 
82
+ # ADR-45 WD1b (#613 / #630) — the classification probes go through the boundary, so an entry that
83
+ # is not there yet (or stops being a directory) is a recorded dependency of the scan's input set.
81
84
  def scannable_paths(services)
82
85
  @scannable_paths ||= services.configuration.paths.flat_map do |entry|
83
- if File.directory?(entry)
86
+ if io_boundary.directory?(entry)
84
87
  Dir.glob(File.join(entry, "**", "*.rb"), sort: true)
85
- elsif File.file?(entry) && entry.end_with?(".rb")
88
+ elsif io_boundary.file?(entry) && entry.end_with?(".rb")
86
89
  [entry]
87
90
  else
88
91
  []
@@ -51,6 +51,7 @@ module Rigor
51
51
  class Hanami < Rigor::Plugin::Base
52
52
  manifest(
53
53
  id: "hanami",
54
+ target_gems: ["hanami"],
54
55
  version: "0.1.0",
55
56
  description: "Enforces the Hanami::Action protocol: #handle(request, response) → void.",
56
57
  config_schema: {
@@ -56,6 +56,7 @@ module Rigor
56
56
  class Mangrove < Rigor::Plugin::Base
57
57
  manifest(
58
58
  id: "mangrove",
59
+ target_gems: ["mangrove"],
59
60
  version: "0.2.0",
60
61
  description: "Instantiates Mangrove Result/Option carrier generics at unwrap call sites, " \
61
62
  "sharpening `unwrap!` / `unwrap_in` / `unwrap_or` from `untyped` to the carried type; " \
@@ -134,7 +135,7 @@ module Rigor
134
135
 
135
136
  private
136
137
 
137
- # @return [Rigor::Type, nil] the receiver's inferred type, or nil when the engine raises on a synthetic
138
+ # @return the receiver's inferred type, or nil when the engine raises on a synthetic
138
139
  # / unrecognised node (mirrors rigor-sorbet's defensive degrade).
139
140
  def receiver_type_of(receiver, scope)
140
141
  scope.type_of(receiver)
@@ -59,9 +59,6 @@ module Rigor
59
59
  module AssertionAnalyzer
60
60
  module_function
61
61
 
62
- # @param call_node [Prism::CallNode]
63
- # @param environment [Rigor::Environment, nil]
64
- # @return [Rigor::FlowContribution, nil]
65
62
  def contribution_for(call_node, environment:)
66
63
  return nil unless call_node.is_a?(Prism::CallNode)
67
64
 
@@ -37,6 +37,7 @@ module Rigor
37
37
  class Minitest < Rigor::Plugin::Base
38
38
  manifest(
39
39
  id: "minitest",
40
+ target_gems: ["minitest"],
40
41
  version: "0.1.0",
41
42
  description: "Narrows locals through Minitest / Test::Unit `assert_*` / `refute_*` " \
42
43
  "and Minitest/spec `_(x).must_*` / `.wont_*` matchers.",
@@ -33,11 +33,6 @@ module Rigor
33
33
  # The policy violations for a single call node (0..2 of them), or `[]` when the node is not a
34
34
  # Pundit entry call or its record type is not statically determinable. ADR-37: the engine owns the
35
35
  # walk, so this is per-node logic.
36
- #
37
- # @param call_node [Prism::Node]
38
- # @param policy_index [PolicyIndex]
39
- # @param scope [Rigor::Scope, nil]
40
- # @return [Array<Violation>]
41
36
  def violations_for(call_node:, policy_index:, scope:)
42
37
  return [] unless call_node.is_a?(Prism::CallNode) && entry_call?(call_node)
43
38
 
@@ -42,7 +42,7 @@ module Rigor
42
42
  @search_paths = search_paths
43
43
  end
44
44
 
45
- # @return [Array<String>] policy class names named by an authorization call, sorted and unique. Not
45
+ # @return policy class names named by an authorization call, sorted and unique. Not
46
46
  # yet intersected with the discovered policies — the caller does that.
47
47
  def policy_names
48
48
  names = Set.new
@@ -93,7 +93,7 @@ module Rigor
93
93
  names << name if name
94
94
  end
95
95
 
96
- # @return [String, nil] the policy class the argument names, or nil when the argument is an
96
+ # @return the policy class the argument names, or nil when the argument is an
97
97
  # expression this reading cannot attribute to a record (a method call with arguments, a literal, an
98
98
  # index read). Silence, not a guess: an unattributable call contributes nothing.
99
99
  def policy_name_for(node)
@@ -28,7 +28,6 @@ module Rigor
28
28
  @base_classes = base_classes.to_set
29
29
  end
30
30
 
31
- # @return [PolicyIndex]
32
31
  def discover
33
32
  entries = []
34
33
  ruby_files_under(@search_paths).each do |path|
@@ -35,7 +35,6 @@ module Rigor
35
35
  freeze
36
36
  end
37
37
 
38
- # @return [Entry, nil]
39
38
  def find(policy_class_name)
40
39
  @by_name[policy_class_name.to_s]
41
40
  end
@@ -46,6 +46,7 @@ module Rigor
46
46
  class Pundit < Rigor::Plugin::Base
47
47
  manifest(
48
48
  id: "pundit",
49
+ target_gems: ["pundit"],
49
50
  # Bumped 2026-08-16 — publishes `:reachability_roots` for `rigor unused` (ADR-102 WD3): the policy
50
51
  # classes the project's own `authorize` / `policy` / `policy_scope` calls name by convention.
51
52
  version: "0.2.0",
@@ -91,22 +91,10 @@ module Rigor
91
91
  RAILS_SHIPPED_KEY_PREFIXES.any? { |prefix| key_str.start_with?(prefix) }
92
92
  end
93
93
 
94
- # @param path [String]
95
- # @param root [Prism::Node]
96
- # @param locale_index [LocaleIndex]
97
- # @param configured_locales [Array<String>]
98
- # @return [Array<Diagnostic>]
99
94
  # The translation violations for a single call node, or `[]` when it is not a recognised `t` /
100
95
  # `translate` call with a literal key. ADR-37: the engine owns the walk; `action` (the enclosing
101
96
  # method, for lazy `t('.key')` expansion) comes from the node-rule `NodeContext`, and
102
97
  # `controller_scope` from the file path.
103
- #
104
- # @param call_node [Prism::Node]
105
- # @param locale_index [LocaleIndex]
106
- # @param configured_locales [Array<String>]
107
- # @param controller_scope [String, nil]
108
- # @param action [String, nil]
109
- # @return [Array<Violation>]
110
98
  def violations_for(call_node:, locale_index:, configured_locales:, controller_scope:, action:)
111
99
  return [] unless call_node.is_a?(Prism::CallNode) && translate_call_candidate?(call_node)
112
100
 
@@ -36,8 +36,7 @@ module Rigor
36
36
 
37
37
  attr_reader :entries, :locales
38
38
 
39
- # @param entries [Array<Entry>]
40
- # @param locales [Array<String>] all locale names that contributed at least one key.
39
+ # @param locales — all locale names that contributed at least one key.
41
40
  def initialize(entries, locales:)
42
41
  @entries = entries.freeze
43
42
  @locales = locales.dup.freeze
@@ -45,7 +44,6 @@ module Rigor
45
44
  freeze
46
45
  end
47
46
 
48
- # @return [Entry, nil]
49
47
  def find(dotted_key)
50
48
  @by_key[dotted_key.to_s]
51
49
  end
@@ -35,7 +35,6 @@ module Rigor
35
35
  @load_errors = []
36
36
  end
37
37
 
38
- # @return [LocaleIndex]
39
38
  def load
40
39
  per_key = {} # dotted_key => { locale => Set<String> }
41
40
  per_key_kinds = {} # dotted_key => { locale => :string|:array|:hash }
@@ -57,6 +57,7 @@ module Rigor
57
57
  class RailsI18n < Rigor::Plugin::Base
58
58
  manifest(
59
59
  id: "rails-i18n",
60
+ target_gems: ["i18n"],
60
61
  # Bumped 2026-06-23 — view template lazy-key scanning (`t('.key')` inside ERB / Haml / Slim under
61
62
  # `view_search_paths`).
62
63
  version: "0.3.0",
@@ -24,8 +24,8 @@ module Rigor
24
24
  module Acronyms
25
25
  module_function
26
26
 
27
- # @param contents [String, nil] the source of `config/initializers/inflections.rb`.
28
- # @return [Array<String>] declared acronyms in declaration order. Empty for a missing / unparseable
27
+ # @param contents the source of `config/initializers/inflections.rb`.
28
+ # @return declared acronyms in declaration order. Empty for a missing / unparseable
29
29
  # file, or one that declares none — which leaves composition exactly as it was.
30
30
  def discover(contents)
31
31
  return [] if contents.nil? || contents.empty?
@@ -51,9 +51,9 @@ module Rigor
51
51
  # camelization of the acronym appears at a camel-word boundary — `Oauth` in `OauthMetadata` and in
52
52
  # `WellKnown::Oauth`, but never the `Oauth` inside a hypothetical `Xoauth`.
53
53
  #
54
- # @param name [String] e.g. `"Activitypub::CollectionsController"`.
55
- # @param acronyms [Enumerable<String>] as returned by {.discover}.
56
- # @return [String] e.g. `"ActivityPub::CollectionsController"`.
54
+ # @param name e.g. `"Activitypub::CollectionsController"`.
55
+ # @param acronyms as returned by {.discover}.
56
+ # @return e.g. `"ActivityPub::CollectionsController"`.
57
57
  def apply(name, acronyms)
58
58
  acronyms.reduce(name) do |current, acronym|
59
59
  plain = Rigor::Plugin::Inflector.camelize(acronym.to_s.downcase)
@@ -61,20 +61,11 @@ module Rigor
61
61
  %r{(?:\A|/)config/}
62
62
  ].freeze
63
63
 
64
- # @param path [String] file being analysed
65
- # @param root [Prism::Node]
66
- # @param helper_table [HelperTable]
67
- # @return [Array<Diagnostic>]
64
+ # @param path file being analysed
68
65
  # The route-helper violations for a single call node (0..2), or `[]` when the node is not an
69
66
  # implicit `*_path` / `*_url` helper call, is shadowed by a same-file binding, or is in a
70
67
  # suppressed directory. ADR-37: the engine owns the walk; the same-file `shadowing` set is built
71
68
  # once as the node-rule file context.
72
- #
73
- # @param call_node [Prism::Node]
74
- # @param helper_table [HelperTable]
75
- # @param shadowing [Set<String>]
76
- # @param path [String]
77
- # @return [Array<Violation>]
78
69
  def violations_for(call_node:, helper_table:, shadowing:, path:)
79
70
  return [] unless call_node.is_a?(Prism::CallNode) && implicit_helper_call?(call_node)
80
71
 
@@ -70,11 +70,11 @@ module Rigor
70
70
 
71
71
  module_function
72
72
 
73
- # @param resource [String, Symbol] the `devise_for` argument — typically `:users`. Used as both the
73
+ # @param resource the `devise_for` argument — typically `:users`. Used as both the
74
74
  # helper-name segment (`new_user_session_path`) and to derive the resource path.
75
- # @param skip [Array<Symbol>] controllers the project disables via `devise_for :users, skip:
75
+ # @param skip controllers the project disables via `devise_for :users, skip:
76
76
  # [:registrations]`.
77
- # @return [Array<HelperTable::Entry>] one entry per generated `_path` helper. The caller is
77
+ # @return one entry per generated `_path` helper. The caller is
78
78
  # expected to pair `_url` variants the same way `RoutesParser` does for other entries.
79
79
  def generate(resource:, skip: [])
80
80
  resource_segment = singularize(resource.to_s)
@@ -66,8 +66,8 @@ module Rigor
66
66
 
67
67
  module_function
68
68
 
69
- # @param skip [Array<Symbol>] controller names the project omits via `skip_controllers :name, ...`.
70
- # @return [Array<HelperTable::Entry>] flattened entries.
69
+ # @param skip controller names the project omits via `skip_controllers :name, ...`.
70
+ # @return flattened entries.
71
71
  def generate(skip: [])
72
72
  skip_set = skip.to_set(&:to_sym)
73
73
  CONTROLLER_HELPERS.flat_map do |controller, rows|
@@ -43,9 +43,9 @@ module Rigor
43
43
 
44
44
  module_function
45
45
 
46
- # @param contents_per_path [Hash{String => String}] file path → source text, read by the caller
46
+ # @param contents_per_path file path → source text, read by the caller
47
47
  # (through the trusted `IoBoundary`, so cache invalidation works).
48
- # @return [Array<String>] the recognised helper-name prefixes (`["api_v3", "api_v4"]`). Empty when
48
+ # @return the recognised helper-name prefixes (`["api_v3", "api_v4"]`). Empty when
49
49
  # the project declares no grape API — nothing changes for such a project.
50
50
  def discover(contents_per_path)
51
51
  declarations = {}
@@ -43,10 +43,10 @@ module Rigor
43
43
 
44
44
  module_function
45
45
 
46
- # @param contents_per_path [Hash{String => String}] file path → source text. The caller is
46
+ # @param contents_per_path file path → source text. The caller is
47
47
  # responsible for reading files (typically through the trusted `IoBoundary` so cache invalidation
48
48
  # works).
49
- # @return [Set<String>] method names suitable for inclusion in the `HelperTable`'s custom-helper set.
49
+ # @return method names suitable for inclusion in the `HelperTable`'s custom-helper set.
50
50
  def discover(contents_per_path)
51
51
  names = []
52
52
  contents_per_path.each_value do |contents|
@@ -26,21 +26,21 @@ module Rigor
26
26
 
27
27
  attr_reader :entries, :custom_helpers, :devise_resources
28
28
 
29
- # @param entries [Array<Entry>] freshly built; the factory below is the canonical construction path.
30
- # @param custom_helpers [Enumerable<String>] names of project-defined helper methods (typically
29
+ # @param entries freshly built; the factory below is the canonical construction path.
30
+ # @param custom_helpers names of project-defined helper methods (typically
31
31
  # pulled from `app/helpers/**/*.rb` by {HelperDiscoverer}) that the analyzer should treat as
32
32
  # known — they do NOT correspond to a route but their presence MUST NOT fire `unknown-helper`. No
33
33
  # arity / path metadata is recorded; the analyzer skips the route-side checks for these names.
34
- # @param devise_resources [Enumerable<String>] resource names declared via `devise_for :resource`
34
+ # @param devise_resources resource names declared via `devise_for :resource`
35
35
  # (already singularised, e.g. `"user"`). Used to recognise the dynamic OmniAuth helper family
36
36
  # (`<resource>_<provider>_omniauth_(authorize|callback)_(path|url)`) whose provider segment is
37
37
  # supplied at runtime — no per-name entry is registered for them but they MUST NOT fire
38
38
  # `unknown-helper` either.
39
- # @param grape_prefixes [Enumerable<String>] helper-name prefixes generated by `grape-path-helpers`
39
+ # @param grape_prefixes helper-name prefixes generated by `grape-path-helpers`
40
40
  # (`"api_v4"`), composed by {GrapeApiDiscoverer} from the project's own `prefix` / `version`
41
41
  # declarations. The names beyond the prefix come from grape's runtime route table and cannot be
42
42
  # enumerated statically, so the namespace is open — same contract as the OmniAuth family above.
43
- # @param controllers [Enumerable<String>] fully-qualified controller class names the routes file
43
+ # @param controllers fully-qualified controller class names the routes file
44
44
  # dispatches to (`"Admin::UsersController"`). Published as the plugin's `:reachability_roots` fact
45
45
  # (ADR-102 WD3) — a controller is an entry point nothing in the project references, so without this
46
46
  # every routed controller reads as unused. Deliberately NOT consulted by any helper-name check.
@@ -58,20 +58,19 @@ module Rigor
58
58
  freeze
59
59
  end
60
60
 
61
- # @return [Array<String>] the routed controller class names (see the `controllers:` parameter). Reads
61
+ # @return the routed controller class names (see the `controllers:` parameter). Reads
62
62
  # through a nil guard rather than `attr_reader` so a table deserialised from a cache slot written
63
63
  # before this field existed answers `[]` instead of `nil`.
64
64
  def controllers
65
65
  @controllers || []
66
66
  end
67
67
 
68
- # @return [Entry, nil] First matching entry; for the uncountable-noun case this is the index helper
68
+ # @return First matching entry; for the uncountable-noun case this is the index helper
69
69
  # (the show helper is also registered but starts second).
70
70
  def find(helper_name)
71
71
  @by_name[helper_name.to_s]&.first
72
72
  end
73
73
 
74
- # @return [Boolean]
75
74
  def known?(helper_name)
76
75
  @by_name.key?(helper_name.to_s)
77
76
  end
@@ -118,7 +117,7 @@ module Rigor
118
117
  end
119
118
  end
120
119
 
121
- # @return [Boolean] true when any entry under this helper name accepts the given positional arity.
120
+ # @return true when any entry under this helper name accepts the given positional arity.
122
121
  #
123
122
  # Rails helpers have the signature `helper(*segments, options = {})`, so `expected + 1` is always
124
123
  # valid — the extra argument is treated as a query-params/options hash (e.g. `users_path(page: 2)`
@@ -127,7 +126,7 @@ module Rigor
127
126
  (@by_name[helper_name.to_s] || []).any? { |entry| entry.arity == arity || entry.arity + 1 == arity }
128
127
  end
129
128
 
130
- # @return [Array<Integer>] all accepted positional arities for a helper name. Empty when unknown.
129
+ # @return all accepted positional arities for a helper name. Empty when unknown.
131
130
  def acceptable_arities(helper_name)
132
131
  (@by_name[helper_name.to_s] || []).map(&:arity).uniq
133
132
  end
@@ -54,11 +54,10 @@ module Rigor
54
54
 
55
55
  module_function
56
56
 
57
- # @param contents [String] raw `config/routes.rb` source
58
- # @param file_reader [Proc, nil] called with `"name.rb"` to load a
57
+ # @param contents raw `config/routes.rb` source
58
+ # @param file_reader called with `"name.rb"` to load a
59
59
  # draw partial from `config/routes/name.rb`. Returns file contents
60
60
  # or nil when the file is absent.
61
- # @return [HelperTable]
62
61
  def parse(contents, file_reader: nil, custom_helpers: [], grape_prefixes: [], acronyms: [])
63
62
  parse_result = Prism.parse(contents)
64
63
  unless parse_result.errors.empty?
@@ -52,6 +52,7 @@ module Rigor
52
52
  class RailsRoutes < Rigor::Plugin::Base
53
53
  manifest(
54
54
  id: "rails-routes",
55
+ target_gems: %w[actionpack railties],
55
56
  # Bumped 2026-05-28 — GitLab FOSS sweep adds: (a) `draw_all :name` support (action_dispatch-draw_all
56
57
  # gem; single-file load semantics matching `draw :name`); (b) keyword-style `scope(path:
57
58
  # ':project_id', as: :project)` — path read from the `:path` keyword, not only from the positional
@@ -52,6 +52,7 @@ module Rigor
52
52
  class Railties < Rigor::Plugin::Base
53
53
  manifest(
54
54
  id: "railties",
55
+ target_gems: ["railties"],
55
56
  # Bumped 2026-09-01 (#534 item 2) — the four `Rails.` singleton readers now return lenient
56
57
  # nominals instead of `Dynamic[top]`.
57
58
  version: "0.2.0",
@@ -8,8 +8,18 @@ module Rigor
8
8
  class RbNaCl < Base
9
9
  manifest(
10
10
  id: "rbnacl",
11
+ target_gems: ["rbnacl"],
11
12
  version: "0.1.0",
12
- description: "Rigor type support for RbNaCl libsodium bindings"
13
+ description: "Rigor type support for RbNaCl libsodium bindings",
14
+ signature_paths: ["sig"],
15
+ # ADR-26 — `sig/rbnacl.rbs` declares three methods of a class that has many more (`nonce_bytes`,
16
+ # `key_bytes`, …). Contributing it without opening the receivers would close those classes and
17
+ # manufacture `call.undefined-method` on correct code, which is the trade the project refuses.
18
+ open_receivers: [
19
+ "RbNaCl::SecretBox",
20
+ "RbNaCl::Signatures::Ed25519::SigningKey",
21
+ "RbNaCl::Signatures::Ed25519::VerifyKey"
22
+ ]
13
23
  )
14
24
 
15
25
  # Issue 2 & 5: use robust positional binding extractor and preserve module context
@@ -28,3 +38,5 @@ module Rigor
28
38
  end
29
39
  end
30
40
  end
41
+
42
+ Rigor::Plugin.register(Rigor::Plugin::RbNaCl)