rigortype 0.3.8 → 0.4.0

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 (634) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/core_overlay/enumerable.rbs +51 -0
  5. data/data/core_overlay/enumerator.rbs +84 -0
  6. data/data/core_overlay/hash_rbs3.rbs +41 -0
  7. data/data/core_overlay/process.rbs +40 -0
  8. data/data/core_overlay/string_io.rbs +33 -0
  9. data/data/effects/core.yml +3 -3
  10. data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
  11. data/docs/handbook/02-everyday-types.md +17 -14
  12. data/docs/handbook/03-narrowing.md +132 -14
  13. data/docs/handbook/04-tuples-and-shapes.md +8 -6
  14. data/docs/handbook/05-methods-and-blocks.md +1 -1
  15. data/docs/handbook/06-classes.md +2 -2
  16. data/docs/handbook/07-rbs-and-extended.md +19 -1
  17. data/docs/handbook/10-sorbet.md +9 -10
  18. data/docs/handbook/11-sig-gen.md +525 -13
  19. data/docs/handbook/README.md +1 -1
  20. data/docs/handbook/appendix-elixir.md +2 -2
  21. data/docs/handbook/appendix-go.md +2 -2
  22. data/docs/handbook/appendix-java-csharp.md +2 -2
  23. data/docs/handbook/appendix-mypy.md +2 -2
  24. data/docs/handbook/appendix-phpstan.md +1 -1
  25. data/docs/handbook/appendix-rust.md +1 -1
  26. data/docs/handbook/appendix-type-theory.md +4 -4
  27. data/docs/handbook/appendix-typescript.md +1 -1
  28. data/docs/llms.txt +2 -0
  29. data/docs/manual/02-cli-reference.md +95 -6
  30. data/docs/manual/03-configuration.md +18 -0
  31. data/docs/manual/04-diagnostics.md +15 -0
  32. data/docs/manual/07-plugins.md +15 -5
  33. data/docs/manual/08-skills.md +23 -2
  34. data/docs/manual/10-mcp-server.md +3 -2
  35. data/docs/manual/11-ci.md +9 -0
  36. data/docs/manual/15-type-protection-coverage.md +8 -0
  37. data/docs/manual/16-rbs-extended-annotations.md +46 -8
  38. data/docs/manual/18-removing-dead-code.md +10 -8
  39. data/docs/manual/19-effect-labels.md +10 -0
  40. data/docs/manual/plugins/README.md +7 -0
  41. data/docs/manual/plugins/rigor-actioncable.md +8 -1
  42. data/docs/manual/plugins/rigor-actionmailer.md +7 -0
  43. data/docs/manual/plugins/rigor-actionpack.md +261 -0
  44. data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
  45. data/docs/manual/plugins/rigor-activejob.md +7 -0
  46. data/docs/manual/plugins/rigor-activerecord.md +138 -4
  47. data/docs/manual/plugins/rigor-activestorage.md +8 -1
  48. data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
  49. data/docs/manual/plugins/rigor-grape.md +106 -0
  50. data/docs/manual/plugins/rigor-graphql.md +23 -2
  51. data/docs/manual/plugins/rigor-pundit.md +8 -1
  52. data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
  53. data/docs/manual/plugins/rigor-rails-routes.md +7 -0
  54. data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
  55. data/docs/manual/plugins/rigor-sidekiq.md +8 -1
  56. data/docs/manual/plugins/rigor-sorbet.md +21 -5
  57. data/exe/rigor +19 -4
  58. data/lib/rigor/analysis/baseline.rb +3 -3
  59. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  60. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  61. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
  62. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  63. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
  64. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
  65. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  66. data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
  67. data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
  68. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  69. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  70. data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
  71. data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
  72. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
  73. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  74. data/lib/rigor/analysis/check_rules.rb +718 -79
  75. data/lib/rigor/analysis/crash_signature.rb +8 -24
  76. data/lib/rigor/analysis/dependency_recorder.rb +23 -0
  77. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  78. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  79. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  80. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  81. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  82. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  83. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  84. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  85. data/lib/rigor/analysis/fact_store.rb +15 -1
  86. data/lib/rigor/analysis/incremental.rb +42 -0
  87. data/lib/rigor/analysis/incremental_session.rb +117 -26
  88. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  89. data/lib/rigor/analysis/project_scan.rb +11 -1
  90. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  91. data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
  92. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  93. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  94. data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
  95. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  96. data/lib/rigor/analysis/result.rb +1 -3
  97. data/lib/rigor/analysis/rule_catalog.rb +131 -1
  98. data/lib/rigor/analysis/run_cache_key.rb +21 -12
  99. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  100. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  101. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  102. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
  103. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  104. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
  105. data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
  106. data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
  107. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  108. data/lib/rigor/analysis/runner.rb +407 -51
  109. data/lib/rigor/analysis/template_unit_collector.rb +303 -0
  110. data/lib/rigor/analysis/template_unit_paths.rb +91 -0
  111. data/lib/rigor/analysis/template_unit_positions.rb +293 -0
  112. data/lib/rigor/analysis/template_units.rb +399 -0
  113. data/lib/rigor/analysis/worker_session.rb +62 -20
  114. data/lib/rigor/bleeding_edge.rb +10 -24
  115. data/lib/rigor/builtins/hkt_builtins.rb +3 -2
  116. data/lib/rigor/builtins/imported_refinements.rb +159 -21
  117. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  118. data/lib/rigor/builtins/regex_refinement.rb +21 -13
  119. data/lib/rigor/builtins/static_return_refinements.rb +7 -6
  120. data/lib/rigor/cache/annotation_location.rb +2 -4
  121. data/lib/rigor/cache/descriptor.rb +63 -20
  122. data/lib/rigor/cache/engine_source.rb +33 -5
  123. data/lib/rigor/cache/file_digest.rb +9 -1
  124. data/lib/rigor/cache/incremental_snapshot.rb +72 -6
  125. data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
  126. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  127. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  128. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  129. data/lib/rigor/cache/rbs_descriptor.rb +112 -14
  130. data/lib/rigor/cache/rbs_environment.rb +8 -4
  131. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  132. data/lib/rigor/cache/store.rb +76 -32
  133. data/lib/rigor/ci_detector.rb +1 -0
  134. data/lib/rigor/cli/annotate_command.rb +5 -6
  135. data/lib/rigor/cli/check_command.rb +57 -10
  136. data/lib/rigor/cli/check_invocation.rb +6 -11
  137. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  138. data/lib/rigor/cli/coverage_command.rb +1 -1
  139. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  140. data/lib/rigor/cli/coverage_scan.rb +1 -5
  141. data/lib/rigor/cli/diff_command.rb +1 -1
  142. data/lib/rigor/cli/doc_links.rb +4 -4
  143. data/lib/rigor/cli/docs_command.rb +6 -6
  144. data/lib/rigor/cli/doctor_command.rb +85 -38
  145. data/lib/rigor/cli/effects_command.rb +2 -2
  146. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  147. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  148. data/lib/rigor/cli/explain_command.rb +34 -1
  149. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  150. data/lib/rigor/cli/lsp_command.rb +1 -1
  151. data/lib/rigor/cli/mcp_command.rb +1 -1
  152. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  153. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  154. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  155. data/lib/rigor/cli/plugin_command.rb +4 -4
  156. data/lib/rigor/cli/plugins_command.rb +2 -1
  157. data/lib/rigor/cli/prism_colorizer.rb +1 -2
  158. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  159. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  160. data/lib/rigor/cli/sig_gen_command.rb +212 -31
  161. data/lib/rigor/cli/skill_command.rb +2 -2
  162. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  163. data/lib/rigor/cli/skill_describe.rb +35 -35
  164. data/lib/rigor/cli/trace_command.rb +4 -4
  165. data/lib/rigor/cli/trace_renderer.rb +5 -6
  166. data/lib/rigor/cli/triage_command.rb +1 -1
  167. data/lib/rigor/cli/type_of_command.rb +29 -11
  168. data/lib/rigor/cli/type_of_renderer.rb +20 -9
  169. data/lib/rigor/cli/type_of_template_probe.rb +189 -0
  170. data/lib/rigor/cli/type_scan_command.rb +4 -4
  171. data/lib/rigor/cli/unused_command.rb +11 -3
  172. data/lib/rigor/cli/upgrade_command.rb +1 -1
  173. data/lib/rigor/cli.rb +89 -9
  174. data/lib/rigor/config_audit.rb +30 -5
  175. data/lib/rigor/configuration/severity_profile.rb +25 -9
  176. data/lib/rigor/configuration.rb +109 -7
  177. data/lib/rigor/effects/ancestry_recorder.rb +191 -0
  178. data/lib/rigor/effects/attribution.rb +12 -5
  179. data/lib/rigor/effects/callee_rule.rb +368 -0
  180. data/lib/rigor/effects/catalog.rb +7 -4
  181. data/lib/rigor/effects/collector.rb +11 -5
  182. data/lib/rigor/effects/config_envelopes.rb +15 -10
  183. data/lib/rigor/effects/definition_context.rb +179 -0
  184. data/lib/rigor/effects/definition_lines.rb +25 -6
  185. data/lib/rigor/effects/effect_table.rb +11 -4
  186. data/lib/rigor/effects/entry_points.rb +0 -2
  187. data/lib/rigor/effects/envelope_check.rb +9 -9
  188. data/lib/rigor/effects/envelope_index.rb +20 -8
  189. data/lib/rigor/effects/file_collection.rb +59 -5
  190. data/lib/rigor/effects/framework_units.rb +5 -7
  191. data/lib/rigor/effects/identity.rb +20 -6
  192. data/lib/rigor/effects/inline_anchor.rb +7 -7
  193. data/lib/rigor/effects/label_intent.rb +3 -4
  194. data/lib/rigor/effects/liskov_check.rb +8 -8
  195. data/lib/rigor/effects/local_ownership.rb +38 -12
  196. data/lib/rigor/effects/method_key.rb +22 -1
  197. data/lib/rigor/effects/mutation_classifier.rb +23 -12
  198. data/lib/rigor/effects/plugin_facts.rb +47 -37
  199. data/lib/rigor/effects/propagator.rb +297 -19
  200. data/lib/rigor/effects/registry.rb +1 -1
  201. data/lib/rigor/effects/scanner.rb +122 -73
  202. data/lib/rigor/effects/signature_sources.rb +4 -6
  203. data/lib/rigor/effects/snapshot.rb +12 -11
  204. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  205. data/lib/rigor/effects/summary.rb +27 -4
  206. data/lib/rigor/effects/unit_scan.rb +393 -38
  207. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  208. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  209. data/lib/rigor/effects/visibility.rb +101 -0
  210. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  211. data/lib/rigor/environment/class_registry.rb +3 -1
  212. data/lib/rigor/environment/failure_slot.rb +2 -2
  213. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  214. data/lib/rigor/environment/lockfile_resolver.rb +68 -4
  215. data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
  216. data/lib/rigor/environment/member_consistency.rb +298 -0
  217. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  218. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  219. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  220. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  221. data/lib/rigor/environment/rbs_loader.rb +674 -79
  222. data/lib/rigor/environment.rb +174 -24
  223. data/lib/rigor/flow_contribution/merger.rb +0 -2
  224. data/lib/rigor/flow_contribution.rb +11 -13
  225. data/lib/rigor/hashing/xxh3.rb +264 -0
  226. data/lib/rigor/inference/acceptance.rb +292 -17
  227. data/lib/rigor/inference/block_auto_splat.rb +216 -0
  228. data/lib/rigor/inference/block_call_timing.rb +338 -0
  229. data/lib/rigor/inference/block_parameter_binder.rb +75 -30
  230. data/lib/rigor/inference/block_repetition.rb +71 -0
  231. data/lib/rigor/inference/body_fixpoint.rb +7 -6
  232. data/lib/rigor/inference/budget_trace.rb +3 -6
  233. data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
  234. data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
  235. data/lib/rigor/inference/captured_locals.rb +389 -18
  236. data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
  237. data/lib/rigor/inference/content_join.rb +200 -27
  238. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  239. data/lib/rigor/inference/def_return_typer.rb +11 -7
  240. data/lib/rigor/inference/define_method_block_self.rb +64 -0
  241. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  242. data/lib/rigor/inference/element_read_widening.rb +180 -0
  243. data/lib/rigor/inference/error_info.rb +196 -0
  244. data/lib/rigor/inference/expression_typer.rb +2124 -468
  245. data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
  246. data/lib/rigor/inference/fork_map.rb +5 -7
  247. data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
  248. data/lib/rigor/inference/global_write_census.rb +239 -0
  249. data/lib/rigor/inference/guard_rebinding.rb +447 -0
  250. data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
  251. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  252. data/lib/rigor/inference/hkt_registry.rb +4 -7
  253. data/lib/rigor/inference/index_write_widening.rb +16 -7
  254. data/lib/rigor/inference/indexed_narrowing.rb +61 -9
  255. data/lib/rigor/inference/jump_targets.rb +82 -0
  256. data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
  257. data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
  258. data/lib/rigor/inference/last_line.rb +340 -0
  259. data/lib/rigor/inference/last_status.rb +144 -0
  260. data/lib/rigor/inference/macro_block_self_type.rb +168 -21
  261. data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
  262. data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
  263. data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
  264. data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
  265. data/lib/rigor/inference/match_rebinding.rb +392 -0
  266. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  267. data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
  268. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  269. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  270. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  271. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
  272. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  273. data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
  274. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
  275. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  276. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  277. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
  278. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  279. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  280. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
  281. data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
  282. data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
  283. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  284. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
  285. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  286. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  287. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  288. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
  289. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  290. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
  291. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  292. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  293. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
  294. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  295. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  296. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  297. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  298. data/lib/rigor/inference/method_dispatcher.rb +225 -25
  299. data/lib/rigor/inference/method_parameter_binder.rb +12 -9
  300. data/lib/rigor/inference/multi_target_binder.rb +341 -55
  301. data/lib/rigor/inference/mutation_rejoin.rb +171 -0
  302. data/lib/rigor/inference/mutation_widening.rb +156 -85
  303. data/lib/rigor/inference/narrowing.rb +835 -162
  304. data/lib/rigor/inference/operand_effects.rb +167 -0
  305. data/lib/rigor/inference/operand_walk.rb +88 -0
  306. data/lib/rigor/inference/optimistic_origin.rb +153 -16
  307. data/lib/rigor/inference/origin_lookup.rb +2 -3
  308. data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
  309. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  310. data/lib/rigor/inference/precision_scanner.rb +3 -4
  311. data/lib/rigor/inference/project_method_ownership.rb +136 -0
  312. data/lib/rigor/inference/project_patched_methods.rb +9 -4
  313. data/lib/rigor/inference/project_patched_scanner.rb +10 -6
  314. data/lib/rigor/inference/protection_scanner.rb +1 -2
  315. data/lib/rigor/inference/range_constant.rb +57 -0
  316. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  317. data/lib/rigor/inference/receiver_alias.rb +93 -4
  318. data/lib/rigor/inference/receiver_blind_block.rb +219 -0
  319. data/lib/rigor/inference/refinement_mutation.rb +76 -0
  320. data/lib/rigor/inference/repeated_or_writes.rb +463 -0
  321. data/lib/rigor/inference/return_barrier.rb +54 -0
  322. data/lib/rigor/inference/rewrite_mutation.rb +120 -0
  323. data/lib/rigor/inference/scope_indexer.rb +5228 -627
  324. data/lib/rigor/inference/statement_evaluator.rb +3452 -595
  325. data/lib/rigor/inference/stored_block_call.rb +54 -0
  326. data/lib/rigor/inference/string_mutation.rb +97 -0
  327. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  328. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  329. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  330. data/lib/rigor/inference/unknown_store_widening.rb +200 -0
  331. data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
  332. data/lib/rigor/inference/version_guard.rb +28 -21
  333. data/lib/rigor/inference/void_origin.rb +3 -3
  334. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  335. data/lib/rigor/language_server/buffer_table.rb +4 -4
  336. data/lib/rigor/language_server/completion_provider.rb +1 -1
  337. data/lib/rigor/language_server/debouncer.rb +0 -1
  338. data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
  339. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  340. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  341. data/lib/rigor/language_server/hover_provider.rb +1 -1
  342. data/lib/rigor/language_server/hover_renderer.rb +4 -4
  343. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  344. data/lib/rigor/language_server/project_context.rb +7 -8
  345. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  346. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  347. data/lib/rigor/language_server/server.rb +9 -9
  348. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  349. data/lib/rigor/language_server/uri.rb +1 -1
  350. data/lib/rigor/mcp/server.rb +2 -1
  351. data/lib/rigor/plugin/base.rb +258 -26
  352. data/lib/rigor/plugin/box_probe.rb +91 -0
  353. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  354. data/lib/rigor/plugin/effect_attribution.rb +65 -11
  355. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  356. data/lib/rigor/plugin/fact_store.rb +6 -6
  357. data/lib/rigor/plugin/io_boundary.rb +93 -8
  358. data/lib/rigor/plugin/isolation.rb +29 -6
  359. data/lib/rigor/plugin/loader.rb +33 -13
  360. data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
  361. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  362. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  363. data/lib/rigor/plugin/manifest.rb +94 -11
  364. data/lib/rigor/plugin/registry.rb +76 -12
  365. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
  366. data/lib/rigor/plugin/template_unit.rb +196 -0
  367. data/lib/rigor/plugin/trust_policy.rb +2 -4
  368. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  369. data/lib/rigor/plugin.rb +1 -0
  370. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  371. data/lib/rigor/project_environment.rb +138 -0
  372. data/lib/rigor/protection/analysis_guard.rb +93 -14
  373. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  374. data/lib/rigor/protection/dependency_closure.rb +5 -8
  375. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  376. data/lib/rigor/protection/discovery_seed.rb +14 -12
  377. data/lib/rigor/protection/kill_signature.rb +1 -4
  378. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  379. data/lib/rigor/protection/mutation_cache.rb +7 -11
  380. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  381. data/lib/rigor/protection/mutator.rb +2 -1
  382. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  383. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  384. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  385. data/lib/rigor/rbs_extended/reporter.rb +36 -10
  386. data/lib/rigor/rbs_extended.rb +88 -21
  387. data/lib/rigor/reflection/constant_ancestors.rb +97 -0
  388. data/lib/rigor/reflection/constant_path.rb +139 -0
  389. data/lib/rigor/reflection.rb +93 -110
  390. data/lib/rigor/runtime/jit.rb +6 -8
  391. data/lib/rigor/scope/discovery_index.rb +165 -2
  392. data/lib/rigor/scope.rb +1041 -32
  393. data/lib/rigor/sig_gen/alias_index.rb +289 -0
  394. data/lib/rigor/sig_gen/classification.rb +22 -5
  395. data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
  396. data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
  397. data/lib/rigor/sig_gen/generator.rb +673 -53
  398. data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
  399. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  400. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  401. data/lib/rigor/sig_gen/method_candidate.rb +47 -3
  402. data/lib/rigor/sig_gen/observation_collector.rb +18 -16
  403. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  404. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  405. data/lib/rigor/sig_gen/renderer.rb +128 -14
  406. data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
  407. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  408. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  409. data/lib/rigor/sig_gen/write_result.rb +19 -3
  410. data/lib/rigor/sig_gen/writer.rb +189 -41
  411. data/lib/rigor/sig_gen.rb +3 -0
  412. data/lib/rigor/signature_path_audit.rb +153 -6
  413. data/lib/rigor/source/literals.rb +0 -23
  414. data/lib/rigor/source/node_children.rb +0 -2
  415. data/lib/rigor/source/node_locator.rb +5 -11
  416. data/lib/rigor/source/node_walker.rb +0 -6
  417. data/lib/rigor/source/parameter_envelope.rb +72 -0
  418. data/lib/rigor/source.rb +1 -0
  419. data/lib/rigor/triage/catalogue.rb +1 -3
  420. data/lib/rigor/triage.rb +3 -5
  421. data/lib/rigor/type/accepts_result.rb +24 -6
  422. data/lib/rigor/type/combinator.rb +144 -5
  423. data/lib/rigor/type/data_class.rb +2 -2
  424. data/lib/rigor/type/data_instance.rb +4 -4
  425. data/lib/rigor/type/difference.rb +1 -0
  426. data/lib/rigor/type/float_range.rb +128 -0
  427. data/lib/rigor/type/hash_shape.rb +6 -6
  428. data/lib/rigor/type/integer_range.rb +13 -8
  429. data/lib/rigor/type/nominal.rb +8 -2
  430. data/lib/rigor/type/refined.rb +4 -3
  431. data/lib/rigor/type/struct_class.rb +3 -3
  432. data/lib/rigor/type/struct_instance.rb +4 -4
  433. data/lib/rigor/type.rb +1 -0
  434. data/lib/rigor/type_node/generic.rb +1 -1
  435. data/lib/rigor/type_node/range_literal.rb +25 -0
  436. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  437. data/lib/rigor/type_node.rb +1 -0
  438. data/lib/rigor/version.rb +1 -1
  439. data/lib/rigor.rb +1 -0
  440. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  441. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  442. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  443. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
  444. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
  445. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  446. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  447. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  448. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
  449. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
  450. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  451. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  452. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
  453. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
  454. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
  455. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
  456. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
  457. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
  458. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
  459. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  460. data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
  461. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
  462. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
  463. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
  464. data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
  465. data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
  466. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  467. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
  468. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  469. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  470. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  471. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -5
  472. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
  473. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
  474. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
  475. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
  476. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  477. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  478. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
  479. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  480. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
  481. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
  482. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
  483. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  484. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
  485. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  486. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
  487. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  488. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  489. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  490. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  491. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  492. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  493. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  494. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
  495. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  496. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  497. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  498. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  499. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
  500. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  501. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  502. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
  503. data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
  504. data/plugins/rigor-grape/sig/grape.rbs +263 -0
  505. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  506. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
  507. data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
  508. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  509. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  510. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
  511. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  512. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  513. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  514. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  515. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  516. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
  517. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  518. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  519. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  520. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
  521. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  522. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
  523. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
  524. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
  525. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  526. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  527. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  528. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  529. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
  530. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  531. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  532. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
  533. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
  534. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  535. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  536. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  537. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  538. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  539. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  540. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  541. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  542. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  543. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  544. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  545. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  546. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
  547. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  548. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  549. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
  550. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  551. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  552. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  553. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  554. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  555. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  556. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
  557. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  558. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
  559. data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
  560. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  561. data/sig/rigor/analysis/baseline.rbs +69 -7
  562. data/sig/rigor/analysis/fact_store.rbs +1 -1
  563. data/sig/rigor/analysis/project_scan.rbs +74 -0
  564. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  565. data/sig/rigor/cache.rbs +2 -3
  566. data/sig/rigor/effects/config_envelopes.rbs +100 -0
  567. data/sig/rigor/effects/effect_table.rbs +60 -0
  568. data/sig/rigor/effects/envelope.rbs +97 -0
  569. data/sig/rigor/effects/envelope_index.rbs +33 -0
  570. data/sig/rigor/effects/file_collection.rbs +81 -0
  571. data/sig/rigor/effects/label.rbs +26 -0
  572. data/sig/rigor/effects/label_set.rbs +46 -0
  573. data/sig/rigor/effects/method_key.rbs +24 -0
  574. data/sig/rigor/effects/origin.rbs +50 -0
  575. data/sig/rigor/effects/plugin_facts.rbs +141 -0
  576. data/sig/rigor/effects/registry.rbs +68 -0
  577. data/sig/rigor/effects/summary.rbs +50 -0
  578. data/sig/rigor/effects/taint_cause.rbs +12 -0
  579. data/sig/rigor/environment.rbs +17 -10
  580. data/sig/rigor/inference/optimistic_origin.rbs +10 -0
  581. data/sig/rigor/inference.rbs +9 -14
  582. data/sig/rigor/plugin/additional_initializer.rbs +36 -0
  583. data/sig/rigor/plugin/base.rbs +30 -7
  584. data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
  585. data/sig/rigor/plugin/effect_attribution.rbs +66 -0
  586. data/sig/rigor/plugin/effect_edge.rbs +33 -0
  587. data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
  588. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  589. data/sig/rigor/plugin/loader.rbs +3 -3
  590. data/sig/rigor/plugin/manifest.rbs +51 -11
  591. data/sig/rigor/plugin/protocol_contract.rbs +68 -0
  592. data/sig/rigor/plugin/registry.rbs +62 -1
  593. data/sig/rigor/plugin.rbs +1 -1
  594. data/sig/rigor/rbs_extended.rbs +1 -1
  595. data/sig/rigor/reflection.rbs +9 -6
  596. data/sig/rigor/scope.rbs +148 -12
  597. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
  598. data/sig/rigor/source.rbs +4 -4
  599. data/sig/rigor/testing.rbs +10 -4
  600. data/sig/rigor/type.rbs +33 -1
  601. data/sig/rigor.rbs +39 -20
  602. data/skills/rigor-ask/SKILL.md +16 -11
  603. data/skills/rigor-baseline-reduce/SKILL.md +4 -6
  604. data/skills/rigor-ci-setup/SKILL.md +17 -21
  605. data/skills/rigor-doctor/SKILL.md +24 -22
  606. data/skills/rigor-doctor/references/01-checks.md +97 -33
  607. data/skills/rigor-editor-setup/SKILL.md +6 -4
  608. data/skills/rigor-mcp-setup/SKILL.md +5 -4
  609. data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
  610. data/skills/rigor-next-steps/SKILL.md +4 -2
  611. data/skills/rigor-plugin-author/SKILL.md +19 -23
  612. data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
  613. data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
  614. data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
  615. data/skills/rigor-plugin-review/SKILL.md +6 -4
  616. data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
  617. data/skills/rigor-plugin-tune/SKILL.md +4 -2
  618. data/skills/rigor-project-init/SKILL.md +15 -10
  619. data/skills/rigor-project-init/references/01-detect.md +13 -9
  620. data/skills/rigor-project-init/references/02-configure.md +43 -14
  621. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
  622. data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
  623. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  624. data/skills/rigor-protection-uplift/SKILL.md +4 -6
  625. data/skills/rigor-rbs-setup/SKILL.md +4 -2
  626. data/skills/rigor-type-oracle/SKILL.md +226 -0
  627. data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
  628. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  629. data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
  630. data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
  631. data/skills/rigor-upgrade/SKILL.md +13 -8
  632. metadata +133 -3
  633. data/lib/rigor/cli/probe_environment.rb +0 -85
  634. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -9,7 +9,7 @@ tooltips, downstream consumers reading your gem's `sig/` —
9
9
  sees what Rigor sees.
10
10
 
11
11
  This chapter is a walkthrough of the command's UX, the
12
- classification model, the three output modes, and the
12
+ classification model, the output modes, and the
13
13
  `--params` policy trade-off that comes straight out of
14
14
  [ADR-5](../adr/5-robustness-principle.md)'s asymmetric
15
15
  "strict on returns, lenient on parameters" rule.
@@ -67,30 +67,70 @@ By default the command writes nothing — it prints the
67
67
  proposal so you can review it. Pass `--write` to apply the
68
68
  proposal to `sig/`.
69
69
 
70
- ## The three output modes
70
+ ## The output modes
71
71
 
72
72
  | Mode | Behaviour |
73
73
  | --- | --- |
74
74
  | `--print` (default) | Print RBS to stdout, grouped by source file + class declaration. |
75
75
  | `--diff` | Show a unified-style diff comparing the existing-declared spelling (if any) against the inferred spelling. Read-only. |
76
76
  | `--write` | Apply the proposal to `sig/<path>.rbs`. Creates files, inserts new methods into existing class declarations, appends new class blocks to files that don't declare them yet. |
77
+ | `--check` | Run the `--write` merge without writing, print what it would change, and exit `1` if anything would change. Read-only. |
78
+
79
+ The four flags are mutually exclusive; passing two different
80
+ ones is a usage error.
77
81
 
78
82
  `--write` is the only mode that touches the filesystem. It
79
83
  operates **only** inside `configuration.signature_paths`
80
84
  (default `sig/`); anything outside that tree is reported as
81
85
  `skipped_outside_sig_root` without being written to.
82
86
 
87
+ ### Keeping `sig/` current in CI
88
+
89
+ `--check` is the freshness gate. It takes the same options as
90
+ `--write` and fails exactly when that `--write` would create or
91
+ change a file, or would refuse one:
92
+
93
+ ```sh
94
+ rigor sig-gen --check lib
95
+ ```
96
+
97
+ ```
98
+ would create sig/greeter.rbs (1 method(s))
99
+ + def greet: (String name) -> String
100
+ ```
101
+
102
+ It also fails while a method stands refused
103
+ (`sig.skipped.inline-differs`), since `--write` refuses it
104
+ too. It exits `0` and prints `sig/ is up to date`
105
+ otherwise. Under `--format=json` the payload is
106
+ `{"up_to_date": …, "results": […], "refused": […]}`: one
107
+ entry per target in the `--write` JSON shape, with `action`
108
+ reading `would_create` / `would_update`, and one per refused
109
+ method.
110
+
111
+ The gate follows `--write`, not `--diff`
112
+ ([ADR-112](../adr/112-extrbs-comment-channel.md) WD4 records
113
+ the departure). A `tighter-return` against a declaration that
114
+ already exists is a proposal `--write` declines without
115
+ `--overwrite`, so it does not fail `--check` either: a
116
+ reviewed, deliberately wider type must not fail the gate
117
+ forever. `--check --overwrite`
118
+ counts it, because `--write --overwrite` would apply it. Pass
119
+ `--check` the `--params` and `--effect-envelopes` flags your
120
+ `--write` uses, or it checks a different output.
121
+
83
122
  ## The classification model
84
123
 
85
- Every method `rigor sig-gen` considers lands in one of five
124
+ Every method `rigor sig-gen` considers lands in one of six
86
125
  states:
87
126
 
88
127
  | Classification | Meaning |
89
128
  | --- | --- |
90
129
  | `new-file` | No RBS file declares the receiver class at all. |
91
130
  | `new-method` | RBS file declares the class but not this method. |
92
- | `tighter-return` | RBS file declares the method, but the inferred return is a strict subtype of the declared return. |
93
- | `equivalent` | The inferred return is not a strict subtype of the declared one — identical, wider, or unrelated — so there is nothing to tighten. Silently skipped. |
131
+ | `tighter-return` | RBS file declares the method, but the inferred return is a strict subtype of the declared return. The proposal is the declared line with only its return replaced; see [Tighter returns keep the declared parameters](#tighter-returns-keep-the-declared-parameters). |
132
+ | `inline-overwrite` | Only under `--overwrite`: a method declared inline with `# @rbs` / `#:` whose `sig/` declaration disagrees with it; the whole `sig/` member is replaced by the inline one. See [Methods declared inline](#methods-declared-inline). |
133
+ | `equivalent` | Nothing for `sig-gen` to propose: the inferred return is identical, wider or unrelated, or it is a narrowing the generator declines (a literal under a wider declaration, anything under a declared `void`, an overloaded declaration or an `alias`), or the method is an `initialize` the class already declares. Silently skipped. |
94
134
  | `skipped` | Disqualified for one of the reasons below. |
95
135
 
96
136
  The `sig.skipped.*` reasons are:
@@ -108,6 +148,17 @@ The `sig.skipped.*` reasons are:
108
148
  - `sig.skipped.user-authored` — `--overwrite` was not set
109
149
  and the method's existing RBS declaration would have to
110
150
  be replaced.
151
+ - `sig.skipped.inline-declared` — `.rigor.yml` sets
152
+ `sig_gen.inline_declared: skip` and the method is declared
153
+ inline. See [Methods declared inline](#methods-declared-inline).
154
+ - `sig.skipped.inline-generic-class` — the method's class is
155
+ generic by an inline declaration and `sig/` does not declare
156
+ it yet, or names its type parameters otherwise. See
157
+ [Classes made generic inline](#classes-made-generic-inline).
158
+ - `sig.skipped.inline-differs` — the method is declared
159
+ inline and in `sig/`, and the two disagree. A refusal:
160
+ `--write` and `--check` exit `1`. See
161
+ [Methods declared inline](#methods-declared-inline).
111
162
  - `sig.skipped.unrenderable-rbs` — the signature Rigor
112
163
  rendered for this method does not parse as RBS. This one
113
164
  is a **bug in Rigor**, not a property of your code: every
@@ -119,6 +170,308 @@ The `sig.skipped.*` reasons are:
119
170
  the skipped method is reported on stderr, and it is worth
120
171
  reporting to us.
121
172
 
173
+ ### Tighter returns keep the declared parameters
174
+
175
+ A `tighter-return` changes the return and nothing else. The
176
+ visibility, the overload's annotations, the method type
177
+ parameters, the parameter list and the block are copied from
178
+ the declaration as written, so applying the proposal never
179
+ changes which calls the signature accepts:
180
+
181
+ ```
182
+ $ rigor sig-gen --diff lib/c.rb
183
+ --- lib/c.rb: C#m
184
+ - def m: (Integer x) -> untyped
185
+ + def m: (Integer x) -> nil
186
+
187
+ --- lib/c.rb: C#n
188
+ - def n: (?) -> untyped
189
+ + def n: (?) -> nil
190
+ ```
191
+
192
+ The `-` line is the declaration the proposal replaces. A method
193
+ declared only on an ancestor is different: the proposal adds an
194
+ override to the method's own class, and it is written from that
195
+ class's `def` like a new method, with `untyped` parameters. The
196
+ ancestor's parameters describe the ancestor's `def`, and the
197
+ override may take other arguments.
198
+
199
+ Two declarations get no proposal:
200
+
201
+ - One with more than one overload
202
+ (`def m: (Integer) -> untyped | (String) -> untyped`). The
203
+ body is typed once for all of its overloads, so the inferred
204
+ return does not say which overload returns what. Merging the
205
+ overloads into one line would drop them, and giving every
206
+ overload the same return would widen the ones you wrote
207
+ narrower.
208
+ - A name the class itself declares through `alias al m`. Its
209
+ parameters are `m`'s, and the proposal would read as a
210
+ rewrite of `m`'s line. An alias only an ancestor declares is
211
+ overridden like any other ancestor declaration.
212
+
213
+ An `initialize` your `sig/` already declares is `equivalent`
214
+ too. sig-gen writes a constructor stub only for a class that
215
+ does not declare one. The exception is `--params=observed`:
216
+ when the observed argument types would replace an `untyped` the
217
+ declaration still has, the stub is proposed so that
218
+ `--overwrite` can apply it.
219
+
220
+ ## Methods declared inline
221
+
222
+ A method you annotated with `# @rbs` or `#:` already has a
223
+ contract, written next to the code. `sig-gen` does not infer
224
+ one for it; it copies yours into `sig/`, so the generated
225
+ signature is the whole contract your gem ships:
226
+
227
+ ```ruby
228
+ class Greeter
229
+ # @rbs name: String
230
+ # @rbs return: String
231
+ def greet(name) = "Hello, #{name}"
232
+
233
+ #: () -> Integer
234
+ def count = 1
235
+
236
+ # @rbs num: Float
237
+ def pair(num) = [num, num.to_s]
238
+ end
239
+ ```
240
+
241
+ ```
242
+ $ rigor sig-gen
243
+ # lib/greeter.rb
244
+ class Greeter
245
+ # [new]
246
+ def greet: (String name) -> String
247
+ # [new]
248
+ def count: () -> Integer
249
+ # [new]
250
+ def pair: (Float num) -> [Float, String]
251
+ end
252
+ ```
253
+
254
+ `count` is written as `Integer`, not the `1` its body proves:
255
+ the declaration is what you meant, and inference does not
256
+ override it. `pair` declares its parameter and not its return,
257
+ so the parameter is copied and the return comes from the body,
258
+ the same split [ADR-107](../adr/107-checked-types-and-typeless-comments.md)
259
+ draws between authored parameters and generated returns. An
260
+ `initialize` is always written `-> void`, whatever its body
261
+ ends with. A member annotation you wrote inline, such as
262
+ `# @rbs %a{deprecated}`, is copied with it. A method with no
263
+ annotation of its own is proposed exactly as in any other
264
+ file.
265
+
266
+ Once `sig/` holds the copy, `rigor check` reads it beside the
267
+ inline declaration, and an identical pair is quiet
268
+ ([ADR-112](../adr/112-extrbs-comment-channel.md) WD5).
269
+ sig-gen compares the two on every run, `attr_*` declarations
270
+ included. They are compared as types, not text:
271
+ parameter names, spacing, how a union is spelled (`String?`
272
+ and `String | nil`, `Integer | String` and `String |
273
+ Integer`, `bool` and `true | false`) and a leading `::` do
274
+ not count. The `::` only counts when it changes what the name
275
+ means: inside `module NS`, `Foo` is `NS::Foo` if `NS`
276
+ declares one, and then it is not `::Foo`. Overload order does
277
+ count, because RBS answers a call with the first overload that
278
+ matches. When the two agree, and `sig/` carries every
279
+ annotation you wrote inline, there is nothing to do.
280
+
281
+ When they differ, sig-gen does not pick a side. The inline
282
+ annotation may be your newer edit; the `sig/` member may be a
283
+ reviewed contract someone changed on purpose. The method is
284
+ refused as `sig.skipped.inline-differs`: nothing is written,
285
+ `--write` prints a `REFUSED` line and exits `1` (under
286
+ `--format=json` the refused methods are listed under
287
+ `refused`), and `--check` fails, so the contradiction cannot
288
+ pass CI unnoticed. Resolve it one of two ways:
289
+
290
+ - Keep the inline declaration: re-run with `--overwrite`. The
291
+ whole `sig/` member is replaced by the inline line, as
292
+ rbs-inline reads it — a parameter you left unannotated is
293
+ `untyped`, an unannotated `&block` is `?{ (?) -> untyped }`
294
+ — and an `attr_*` declaration is replaced by the inline
295
+ attribute in the same spelling. The two sides are never
296
+ mixed slot by slot: their overloads and type variables need
297
+ not correspond. That also means an overload written only in
298
+ `sig/` is dropped, and a caller that relied on it can stop
299
+ type-checking; read the `--diff` first. Comments inside the
300
+ replaced member, and annotations already on it, are kept.
301
+ - Keep the `sig/` member: edit or delete the inline annotation
302
+ to match.
303
+
304
+ A `sig/` member written `def m: ... | ...` is not a copy: it
305
+ adds overloads to the inline declaration, and sig-gen leaves it
306
+ alone.
307
+
308
+ A parameter-only annotation (`# @rbs name: String`, no
309
+ `return:`) states the parameters and nothing else, so only the
310
+ parameters are compared. When they match, the return in `sig/`
311
+ is weighed like any declared return: a `-> void`, a type wider
312
+ than what the body proves, or a literal the body happens to
313
+ return (`-> String` over `"x"`) is left as it is, and a return
314
+ the body proves strictly narrower is a `tighter-return`
315
+ proposal, applied only with `--overwrite`. When the parameters
316
+ differ, the method is refused even under `--overwrite`: the
317
+ body is typed under the parameters the `sig/` member declares,
318
+ so a return inferred for the new line would describe
319
+ parameters that are about to change. Delete the `sig/` member
320
+ and re-run; sig-gen then writes the method afresh.
321
+
322
+ ### Classes made generic inline
323
+
324
+ sig-gen does not write a class's type parameters yet. A class
325
+ declared generic inline (`# @rbs generic T`) is therefore not
326
+ opened in `sig/`: a header without its parameters would make
327
+ rbs reject the class, and every class whose signature mentions
328
+ it, with `GenericParameterMismatchError`. Its methods, and
329
+ those of classes nested in it, are skipped as
330
+ `sig.skipped.inline-generic-class`. Declare the class in
331
+ `sig/` with the same parameters (`class Box[T]` ... `end`) and
332
+ sig-gen writes the members into that declaration. A `sig/`
333
+ declaration that names them otherwise (`class Box[U]`) is
334
+ skipped the same way: a copied `-> T` would name a parameter
335
+ nothing binds.
336
+
337
+ ### Projects that run Steep on the same annotations
338
+
339
+ If Steep reads your inline annotations (`check "lib", inline:
340
+ true` beside `signature "sig"`), a copy in `sig/` is a second
341
+ declaration of each method, and Steep rejects the class with
342
+ `DuplicatedMethodDefinition`. Tell `sig-gen` to leave those
343
+ methods out:
344
+
345
+ ```yaml
346
+ sig_gen:
347
+ inline_declared: skip
348
+ ```
349
+
350
+ Every method the inline reader declares is then skipped as
351
+ `sig.skipped.inline-declared`. That includes the un-annotated
352
+ `def`s of a file that carries any annotation, because the
353
+ reader declares those too (as `untyped`), and so does rbs's
354
+ own inline parser, which Steep's `inline: true` uses. A file
355
+ with no annotation at all is not read inline by Rigor, so its
356
+ methods are still written; if Steep reads that file inline,
357
+ keep it out of the paths you give `sig-gen`.
358
+
359
+ What the setting costs: `sig/` is no longer the whole
360
+ contract. A consumer that reads only your shipped `sig/` —
361
+ Rigor or Steep in a project that depends on your gem — never
362
+ sees the skipped methods. Their inline annotations, including
363
+ a Rigor refinement written there, take effect only where the
364
+ source itself is analysed, which is your own project.
365
+
366
+ ## Emitting effect annotations
367
+
368
+ If your `.rigor.yml` carries an `effects:` block, `sig-gen`
369
+ writes one more thing: `%a{pure}`, the purity annotation rbs
370
+ and Steep already understand, above the methods whose whole
371
+ footprint Rigor read and found to be nothing.
372
+
373
+ ```ruby
374
+ class Label
375
+ def render
376
+ parts = []
377
+ parts << "a"
378
+ parts.join
379
+ end
380
+ end
381
+ ```
382
+
383
+ ```
384
+ $ rigor sig-gen
385
+ # lib/label.rb
386
+ class Label
387
+ # [new]
388
+ %a{pure}
389
+ def render: () -> String
390
+ end
391
+ ```
392
+
393
+ Five conditions have to hold before an annotation is written,
394
+ and every one of them exists to keep a wrong one off the
395
+ page. An emitted annotation is not a hint — the effects opt-in reads
396
+ it back as an **envelope** and enforces it on the method and
397
+ on everything the method reaches, so a `%a{pure}` `sig-gen`
398
+ invented would put `effect.envelope-exceeded` on code that
399
+ is correct.
400
+
401
+ - The summary must be **exhaustive**: every call the method
402
+ reaches was resolved. A summary that is not reads "these
403
+ effects, and possibly more", which is exactly the claim an
404
+ envelope must not make.
405
+ - The summary must be **undischarged**: nothing in the
406
+ method's footprint may be invisible only because
407
+ `effects.tolerated:` says to ignore it. A method whose
408
+ whole footprint is a tolerated `telemetry` call looks
409
+ clean to *your* project and to nobody else — not to a
410
+ consumer reading your shipped `sig/`, and not to your own
411
+ `--no-tolerated-effects` audit.
412
+ - Every callee must be **described by something**: a
413
+ catalogue row, a plugin, an envelope, or a definition in
414
+ your own project. "Every call resolved" and "every callee's
415
+ footprint is known" are different questions. A method whose
416
+ body is one call into a gem nobody has written a row or an
417
+ envelope for is exhaustive and tells you nothing, so it is
418
+ left bare rather than called pure.
419
+ - The method must not **already carry a bound of its own**.
420
+ An annotation on the method or on its class — in `sig/` or
421
+ as an rbs-inline `# @rbs %a{…}` — or an `effects.envelopes:`
422
+ stanza selecting it by `namespace:` or by `match:`, is a
423
+ contract you wrote about this body; sig-gen will not replace
424
+ it with an inference about the same body. A bound whose
425
+ label is misspelled counts too: it bounds nothing, but
426
+ overwriting it would delete the annotation the
427
+ `effect.unknown-label` report points at.
428
+ - The `≤` lane must be **empty**. A callee that states its
429
+ own bound puts that claim in your method's declared lane
430
+ without proving anything, so `rigor effects` shows
431
+ `[] ≤ [io.net.http]` where the proven lane is empty. Rigor
432
+ will not write `%a{pure}` over a claim it never proved
433
+ away.
434
+
435
+ `--effect-envelopes` adds the labelled spelling,
436
+ `%a{rigor:v1:effect io.db, nondet.time}`, for methods that
437
+ do have a footprint. It is a separate flag because `%a{pure}`
438
+ is the ecosystem's annotation and this one is Rigor's: a
439
+ labelled envelope in your `sig/` is a Rigor-specific contract,
440
+ and you should ask for it by name.
441
+
442
+ Under `--write`, an annotation goes on the line above the
443
+ declaration it binds. A declaration that **already** carries
444
+ annotations is left byte-untouched and reported as
445
+ `sig.effect.left-unreadable`: the writer cannot tell an
446
+ annotation it wrote from one you wrote, and it has no grammar
447
+ for merging two, so it will not rewrite that region. Decide
448
+ what it should say and write it yourself.
449
+
450
+ The `sig.effect.*` reasons are:
451
+
452
+ - `sig.effect.emitted` — an annotation was rendered.
453
+ - `sig.effect.withheld-tolerated` — the footprint is only
454
+ clean under `effects.tolerated:`.
455
+ - `sig.effect.withheld-non-exhaustive` — some call the
456
+ method reaches could not be resolved.
457
+ - `sig.effect.withheld-unclaimed-callee` — some call it
458
+ reaches resolved, and nothing anywhere says what that
459
+ callee does.
460
+ - `sig.effect.withheld-declared` — the method already carries
461
+ an authored bound, or a label survives in the `≤` lane.
462
+ - `sig.effect.left-unreadable` — the target declaration
463
+ already carries annotations, so nothing was written there.
464
+
465
+ One limit worth knowing: annotations ride on the signature
466
+ lines `sig-gen` proposes, so a method whose declaration is
467
+ already exactly right gets none. An up-to-date `sig/` is
468
+ therefore not annotated in place; the reader for those is
469
+ `rigor effects --pure`, and writing them is still a hand
470
+ edit.
471
+
472
+ With no `effects:` block in `.rigor.yml`, none of this runs
473
+ and the output is byte-for-byte what it was before.
474
+
122
475
  The three `sig.generated.*` identifiers
123
476
  (`sig.generated.new-file` / `new-method` / `tighter-return`)
124
477
  are emitted as JSON fields under `--format=json` so CI
@@ -129,6 +482,77 @@ its reason next to the rows that did emit. In text mode a
129
482
  one-line stderr summary counts the skipped methods per
130
483
  reason instead; stdout stays paste-clean.
131
484
 
485
+ ## Recording a gap the generator cannot close
486
+
487
+ `sig-gen --diff` answers "is this declaration what the
488
+ implementation proves?" for every method it can type. The
489
+ answers it cannot give are the interesting ones: a
490
+ `tighter-return` you decided not to apply, a method it
491
+ skipped, a declaration with no `def` behind it at all. Each
492
+ of those is a hand-written type, and a hand-written type
493
+ that nobody wrote down a reason for is indistinguishable
494
+ from one nobody has looked at since.
495
+
496
+ The convention Rigor uses on its own `sig/` is a line in the
497
+ member's RBS comment:
498
+
499
+ ```rbs
500
+ class Registry
501
+ # sig-gen gap: #1234 — sig-gen types the body `untyped`
502
+ # (the ivar has no inferred field type yet), so this
503
+ # return is hand-written until it can prove one.
504
+ def resolve: (String name) -> Entry
505
+ end
506
+ ```
507
+
508
+ A `void` return needs no marker. `sig-gen` never proposes a
509
+ value for one: `void` says the return is not part of the
510
+ contract, and no inference synthesizes it, so a `void`
511
+ declaration is authored intent the way a parameter type is
512
+ ([ADR-14](../adr/14-rbs-sig-generation.md) § "The
513
+ inference-vs-RBS contradiction rule").
514
+
515
+ Neither does a declaration the body proves as a literal.
516
+ `sig-gen` will not propose `"bot"` for a `def describe: ()
517
+ -> String` whose body is the string `"bot"`: the declared
518
+ type is your abstraction over that body, and pinning the
519
+ literal would rewrite a contract every sibling class shares.
520
+ A method no `.rbs` declares is unaffected — the literal is
521
+ still the strictest thing the body proves, and that is what
522
+ gets written.
523
+
524
+ A comment rather than a `%a{…}` annotation, for three
525
+ reasons: it stays out of the `rigor:v1:` directive namespace
526
+ (`docs/type-specification/rbs-extended.md`), no engine path
527
+ reads it, and `RBS::Parser` binds it to the member, so a
528
+ check can find it on the AST instead of scanning lines. The
529
+ issue number is the load-bearing half — it points at the
530
+ engine work that would let the generator answer, which is
531
+ the [ADR-14](../adr/14-rbs-sig-generation.md) rule that a
532
+ gap pushing you toward hand-written RBS is the more valuable
533
+ signal.
534
+
535
+ Nothing in the CLI requires this. It is a convention you can
536
+ gate in your own suite: parse `sig/**/*.rbs`, run the
537
+ generator, and fail on a declaration that is neither
538
+ generated-equivalent nor marked. Rigor's own gate is
539
+ `spec/rigor/sig_gen/provenance_spec.rb`
540
+ ([ADR-107](../adr/107-checked-types-and-typeless-comments.md)
541
+ G3).
542
+
543
+ One case in that list is not a marker case at all. A
544
+ declaration with no `def` behind it may simply describe a
545
+ method Ruby generates — an `attr_*`, a `Data` member, one a
546
+ class macro defines at load — or it may be left over from a
547
+ `def` that was deleted or renamed, which nothing catches,
548
+ because `rigor check` and Steep both ask whether the
549
+ implementation matches `sig/` and never the converse.
550
+ Rigor's gate separates the two by asking its own
551
+ project index and then the loaded tree
552
+ ([#839](https://github.com/rigortype/rigor/issues/839)).
553
+ That check requires the code under `sig/`, so it stays a
554
+ repository gate: `sig-gen` in your project is unchanged.
555
+
132
556
  ## What method shapes the generator covers
133
557
 
134
558
  Slice-by-slice (each shipped via a CHANGELOG entry — this
@@ -148,7 +572,14 @@ list is the current state):
148
572
  RBS.
149
573
  - **`attr_reader` / `attr_writer` / `attr_accessor`** with
150
574
  literal Symbol arguments. The return type is the
151
- accumulated ivar type from `Scope#class_ivars_for`. The
575
+ accumulated ivar type from `Scope#class_ivars_for`. A
576
+ writer stores whatever its caller passes, so an
577
+ `attr_writer` / `attr_accessor` ivar reads untyped.
578
+ The accessor, and any other method returning that
579
+ ivar, is skipped even beside a concrete write such as
580
+ `@logger = Logger.new`, unless `--params=observed`
581
+ types the constructor parameter the ivar is assigned
582
+ from. The
152
583
  generator emits the long-form `def name: () -> T`
153
584
  spelling so the writer's merge path applies unchanged;
154
585
  existing short-form `attr_reader name: T` declarations
@@ -172,7 +603,7 @@ two are wired today, one is reserved.
172
603
  | Policy | Behaviour |
173
604
  | --- | --- |
174
605
  | `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. |
175
- | `observed` | Collect argument types from every call site under `--observe=PATH...` (defaults to `spec/` when present), union per parameter position, erase to RBS, emit the union. |
606
+ | `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, sig-gen says so on stderr. |
176
607
  | `observed-strict` | Reserved. Will additionally widen to capability roles (`_ToStr`, `_ToS`, …) once the role catalog ships. Currently rejected with a usage error. |
177
608
 
178
609
  The default deliberately favours `untyped` because of
@@ -190,9 +621,76 @@ parameter contract I want."* That is a correctness-
190
621
  preserving widening — every existing caller still passes —
191
622
  but it does narrow the contract relative to `untyped`.
192
623
 
624
+ ## Type aliases your project declares
625
+
626
+ When a proposal's type is a union whose members are exactly
627
+ the expansion of a `type` alias declared in your own
628
+ `sig/`, the alias name is what gets emitted. For a method
629
+ on `Rigor::Type::Combinator`, sig-gen emits
630
+
631
+ ```
632
+ def hash_shape_keys: (untyped) -> ::Rigor::Type::t
633
+ ```
634
+
635
+ rather than the twenty-two members `Rigor::Type::t`
636
+ expands to. The name is written in its absolute form so it
637
+ cannot rebind to a nearer constant of the same spelling.
638
+
639
+ This applies wherever sig-gen renders a type, not only to
640
+ returns: `--params=observed` parameter positions, `attr_*`
641
+ accessors, and `Data` / `Struct` members all fold the same
642
+ way. The `--format=json` payload carries the folded
643
+ spelling in its `rbs` field; `inferred_return` is the
644
+ carrier the engine computed and stays expanded.
645
+
646
+ The rule is member-set equality, so a proposal missing one
647
+ arm of the alias still prints in full — the alias names a
648
+ type the method cannot return, and claiming it would be
649
+ wrong rather than merely verbose.
650
+
651
+ A wrong alias name is worse than a long union: it is an RBS
652
+ claim you did not make. Four rules keep the fold from
653
+ guessing.
654
+
655
+ **Your aliases only.** The candidates come from your
656
+ configured `signature_paths:` (or `sig/` when you
657
+ configured none) — not from gems in your bundle, not from
658
+ an `rbs collection` tree, and not from core or stdlib RBS.
659
+ Core declares `Warning::category` as `:deprecated |
660
+ :experimental | :performance`; a project that never wrote
661
+ that alias does not get it in its proposals. A loaded
662
+ plugin's own signature directory is subtracted even when
663
+ you listed it in `signature_paths:` yourself — those
664
+ aliases are the plugin's vocabulary.
665
+
666
+ **Namespace proximity.** An alias is only offered to a
667
+ method whose owner is inside the alias's own namespace, and
668
+ the nearest such alias wins — most specific first, then
669
+ declaration order by (file, line, name). Without this,
670
+ `:positive | :negative` anywhere in a codebase would pick
671
+ up any alias that happens to name that pair.
672
+
673
+ **Unambiguous alias bodies only.** Some RBS forms reach the
674
+ renderer looking like something else: an intersection can
675
+ read as one of its members, a proc type as a bare `Proc`, a
676
+ nested alias as whatever it expanded to. An alias whose
677
+ body contains one of those is skipped, because a fold into
678
+ it would put a type in your signature that the method does
679
+ not return. Only class instances, singletons, literals,
680
+ unions, optionals, tuples and the `nil` / `bool` / `bot`
681
+ bases qualify.
682
+
683
+ **Unions only, non-generic only.** A `type name = String`
684
+ alias never rewrites an ordinary `String` return, and a
685
+ generic alias (`type boxed[T] = ...`) has no fixed member
686
+ set to match.
687
+
688
+ A project that declares no aliases gets byte-for-byte the
689
+ output it got before.
690
+
193
691
  ## RSpec-aware observations
194
692
 
195
- When you point `--observe` at a `spec/` directory, the
693
+ When the observed test roots hold an RSpec suite, the
196
694
  generator recognises three RSpec-shaped binding patterns
197
695
  and uses them to type receivers that would otherwise
198
696
  degrade to `Dynamic[top]`:
@@ -218,6 +716,13 @@ across nested scopes are last-wins; the recogniser does not
218
716
  re-implement RSpec's full scope rules — the typical
219
717
  one-spec-file shape is the target.
220
718
 
719
+ A Minitest suite under `test/` is observed too, with no
720
+ recogniser of its own: a call whose receiver types to one
721
+ class counts. A receiver assigned in `setup` is the gap —
722
+ inside a `test_*` method it reads as `Foo | nil`, and such
723
+ a call is not observed yet
724
+ ([#1389](https://github.com/rigortype/rigor/issues/1389)).
725
+
221
726
  The recogniser is part of the generator itself; you do not
222
727
  need to install `rigor-rspec` to benefit from it. If you
223
728
  already use `rigor-rspec` for diagnostics, the two run side
@@ -237,9 +742,16 @@ by side without coordination.
237
742
  tree.
238
743
  - **Will not** replace an existing method declaration
239
744
  unless `--overwrite` is set AND the candidate is a
240
- `tighter-return`. Without `--overwrite`, existing
241
- declarations are user-authored and the new method is
242
- silently skipped.
745
+ `tighter-return`, and then only its return changes: the
746
+ rest of the declaration is kept. Without `--overwrite`,
747
+ existing declarations are user-authored and the new method
748
+ is silently skipped.
749
+ - **Will not** change an existing method declaration that
750
+ disagrees with the method's inline declaration unless
751
+ `--overwrite` is set; it reports the method as `REFUSED`
752
+ and exits `1`. With `--overwrite` the whole member is
753
+ replaced by the inline declaration (`inline-overwrite`),
754
+ keeping the comments and annotations already on it.
243
755
  - **Will not** touch `attr_reader` / `attr_writer` /
244
756
  `attr_accessor` declarations in existing RBS — those are
245
757
  always treated as user-authored.
@@ -291,8 +803,8 @@ A typical iteration on a new file:
291
803
  # 1. See what Rigor would propose.
292
804
  rigor sig-gen lib/calc.rb
293
805
 
294
- # 2. Run with the observed-params policy to use spec/ as
295
- # a parameter-type signal.
806
+ # 2. Run with the observed-params policy to use the test
807
+ # roots (`test_paths:`) as a parameter-type signal.
296
808
  rigor sig-gen --params=observed lib/calc.rb
297
809
 
298
810
  # 3. Compare against the current sig/ tree.
@@ -105,7 +105,7 @@ choices.
105
105
  conditional / mapped types do not have a Rigor analogue.
106
106
  - [**Coming from PHPStan**](appendix-phpstan.md) — the
107
107
  closest peer in spirit. Identical refinement vocabulary
108
- (`non-empty-string`, `int<min, max>`, `numeric-string`,
108
+ (`non-empty-string`, `Integer[min..max]`, `numeric-string`,
109
109
  `literal-string`), `@phpstan-assert*` ↔ `RBS::Extended`,
110
110
  Type-Specifying Extensions ↔ plugins, baseline diffing.
111
111
  - [**Coming from mypy / Pyright**](appendix-mypy.md) — gradual
@@ -224,7 +224,7 @@ end
224
224
  | --- | --- | --- |
225
225
  | `positive-int` | `when n > 0` | Rigor names and carries the result. |
226
226
  | `non-empty-string` | `when s != ""` / `byte_size(s) > 0` | Rigor produces it from `unless s.empty?`. |
227
- | `int<1, 9>` | `when x in 1..9` | Rigor's range carrier handles arbitrary bounds. |
227
+ | `Integer[1..9]` | `when x in 1..9` | Rigor's range carrier handles arbitrary bounds. |
228
228
  | `non-empty-array[T]` | `when xs != []` | Rigor produces it from `unless arr.empty?`. |
229
229
  | `numeric-string` | `Integer.parse/1` + match | No direct Elixir analogue. |
230
230
 
@@ -283,7 +283,7 @@ The other direction:
283
283
  and Elixir's types do not fold call results to singleton
284
284
  types this way.
285
285
  - **Named refinement carriers.** `non-empty-string`,
286
- `positive-int`, `int<1, 9>`, `numeric-string` — first-class,
286
+ `positive-int`, `Integer[1..9]`, `numeric-string` — first-class,
287
287
  named, and flowed onward from a guard.
288
288
  - **Inferred object shapes and capability roles.** Beyond
289
289
  behaviours and protocols, Rigor infers anonymous structural
@@ -243,7 +243,7 @@ carriers that encode an invariant on the value.
243
243
  | --- | --- | --- |
244
244
  | `non-empty-string` | runtime `if len(s) == 0` check | Rigor produces it from `unless s.empty?`. |
245
245
  | `positive-int` | runtime `if n <= 0` check | Rigor narrows from `n > 0`. |
246
- | `int<1, 9>` | runtime range check | Rigor's range carrier handles arbitrary bounds. |
246
+ | `Integer[1..9]` | runtime range check | Rigor's range carrier handles arbitrary bounds. |
247
247
  | `numeric-string` | `strconv.Atoi` + error check | No type-level analogue in Go. |
248
248
  | `non-empty-array[T]` | runtime `len(xs) == 0` check | Rigor produces it from `unless arr.empty?`. |
249
249
 
@@ -311,7 +311,7 @@ is deliberately minimal:
311
311
  - **Constant folding through method calls.** `"foo".upcase` is
312
312
  `Constant<"FOO">`, not `string`.
313
313
  - **Refinements.** `non-empty-string`, `positive-int`,
314
- `int<1, 9>` — invariants on the value, no runtime check needed
314
+ `Integer[1..9]` — invariants on the value, no runtime check needed
315
315
  to know them statically.
316
316
  - **Inferred shapes beyond interfaces.** Rigor infers anonymous
317
317
  object shapes and capability roles, not just satisfaction of a