rigortype 0.4.6 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (550) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/data/core_overlay/bigdecimal.rbs +33 -0
  4. data/data/core_overlay/resolv.rbs +30 -0
  5. data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
  6. data/data/gem_overlay/addressable/addressable.rbs +40 -0
  7. data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
  8. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
  9. data/docs/handbook/01-getting-started.md +6 -3
  10. data/docs/handbook/11-sig-gen.md +85 -3
  11. data/docs/handbook/appendix-phpstan.md +4 -4
  12. data/docs/install.md +20 -0
  13. data/docs/manual/01-installation.md +67 -26
  14. data/docs/manual/02-cli-reference.md +455 -113
  15. data/docs/manual/03-configuration.md +76 -5
  16. data/docs/manual/04-diagnostics.md +5 -4
  17. data/docs/manual/06-baseline.md +6 -4
  18. data/docs/manual/08-skills.md +11 -0
  19. data/docs/manual/09-editor-integration.md +21 -2
  20. data/docs/manual/10-mcp-server.md +38 -2
  21. data/docs/manual/12-caching.md +4 -3
  22. data/docs/manual/14-rails-quickstart.md +1 -1
  23. data/docs/manual/19-effect-labels.md +3 -4
  24. data/docs/manual/plugins/rigor-actionpack.md +32 -15
  25. data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
  26. data/docs/manual/plugins/rigor-sorbet.md +17 -3
  27. data/lib/rigor/analysis/baseline.rb +24 -27
  28. data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
  29. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
  30. data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
  31. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
  32. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
  33. data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
  34. data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
  35. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
  36. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
  37. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
  38. data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
  40. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
  41. data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
  42. data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
  43. data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
  44. data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
  45. data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
  46. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
  47. data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
  48. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  49. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
  50. data/lib/rigor/analysis/check_rules.rb +720 -141
  51. data/lib/rigor/analysis/crash_signature.rb +45 -3
  52. data/lib/rigor/analysis/dependency_recorder.rb +237 -80
  53. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
  54. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
  55. data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
  56. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  57. data/lib/rigor/analysis/edit_replay.rb +93 -6
  58. data/lib/rigor/analysis/fact_store.rb +3 -3
  59. data/lib/rigor/analysis/incremental.rb +11 -1
  60. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
  61. data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
  62. data/lib/rigor/analysis/incremental_session.rb +306 -53
  63. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
  64. data/lib/rigor/analysis/rule_catalog.rb +39 -3
  65. data/lib/rigor/analysis/run_cache_key.rb +27 -3
  66. data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
  67. data/lib/rigor/analysis/run_stats.rb +2 -2
  68. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
  69. data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
  70. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
  71. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
  72. data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
  73. data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
  74. data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
  75. data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
  76. data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
  77. data/lib/rigor/analysis/runner.rb +298 -109
  78. data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
  79. data/lib/rigor/analysis/template_unit_positions.rb +2 -3
  80. data/lib/rigor/analysis/template_units.rb +9 -1
  81. data/lib/rigor/analysis/worker_session.rb +65 -71
  82. data/lib/rigor/bleeding_edge.rb +22 -10
  83. data/lib/rigor/builtins/imported_refinements.rb +6 -8
  84. data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
  85. data/lib/rigor/builtins/regex_refinement.rb +4 -6
  86. data/lib/rigor/cache/descriptor.rb +6 -5
  87. data/lib/rigor/cache/file_digest.rb +39 -31
  88. data/lib/rigor/cache/incremental_snapshot.rb +95 -17
  89. data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
  90. data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
  91. data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
  92. data/lib/rigor/cache/rbs_constant_table.rb +1 -1
  93. data/lib/rigor/cache/rbs_descriptor.rb +90 -4
  94. data/lib/rigor/cache/rbs_environment.rb +8 -1
  95. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
  96. data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
  97. data/lib/rigor/cache/store.rb +11 -10
  98. data/lib/rigor/ci_detector.rb +1 -1
  99. data/lib/rigor/cli/annotate_command.rb +24 -19
  100. data/lib/rigor/cli/baseline_command.rb +160 -57
  101. data/lib/rigor/cli/catalogue.rb +256 -0
  102. data/lib/rigor/cli/check_command.rb +112 -104
  103. data/lib/rigor/cli/check_runner_factory.rb +2 -2
  104. data/lib/rigor/cli/command.rb +15 -1
  105. data/lib/rigor/cli/coverage_command.rb +21 -8
  106. data/lib/rigor/cli/coverage_mutation.rb +5 -7
  107. data/lib/rigor/cli/coverage_renderer.rb +2 -1
  108. data/lib/rigor/cli/coverage_scan.rb +3 -5
  109. data/lib/rigor/cli/diff_command.rb +25 -20
  110. data/lib/rigor/cli/docs_command.rb +1 -2
  111. data/lib/rigor/cli/doctor_command.rb +12 -12
  112. data/lib/rigor/cli/effects_command.rb +50 -40
  113. data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
  114. data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
  115. data/lib/rigor/cli/effects_renderer.rb +2 -1
  116. data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
  117. data/lib/rigor/cli/explain_command.rb +18 -8
  118. data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
  119. data/lib/rigor/cli/json_document.rb +41 -0
  120. data/lib/rigor/cli/lens_command.rb +29 -10
  121. data/lib/rigor/cli/lsp_command.rb +250 -23
  122. data/lib/rigor/cli/mcp_command.rb +12 -7
  123. data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
  124. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
  125. data/lib/rigor/cli/options.rb +40 -1
  126. data/lib/rigor/cli/plugin_command.rb +93 -46
  127. data/lib/rigor/cli/plugins_command.rb +17 -18
  128. data/lib/rigor/cli/plugins_renderer.rb +10 -6
  129. data/lib/rigor/cli/protection_fork_scan.rb +2 -2
  130. data/lib/rigor/cli/protection_renderer.rb +2 -1
  131. data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
  132. data/lib/rigor/cli/sig_gen_command.rb +92 -27
  133. data/lib/rigor/cli/skill_command.rb +32 -3
  134. data/lib/rigor/cli/skill_deep_probe.rb +1 -1
  135. data/lib/rigor/cli/skill_describe.rb +2 -1
  136. data/lib/rigor/cli/trace_command.rb +14 -9
  137. data/lib/rigor/cli/triage_command.rb +10 -11
  138. data/lib/rigor/cli/triage_renderer.rb +2 -1
  139. data/lib/rigor/cli/type_of_command.rb +16 -11
  140. data/lib/rigor/cli/type_of_project_scope.rb +1 -4
  141. data/lib/rigor/cli/type_of_renderer.rb +4 -1
  142. data/lib/rigor/cli/type_scan_command.rb +14 -9
  143. data/lib/rigor/cli/type_scan_renderer.rb +2 -1
  144. data/lib/rigor/cli/unused_command.rb +27 -17
  145. data/lib/rigor/cli/upgrade_command.rb +5 -0
  146. data/lib/rigor/cli.rb +214 -56
  147. data/lib/rigor/config_audit.rb +33 -19
  148. data/lib/rigor/configuration/severity_profile.rb +14 -5
  149. data/lib/rigor/configuration.rb +104 -35
  150. data/lib/rigor/effects/collector.rb +53 -21
  151. data/lib/rigor/effects/definition_context.rb +0 -2
  152. data/lib/rigor/effects/envelope.rb +1 -1
  153. data/lib/rigor/effects/framework_units.rb +1 -1
  154. data/lib/rigor/effects/mutation_classifier.rb +4 -7
  155. data/lib/rigor/effects/propagator.rb +1 -1
  156. data/lib/rigor/effects/registry.rb +2 -2
  157. data/lib/rigor/effects/snapshot.rb +1 -1
  158. data/lib/rigor/effects/taint_cause.rb +0 -2
  159. data/lib/rigor/effects/unit_scan.rb +9 -13
  160. data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
  161. data/lib/rigor/environment/class_registry.rb +3 -3
  162. data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
  163. data/lib/rigor/environment/core_root.rb +77 -0
  164. data/lib/rigor/environment/default_libraries.rb +18 -1
  165. data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
  166. data/lib/rigor/environment/gated_signature_guard.rb +1 -1
  167. data/lib/rigor/environment/gated_signature_plan.rb +5 -2
  168. data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
  169. data/lib/rigor/environment/lockfile_resolver.rb +28 -12
  170. data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
  171. data/lib/rigor/environment/member_consistency.rb +56 -7
  172. data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
  173. data/lib/rigor/environment/project_path_gems.rb +124 -0
  174. data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
  175. data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
  176. data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
  177. data/lib/rigor/environment/rbs_loader.rb +772 -110
  178. data/lib/rigor/environment/reflection.rb +7 -7
  179. data/lib/rigor/environment/required_features.rb +0 -3
  180. data/lib/rigor/environment/ruby_library_roots.rb +1 -1
  181. data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
  182. data/lib/rigor/environment.rb +169 -42
  183. data/lib/rigor/flow_contribution/merger.rb +4 -4
  184. data/lib/rigor/inference/acceptance.rb +136 -40
  185. data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
  186. data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
  187. data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
  188. data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
  189. data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
  190. data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
  191. data/lib/rigor/inference/block_auto_splat.rb +1 -1
  192. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  193. data/lib/rigor/inference/block_run_count.rb +185 -0
  194. data/lib/rigor/inference/budget_trace.rb +8 -2
  195. data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
  196. data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
  197. data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
  198. data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
  199. data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
  200. data/lib/rigor/inference/callee_write_census.rb +815 -0
  201. data/lib/rigor/inference/captured_locals.rb +2 -0
  202. data/lib/rigor/inference/census_foreign_block.rb +354 -0
  203. data/lib/rigor/inference/clause_widening.rb +1 -1
  204. data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
  205. data/lib/rigor/inference/construction_escape.rb +258 -0
  206. data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
  207. data/lib/rigor/inference/declaration_walk.rb +4 -0
  208. data/lib/rigor/inference/deep_erasure.rb +39 -7
  209. data/lib/rigor/inference/def_handle.rb +1 -1
  210. data/lib/rigor/inference/def_node_resolver.rb +33 -15
  211. data/lib/rigor/inference/def_return_typer.rb +47 -5
  212. data/lib/rigor/inference/definer_resolution.rb +19 -9
  213. data/lib/rigor/inference/definers_index.rb +162 -0
  214. data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
  215. data/lib/rigor/inference/element_read_widening.rb +49 -3
  216. data/lib/rigor/inference/error_info.rb +16 -0
  217. data/lib/rigor/inference/expression_typer.rb +1559 -320
  218. data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
  219. data/lib/rigor/inference/flow_tracer.rb +31 -14
  220. data/lib/rigor/inference/fork_map.rb +3 -0
  221. data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
  222. data/lib/rigor/inference/global_write_census.rb +81 -5
  223. data/lib/rigor/inference/global_writer_reach.rb +260 -0
  224. data/lib/rigor/inference/hkt_body.rb +1 -2
  225. data/lib/rigor/inference/hkt_body_parser.rb +3 -5
  226. data/lib/rigor/inference/hkt_reducer.rb +2 -2
  227. data/lib/rigor/inference/hkt_registry.rb +3 -5
  228. data/lib/rigor/inference/in_effect_refinements.rb +7 -0
  229. data/lib/rigor/inference/indexed_narrowing.rb +61 -15
  230. data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
  231. data/lib/rigor/inference/ivar_writer_census.rb +154 -8
  232. data/lib/rigor/inference/key_presence_guard.rb +51 -24
  233. data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
  234. data/lib/rigor/inference/last_line.rb +5 -2
  235. data/lib/rigor/inference/loop_value_record.rb +66 -14
  236. data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
  237. data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
  238. data/lib/rigor/inference/match_rebinding.rb +28 -5
  239. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
  240. data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
  241. data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
  242. data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
  243. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
  244. data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
  245. data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
  246. data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
  247. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
  248. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
  249. data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
  250. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
  251. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
  252. data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
  253. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
  254. data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
  255. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
  256. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
  257. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
  258. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
  259. data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
  260. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
  261. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
  262. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
  263. data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
  264. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
  265. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
  266. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  267. data/lib/rigor/inference/method_dispatcher.rb +123 -25
  268. data/lib/rigor/inference/method_parameter_binder.rb +136 -56
  269. data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
  270. data/lib/rigor/inference/module_function_state.rb +24 -3
  271. data/lib/rigor/inference/multi_target_binder.rb +2 -2
  272. data/lib/rigor/inference/mutation_rejoin.rb +240 -4
  273. data/lib/rigor/inference/mutation_widening.rb +199 -29
  274. data/lib/rigor/inference/narrowing.rb +238 -50
  275. data/lib/rigor/inference/operand_effects.rb +43 -2
  276. data/lib/rigor/inference/optimistic_origin.rb +134 -9
  277. data/lib/rigor/inference/origin_lookup.rb +1 -1
  278. data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
  279. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  280. data/lib/rigor/inference/pre_eval_constants.rb +1 -1
  281. data/lib/rigor/inference/precision_scanner.rb +1 -1
  282. data/lib/rigor/inference/project_method_ownership.rb +30 -9
  283. data/lib/rigor/inference/project_patched_methods.rb +65 -13
  284. data/lib/rigor/inference/project_patched_scanner.rb +120 -25
  285. data/lib/rigor/inference/project_patched_visibility.rb +39 -3
  286. data/lib/rigor/inference/project_yielder.rb +11 -17
  287. data/lib/rigor/inference/rbs_type_translator.rb +68 -15
  288. data/lib/rigor/inference/receiver_alias.rb +20 -0
  289. data/lib/rigor/inference/receiver_writer_call.rb +67 -0
  290. data/lib/rigor/inference/recursion_rebinding.rb +157 -0
  291. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
  292. data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
  293. data/lib/rigor/inference/refined_dispatch.rb +13 -0
  294. data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
  295. data/lib/rigor/inference/remover_holder_scan.rb +287 -0
  296. data/lib/rigor/inference/remover_narrowing.rb +224 -0
  297. data/lib/rigor/inference/rewrite_mutation.rb +53 -2
  298. data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
  299. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
  300. data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
  301. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
  302. data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
  303. data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
  304. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
  305. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
  306. data/lib/rigor/inference/scope_indexer.rb +2213 -226
  307. data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
  308. data/lib/rigor/inference/self_preserving_block.rb +87 -0
  309. data/lib/rigor/inference/set_mutation.rb +189 -0
  310. data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
  311. data/lib/rigor/inference/singleton_object_constant.rb +32 -2
  312. data/lib/rigor/inference/statement_evaluator.rb +3268 -358
  313. data/lib/rigor/inference/string_mutation.rb +14 -0
  314. data/lib/rigor/inference/struct_fold_safety.rb +4 -6
  315. data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
  316. data/lib/rigor/inference/toplevel_def_census.rb +67 -0
  317. data/lib/rigor/inference/unknown_store_widening.rb +103 -4
  318. data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
  319. data/lib/rigor/inference/untyped_rejoin.rb +1 -1
  320. data/lib/rigor/inference/void_tail_summary.rb +29 -9
  321. data/lib/rigor/language_server/buffer_table.rb +3 -3
  322. data/lib/rigor/language_server/completion_provider.rb +26 -20
  323. data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
  324. data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
  325. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  326. data/lib/rigor/language_server/folding_range_provider.rb +1 -2
  327. data/lib/rigor/language_server/hover_provider.rb +1 -7
  328. data/lib/rigor/language_server/hover_renderer.rb +6 -8
  329. data/lib/rigor/language_server/project_context.rb +39 -1
  330. data/lib/rigor/language_server/publish_batcher.rb +3 -4
  331. data/lib/rigor/language_server/selection_range_provider.rb +2 -4
  332. data/lib/rigor/language_server/server.rb +7 -3
  333. data/lib/rigor/language_server/signature_help_provider.rb +4 -4
  334. data/lib/rigor/lens/claims/comparison.rb +78 -0
  335. data/lib/rigor/lens/claims/request.rb +131 -0
  336. data/lib/rigor/lens/claims/type_reader.rb +216 -0
  337. data/lib/rigor/lens/claims.rb +208 -0
  338. data/lib/rigor/lens/contract_reader.rb +5 -0
  339. data/lib/rigor/lens/document.rb +1 -1
  340. data/lib/rigor/lens/file_reader.rb +21 -8
  341. data/lib/rigor/lens/plugins.rb +1 -1
  342. data/lib/rigor/lens/run.rb +8 -0
  343. data/lib/rigor/lens.rb +1 -1
  344. data/lib/rigor/library_probe.rb +68 -0
  345. data/lib/rigor/mcp/server.rb +73 -2
  346. data/lib/rigor/mcp.rb +1 -1
  347. data/lib/rigor/memo_audit.rb +282 -0
  348. data/lib/rigor/plugin/access_denied_error.rb +1 -2
  349. data/lib/rigor/plugin/base.rb +15 -2
  350. data/lib/rigor/plugin/bundled_catalog.rb +1 -12
  351. data/lib/rigor/plugin/io_boundary.rb +26 -4
  352. data/lib/rigor/plugin/loader.rb +25 -8
  353. data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
  354. data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
  355. data/lib/rigor/plugin/manifest.rb +16 -16
  356. data/lib/rigor/plugin/registry.rb +16 -5
  357. data/lib/rigor/plugin/services.rb +2 -3
  358. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
  359. data/lib/rigor/plugin/trust_policy.rb +48 -2
  360. data/lib/rigor/plugin/type_node_resolver.rb +2 -4
  361. data/lib/rigor/plugin.rb +6 -8
  362. data/lib/rigor/plugin_gap_advisory.rb +5 -1
  363. data/lib/rigor/project_environment.rb +16 -2
  364. data/lib/rigor/protection/analysis_guard.rb +1 -1
  365. data/lib/rigor/protection/discovery_seed.rb +44 -5
  366. data/lib/rigor/protection/mutation_cache.rb +4 -1
  367. data/lib/rigor/protection/mutator.rb +1 -1
  368. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  369. data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
  370. data/lib/rigor/rbs_extended/reporter.rb +27 -3
  371. data/lib/rigor/rbs_extended.rb +2 -3
  372. data/lib/rigor/reflection/constant_ancestors.rb +81 -22
  373. data/lib/rigor/reflection/declaration_collectors.rb +55 -0
  374. data/lib/rigor/reflection/declarations.rb +58 -0
  375. data/lib/rigor/reflection/project_method.rb +177 -0
  376. data/lib/rigor/reflection.rb +2 -0
  377. data/lib/rigor/rooted_glob.rb +32 -1
  378. data/lib/rigor/run_scoped_slots.rb +38 -0
  379. data/lib/rigor/runtime/jit.rb +1 -1
  380. data/lib/rigor/scope/discovery_index.rb +168 -8
  381. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
  382. data/lib/rigor/scope/resolution_chain.rb +901 -54
  383. data/lib/rigor/scope.rb +644 -37
  384. data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
  385. data/lib/rigor/sig_gen/classification.rb +18 -1
  386. data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
  387. data/lib/rigor/sig_gen/generator.rb +588 -66
  388. data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
  389. data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
  390. data/lib/rigor/sig_gen/layout_index.rb +3 -3
  391. data/lib/rigor/sig_gen/method_candidate.rb +37 -4
  392. data/lib/rigor/sig_gen/observation_collector.rb +14 -7
  393. data/lib/rigor/sig_gen/observed_call.rb +1 -1
  394. data/lib/rigor/sig_gen/path_mapper.rb +6 -0
  395. data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
  396. data/lib/rigor/sig_gen/renderer.rb +15 -9
  397. data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
  398. data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
  399. data/lib/rigor/sig_gen/write_reporter.rb +63 -14
  400. data/lib/rigor/sig_gen/write_result.rb +23 -12
  401. data/lib/rigor/sig_gen/writer.rb +238 -37
  402. data/lib/rigor/sig_gen.rb +1 -0
  403. data/lib/rigor/source/forwardable_delegators.rb +122 -0
  404. data/lib/rigor/source/node_children.rb +13 -0
  405. data/lib/rigor/type/acceptance_router.rb +2 -2
  406. data/lib/rigor/type/accepts_result.rb +1 -1
  407. data/lib/rigor/type/app.rb +4 -6
  408. data/lib/rigor/type/combinator.rb +173 -21
  409. data/lib/rigor/type/constant.rb +4 -5
  410. data/lib/rigor/type/hash_shape.rb +21 -40
  411. data/lib/rigor/type/interface.rb +60 -0
  412. data/lib/rigor/type/union.rb +1 -1
  413. data/lib/rigor/type.rb +1 -0
  414. data/lib/rigor/type_node/identifier.rb +1 -2
  415. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  416. data/lib/rigor/type_node/string_literal.rb +1 -2
  417. data/lib/rigor/type_node/symbol_literal.rb +1 -2
  418. data/lib/rigor/type_node/union.rb +3 -6
  419. data/lib/rigor/type_of_memo/serving.rb +99 -0
  420. data/lib/rigor/type_of_memo/stats.rb +43 -0
  421. data/lib/rigor/type_of_memo.rb +337 -0
  422. data/lib/rigor/typing_state.rb +1094 -0
  423. data/lib/rigor/value_semantics.rb +1 -10
  424. data/lib/rigor/version.rb +1 -1
  425. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
  426. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
  427. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
  428. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
  429. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
  430. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
  431. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
  432. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
  433. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
  434. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
  435. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
  436. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
  437. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
  438. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
  439. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  440. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
  441. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
  442. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
  443. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
  444. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
  445. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
  446. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
  447. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
  448. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
  449. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
  450. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
  451. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
  452. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
  453. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
  454. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
  455. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
  456. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
  457. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
  458. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
  459. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
  460. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
  461. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
  462. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
  463. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
  464. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
  465. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
  466. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
  467. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
  468. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
  469. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
  470. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
  471. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
  472. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
  473. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
  474. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
  475. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
  476. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
  477. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
  478. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
  479. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
  480. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
  481. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
  482. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
  483. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
  484. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
  485. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
  486. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
  487. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
  488. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
  489. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
  490. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
  491. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
  492. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
  493. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
  494. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
  495. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
  496. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
  497. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
  498. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
  499. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
  500. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
  501. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
  502. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
  503. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
  504. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
  505. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
  506. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
  507. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
  508. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
  509. data/sig/rigor/analysis/baseline.rbs +0 -3
  510. data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
  511. data/sig/rigor/analysis/fact_store.rbs +2 -2
  512. data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
  513. data/sig/rigor/cache/descriptor.rbs +14 -0
  514. data/sig/rigor/cli/diff_command.rbs +1 -1
  515. data/sig/rigor/cli/explain_command.rbs +1 -1
  516. data/sig/rigor/effects/registry.rbs +2 -2
  517. data/sig/rigor/environment.rbs +4 -1
  518. data/sig/rigor/inference.rbs +4 -1
  519. data/sig/rigor/plugin/additional_initializer.rbs +0 -5
  520. data/sig/rigor/plugin/base.rbs +53 -18
  521. data/sig/rigor/plugin/effect_attribution.rbs +3 -3
  522. data/sig/rigor/plugin/effect_edge.rbs +1 -1
  523. data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
  524. data/sig/rigor/plugin/interfaces.rbs +51 -0
  525. data/sig/rigor/plugin/io_boundary.rbs +3 -3
  526. data/sig/rigor/plugin/manifest.rbs +9 -9
  527. data/sig/rigor/plugin/node_context.rbs +21 -0
  528. data/sig/rigor/plugin/protocol_contract.rbs +3 -7
  529. data/sig/rigor/reflection.rbs +37 -0
  530. data/sig/rigor/scope.rbs +43 -4
  531. data/sig/rigor/trinary.rbs +1 -0
  532. data/sig/rigor/type.rbs +9 -3
  533. data/sig/rigor.rbs +3 -2
  534. data/skills/rigor-ask/SKILL.md +1 -0
  535. data/skills/rigor-plugin-author/SKILL.md +4 -4
  536. data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
  537. data/skills/rigor-plugin-review/SKILL.md +3 -3
  538. data/skills/rigor-project-init/references/01-detect.md +1 -0
  539. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
  540. data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
  541. data/skills/rigor-rbs-contract/SKILL.md +147 -0
  542. data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
  543. data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
  544. data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
  545. data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
  546. data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
  547. data/skills/rigor-rbs-setup/SKILL.md +3 -1
  548. data/skills/rigor-type-oracle/SKILL.md +26 -1
  549. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
  550. metadata +82 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 608dc6b03dfc9267330c24c6493f3f4ee2d025ac5ce01a044202828fc282e258
4
- data.tar.gz: 395692680ea16ec6dc42d4187fc57c10ce00731eea4cd1d03176fcc2daa07c14
3
+ metadata.gz: d2aa3cbe98e027e6ed461d3252980ae558778e4df0b2c78f658b848e73b13d8b
4
+ data.tar.gz: 611f238af77124e90cab5844f65be0720898b93469eb7a908d1e82eb4d9a1177
5
5
  SHA512:
6
- metadata.gz: c7145aafeaf9fd22fe3759970c9b4eaafd8720796048641a387ec7a02d4e579689fde37540db686da9904b7d8419a6338b9db7caa38997d7184960c346253f5a
7
- data.tar.gz: bff814da943de361c7c1e5bee99fd0e4847d16bb8a9bd3e36609e69246e8eb0e141c6c6e87b425720d709e2277195d9a14100a3018fef75670361fe2acd2160b
6
+ metadata.gz: 8b82ef7bdde5d2f8ca978379190effc8730bd6dcb7224207282b9b820d06a2853cc61f87314ad6b8b7a0d81351f3aedd869d1f8449bb6f75e74f83baa56590b8
7
+ data.tar.gz: fe1685b7aad2412ea38090bdc53e350972c25522036330375eba7424457e086057018bad9827c35c1e4e0c65ca48d496bb54e5a8f58f0213668f4609053c7fdd
data/README.md CHANGED
@@ -20,6 +20,8 @@ rigor check app lib # find bugs in the code you already have
20
20
  global `-g` install keeps it off your project's pins. See
21
21
  [Get started](#get-started-in-one-prompt).)
22
22
 
23
+ If your project's bundle also lists `rbs` (directly, or through Steep or `rbs_rails`), the `rbs` that `gem:rigortype` puts on `PATH` can shadow it; see the [install guide's note on `rbs` shadowing](docs/install.md#case-a--mise-is-available-recommended).
24
+
23
25
  No annotations required, no runtime dependency, no changes to your
24
26
  code — and any RBS you do add, Rigor reads and uses. Rigor parses Ruby
25
27
  with
@@ -131,7 +133,7 @@ and [CI guide](https://rigor.typedduck.fail/manual/11-ci/).
131
133
 
132
134
  ```sh
133
135
  rigor check lib # find bugs (caches under .rigor/ — gitignore it)
134
- rigor init # drop a starter .rigor.yml
136
+ rigor init # drop a starter .rigor.yml (suggests plugins from Gemfile.lock)
135
137
  rigor annotate lib/foo.rb # reprint a file with inferred types in the margin
136
138
  rigor triage lib # summarise diagnostics: distribution, hotspots
137
139
  rigor sig-gen --diff lib/foo.rb # emit RBS from inference (--write to save)
@@ -232,7 +234,7 @@ rigor docs --list # list every bundled page
232
234
 
233
235
  ## Status
234
236
 
235
- Current release: **`v0.4.6`** (2026-10-23) — on the
237
+ Current release: **`v0.4.8`** (2026-11-15) — on the
236
238
  `0.4.x` line, continuing the evaluation era opened by `v0.2.0`, the first
237
239
  publicly-announced (general / evaluation) release. The line publishes
238
240
  an enumerated [compatibility surface](docs/compatibility.md) as a
@@ -0,0 +1,33 @@
1
+ # Rigor core overlay — supplemental core signatures.
2
+ #
3
+ # --- BigDecimal precision readers (issue #2969) ---
4
+ #
5
+ # rbs 4.2 ships `stdlib/bigdecimal/0/big_decimal.rbs` without `precision`, `scale`,
6
+ # `precision_scale` or `n_significant_digits`, and the bigdecimal gem's own `sig/` (4.1.3)
7
+ # omits them too, so `BigDecimal("1.5").precision` reported `call.undefined-method` on correct code.
8
+ #
9
+ # Upstream: bigdecimal 4.1.3 `ext/bigdecimal/bigdecimal.c` registers the four readers as instance
10
+ # methods (`rb_define_method(rb_cBigDecimal, "precision", ...)`, and likewise for the other three).
11
+ # Their documented results: `precision -> integer` (decimal digits), `scale -> integer`,
12
+ # `precision_scale -> [integer, integer]`, and `n_significant_digits -> integer`.
13
+ #
14
+ # bigdecimal is a bundled gem, not a default library, so this file is gated on it
15
+ # (`Rigor::Environment::RbsLoader::LIBRARY_SUPPLEMENT_CORE_OVERLAYS`). Without the library the file would
16
+ # open a phantom `BigDecimal` that nothing else declares.
17
+ #
18
+ # The methods sit on a module the class includes, not as direct `def`s, so a later rbs or a project
19
+ # `sig/` that declares one of them directly overrides it instead of raising
20
+ # `DuplicatedMethodDefinitionError` (the same shape as `process.rbs` and `ruby41_core_overlay`).
21
+
22
+ module RigorCoreOverlay
23
+ module BigDecimalPrecision
24
+ def precision: () -> Integer
25
+ def scale: () -> Integer
26
+ def precision_scale: () -> [Integer, Integer]
27
+ def n_significant_digits: () -> Integer
28
+ end
29
+ end
30
+
31
+ class BigDecimal
32
+ include RigorCoreOverlay::BigDecimalPrecision
33
+ end
@@ -29,3 +29,33 @@ class Resolv
29
29
  def initialize: (?(Array[Resolv::Hosts | Resolv::DNS] | Hash[Symbol, untyped])? resolvers, ?use_ipv6: bool?) -> untyped
30
30
  | ...
31
31
  end
32
+
33
+ # --- Resolv::DNS#getresources / #getresource / #each_resource ---
34
+ #
35
+ # `rbs` declares the three typeclass lookups as answering the base class
36
+ # whatever the typeclass argument names:
37
+ #
38
+ # def getresources: (dns_name name, singleton(Resolv::DNS::Query) typeclass) -> Array[Resolv::DNS::Resource]
39
+ #
40
+ # so `dns.getresources(d, Resolv::DNS::Resource::IN::MX).map(&:exchange)`
41
+ # false-fires `call.undefined-method` (`exchange` is declared on
42
+ # `Resolv::DNS::Resource::MX`, not on `Resource`). At runtime every returned
43
+ # record is an instance of the typeclass (rigortype/rigor#2828).
44
+ #
45
+ # The overlay prepends a generic overload binding `T` from `singleton(T)`, so
46
+ # the answer is `Array[Resolv::DNS::Resource::IN::MX]`. An argument the
47
+ # binder cannot read as a class object under the `Resource` bound leaves `T`
48
+ # unbound, and the base overload (`| ...`) still accepts every `Query`
49
+ # typeclass the upstream signature does.
50
+ class Resolv
51
+ class DNS
52
+ def each_resource: [T < Resolv::DNS::Resource] (Resolv::DNS::dns_name name, singleton(T) typeclass) { (T) -> void } -> void
53
+ | ...
54
+
55
+ def getresource: [T < Resolv::DNS::Resource] (Resolv::DNS::dns_name name, singleton(T) typeclass) -> T
56
+ | ...
57
+
58
+ def getresources: [T < Resolv::DNS::Resource] (Resolv::DNS::dns_name name, singleton(T) typeclass) -> Array[T]
59
+ | ...
60
+ end
61
+ end
@@ -347,6 +347,35 @@ class Float
347
347
  def gigabytes: () -> Float
348
348
  end
349
349
 
350
+ # ---------------------------------------------------------------
351
+ # NumericWithFormat — ActiveSupport's `core_ext/numeric/conversions`, included into Integer, Float and
352
+ # BigDecimal. Declared on the module and included, not as a `def` on each class, so a project `sig/` that
353
+ # declares `Integer#to_fs` itself overrides it instead of raising `DuplicatedMethodDefinitionError` (the
354
+ # `process.rbs` shape). Not pure: the Symbol formats read `I18n` for their locale. An Integer or String
355
+ # format is `to_s(format)`, and no format is `to_s`, so every form answers a String. `options` is any
356
+ # Hash-like value: ActiveSupport calls `symbolize_keys` on it, so String keys and a HashWithIndifferentAccess
357
+ # are both valid.
358
+ # ---------------------------------------------------------------
359
+
360
+ module ActiveSupport
361
+ module NumericWithFormat
362
+ def to_fs: (?(Symbol | Integer | String | nil) format, ?Hash[untyped, untyped]? options) -> String
363
+ def to_formatted_s: (?(Symbol | Integer | String | nil) format, ?Hash[untyped, untyped]? options) -> String
364
+ end
365
+ end
366
+
367
+ class Integer
368
+ include ActiveSupport::NumericWithFormat
369
+ end
370
+
371
+ class Float
372
+ include ActiveSupport::NumericWithFormat
373
+ end
374
+
375
+ class BigDecimal
376
+ include ActiveSupport::NumericWithFormat
377
+ end
378
+
350
379
  # ---------------------------------------------------------------
351
380
  # ActiveSupport::Duration — issue #632
352
381
  # ---------------------------------------------------------------
@@ -898,31 +927,34 @@ end
898
927
 
899
928
  class Hash[unchecked out K, unchecked out V]
900
929
  # `core_ext/hash/keys`
901
- def symbolize_keys: () -> Hash[Symbol, V]
930
+ # `key.to_sym rescue key` keeps a key without `to_sym`, so no one key type is right: `Hash[untyped, V]` here,
931
+ # and with the plugin `dynamic_return` hook (#2433) maps each key member where it can.
932
+ def symbolize_keys: () -> Hash[untyped, V]
902
933
  def symbolize_keys!: () -> self
903
934
  def deep_symbolize_keys: () -> Hash[untyped, untyped]
904
- def deep_symbolize_keys!: () -> self
935
+ def deep_symbolize_keys!: () -> Hash[untyped, untyped]
905
936
  def stringify_keys: () -> Hash[String, V]
906
- def stringify_keys!: () -> self
937
+ def stringify_keys!: () -> Hash[String, V]
907
938
  def deep_stringify_keys: () -> Hash[untyped, untyped]
908
- def deep_stringify_keys!: () -> self
939
+ def deep_stringify_keys!: () -> Hash[untyped, untyped]
909
940
  def assert_valid_keys: (*K | Array[K]) -> self
910
- def deep_transform_keys: () { (K) -> K } -> Hash[K, untyped]
911
- def deep_transform_keys!: () { (K) -> K } -> self
941
+ def deep_transform_keys: [X] () { (K) -> X } -> Hash[X, untyped]
942
+ def deep_transform_keys!: [X] () { (K) -> X } -> Hash[X, untyped]
912
943
  def deep_transform_values: () { (V) -> untyped } -> Hash[K, untyped]
913
- def deep_transform_values!: () { (V) -> untyped } -> self
944
+ def deep_transform_values!: () { (V) -> untyped } -> Hash[K, untyped]
914
945
  # `alias_method`s of `symbolize_keys` / `symbolize_keys!`.
915
- def to_options: () -> Hash[Symbol, V]
946
+ def to_options: () -> Hash[untyped, V]
916
947
  def to_options!: () -> self
917
948
 
918
949
  # `core_ext/hash/deep_dup`
919
950
  def deep_dup: () -> Hash[K, V]
920
951
 
921
- # `core_ext/hash/deep_merge`
922
- def deep_merge: (Hash[K, V]) -> Hash[K, V]
923
- | (Hash[K, V]) { (K, V, V) -> V } -> Hash[K, V]
924
- def deep_merge!: (Hash[K, V]) -> self
925
- | (Hash[K, V]) { (K, V, V) -> V } -> self
952
+ # `core_ext/hash/deep_merge` — the twin of the plugin's rows (#2424 review): the argument's keys join the
953
+ # receiver's, and a nested merge makes a value neither side declares, so values read `untyped`.
954
+ def deep_merge: [A, B] (Hash[A, B] other_hash) -> Hash[K | A, untyped]
955
+ | [A, B] (Hash[A, B] other_hash) { (K | A, untyped, untyped) -> untyped } -> Hash[K | A, untyped]
956
+ def deep_merge!: [A, B] (Hash[A, B] other_hash) -> Hash[K | A, untyped]
957
+ | [A, B] (Hash[A, B] other_hash) { (K | A, untyped, untyped) -> untyped } -> Hash[K | A, untyped]
926
958
  # `other.is_a?(Hash)` — the hook `ActiveSupport::DeepMergeable` asks before
927
959
  # recursing into a value. `:nodoc:`, but public.
928
960
  def deep_merge?: (untyped other) -> bool
@@ -0,0 +1,40 @@
1
+ # Addressable — `rbs collection` correction overlay (issue #2857).
2
+ #
3
+ # gem_rbs_collection's `gems/addressable/*/addressable.rbs` declares
4
+ # `Addressable::URI.parse` as `(String | URI uri) -> instance`, but
5
+ # `Addressable::URI.parse(nil)` returns nil, so `Addressable::URI.parse(x)`
6
+ # on a `String?` — the mastodon `fetch_link_card_service.rb` shape — drew a
7
+ # false `call.argument-type-mismatch` on correct code.
8
+ #
9
+ # Unlike the activesupport overlay this directory does NOT ride the ADR-72
10
+ # `:missing` gate: it loads only when `rbs collection` supplies the gem's
11
+ # RBS (`:rbs_collection` coverage — `Environment::GEM_OVERLAY_COVERAGE_SOURCES`).
12
+ # The deferred-signature plan (#2422) then composes this declaration over the
13
+ # collection's copy as an overload continuation, so `parse(nil)` binds nil
14
+ # while `parse(String | URI)` keeps the URI answer and a `String?` argument
15
+ # mismatches nothing. Under `:missing` coverage — no sigs at all — the
16
+ # overlay stays unloaded on purpose: this partial `Addressable::URI`
17
+ # declaration would make every member it omits (`host`, `path`, …) a false
18
+ # `call.undefined-method`, which is worse than the `Dynamic` silence a
19
+ # sig-less gem already has.
20
+ module Addressable
21
+ class URI
22
+ # Two arms, URI-carrying first — the order is load-bearing, not cosmetic.
23
+ # `Addressable::URI` is RBS-known but not loadable in the analyzer's host
24
+ # process, so `NilClass`-vs-`Addressable::URI` acceptance answers `maybe`,
25
+ # and the overload selector's strict pass takes the FIRST maybe-match: a
26
+ # leading `(::NilClass)` arm would shadow this one and type
27
+ # `parse(existing_uri)` as `nil`. With the URI arm first it answers `yes`
28
+ # and wins; `nil` still reaches the second arm.
29
+ #
30
+ # `::NilClass`, not the `nil` keyword: they accept the same arguments, but
31
+ # RBS types `nil` as `Types::Bases::Nil`, which the
32
+ # `call.argument-type-mismatch` non-nil channel reads as "undecidable →
33
+ # admits", silently dropping `Addressable::URI.parse(123)`. `NilClass` is
34
+ # a `ClassInstance`, so the channel still refutes what no overload admits.
35
+ #
36
+ # The collection's own `(String | URI uri) -> instance` stays reachable as
37
+ # the `| ...` fallback the deferred plan appends.
38
+ def self.parse: (String | URI uri) -> URI | (::NilClass) -> nil
39
+ end
40
+ end
@@ -150,7 +150,7 @@ module Bundler
150
150
  # `#initialize`, `#specs` — declared by `sig/shims/bundler.rbs` with the same types.
151
151
  def platforms: () -> Array[untyped]
152
152
  def sources: () -> Array[untyped]
153
- def dependencies: () -> Array[Dependency]
153
+ def dependencies: () -> Hash[String, Dependency]
154
154
  def ruby_version: () -> untyped
155
155
  def bundler_version: () -> untyped
156
156
  end
@@ -112,8 +112,9 @@ class Gem::Specification
112
112
 
113
113
  # Instance-side surface widely used by bundler / rubygems
114
114
  # internals. `attr_accessor` mirrors the writer + reader pair
115
- # so the survey's `.name=` / `.version=` / `.full_gem_path=` /
116
- # `.loaded_from=` / `.post_install_message=` writes resolve too.
115
+ # so the survey's `.name=` / `.version=` / `.loaded_from=` /
116
+ # `.post_install_message=` writes resolve too. `.full_gem_path` is declared on `Gem::BasicSpecification`,
117
+ # which this class inherits from.
117
118
  def activate: () -> untyped
118
119
  def specification_version: () -> Integer
119
120
  def files: () -> Array[String]
@@ -127,13 +128,13 @@ class Gem::Specification
127
128
  # `#version`'s READER is declared by `sig/shims/rubygems.rbs` (`attr_reader version (): Version`);
128
129
  # only the writer is ours.
129
130
  def version=: (Gem::Version) -> Gem::Version
130
- attr_accessor full_gem_path: String
131
131
  attr_accessor loaded_from: String?
132
132
  attr_accessor post_install_message: String?
133
133
  end
134
134
 
135
135
  class Gem::BasicSpecification
136
136
  def version: () -> Gem::Version
137
+ attr_accessor full_gem_path: String
137
138
  def executables: () -> Array[String]
138
139
  def name: () -> String
139
140
  def full_name: () -> String
@@ -59,7 +59,7 @@ mise use gem:rigortype
59
59
  With mise activated in your shell, `rigor` is on your `PATH`. The
60
60
  gem is named `rigortype` (the name `rigor` was taken on RubyGems);
61
61
  the executable it installs is `rigor`. If you already have Ruby
62
- 4.0, `gem install rigortype` works too.
62
+ 4.0, `gem install rigortype` works too. If the project's bundle lists `rbs`, the `rbs` that `gem:rigortype` puts on `PATH` can shadow it; see the [install guide's note on `rbs` shadowing](../install.md#case-a--mise-is-available-recommended).
63
63
 
64
64
  For shell activation and shims, `asdf`, and developing inside a
65
65
  container, see [Installing Rigor](../manual/01-installation.md);
@@ -283,8 +283,11 @@ If you used the [AI-assisted setup](#the-fast-path-let-an-ai-agent-set-it-up),
283
283
  the `rigor-project-init` skill already wrote one for you. To
284
284
  write a starter by hand, `rigor init` emits `.rigor.dist.yml`
285
285
  — the project default that gets committed, with `paths` and a
286
- `severity_profile` filled in, `target_ruby` commented out until
287
- you set it to your project's Ruby, and the rest commented out. That is all most projects need; the key
286
+ `severity_profile` filled in. When your `Gemfile.lock` declares a
287
+ gem a bundled plugin models, the template suggests that plugin
288
+ under `plugins:`, commented out, for you to enable. `target_ruby`
289
+ is commented out until you set it to your project's Ruby, and the
290
+ rest is commented out too. That is all most projects need; the key
288
291
  reference, the JSON-schema editor integration, and `includes:`
289
292
  composition are in
290
293
  [Configuration](../manual/03-configuration.md).
@@ -155,10 +155,41 @@ The `sig.skipped.*` reasons are:
155
155
  generic by an inline declaration and `sig/` does not declare
156
156
  it yet, or names its type parameters otherwise. See
157
157
  [Classes made generic inline](#classes-made-generic-inline).
158
+ - `sig.skipped.generic-reopening` — the method's class, or one
159
+ it is nested in, is declared generic by loaded RBS with a
160
+ type parameter that has a bound or a default, and `sig/`
161
+ does not declare it. See
162
+ [Reopening a generic class](#reopening-a-generic-class).
158
163
  - `sig.skipped.inline-differs` — the method is declared
159
164
  inline and in `sig/`, and the two disagree. A refusal:
160
165
  `--write` and `--check` exit `1`. See
161
166
  [Methods declared inline](#methods-declared-inline).
167
+ - `sig.skipped.overridden-bot` — the method's body never
168
+ returns (its inferred return is `bot`), and the analysed
169
+ code defines the method again, with `def` or
170
+ `define_method`: in a subclass, signed or not, in a class
171
+ that includes the method's module, in a module included
172
+ later or prepended, or as an extending class's own
173
+ singleton method. `bot` would describe only the base body
174
+ the overrides replace. A class left with nothing to write
175
+ because of this is still written as an empty class, so its
176
+ subclasses keep their signatures, unless your `sig/` or a
177
+ gem's signatures already declare it.
178
+ - `sig.skipped.abstract-method` — the method's inferred return
179
+ is `bot` from an abstract-style raise: `NotImplementedError`,
180
+ an error class named `Abstract…` or `NotImplemented…`, or a
181
+ message that asks for the method to be implemented or
182
+ overridden, or to define it by name ("please define
183
+ about"; not "you must define a block"). The
184
+ implementations may live outside the analysed code, as in a
185
+ plugin gem, so no `bot` is proposed.
186
+ - `sig.skipped.template-method` — the method's inferred return
187
+ is `bot`, its body does not end in a direct `raise`, `fail`,
188
+ `throw`, `exit` or `abort`, and it calls a method the
189
+ analysed code overrides or declares abstract, directly or
190
+ through self-dispatched calls. The call is matched by name,
191
+ whatever the receiver. A `bot` that ends in a direct
192
+ `throw` or a raise nothing overrides is still proposed.
162
193
  - `sig.skipped.top-level-def` — a plain top-level `def`, which
163
194
  defines a private `Object` method. sig-gen lists it (file,
164
195
  method name and this reason in `--format=json`) but writes no
@@ -180,6 +211,15 @@ The `sig.skipped.*` reasons are:
180
211
  down with it. The rest of the signatures are unaffected;
181
212
  the skipped method is reported on stderr, and it is worth
182
213
  reporting to us.
214
+ - `sig.skipped.platform-guarded` — the method is defined in
215
+ a file Ruby loads only under a `RUBY_ENGINE`,
216
+ `RUBY_PLATFORM` or `defined?(…)` require guard (the
217
+ `if RUBY_ENGINE == 'truffleruby'` → `require
218
+ "stackprof/truffleruby"` shape), so it is not an ordinary
219
+ definition for every platform. sig-gen withholds the
220
+ proposal when another scanned file also defines the
221
+ method, or when the inferred return is `bot` — the shape
222
+ a raising stub takes (#3035).
183
223
 
184
224
  ### Tighter returns keep the declared parameters
185
225
 
@@ -330,10 +370,34 @@ so a return inferred for the new line would describe
330
370
  parameters that are about to change. Delete the `sig/` member
331
371
  and re-run; sig-gen then writes the method afresh.
332
372
 
373
+ ### Reopening a generic class
374
+
375
+ When your code reopens a class that loaded RBS declares
376
+ generic — `class Array`, `class Hash`, a gem's class, or one
377
+ your `sig/` declares — sig-gen writes the header with that
378
+ declaration's type parameters: `class Array[unchecked out E]`.
379
+ rbs rejects a reopening whose parameters differ, so a bare
380
+ `class Array` would fail the class, and every class whose
381
+ signature mentions it, with `GenericParameterMismatchError`.
382
+ The members still say `untyped` for the element type, and a
383
+ class whose name matches a copied parameter is written
384
+ anchored (`::E`), since a bare `E` would name the parameter.
385
+
386
+ The parameters are copied only when every one is plain: a
387
+ name, a variance and `unchecked`. A bound or a default
388
+ (`class Box[T < Comparable]`) is a type written for the
389
+ declaring file's namespace and could mean another class
390
+ where sig-gen writes, so that class's methods, and those of
391
+ classes nested in it, are skipped as
392
+ `sig.skipped.generic-reopening`. Declare the class in `sig/`
393
+ with its parameters and sig-gen writes the members into that
394
+ declaration. A file that already declares the class keeps its
395
+ own header.
396
+
333
397
  ### Classes made generic inline
334
398
 
335
- sig-gen does not write a class's type parameters yet. A class
336
- declared generic inline (`# @rbs generic T`) is therefore not
399
+ sig-gen does not copy type parameters from an inline
400
+ declaration. A class declared generic inline (`# @rbs generic T`) is therefore not
337
401
  opened in `sig/`: a header without its parameters would make
338
402
  rbs reject the class, and every class whose signature mentions
339
403
  it, with `GenericParameterMismatchError`. Its methods, and
@@ -626,6 +690,24 @@ list is the current state):
626
690
  are recognised as user-authored and never produce a
627
691
  duplicate `def` insertion.
628
692
 
693
+ Two rules shape the class declarations and type names around
694
+ those methods:
695
+
696
+ - **A subclass of a core or stdlib class** is declared even
697
+ when it has no method of its own to emit, so a method
698
+ returning one resolves: `class MyHash < Hash; end` is
699
+ written `class MyHash < ::Hash[untyped, untyped]`. The
700
+ declaration goes into the file written for its source file,
701
+ so it appears only when that source file has at least one
702
+ method to emit. A class some signature already declares is
703
+ left to that declaration.
704
+ - **A type name a namespace shadows** is written with a
705
+ leading `::`. Inside a `module Foo` that declares a
706
+ `Foo::Set`, a method returning a core `Set` is written
707
+ `-> ::Set[untyped]`, because RBS would read a bare `Set`
708
+ there as `Foo::Set`. Where nothing shadows the name, it is
709
+ written bare as before.
710
+
629
711
  Method shapes the generator does **not** cover yet:
630
712
 
631
713
  - `define_method(:name) { ... }`.
@@ -643,7 +725,7 @@ two are wired today, one is reserved.
643
725
  | Policy | Behaviour |
644
726
  | --- | --- |
645
727
  | `untyped` (default) | Every parameter is spelled `untyped`. No inference-derived parameter contract is imposed on future callers. The user retains complete authorship over parameter typing. |
646
- | `observed` | Collect argument types from every call site under `--observe=PATH...` (defaults to the configured `test_paths:`, or whichever of `spec/` and `test/` exist), union per parameter position, erase to RBS, emit the union. With no test root to observe, or a declared root that does not exist or is not read, sig-gen says so on stderr, and `--format=json` carries the same fact in a top-level `observation` key (below). |
728
+ | `observed` | Collect argument types from every call site under `--observe=PATH...` (defaults to the configured `test_paths:`, or whichever of `spec/` and `test/` exist), union per parameter position, erase to RBS, emit the union. A literal argument is widened to its class first, so `"hello"` proposes `String` (#2254). With no test root to observe, or a declared root that does not exist or is not read, sig-gen says so on stderr, and `--format=json` carries the same fact in a top-level `observation` key (below). |
647
729
  | `observed-strict` | Reserved. Will additionally widen to capability roles (`_ToStr`, `_ToS`, …) once the role catalog ships. Currently rejected with a usage error. |
648
730
 
649
731
  Under `--params=observed`, the `--print` / `--diff` JSON payload gains
@@ -20,7 +20,7 @@ unchanged. This appendix maps the vocabulary.
20
20
  | Default behaviour | Inference, fall back silent | Inference, fall back silent |
21
21
  | "Levels" | 0 – 10 (numeric) | `lenient` / `balanced` / `strict` (named) |
22
22
  | Per-rule control | `ignoreErrors:` regex, level demotion | `disable:`, `severity_overrides:` |
23
- | Baseline | `phpstan-baseline.neon` | `.rigor-baseline.yml` (managed) / `rigor.baseline.json` (ad-hoc) |
23
+ | Baseline | `phpstan-baseline.neon` | `.rigor-baseline.yml` (managed) / `rigor-check.json` (ad-hoc) |
24
24
  | Stub format | PHP stub files | RBS files |
25
25
  | Custom narrowing | Type-Specifying Extensions | Plugins (Chapter 9) |
26
26
  | Custom return shape | Dynamic Return Type Extensions | Plugin `dynamic_return` |
@@ -140,10 +140,10 @@ severity controls, and includes.
140
140
  | `ignoreErrors:` (regex / pattern) | `disable:` (rule identifier or wildcard) |
141
141
  | `parameters: ignoreErrors:` per-path | `# rigor:disable-file <rule>` at the file head |
142
142
  | `includes:` | `includes:` |
143
- | `phpstan-baseline.neon` | `.rigor-baseline.yml` (managed) — or `rigor.baseline.json` (ad-hoc) |
144
- | `phpstan analyse --generate-baseline` | `rigor baseline generate` (managed) — or `rigor check --format=json > rigor.baseline.json` (ad-hoc) |
143
+ | `phpstan-baseline.neon` | `.rigor-baseline.yml` (managed) — or `rigor-check.json` (ad-hoc) |
144
+ | `phpstan analyse --generate-baseline` | `rigor baseline generate` (managed) — or `rigor check --format=json > rigor-check.json` (ad-hoc) |
145
145
  | `phpstan analyse` | `rigor check` |
146
- | `phpstan analyse --baseline` | `rigor check` with `baseline: .rigor-baseline.yml` configured (managed) — or `rigor diff rigor.baseline.json` (ad-hoc) |
146
+ | `phpstan analyse --baseline` | `rigor check` with `baseline: .rigor-baseline.yml` configured (managed) — or `rigor diff rigor-check.json` (ad-hoc) |
147
147
  | Path resolution: relative to declaring file | Path resolution: relative to declaring file (same rule). |
148
148
 
149
149
  Rigor has two baseline mechanisms: a **managed** baseline
data/docs/install.md CHANGED
@@ -109,6 +109,26 @@ A user-wide `~/.config/mise/config.toml` entry for `gem:rigortype`
109
109
  keeps applying outside this project. Mention it if it names an older
110
110
  version, but do not edit global config without the user's consent.
111
111
 
112
+ `gem:rigortype` also puts an `rbs` on `PATH`: mise wraps every
113
+ executable the gem's dependencies install, and that wrapper runs with
114
+ rigortype's private `GEM_HOME`. When `rbs` is in the project's bundle
115
+ (`Gemfile.lock` lists it, directly or through Steep or `rbs_rails`)
116
+ and the bundle uses the default path, `bundle exec rbs` runs the
117
+ wrapper instead of the project's `rbs`. A git-sourced gem then fails
118
+ with `is not yet checked out` under rigortype's `GEM_HOME`. A bundle
119
+ installed under a configured path such as `vendor/bundle` is not
120
+ affected, because `bundle exec` puts that path's `bin` first. Only
121
+ when `Gemfile.lock` lists `rbs`, run `mise exec -- bundle exec which
122
+ rbs` (the shell you are in has not picked up the new tool yet, so a
123
+ bare `bundle exec which rbs` can miss it). If it prints a path under
124
+ `installs/gem-rigortype/`, tell the user, and tell them the fix is
125
+ local to this checkout: `bundle config set --local path vendor/bundle`
126
+ followed by `bundle install` writes an untracked `.bundle/config`
127
+ (teammates and CI keep the default path), puts the bundle in
128
+ `vendor/`, and makes Rigor read each bundled gem's shipped `sig/`
129
+ (ADR-27). Offer it; do not change the bundle path without their
130
+ consent.
131
+
112
132
  Then verify:
113
133
 
114
134
  ```sh
@@ -28,6 +28,14 @@ so nothing is lost by installing it separately.
28
28
  > [Rails quickstart](14-rails-quickstart.md)). The channels below
29
29
  > are the manual route.
30
30
 
31
+ > **Only trying Rigor in VS Code?** When the
32
+ > [Rigor extension](../../editors/vscode/README.md) finds no `rigor`,
33
+ > it offers to install one into its own storage, using a Ruby 4.0
34
+ > already on the machine (or one `mise install ruby@4.0` provides).
35
+ > That install serves the editor only and is never used for a project
36
+ > that pins `rigortype`; for the command line and CI, use a channel
37
+ > below.
38
+
31
39
  ### Set up in your language
32
40
 
33
41
  The prompt above is plain natural language — your agent follows the
@@ -269,6 +277,8 @@ A few things worth knowing if you have not used mise before:
269
277
  The gem is `rigortype`; the executable it installs (and the only
270
278
  command you run) is `rigor`.
271
279
 
280
+ If the project's bundle lists `rbs`, the `rbs` that `gem:rigortype` puts on `PATH` can shadow the project's own; see the [install guide's note on `rbs` shadowing](../install.md#case-a--mise-is-available-recommended).
281
+
272
282
  ### Putting `rigor` on your PATH
273
283
 
274
284
  Installing the tool is not enough on its own — `rigor` reaches your
@@ -356,8 +366,63 @@ gem install rigortype
356
366
  `asdf` has no general-purpose gem backend, so the gem itself is
357
367
  installed with `gem install` rather than an `asdf` command. `mise`
358
368
  (above) is the more integrated option because its `gem:` backend
359
- records the gem in the same config that records Ruby, and — as the
360
- next section explains — keeps the two from interfering.
369
+ records the gem in the same config that records Ruby, and — as
370
+ [Simple alternative](#simple-alternative--gem-install) explains —
371
+ keeps the two from interfering.
372
+
373
+ ## Secondary — a pinned bundle
374
+
375
+ The rule at the top of this chapter is about your *application's*
376
+ `Gemfile` — the one Bundler resolves against your app's gems, and whose
377
+ entries `Bundler.require` loads at boot. It is not a rule against
378
+ Bundler. A `Gemfile` holding nothing but Rigor, resolved separately and
379
+ selected with `BUNDLE_GEMFILE`, keeps every property the prohibition
380
+ exists to protect: your application's dependency graph never meets
381
+ Rigor's, and your application's Ruby is never constrained by Rigor's.
382
+
383
+ Commit it, with its `Gemfile.lock`, at `.github/rigor/`, the path Rigor's
384
+ CI template uses. The VS Code extension runs it (discovery kind
385
+ `pinned-bundle`, under a Ruby it finds the way it does for its managed
386
+ Rigor, preferring one that matches the lockfile's `RUBY VERSION` or
387
+ the Gemfile's `ruby` line; `rigor.server.bundleGemfile` names another
388
+ location), and never a different Rigor while it pins one.
389
+ `rigor doctor` does not recognise it yet
390
+ ([#2751](https://github.com/rigortype/rigor/issues/2751)):
391
+
392
+ ```ruby
393
+ # .github/rigor/Gemfile
394
+ source "https://rubygems.org"
395
+ ruby ">= 4.0.0", "< 4.1"
396
+ gem "rigortype", "~> 0.4"
397
+ ```
398
+
399
+ Each command has to run under a Ruby 4.0, which is usually not your
400
+ project's Ruby, so select it on the command line:
401
+
402
+ ```sh
403
+ # with mise
404
+ BUNDLE_GEMFILE=.github/rigor/Gemfile mise exec ruby@4.0 -- bundle install
405
+ BUNDLE_GEMFILE=.github/rigor/Gemfile mise exec ruby@4.0 -- bundle exec rigor check
406
+ # with rbenv
407
+ BUNDLE_GEMFILE=.github/rigor/Gemfile RBENV_VERSION=4.0.5 bundle exec rigor check
408
+ ```
409
+
410
+ A `.ruby-version` inside `.github/rigor/` does not select it, because
411
+ Rigor runs from the project root, and neither does a Bundler binstub,
412
+ whose `#!/usr/bin/env ruby` runs the project's Ruby.
413
+
414
+ The same pin then serves local runs and CI, Dependabot can keep it
415
+ current (see [Pinning Rigor's version](11-ci.md#pinning-rigors-version)
416
+ in the CI chapter), and it is where a third-party Rigor plugin gem
417
+ goes. What it does **not** do is provide Ruby: every `bundle exec`
418
+ must already run under a Ruby 4.0, which the `ruby` line checks. Only
419
+ `rigortype` is pinned exactly; the lockfile records the dependency
420
+ versions Bundler resolved when you installed.
421
+
422
+ It is more moving parts than `mise use --pin gem:rigortype`, which
423
+ records the version and the Ruby in one place and needs no `bundle
424
+ exec`. Reach for it when your team already runs everything through
425
+ Bundler, or needs a plugin gem — not as a first choice.
361
426
 
362
427
  ## Simple alternative — gem install
363
428
 
@@ -386,30 +451,6 @@ reach it. A `gem install` under a Ruby 4.0 you never switch away from
386
451
  is perfectly fine; one that shares a Ruby with projects you switch
387
452
  between is a footgun.
388
453
 
389
- ## If you want Bundler to manage the version
390
-
391
- The rule at the top of this chapter is about your *application's*
392
- `Gemfile` — the one Bundler resolves against your app's gems, and whose
393
- entries `Bundler.require` loads at boot. It is not a rule against
394
- Bundler. A `Gemfile` holding nothing but Rigor, resolved separately and
395
- selected with `BUNDLE_GEMFILE`, keeps every property the prohibition
396
- exists to protect: your application's dependency graph never meets
397
- Rigor's, and your application's Ruby is never constrained by Rigor's.
398
-
399
- That arrangement — a `Gemfile` under `.github/rigor/`, plus the
400
- Dependabot config that keeps it current — is written up under
401
- [Pinning Rigor's version](11-ci.md#pinning-rigors-version) in the CI
402
- chapter. Nothing about it is CI-specific:
403
-
404
- ```sh
405
- BUNDLE_GEMFILE=.github/rigor/Gemfile bundle exec rigor check
406
- ```
407
-
408
- It is more moving parts than `mise use --pin gem:rigortype`, which
409
- records the same version in one line and needs no `bundle exec`. Reach
410
- for it when your team already runs everything through Bundler and wants
411
- Rigor on the same footing — not as a first choice.
412
-
413
454
  ## Nix
414
455
 
415
456
  If you use Nix, Rigor's flake exposes the executable as a package,