rigortype 0.3.9 → 0.4.1

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 (407) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -4
  3. data/data/core_overlay/enumerable.rbs +51 -0
  4. data/data/core_overlay/enumerator.rbs +84 -0
  5. data/data/core_overlay/hash_rbs3.rbs +41 -0
  6. data/data/core_overlay/process.rbs +40 -0
  7. data/data/core_overlay/string_io.rbs +33 -0
  8. data/data/effects/core.yml +3 -3
  9. data/data/gem_overlay/activesupport/core_ext.rbs +127 -4
  10. data/docs/handbook/03-narrowing.md +95 -10
  11. data/docs/handbook/04-tuples-and-shapes.md +8 -6
  12. data/docs/handbook/07-rbs-and-extended.md +16 -11
  13. data/docs/handbook/08-understanding-errors.md +3 -2
  14. data/docs/handbook/10-sorbet.md +9 -10
  15. data/docs/handbook/11-sig-gen.md +454 -13
  16. data/docs/install.md +87 -15
  17. data/docs/manual/02-cli-reference.md +65 -4
  18. data/docs/manual/03-configuration.md +12 -0
  19. data/docs/manual/04-diagnostics.md +4 -0
  20. data/docs/manual/07-plugins.md +1 -1
  21. data/docs/manual/08-skills.md +4 -3
  22. data/docs/manual/10-mcp-server.md +3 -2
  23. data/docs/manual/12-caching.md +43 -4
  24. data/docs/manual/14-rails-quickstart.md +19 -11
  25. data/docs/manual/16-rbs-extended-annotations.md +40 -7
  26. data/docs/manual/19-effect-labels.md +10 -0
  27. data/docs/manual/plugins/README.md +7 -0
  28. data/docs/manual/plugins/rigor-actioncable.md +8 -1
  29. data/docs/manual/plugins/rigor-actionmailer.md +7 -0
  30. data/docs/manual/plugins/rigor-actionpack.md +243 -0
  31. data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
  32. data/docs/manual/plugins/rigor-activejob.md +7 -0
  33. data/docs/manual/plugins/rigor-activerecord.md +110 -4
  34. data/docs/manual/plugins/rigor-activestorage.md +8 -1
  35. data/docs/manual/plugins/rigor-activesupport-core-ext.md +11 -5
  36. data/docs/manual/plugins/rigor-grape.md +106 -0
  37. data/docs/manual/plugins/rigor-graphql.md +23 -2
  38. data/docs/manual/plugins/rigor-pundit.md +8 -1
  39. data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
  40. data/docs/manual/plugins/rigor-rails-routes.md +7 -0
  41. data/docs/manual/plugins/rigor-rbs-inline.md +212 -25
  42. data/docs/manual/plugins/rigor-sidekiq.md +8 -1
  43. data/docs/manual/plugins/rigor-sorbet.md +21 -5
  44. data/exe/rigor +19 -4
  45. data/lib/rigor/analysis/baseline.rb +1 -1
  46. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
  47. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
  48. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -3
  49. data/lib/rigor/analysis/check_rules/rule_ids.rb +20 -5
  50. data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
  51. data/lib/rigor/analysis/check_rules/source_arity.rb +427 -0
  52. data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
  53. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +36 -2
  54. data/lib/rigor/analysis/check_rules.rb +638 -136
  55. data/lib/rigor/analysis/dependency_recorder.rb +222 -17
  56. data/lib/rigor/analysis/fact_store.rb +9 -0
  57. data/lib/rigor/analysis/incremental.rb +26 -0
  58. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +266 -0
  59. data/lib/rigor/analysis/incremental_run_slot.rb +171 -0
  60. data/lib/rigor/analysis/incremental_session.rb +309 -53
  61. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +24 -13
  62. data/lib/rigor/analysis/project_scan.rb +11 -1
  63. data/lib/rigor/analysis/reachability/plugin_roots.rb +0 -1
  64. data/lib/rigor/analysis/reachability/scan_cache.rb +3 -1
  65. data/lib/rigor/analysis/rule_catalog.rb +127 -0
  66. data/lib/rigor/analysis/run_cache_key.rb +46 -18
  67. data/lib/rigor/analysis/run_stats.rb +2 -2
  68. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +168 -12
  69. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
  70. data/lib/rigor/analysis/runner/pool_coordinator.rb +109 -17
  71. data/lib/rigor/analysis/runner/project_pre_passes.rb +44 -17
  72. data/lib/rigor/analysis/runner.rb +603 -54
  73. data/lib/rigor/analysis/shadow_harness.rb +27 -0
  74. data/lib/rigor/analysis/source_rbs_gate.rb +157 -0
  75. data/lib/rigor/analysis/template_unit_collector.rb +303 -0
  76. data/lib/rigor/analysis/template_unit_paths.rb +93 -0
  77. data/lib/rigor/analysis/template_unit_positions.rb +293 -0
  78. data/lib/rigor/analysis/template_units.rb +409 -0
  79. data/lib/rigor/analysis/worker_session.rb +57 -13
  80. data/lib/rigor/bleeding_edge.rb +0 -2
  81. data/lib/rigor/builtins/hkt_builtins.rb +1 -0
  82. data/lib/rigor/builtins/imported_refinements.rb +4 -0
  83. data/lib/rigor/builtins/regex_refinement.rb +17 -9
  84. data/lib/rigor/builtins/static_return_refinements.rb +2 -0
  85. data/lib/rigor/cache/descriptor.rb +45 -1
  86. data/lib/rigor/cache/engine_source.rb +29 -1
  87. data/lib/rigor/cache/file_digest.rb +28 -0
  88. data/lib/rigor/cache/incremental_snapshot.rb +102 -8
  89. data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
  90. data/lib/rigor/cache/rbs_descriptor.rb +37 -7
  91. data/lib/rigor/cache/store.rb +63 -4
  92. data/lib/rigor/cache/value_digest.rb +325 -0
  93. data/lib/rigor/ci_detector.rb +1 -0
  94. data/lib/rigor/cli/check_command.rb +83 -13
  95. data/lib/rigor/cli/doc_links.rb +1 -1
  96. data/lib/rigor/cli/docs_command.rb +4 -4
  97. data/lib/rigor/cli/doctor_command.rb +1 -1
  98. data/lib/rigor/cli/effects_report.rb +2 -2
  99. data/lib/rigor/cli/plugin_command.rb +3 -3
  100. data/lib/rigor/cli/prism_colorizer.rb +0 -1
  101. data/lib/rigor/cli/sig_gen_command.rb +210 -29
  102. data/lib/rigor/cli/skill_command.rb +1 -1
  103. data/lib/rigor/cli/skill_describe.rb +0 -1
  104. data/lib/rigor/cli/type_of_command.rb +19 -5
  105. data/lib/rigor/cli/type_of_renderer.rb +20 -9
  106. data/lib/rigor/cli/type_of_template_probe.rb +189 -0
  107. data/lib/rigor/cli/unused_command.rb +3 -3
  108. data/lib/rigor/cli.rb +21 -3
  109. data/lib/rigor/config_audit.rb +3 -2
  110. data/lib/rigor/config_path.rb +29 -0
  111. data/lib/rigor/configuration/severity_profile.rb +24 -3
  112. data/lib/rigor/configuration.rb +71 -7
  113. data/lib/rigor/effects/ancestry_recorder.rb +191 -0
  114. data/lib/rigor/effects/attribution.rb +11 -2
  115. data/lib/rigor/effects/callee_rule.rb +368 -0
  116. data/lib/rigor/effects/catalog.rb +7 -4
  117. data/lib/rigor/effects/collector.rb +11 -5
  118. data/lib/rigor/effects/config_envelopes.rb +9 -2
  119. data/lib/rigor/effects/definition_context.rb +179 -0
  120. data/lib/rigor/effects/effect_table.rb +11 -3
  121. data/lib/rigor/effects/envelope_check.rb +1 -1
  122. data/lib/rigor/effects/envelope_index.rb +15 -0
  123. data/lib/rigor/effects/file_collection.rb +59 -5
  124. data/lib/rigor/effects/framework_units.rb +1 -1
  125. data/lib/rigor/effects/identity.rb +16 -0
  126. data/lib/rigor/effects/local_ownership.rb +38 -12
  127. data/lib/rigor/effects/method_key.rb +21 -0
  128. data/lib/rigor/effects/mutation_classifier.rb +23 -12
  129. data/lib/rigor/effects/plugin_facts.rb +49 -37
  130. data/lib/rigor/effects/propagator.rb +295 -16
  131. data/lib/rigor/effects/registry.rb +1 -1
  132. data/lib/rigor/effects/scanner.rb +121 -72
  133. data/lib/rigor/effects/signature_sources.rb +1 -1
  134. data/lib/rigor/effects/snapshot.rb +2 -1
  135. data/lib/rigor/effects/summary.rb +27 -4
  136. data/lib/rigor/effects/unit_scan.rb +385 -30
  137. data/lib/rigor/effects/visibility.rb +101 -0
  138. data/lib/rigor/environment/bundle_sig_discovery.rb +5 -3
  139. data/lib/rigor/environment/lockfile_resolver.rb +27 -1
  140. data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
  141. data/lib/rigor/environment/member_consistency.rb +298 -0
  142. data/lib/rigor/environment/rbs_collection_discovery.rb +10 -1
  143. data/lib/rigor/environment/rbs_loader.rb +400 -134
  144. data/lib/rigor/environment/source_rbs_synthesis.rb +147 -0
  145. data/lib/rigor/environment.rb +104 -96
  146. data/lib/rigor/hashing/xxh3.rb +264 -0
  147. data/lib/rigor/inference/acceptance.rb +139 -9
  148. data/lib/rigor/inference/block_auto_splat.rb +216 -0
  149. data/lib/rigor/inference/block_call_timing.rb +338 -0
  150. data/lib/rigor/inference/block_parameter_binder.rb +88 -26
  151. data/lib/rigor/inference/block_repetition.rb +71 -0
  152. data/lib/rigor/inference/body_fixpoint.rb +2 -1
  153. data/lib/rigor/inference/budget_trace.rb +2 -1
  154. data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
  155. data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
  156. data/lib/rigor/inference/captured_locals.rb +393 -15
  157. data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
  158. data/lib/rigor/inference/content_join.rb +200 -27
  159. data/lib/rigor/inference/declaration_walk/context.rb +195 -0
  160. data/lib/rigor/inference/declaration_walk/errors.rb +18 -0
  161. data/lib/rigor/inference/declaration_walk/shadow.rb +147 -0
  162. data/lib/rigor/inference/declaration_walk/traversal.rb +441 -0
  163. data/lib/rigor/inference/declaration_walk.rb +7 -0
  164. data/lib/rigor/inference/def_node_resolver.rb +3 -2
  165. data/lib/rigor/inference/def_return_typer.rb +11 -7
  166. data/lib/rigor/inference/define_method_block_self.rb +64 -0
  167. data/lib/rigor/inference/definer_resolution.rb +266 -0
  168. data/lib/rigor/inference/element_read_widening.rb +22 -10
  169. data/lib/rigor/inference/error_info.rb +196 -0
  170. data/lib/rigor/inference/expression_typer.rb +1665 -538
  171. data/lib/rigor/inference/external_ancestor_resolution.rb +264 -0
  172. data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
  173. data/lib/rigor/inference/global_write_census.rb +239 -0
  174. data/lib/rigor/inference/guard_rebinding.rb +488 -0
  175. data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
  176. data/lib/rigor/inference/index_write_widening.rb +16 -3
  177. data/lib/rigor/inference/indexed_narrowing.rb +61 -9
  178. data/lib/rigor/inference/jump_targets.rb +82 -0
  179. data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
  180. data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
  181. data/lib/rigor/inference/last_line.rb +340 -0
  182. data/lib/rigor/inference/last_status.rb +144 -0
  183. data/lib/rigor/inference/macro_block_self_type.rb +179 -17
  184. data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
  185. data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
  186. data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
  187. data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
  188. data/lib/rigor/inference/match_rebinding.rb +392 -0
  189. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  190. data/lib/rigor/inference/method_dispatcher/block_folding.rb +85 -24
  191. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
  192. data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
  193. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +4 -0
  194. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
  195. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +113 -73
  196. data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
  197. data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
  198. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +901 -38
  199. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
  200. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +128 -53
  201. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +1 -1
  202. data/lib/rigor/inference/method_dispatcher.rb +214 -13
  203. data/lib/rigor/inference/method_parameter_binder.rb +8 -3
  204. data/lib/rigor/inference/module_function_state.rb +213 -0
  205. data/lib/rigor/inference/multi_target_binder.rb +345 -52
  206. data/lib/rigor/inference/mutation_rejoin.rb +4 -1
  207. data/lib/rigor/inference/mutation_widening.rb +70 -48
  208. data/lib/rigor/inference/narrowing.rb +674 -124
  209. data/lib/rigor/inference/operand_effects.rb +167 -0
  210. data/lib/rigor/inference/operand_walk.rb +88 -0
  211. data/lib/rigor/inference/optimistic_origin.rb +152 -9
  212. data/lib/rigor/inference/parameter_inference_collector.rb +13 -11
  213. data/lib/rigor/inference/project_method_ownership.rb +136 -0
  214. data/lib/rigor/inference/project_patched_methods.rb +7 -2
  215. data/lib/rigor/inference/project_patched_scanner.rb +7 -3
  216. data/lib/rigor/inference/receiver_alias.rb +90 -1
  217. data/lib/rigor/inference/receiver_blind_block.rb +219 -0
  218. data/lib/rigor/inference/refinement_mutation.rb +15 -11
  219. data/lib/rigor/inference/repeated_or_writes.rb +463 -0
  220. data/lib/rigor/inference/return_barrier.rb +54 -0
  221. data/lib/rigor/inference/rewrite_mutation.rb +120 -0
  222. data/lib/rigor/inference/scope_indexer/certainty.rb +176 -0
  223. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +35 -0
  224. data/lib/rigor/inference/scope_indexer/def_nestings_collector.rb +47 -0
  225. data/lib/rigor/inference/scope_indexer/layered_def_nestings.rb +36 -0
  226. data/lib/rigor/inference/scope_indexer/member_layouts_collector.rb +61 -0
  227. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +130 -0
  228. data/lib/rigor/inference/scope_indexer/superclasses_collector.rb +64 -0
  229. data/lib/rigor/inference/scope_indexer.rb +5913 -701
  230. data/lib/rigor/inference/singleton_hook_decline.rb +253 -0
  231. data/lib/rigor/inference/statement_evaluator.rb +3249 -497
  232. data/lib/rigor/inference/stored_block_call.rb +54 -0
  233. data/lib/rigor/inference/string_mutation.rb +44 -7
  234. data/lib/rigor/inference/unknown_store_widening.rb +200 -0
  235. data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
  236. data/lib/rigor/language_server/debouncer.rb +0 -1
  237. data/lib/rigor/language_server/diagnostic_publisher.rb +3 -2
  238. data/lib/rigor/language_server/hover_renderer.rb +3 -3
  239. data/lib/rigor/language_server/project_context.rb +5 -3
  240. data/lib/rigor/mcp/server.rb +2 -1
  241. data/lib/rigor/plugin/base.rb +196 -13
  242. data/lib/rigor/plugin/box_probe.rb +91 -0
  243. data/lib/rigor/plugin/bundled_catalog.rb +1 -1
  244. data/lib/rigor/plugin/effect_attribution.rb +58 -4
  245. data/lib/rigor/plugin/io_boundary.rb +82 -4
  246. data/lib/rigor/plugin/isolation.rb +1 -1
  247. data/lib/rigor/plugin/loader.rb +2 -1
  248. data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
  249. data/lib/rigor/plugin/manifest.rb +71 -10
  250. data/lib/rigor/plugin/registry.rb +35 -1
  251. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
  252. data/lib/rigor/plugin/template_unit.rb +196 -0
  253. data/lib/rigor/plugin/trust_policy.rb +3 -3
  254. data/lib/rigor/plugin.rb +1 -0
  255. data/lib/rigor/protection/discovery_seed.rb +20 -9
  256. data/lib/rigor/protection/kill_signature.rb +0 -1
  257. data/lib/rigor/protection/mutation_cache.rb +4 -5
  258. data/lib/rigor/rbs_extended.rb +27 -0
  259. data/lib/rigor/reflection/constant_ancestors.rb +146 -0
  260. data/lib/rigor/reflection/constant_path.rb +41 -13
  261. data/lib/rigor/reflection.rb +73 -107
  262. data/lib/rigor/scope/discovery_index.rb +351 -2
  263. data/lib/rigor/scope/resolution_chain/relevance.rb +165 -0
  264. data/lib/rigor/scope/resolution_chain.rb +884 -0
  265. data/lib/rigor/scope.rb +981 -198
  266. data/lib/rigor/sig_gen/alias_index.rb +289 -0
  267. data/lib/rigor/sig_gen/classification.rb +22 -5
  268. data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
  269. data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
  270. data/lib/rigor/sig_gen/generator.rb +580 -47
  271. data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
  272. data/lib/rigor/sig_gen/method_candidate.rb +47 -3
  273. data/lib/rigor/sig_gen/observation_collector.rb +1 -1
  274. data/lib/rigor/sig_gen/renderer.rb +124 -9
  275. data/lib/rigor/sig_gen/skip_reason_catalog.rb +44 -1
  276. data/lib/rigor/sig_gen/write_result.rb +19 -3
  277. data/lib/rigor/sig_gen/writer.rb +166 -35
  278. data/lib/rigor/sig_gen.rb +3 -0
  279. data/lib/rigor/signature_path_audit.rb +1 -1
  280. data/lib/rigor/source/node_walker.rb +0 -3
  281. data/lib/rigor/source/parameter_envelope.rb +72 -0
  282. data/lib/rigor/source.rb +1 -0
  283. data/lib/rigor/type/combinator.rb +112 -12
  284. data/lib/rigor/type/difference.rb +1 -0
  285. data/lib/rigor/type/hash_shape.rb +1 -1
  286. data/lib/rigor/type/refined.rb +1 -0
  287. data/lib/rigor/version.rb +1 -1
  288. data/lib/rigor.rb +1 -0
  289. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
  290. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +9 -5
  291. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -4
  292. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +0 -1
  293. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
  294. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
  295. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
  296. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
  297. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
  298. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
  299. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +239 -4
  300. data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
  301. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
  302. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
  303. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
  304. data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
  305. data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
  306. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -0
  307. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +9 -5
  308. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
  309. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
  310. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +380 -3
  311. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
  312. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +281 -35
  313. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
  314. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +25 -14
  315. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
  316. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +171 -5
  317. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +2 -0
  318. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  319. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
  320. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +10 -0
  321. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
  322. data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
  323. data/plugins/rigor-grape/sig/grape.rbs +263 -0
  324. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +72 -2
  325. data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
  326. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -0
  327. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +11 -7
  328. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +35 -37
  329. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -1
  330. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -0
  331. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +1 -0
  332. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +9 -17
  333. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
  334. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +217 -36
  335. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -0
  336. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +9 -5
  337. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +0 -1
  338. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +46 -1
  339. data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
  340. data/sig/rigor/analysis/baseline.rbs +69 -7
  341. data/sig/rigor/analysis/fact_store.rbs +1 -1
  342. data/sig/rigor/analysis/project_scan.rbs +74 -0
  343. data/sig/rigor/effects/config_envelopes.rbs +100 -0
  344. data/sig/rigor/effects/effect_table.rbs +60 -0
  345. data/sig/rigor/effects/envelope.rbs +97 -0
  346. data/sig/rigor/effects/envelope_index.rbs +33 -0
  347. data/sig/rigor/effects/file_collection.rbs +81 -0
  348. data/sig/rigor/effects/label.rbs +26 -0
  349. data/sig/rigor/effects/label_set.rbs +46 -0
  350. data/sig/rigor/effects/method_key.rbs +24 -0
  351. data/sig/rigor/effects/origin.rbs +50 -0
  352. data/sig/rigor/effects/plugin_facts.rbs +141 -0
  353. data/sig/rigor/effects/registry.rbs +68 -0
  354. data/sig/rigor/effects/summary.rbs +50 -0
  355. data/sig/rigor/effects/taint_cause.rbs +12 -0
  356. data/sig/rigor/environment.rbs +14 -10
  357. data/sig/rigor/inference/optimistic_origin.rbs +10 -0
  358. data/sig/rigor/inference.rbs +6 -4
  359. data/sig/rigor/plugin/additional_initializer.rbs +36 -0
  360. data/sig/rigor/plugin/base.rbs +30 -7
  361. data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
  362. data/sig/rigor/plugin/effect_attribution.rbs +66 -0
  363. data/sig/rigor/plugin/effect_edge.rbs +33 -0
  364. data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
  365. data/sig/rigor/plugin/io_boundary.rbs +2 -1
  366. data/sig/rigor/plugin/loader.rbs +3 -3
  367. data/sig/rigor/plugin/manifest.rbs +50 -11
  368. data/sig/rigor/plugin/protocol_contract.rbs +68 -0
  369. data/sig/rigor/plugin/registry.rbs +62 -1
  370. data/sig/rigor/plugin.rbs +1 -1
  371. data/sig/rigor/rbs_extended.rbs +1 -1
  372. data/sig/rigor/reflection.rbs +7 -6
  373. data/sig/rigor/scope.rbs +177 -20
  374. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -7
  375. data/sig/rigor/source.rbs +4 -4
  376. data/sig/rigor/testing.rbs +10 -4
  377. data/sig/rigor/type.rbs +6 -0
  378. data/sig/rigor.rbs +38 -20
  379. data/skills/rigor-ask/SKILL.md +8 -5
  380. data/skills/rigor-baseline-reduce/SKILL.md +4 -6
  381. data/skills/rigor-ci-setup/SKILL.md +17 -21
  382. data/skills/rigor-doctor/SKILL.md +24 -22
  383. data/skills/rigor-doctor/references/01-checks.md +97 -33
  384. data/skills/rigor-editor-setup/SKILL.md +6 -4
  385. data/skills/rigor-mcp-setup/SKILL.md +5 -4
  386. data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
  387. data/skills/rigor-next-steps/SKILL.md +4 -2
  388. data/skills/rigor-plugin-author/SKILL.md +19 -23
  389. data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
  390. data/skills/rigor-plugin-author/references/02-walker-and-types.md +12 -25
  391. data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
  392. data/skills/rigor-plugin-review/SKILL.md +6 -4
  393. data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
  394. data/skills/rigor-plugin-tune/SKILL.md +4 -2
  395. data/skills/rigor-project-init/SKILL.md +46 -18
  396. data/skills/rigor-project-init/references/01-detect.md +13 -9
  397. data/skills/rigor-project-init/references/02-configure.md +40 -10
  398. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
  399. data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
  400. data/skills/rigor-protection-uplift/SKILL.md +4 -6
  401. data/skills/rigor-rbs-setup/SKILL.md +4 -2
  402. data/skills/rigor-type-oracle/SKILL.md +4 -6
  403. data/skills/rigor-type-oracle/references/01-oracle-commands.md +12 -8
  404. data/skills/rigor-type-oracle/references/03-gap-protocol.md +0 -3
  405. data/skills/rigor-unused-adjudicate/SKILL.md +4 -2
  406. data/skills/rigor-upgrade/SKILL.md +13 -8
  407. metadata +132 -1
@@ -165,20 +165,14 @@ Rigor::Plugin::Base.suggest(typo, known_names) # nearest match, or nil
165
165
 
166
166
  ## Optional — contribute a return type with `dynamic_return` / `narrowing_facts`
167
167
 
168
- > **Critical — these hooks do NOT make a method "defined", so they do
169
- > NOT suppress `call.undefined-method`.** Method *existence* and call
170
- > *type* are two independent checks. A return-type contribution sharpens
171
- > the type of a call the analyzer has **already resolved to a real
172
- > method** (turning a `Dynamic` return into something precise). It is
173
- > never consulted for a receiver/method the analyzer cannot find — that
174
- > fires `call.undefined-method` first, and a contribution does nothing
175
- > to silence it. **If your goal is to kill a `call.undefined-method`
176
- > cluster on a DSL-generated method (the common reason
177
- > `rigor-project-init` hands off to this skill), the fix is to make the
178
- > method *exist* in Rigor's view — ship RBS declaring it (see "Shipping
179
- > RBS for the DSL" below), not a return-type contribution.** Reach for
180
- > these only when the call already resolves and you want a *better
181
- > return type*.
168
+ > **Prefer RBS to make a DSL method exist.** A `dynamic_return` answer
169
+ > suppresses `call.undefined-method` only at the call sites where the
170
+ > rule fires (its gated receiver kind and methods); RBS makes the method
171
+ > defined for all of core inference — arity, argument types, every call
172
+ > site. To clear a `call.undefined-method` cluster on DSL-generated
173
+ > methods (the common reason `rigor-project-init` hands off to this
174
+ > skill), ship RBS (see "Shipping RBS for the DSL" below). Reach for
175
+ > these hooks when you want a better *return type* for a call.
182
176
 
183
177
  A plugin can do more than emit diagnostics: it can *supply* the
184
178
  inferred return type (or narrowing facts) for a call site the core
@@ -213,10 +207,6 @@ narrowing_facts methods: [:assert_kind_of] do |call_node, scope|
213
207
  end
214
208
  ```
215
209
 
216
- > **`narrowing_facts` was renamed from `type_specifier` in ADR-80.**
217
- > The old verb was removed in 0.3.0 — `narrowing_facts` is the only
218
- > spelling a plugin can declare.
219
-
220
210
  Build return types with `Rigor::Type::Combinator`:
221
211
 
222
212
  ```ruby
@@ -233,12 +223,9 @@ plugin is confident; a wrong contribution propagates downstream.
233
223
  `rigor plugins --capabilities` catalogue enumerates — run it to see
234
224
  exactly what each loaded plugin contributes.
235
225
 
236
- > **The removed fat hook.** The original `flow_contribution_for(call_node:,
237
- > scope:)` was **deleted pre-1.0 in ADR-52 WD3** — defining it now raises
238
- > `ArgumentError` at load time. The two shapes it used to cover are
239
- > expressed by `dynamic_return`'s callable gates: a **method-gated return
240
- > type** (an RSpec `let(:x) { … }` binding, a Sorbet `sig`-driven return —
241
- > keyed on the method, not a fixed receiver class) uses
226
+ > **Callable gates.** A **method-gated return type** (an RSpec
227
+ > `let(:x) { … }` binding, a Sorbet `sig`-driven return — keyed on the
228
+ > method, not a fixed receiver class) uses
242
229
  > `dynamic_return methods: -> { [...] }` or `file_methods: ->(path) { [...] }`;
243
230
  > a **dynamic per-project receiver set** (ActiveStorage's `Attached::One`
244
231
  > on discovered model classes) uses `dynamic_return receivers: -> { [...] }`,
@@ -247,7 +234,7 @@ exactly what each loaded plugin contributes.
247
234
  > if the plugin genuinely needs to sharpen call-site types; a
248
235
  > diagnostics-only plugin skips them entirely.
249
236
 
250
- ## Shipping RBS for the DSL — the way to suppress `call.undefined-method`
237
+ ## Shipping RBS for the DSL — making DSL methods exist
251
238
 
252
239
  If the DSL introduces methods or classes that Rigor cannot see (a
253
240
  `Money` class defined by metaprogramming, `Setting.<name>` accessors a
@@ -106,11 +106,12 @@ CLI tests for the end-to-end wiring.
106
106
 
107
107
  ## Version pinning — the pre-1.0 contract
108
108
 
109
- The plugin contract is **not frozen until `rigortype` v0.2.0** (see
109
+ The plugin contract is **not frozen until `rigortype` v1.0.0** (see
110
110
  SKILL.md). Concretely:
111
111
 
112
- - Gemspec / Gemfile: `rigortype` `>= 0.1.0, < 0.2.0`. Never `>= 0.1`
113
- alone — that floats across the contract-changing v0.2.0 boundary.
112
+ - Gemspec / Gemfile: `rigortype` `~> X.Y.0` for the minor you tested
113
+ (`rigor --version`). Never an open `>=` range — that floats across
114
+ contract-changing minors.
114
115
  - Your plugin's own version is normal semver, independent of
115
116
  `rigortype`'s.
116
117
  - When you bump the `rigortype` pin to a new minor, **re-run the
@@ -120,9 +121,6 @@ SKILL.md). Concretely:
120
121
  - State the supported `rigortype` range in the README so users do
121
122
  not pair the plugin with an incompatible Rigor.
122
123
 
123
- When v0.2.0 lands, re-pin to the stable range and ordinary
124
- compatibility rules apply.
125
-
126
124
  ## README
127
125
 
128
126
  A plugin README should carry:
@@ -151,13 +149,13 @@ wired and the fixture tests run.
151
149
  Either way, if the plugin uncovered a gap that *should* be core
152
150
  Rigor behaviour — or if you hit a plugin-contract rough edge — report
153
151
  it: <https://github.com/rigortype/rigor/issues>. External plugin
154
- authors are the main source of pre-v0.2.0 contract feedback.
152
+ authors are the main source of contract feedback before v1.0.
155
153
 
156
154
  ## Output of this module — plugin shipped
157
155
 
158
156
  - A test suite: fast unit tests + fixture-driven `rigor check`
159
157
  tests, in RSpec or Minitest.
160
- - A `rigortype` pin tight to `< 0.2.0`.
158
+ - A `rigortype` pin tight to the tested minor.
161
159
  - A README stating the compatibility range.
162
160
  - The plugin published as a gem, or committed project-private with
163
161
  CI wired.
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  name: rigor-plugin-review
3
- description: |
4
- Review an existing Rigor plugin's source against the current authoring contract and produce a prioritized upgrade path — the modernization counterpart to rigor-plugin-author. Audits config-default declaration (ADR-40), the AST-walk model (node_rule vs a hand-rolled traversal), return-type / narrowing hooks (dynamic_return / narrowing_facts, not the removed flow_contribution_for or type_specifier), the ADR-60 WD4 authoring helpers (diagnostic / diagnostics_for / suggest / producer_value / read_fact), engine-collaboration vs reimplementation, cache-producer soundness, manifest-field hygiene, and doc freshness. Triggers: "review this Rigor plugin", "does my plugin follow best practices", "upgrade our rigor-prefixed plugin to the latest contract", "modernize this plugin", "is this plugin using the current API". NOT for authoring a new plugin (use rigor-plugin-author), enabling bundled plugins on a project (use rigor-plugin-tune), or tuning plugin config.
3
+ description: >-
4
+ Audit an existing Rigor plugin against the current authoring contract and produce a prioritized upgrade
5
+ path. Use when modernizing or reviewing a `rigor-*` plugin; not for authoring a new plugin, enabling
6
+ bundled plugins, or tuning project config.
5
7
  license: MPL-2.0
6
8
  metadata:
7
9
  version: 0.1.0
@@ -120,7 +122,7 @@ green first, so you can prove each later step is a faithful refactor:
120
122
  # external gem:
121
123
  bundle exec rspec spec/
122
124
  # in the rigor monorepo:
123
- nix … develop --command bundle exec rspec spec/integration/<plugins|examples>/<id>_plugin_spec.rb
125
+ nix develop --command bundle exec rspec spec/integration/<plugins|examples>/<id>_plugin_spec.rb
124
126
  ```
125
127
 
126
128
  If there is no spec, **write one first** (per `rigor-plugin-author`
@@ -148,7 +150,7 @@ step**:
148
150
  ### Phase 5 — Verify
149
151
 
150
152
  ```sh
151
- rigor check <plugin>/lib # ADR-43 contract self-check — MUST be clean
153
+ rigor check <plugin>/lib # ADR-43 contract self-check — must be clean
152
154
  rigor plugins --strict # the plugin still loads
153
155
  rigor plugins --capabilities # node-rule types / dynamic_return receivers look right
154
156
  bundle exec rspec … # the oracle spec, still green
@@ -205,7 +205,8 @@ and put that in a CHANGELOG / migration note, not the class docstring.
205
205
 
206
206
  - `rigor check <plugin>/lib` — the ADR-43 contract self-check resolves
207
207
  the plugin's inherited `Plugin::Base` calls and warns on contract
208
- misuse. MUST be clean; fix the cause, never disable the rule.
208
+ misuse. It must be clean; fix the cause rather than disabling the
209
+ rule.
209
210
  - `rigor plugins --strict` — the plugin still activates.
210
211
  - `rigor plugins --capabilities` — `node_rule_types` /
211
212
  `dynamic_return_receivers` / `narrowing_facts_methods` reflect the
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  name: rigor-plugin-tune
3
- description: |
4
- Re-scan a configured project's Gemfile.lock against Rigor's bundled plugin catalogue and enable the plugins that match its current dependencies (Rails, RSpec, dry-rb, Sidekiq, Devise, …), then verify they all load. Run it after adding a gem, or when an onboarding predates a dependency the project now uses. Triggers: "enable the right Rigor plugins", "I added gem X, does Rigor have a plugin?", "which rigor plugins should this project use?", "rigor plugins for my stack". NOT for first-time onboarding (use rigor-project-init, which does the initial selection) and NOT for authoring a new plugin (use rigor-plugin-author).
3
+ description: >-
4
+ Match a configured project's `Gemfile.lock` to Rigor's bundled plugin catalogue and enable the plugins
5
+ its current dependencies need. Use after adding a gem or when plugin selection is stale; not for
6
+ first-time onboarding or authoring a new plugin.
5
7
  license: MPL-2.0
6
8
  metadata:
7
9
  version: 0.1.0
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  name: rigor-project-init
3
- description: |
4
- Onboard a project to Rigor type-checking from scratch: detect the stack, choose an adoption mode (baseline vs. strict), select plugins, write `.rigor.dist.yml`, then snapshot a baseline or commit to a zero-diagnostic gate. Triggers: "set up Rigor in this project", "configure rigor for X", "add type checking", or running `rigor check` in a Gemfile directory with no `.rigor.yml`. NOT for reducing an existing baseline (use rigor-baseline-reduce) or authoring a plugin (use rigor-plugin-author).
3
+ description: >-
4
+ Onboard a project to Rigor from scratch by detecting the stack, selecting plugins and an adoption mode,
5
+ and writing the initial config and baseline or strict gate. Use for first-time Rigor setup; not for an
6
+ existing baseline or plugin authoring.
5
7
  license: MPL-2.0
6
8
  metadata:
7
9
  version: 0.1.0
@@ -52,7 +54,7 @@ Do NOT trigger for:
52
54
  `rigor-baseline-reduce` skill.
53
55
  - **Writing a Rigor plugin** for the project's own DSL /
54
56
  metaprogramming — that is the `rigor-plugin-author` skill. (This
55
- skill *points at* plugin authoring as an escalation in Phase 7;
57
+ skill *points at* plugin authoring as an escalation in Phase 8;
56
58
  it does not do it.)
57
59
  - **Tweaking an already-configured project** — ordinary edits to an
58
60
  existing `.rigor.yml`; no onboarding pipeline needed.
@@ -77,7 +79,7 @@ adoption entirely.
77
79
 
78
80
  So **before writing any config, present the user with two modes**
79
81
  and let them choose. The mode drives the severity profile, whether a
80
- baseline is generated, and how Phase 7 frames the leftover
82
+ baseline is generated, and how Phase 8 frames the leftover
81
83
  diagnostics.
82
84
 
83
85
  | | **Acknowledge mode** (baseline adoption) | **Strict mode** (no compromise) |
@@ -86,7 +88,7 @@ diagnostics.
86
88
  | Today's diagnostics | Snapshotted into `.rigor-baseline.yml`; suppressed as long as the count does not grow. | All surfaced; every one is fixed or consciously suppressed. |
87
89
  | Hard-to-fix diagnostics | Left in the baseline. The project **trusts its test / spec suite** to cover runtime correctness for those sites — the static `T | nil` reading is worst-case-sound, the suite proves the worst case is not hit. | Fixed, or annotated `# rigor:disable <rule>` with an author-intent reason at the specific line. No blanket suppression. |
88
90
  | `severity_profile` | `lenient` (or `balanced` for a small project). | `strict`. |
89
- | Best for | Mature codebases; incremental adoption; teams that want the regression guard without a big upfront fix. | New / small projects; libraries; teams that want the maximum guarantee and have the budget to reach zero. |
91
+ | Best for | **Every project by default** — mature or brand-new, large or small. | Users fluent in both type theory and RBS who opt in and will resolve Rigor's inference gaps themselves. |
90
92
  | New diagnostics later | Surface immediately — anything beyond the baseline envelope is a regression. | Surface immediately — there is no envelope; every diagnostic is live. |
91
93
 
92
94
  Both modes give the same core guarantee: **a change that introduces
@@ -95,12 +97,35 @@ diagnostics that exist *today*. Acknowledge mode parenthesises them
95
97
  behind a baseline and leans on the test suite; strict mode refuses to
96
98
  parenthesise anything.
97
99
 
98
- If the project's first `rigor check` reports more than ~100 errors,
99
- recommend acknowledge mode as the default and let the user override.
100
- Below that, either mode is reasonable — ask.
101
-
102
- Note the ordering: the error count that feeds this recommendation (and
103
- the `severity_profile` choice in Phase 4) is only *measured* in
100
+ **Recommend acknowledge mode for every project, including new and
101
+ small ones.** Rigor's inference still has gaps, so even a fresh
102
+ project meets diagnostics on correct code. Under strict mode each one
103
+ needs a `sig/` fix, an RBS workaround, or a reasoned
104
+ `# rigor:disable` — work that takes type theory and RBS. Without that
105
+ knowledge, users rewrite correct code for the tool or scatter
106
+ suppressions. Acknowledge mode keeps the same regression guard with an
107
+ exit: a new diagnostic still fails the check, and once it is confirmed
108
+ as an inference gap, the baseline can absorb it (report the false
109
+ positive upstream). `rigor baseline regenerate` rewrites the baseline
110
+ from every live diagnostic, so run it only when a whole-project
111
+ `rigor check` (no path arguments) shows nothing but confirmed false
112
+ positives; otherwise it silences a real
113
+ bug alongside them.
114
+
115
+ Present strict mode as an opt-in for users who know type theory and
116
+ RBS, want the zero-diagnostic gate, and accept resolving inference
117
+ gaps themselves — never as the recommendation, and never because the
118
+ project is new, small, or a library. If the user picks it, tell them
119
+ how to move to acknowledge mode later: optionally relax
120
+ `severity_profile`, re-run triage, then run Phase 7 (generate a
121
+ baseline and wire `baseline:`). The profile change comes first because
122
+ it changes which rules fire.
123
+
124
+ For a large first run (more than ~100 errors), acknowledge mode also
125
+ suggests `severity_profile: lenient`; see Phase 4.
126
+
127
+ Note the ordering: the error count that feeds the `severity_profile`
128
+ choice in Phase 4 is only *measured* in
104
129
  Phase 6's triage run. Any profile written in Phase 4 is therefore
105
130
  **provisional** — Phase 6 revisits it against the measured count (see
106
131
  [`references/02-configure.md`](references/02-configure.md)
@@ -109,12 +134,15 @@ Phase 6's triage run. Any profile written in Phase 4 is therefore
109
134
  ## Non-interactive / agent-driven runs
110
135
 
111
136
  This skill has several ask-the-user points (the mode choice, the RBS
112
- collection install, the final gitignore-and-commit confirmation). When
113
- the user delegated the onboarding up front — including the adoption
114
- mode — those points resolve without blocking:
137
+ collection install, the final gitignore-and-commit confirmation). A
138
+ *delegation* is the user asking, before the run, for the onboarding to
139
+ proceed without these questions ("set it up, don't ask me"). An
140
+ ordinary request to set Rigor up is not one: ask as usual. Under a
141
+ delegation those points resolve without blocking:
115
142
 
116
143
  - The **mode** the user pre-declared is settled; record it and skip the
117
- Phase 2 presentation.
144
+ Phase 2 presentation. If the delegation names no mode, choose
145
+ acknowledge mode and say so in the report — never strict.
118
146
  - Low-risk, reversible, in-repo confirmations (`rbs collection
119
147
  install`, the `.gitignore` addition) are covered by the delegation —
120
148
  act, and note each self-answer in your report.
@@ -154,7 +182,7 @@ committed `sig/` directory.
154
182
  | 5 | [`references/06-agent-contract.md`](references/06-agent-contract.md) | **Phase 8a.** The one paragraph the project's `AGENTS.md` / `CLAUDE.md` keeps so every agent session sources types from Rigor rather than guessing them. Where to append it, when to create the file, and what never to overwrite. |
155
183
  | — (optional) | [`references/05-jit-performance.md`](references/05-jit-performance.md) | **Operational, not a phase.** Run speed via a Ruby JIT: Rigor auto-enables YJIT for long runs (~5 s break-even), how to detect JIT support in your install, the override env vars, and why YJIT beats ZJIT for Rigor on Ruby 4.0. Read only when a large project's `rigor check` wall time matters. |
156
184
 
157
- ## Escalation paths (Phase 7 preview)
185
+ ## Escalation paths (Phase 8 preview)
158
186
 
159
187
  Some diagnostic clusters are neither a quick fix nor honest baseline
160
188
  material. Two of them have a dedicated answer this skill hands off to:
@@ -183,7 +211,7 @@ material. Two of them have a dedicated answer this skill hands off to:
183
211
  support, **open an issue on the Rigor project** asking for it:
184
212
  <https://github.com/rigortype/rigor/issues>.
185
213
 
186
- Neither is a Phase 7 obligation — they are options to *offer* the
214
+ Neither is a Phase 8 obligation — they are options to *offer* the
187
215
  user when the triage report points at one of these causes. The
188
216
  project-DSL handoff is detailed in
189
217
  [`references/03-baseline-and-bugs.md`](references/03-baseline-and-bugs.md)
@@ -223,7 +251,7 @@ only in acknowledge mode). For each, give the commit recommendation:
223
251
 
224
252
  | File | What it is | Commit? |
225
253
  | --- | --- | --- |
226
- | `.rigor.dist.yml` | The shared project config (Phase 4) — `target_ruby`, `paths:`, `plugins:`, `severity_profile:`, and the `baseline:` pointer. The single source of truth every contributor's `rigor check` reads. | **Yes** — it is the shared config; sharing it is the whole point. |
254
+ | `.rigor.dist.yml` | The shared project config (Phase 4) — `target_ruby`, `paths:`, `test_paths:`, `plugins:`, `severity_profile:`, and the `baseline:` pointer. The single source of truth every contributor's `rigor check` reads. | **Yes** — it is the shared config; sharing it is the whole point. |
227
255
  | `.rigor-baseline.yml` | Acknowledge mode only (Phase 7) — the snapshot of today's known diagnostics. Doubles as a record of project state; without it each developer's baseline diverges and the regression guard means different things per machine. | **Yes** — commit it; it documents project state and pins the regression envelope. |
228
256
  | `sig/` | RBS skeletons from `rigor sig-gen` (Phase 5), if that phase ran. A first-class project artefact — it sharpens inference for everyone. | **Yes** — commit it (see [`references/04-sig-uplift.md`](references/04-sig-uplift.md) § "Commit the sig/ directory"). |
229
257
  | `.rigor/` (contains `cache/`) | The per-file analysis cache `rigor check` writes to speed up re-runs. Regenerable and machine-local. | **No** — add `.rigor/` to `.gitignore`. |
@@ -71,14 +71,13 @@ The directory should contain RBS gem subdirectories. Continue to
71
71
  Phase 2 — the collection will be auto-detected by Rigor at analysis
72
72
  time.
73
73
 
74
- **Note: RBS collision after install.** Some gems (e.g. `cgi`, `logger`,
75
- `base64`) were extracted from Ruby's stdlib into standalone gems from
76
- Ruby 3.3 onwards. When these appear in both `.gem_rbs_collection/` and
77
- Rigor's bundled stdlib, `rigor triage` may print a
78
- `RBS::DuplicatedDeclarationError`. If this happens, note the error and
79
- continue — plugin-based diagnostics are unaffected. File a Rigor issue
80
- at <https://github.com/rigortype/rigor/issues> so the engine can
81
- deduplicate stdlib gems from the collection automatically.
74
+ **Note: RBS collision after install.** Rigor skips collection entries
75
+ for gems it already loads from its bundled RBS — including gems
76
+ extracted from Ruby's stdlib, such as `cgi` and `logger` — so the
77
+ collection does not double-declare them. If `rigor triage` still prints
78
+ an `RBS::DuplicatedDeclarationError`, note it and continue, and report
79
+ it at <https://github.com/rigortype/rigor/issues> with the gem names the
80
+ error mentions.
82
81
 
83
82
  ### Path scope
84
83
 
@@ -92,6 +91,11 @@ Note the conventional source roots so Phase 4 can set `paths:`:
92
91
  are checked differently and inflate the diagnostic count. `vendor/`
93
92
  and `tmp/` are always excluded.
94
93
 
94
+ Note the **test roots** too — every directory holding the project's
95
+ tests, not only the conventional one (`spec/`, `test/`, and any
96
+ extra suite such as `test/system` kept elsewhere or an
97
+ `integration/` tree). Phase 4 writes them to `test_paths:`.
98
+
95
99
  ## Phase 3 — Plugin selection
96
100
 
97
101
  Propose a plugin set from the detected families. Present it to the
@@ -120,7 +124,7 @@ ActiveSupport monkey-patches the core classes (`3.days`,
120
124
  `rigor-activesupport-core-ext` bundle, every such call reports
121
125
  `call.undefined-method` — on a real Rails app this is the single
122
126
  largest diagnostic cluster (a measured Mastodon run: ~365 of 489
123
- diagnostics were exactly this). Phase 5's `rigor triage` flags it as
127
+ diagnostics were exactly this). Phase 6's `rigor triage` flags it as
124
128
  hint `activesupport-core-ext`.
125
129
 
126
130
  `rigor-activesupport-core-ext` is a **plugin** (an RBS-bundle plugin
@@ -91,6 +91,11 @@ paths:
91
91
  - app
92
92
  - lib
93
93
 
94
+ # Where the tests live. `rigor sig-gen --params=observed` reads their
95
+ # call sites to type parameters (Phase 5). Not analysed by `rigor check`.
96
+ test_paths:
97
+ - spec
98
+
94
99
  exclude:
95
100
  - vendor
96
101
  - tmp
@@ -107,7 +112,7 @@ plugins:
107
112
 
108
113
  severity_profile: lenient
109
114
 
110
- # Phase 6 (acknowledge mode) appends this line after generating the
115
+ # Phase 7 (acknowledge mode) appends this line after generating the
111
116
  # baseline. Strict mode leaves it out entirely.
112
117
  # baseline: .rigor-baseline.yml
113
118
  ```
@@ -133,14 +138,38 @@ entirely — including a `sig/` that `rigor sig-gen --write` created.
133
138
  List only directories that contain `.rbs` files or subdirectories of
134
139
  them. Rigor walks each path recursively.
135
140
 
136
- A strict-mode plain-Ruby gem is shorter:
141
+ A plain-Ruby gem is shorter. The `balanced` default needs no
142
+ `severity_profile:` key:
137
143
 
138
144
  ```yaml
139
145
  paths:
140
146
  - lib
141
- severity_profile: strict
147
+ test_paths:
148
+ - test
149
+
150
+ # Phase 7 (acknowledge mode) appends this line after generating the
151
+ # baseline. Strict mode (opt-in) sets `severity_profile: strict` and
152
+ # leaves it out.
153
+ # baseline: .rigor-baseline.yml
142
154
  ```
143
155
 
156
+ ### Test roots — write `test_paths:` explicitly
157
+
158
+ Always write `test_paths:` with the test roots Phase 1 found, even
159
+ when they are the conventional `spec/` or `test/`. Left unset, Rigor
160
+ auto-detects whichever of `spec/` and `test/` exist; that covers the
161
+ common layouts, but the committed config is then silent about where
162
+ the tests are, and a suite anywhere else (a second tree, an
163
+ `integration/` directory) is never read. Declaring the key replaces
164
+ auto-detection, so list every root. A project with no tests yet
165
+ writes `test_paths: []`.
166
+
167
+ The key affects no diagnostic. Its reader today is `rigor sig-gen
168
+ --params=observed` (Phase 5), which types a parameter from the
169
+ arguments the tests pass. When there is no root to read, or a declared
170
+ root does not exist, sig-gen says so on stderr and the affected
171
+ parameters stay `untyped`.
172
+
144
173
  ### Key reference
145
174
 
146
175
  Only the keys this skill needs. `rigor --help` and the project
@@ -150,19 +179,20 @@ handbook document the full surface.
150
179
  | --- | --- |
151
180
  | `paths:` | Directories Rigor analyses. Source roots only — not `spec/` / `test/`. |
152
181
  | `exclude:` | Paths removed from the `paths:` walk. |
182
+ | `test_paths:` | The project's test roots (`spec`, `test`, or several). Write it explicitly; see § "Test roots". Relative entries resolve against the config file. `rigor check` ignores it; `sig-gen` names a declared root that does not exist. |
153
183
  | `plugins:` | Plugin ids to activate (the Phase 3 set). |
154
184
  | `signature_paths:` | Extra RBS source **directories** (paths, not gem names; resolved relative to the config file). Use it for the project's own local `sig/` if it has one. RBS-bundle *plugins* like `rigor-activesupport-core-ext` ship their own `sig/` and need no entry here — list them under `plugins:`. |
155
185
  | `severity_profile:` | `lenient` / `balanced` / `strict`. See the table above. |
156
186
  | `severity_overrides:` | Per-rule severity tweaks. Leave empty at init; the baseline-reduce workflow tunes it later. |
157
- | `baseline:` | Path to the baseline file. **Only acknowledge mode sets it**, and only in Phase 6 *after* the file exists. Per Rigor's no-magic rule, a `.rigor-baseline.yml` on disk does nothing until this key names it. |
158
- | `pre_eval:` | Project files Rigor walks before per-file inference — used to register in-project monkey-patches. Leave empty at init; Phase 7 may suggest it. |
159
- | `dependencies.source_inference:` | Opt-in inference for gems shipping no RBS. Leave empty at init; Phase 7 may suggest it. |
187
+ | `baseline:` | Path to the baseline file. **Only acknowledge mode sets it**, and only in Phase 7 *after* the file exists. Per Rigor's no-magic rule, a `.rigor-baseline.yml` on disk does nothing until this key names it. |
188
+ | `pre_eval:` | Project files Rigor walks before per-file inference — used to register in-project monkey-patches. Leave empty at init; Phase 6a may add it. |
189
+ | `dependencies.source_inference:` | Opt-in inference for gems shipping no RBS. Leave empty at init; Phase 8 may suggest it. |
160
190
 
161
191
  ## Do not write the baseline yet
162
192
 
163
193
  Phase 4 writes the config with the `baseline:` line **commented out
164
- or absent**. The baseline file does not exist until Phase 6, and a
165
- `baseline:` pointing at a missing file is an error. Phase 6 writes
194
+ or absent**. The baseline file does not exist until Phase 7, and a
195
+ `baseline:` pointing at a missing file is an error. Phase 7 writes
166
196
  the file and uncomments / appends the line in one step.
167
197
 
168
198
  Strict mode never adds `baseline:` at all.
@@ -231,8 +261,8 @@ with the message text.
231
261
 
232
262
  ## Output of this module
233
263
 
234
- A committed `.rigor.dist.yml` with `paths:`, `exclude:`,
235
- `plugins:`, and `severity_profile:` set — and no active
264
+ A committed `.rigor.dist.yml` with `paths:`, `test_paths:`,
265
+ `exclude:`, `plugins:`, and `severity_profile:` set — and no active
236
266
  `baseline:` line. `rigor plugins` reports every entry loaded,
237
267
  zero load errors. No Gemfile changes; plugins are bundled inside
238
268
  `rigortype`.
@@ -75,12 +75,12 @@ Use the sections like this:
75
75
  | --- | --- | --- |
76
76
  | `activesupport-core-ext` | ActiveSupport core-class monkey-patches not loaded. | Go back to Phase 3/4: add `rigor-activesupport-core-ext` to `plugins:` (it is an RBS-bundle plugin), re-run triage. This is a config gap, not a bug. |
77
77
  | `gem-without-rbs` | A dependency ships no RBS. | If `rbs_collection.lock.yaml` was present and Phase 1 installed the collection, re-run `rigor triage` — the hint may shrink or disappear. Otherwise: Phase 8 escalation — `bundle exec rbs collection install`, or `dependencies.source_inference:`, or open a Rigor issue. |
78
- | `project-monkey-patch-known` | **High confidence.** The engine proved the called method *is* defined by a project file (a reopened core/stdlib/gem class) but is not applied cross-file. The hint **names the defining file(s)**. | Phase 7 escalation — copy the named file(s) straight into `pre_eval:`. No detective work needed; the diagnostic already found the source. |
79
- | `project-monkey-patch` | An in-project monkey-patch / refinement Rigor did not see, inferred from the *spread* of the same method across ≥3 files (no proven def site). | Phase 7 escalation — find the defining file (grep for `def <method>` / `class <Receiver>`), register it via `pre_eval:`, or (if it is a DSL) write a project plugin. |
80
- | `unresolved-toplevel` | Toplevel calls (outside any `def`/`class`/`module`) that resolve to nothing visible — usually a script relying on a monkey-patch or a `require`d helper Rigor did not walk (ADR-34). | Phase 7 escalation — if a project file defines these (toplevel `def`, or a patch on `Object`/`Kernel`), list it in `pre_eval:`. If nothing defines them, treat as genuine typos / missing requires (Phase 8). |
78
+ | `project-monkey-patch-known` | **High confidence.** The engine proved the called method *is* defined by a project file (a reopened core/stdlib/gem class) but is not applied cross-file. The hint **names the defining file(s)**. | Phase 6a — copy the named file(s) straight into `pre_eval:`. No detective work needed; the diagnostic already found the source. |
79
+ | `project-monkey-patch` | An in-project monkey-patch / refinement Rigor did not see, inferred from the *spread* of the same method across ≥3 files (no proven def site). | Phase 6a — find the defining file (grep for `def <method>` / `class <Receiver>`), register it via `pre_eval:`, or (if it is a DSL) write a project plugin. |
80
+ | `unresolved-toplevel` | Toplevel calls (outside any `def`/`class`/`module`) that resolve to nothing visible — usually a script relying on a monkey-patch or a `require`d helper Rigor did not walk (ADR-34). | Phase 6a — if a project file defines these (toplevel `def`, or a patch on `Object`/`Kernel`), list it in `pre_eval:`. If nothing defines them, treat as genuine typos / missing requires (Phase 8). |
81
81
  | `activerecord-relation-misinference` | An ActiveRecord relation inferred as `Array`. | Ensure `rigor-activerecord` is enabled (Phase 3). If it persists, it is an engine gap — open a Rigor issue. |
82
82
  | `systemic-file-cluster` | One file × one rule, large count. | Acknowledge mode: a clean baseline bucket. Strict mode: a single fix may clear many — review that file first. |
83
- | `genuine-bugs` | Low-count rules scattered across files. | **Phase 7** — these are the localised bugs Rigor caught. Review first, in both modes. Note: the hint groups all low-count rules regardless of severity — filter for `error` severity when prioritising actionable items. |
83
+ | `genuine-bugs` | Low-count rules scattered across files. | **Phase 8** — these are the localised bugs Rigor caught. Review first, in both modes. Note: the hint groups all low-count rules regardless of severity — filter for `error` severity when prioritising actionable items. |
84
84
 
85
85
  If triage flags `activesupport-core-ext` (or any config gap),
86
86
  **fix the config and re-run `rigor triage` before continuing**. The
@@ -170,8 +170,8 @@ bundle exec rbs collection install # if rbs is in Gemfile
170
170
  # or: rbs collection install
171
171
  ```
172
172
 
173
- Re-run `rigor triage`. If the `gem-without-rbs` count drops,
174
- re-generate the baseline against the new number.
173
+ Re-run `rigor triage`. If the `gem-without-rbs` count drops, carry
174
+ the new, smaller count into Phase 7.
175
175
 
176
176
  ## Phase 7 — Generate the baseline (acknowledge mode only)
177
177
 
@@ -215,7 +215,8 @@ So ordinary coding cannot quietly grow the diagnostic count: the
215
215
  baseline is a ceiling, not a blanket. Reducing it later is the
216
216
  `rigor-baseline-reduce` skill's job.
217
217
 
218
- Commit `.rigor-baseline.yml` — it documents project state.
218
+ Recommend committing `.rigor-baseline.yml` — it documents project
219
+ state; list it in the Final step's file inventory.
219
220
 
220
221
  Print the suppression summary for the user: "N diagnostics recorded
221
222
  as baseline; M will surface on subsequent runs."
@@ -288,18 +289,17 @@ sig issue.
288
289
 
289
290
  #### `call.argument-type-mismatch` on regex capture variables (`$1`, `$~`)
290
291
 
291
- Rigor infers `$1`, `$~`, and similar capture variables as
292
- `String | nil` everywhere, even inside `gsub`/`match` blocks where
293
- they are guaranteed non-nil by the match condition. Diagnostics of
294
- the form:
292
+ Rigor narrows `$1`, `$~`, and similar capture variables to non-nil
293
+ after a successful `=~` match and inside a `when /re/` branch. Where
294
+ the match is guaranteed by some other shape Rigor does not track, they
295
+ stay `String | nil`, and a diagnostic of the form:
295
296
 
296
297
  ```
297
298
  expected String, got String | nil (on $1 / $~)
298
299
  ```
299
300
 
300
- are **engine FPs** (ADR-24 WD3 / known limitation). Note them as
301
- noise rather than surfacing them as bugs. They belong in the
302
- baseline.
301
+ is an **engine FP** (ADR-24 WD3 / known limitation). Note it as noise rather than
302
+ surfacing it as a bug; it belongs in the baseline.
303
303
 
304
304
  ### Escalation path A — application-specific metaprogramming
305
305
 
@@ -28,7 +28,8 @@ rigor sig-gen lib # adjust path to match the paths: key
28
28
  Typical output at this point: most `new_method` candidates have
29
29
  literal or concrete return types (`"hello"`, `42`, `:done`, `nil`).
30
30
  Methods whose return type cannot be inferred show up with
31
- `skip_reason: :untyped_return` — these are the sig precision targets.
31
+ `skip_reason: "sig.skipped.untyped-return"` — these are the sig
32
+ precision targets.
32
33
 
33
34
  To get a breakdown in JSON:
34
35
 
@@ -50,6 +51,12 @@ rigor sig-gen --format json lib | ruby -e '
50
51
 
51
52
  ## Step 5-b — Write the baseline sigs
52
53
 
54
+ If the project's `Steepfile` reads inline annotations (`inline: true`
55
+ beside `signature "sig"`), first add `sig_gen:` / `inline_declared: skip`
56
+ to `.rigor.yml`. Otherwise sig-gen copies every `# @rbs` / `#:`
57
+ declaration into `sig/`, and Steep reports each copied method as
58
+ `DuplicatedMethodDefinition`.
59
+
53
60
  ```sh
54
61
  rigor sig-gen --write lib
55
62
  ```
@@ -63,12 +70,18 @@ head -40 sig/lib/your_class.rbs
63
70
 
64
71
  At this point, `attr_reader` and `attr_accessor` methods that rely on
65
72
  ivar types set from `initialize` parameters will likely still be
66
- absent (classified as `:untyped_return`). Step 5-c fixes that.
73
+ absent (skipped as `sig.skipped.untyped-return`). Step 5-c fixes that.
74
+ An `attr_writer` or `attr_accessor` stays skipped even then unless its
75
+ ivar is assigned from an `initialize` parameter: the writer stores
76
+ whatever its caller passes, so the ivar reads untyped. That covers
77
+ `@count = 0` and `@logger = Logger.new` alike, and any other method
78
+ that returns the ivar.
67
79
 
68
80
  ## Step 5-c — Precision uplift with --params=observed
69
81
 
70
82
  `--params=observed` tells sig-gen to collect observed argument types
71
- from every call site it processes during the analysis pass. The most
83
+ from the call sites in the project's test roots — the `test_paths:`
84
+ Phase 4 wrote (`--observe=PATH` overrides them for one run). The most
72
85
  important use case: **`attr_reader` / `attr_writer` / `attr_accessor`
73
86
  methods whose `@ivar` is assigned from an `initialize` parameter**.
74
87
 
@@ -88,7 +101,8 @@ Person.new("Alice", 30)
88
101
  Person.new("Bob", 25)
89
102
  ```
90
103
 
91
- Without observations: `attr_reader :name` → skipped as `:untyped_return`
104
+ Without observations: `attr_reader :name` → skipped as
105
+ `sig.skipped.untyped-return`
92
106
  (the ivar's type is unknown because the blank inference scope never
93
107
  sees the parameter values).
94
108
 
@@ -129,9 +143,9 @@ there are a few options depending on the cause:
129
143
  | Pattern | Cause | Fix |
130
144
  |---|---|---|
131
145
  | `attr_reader :x` with `@x` never set in `initialize` | ivar set from a DB query, config read, or side effect | Add a hand-written sig: create (or edit) `sig/your_class.rbs` with `attr_reader x: String` |
132
- | Deep method chains on untyped receivers | Cascade from a gem with no RBS | `rbs collection install`; Phase 7 escalation path B |
146
+ | Deep method chains on untyped receivers | Cascade from a gem with no RBS | `rbs collection install`; Phase 8 escalation path B |
133
147
  | Recursive or mutually recursive methods | Return type not inferrable without a base case | Add a `# @rbs return: YourType` inline annotation, or a hand-written sig |
134
- | Dynamic methods (`define_method`, DSL) | Metaprogramming Rigor cannot follow | Phase 7 escalation path A (project plugin) |
148
+ | Dynamic methods (`define_method`, DSL) | Metaprogramming Rigor cannot follow | Phase 8 escalation path A (project plugin) |
135
149
 
136
150
  Do not spend long on residual `untyped` methods at this stage — a
137
151
  handful of `untyped` returns in `sig/` does not block adoption. The
@@ -140,12 +154,10 @@ reach perfect sig coverage.
140
154
 
141
155
  ## Step 5-e — Commit the sig/ directory
142
156
 
143
- Once you are satisfied with the initial sig quality:
144
-
145
- ```sh
146
- git add sig/
147
- git commit -m "Add initial RBS sigs from rigor sig-gen (--params=observed)"
148
- ```
157
+ Once you are satisfied with the initial sig quality, recommend
158
+ committing `sig/`: list it in the Final step's file inventory
159
+ (`SKILL.md` § "Final step") rather than committing it here — the user
160
+ confirms every commit.
149
161
 
150
162
  A committed `sig/` is a first-class project artefact: it improves
151
163
  inference quality on every subsequent run and is maintained alongside
@@ -164,8 +176,9 @@ the source (add new sig files when adding classes; update sigs with
164
176
 
165
177
  ## Output of this module
166
178
 
167
- A committed `sig/` directory with RBS skeletons for all statically
168
- inferrable methods. Remaining `:untyped_return` methods are noted for
179
+ A `sig/` directory, ready to commit, with RBS skeletons for all
180
+ statically inferrable methods. Remaining `sig.skipped.untyped-return`
181
+ methods are noted for
169
182
  potential manual annotation; they do not block Phase 6.
170
183
 
171
184
  Proceed to Phase 6 ([`03-baseline-and-bugs.md`](03-baseline-and-bugs.md)).
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  name: rigor-protection-uplift
3
- description: |
4
- Close the type-protection holes `rigor coverage --protection` surfaces: for each unprotected dispatch site, run `rigor sig-gen` first, hand-author only the minimal residual annotation, then verify with a double gate — the site becomes protected AND `rigor check` gains no new diagnostic. Triggers: "raise type protection", "add types where Rigor can't catch bugs", "act on coverage --protection / --mutation output", "make more of this code bug-catchable". NOT for Rigor's own `lib/` or the bundled plugins (use `rigor sig-gen` directly there and treat gaps as engine signal), and NOT for first-time setup (use rigor-project-init).
3
+ description: >-
4
+ Close the protection gaps reported by `rigor coverage --protection`, using generated signatures before
5
+ minimal residual annotations and a no-new-diagnostics gate. Use when increasing bug-catching coverage
6
+ in an adopting project; not for Rigor's own tree, bundled plugins, or first-time setup.
5
7
  license: MPL-2.0
6
8
  metadata:
7
9
  version: 0.1.0
@@ -40,10 +42,6 @@ copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
40
42
 
41
43
  ## When NOT to use
42
44
 
43
- - **Rigor's own `lib/`, or the bundled `plugins/` / `examples/`** — the
44
- self-check tree. Hand-authoring types there collides with the
45
- sig-gen-first ethos; run `rigor sig-gen` directly and treat residual
46
- gaps as engine signal to report, not a private fix.
47
45
  - **"Make my code more precise" with no protection goal** — that is
48
46
  `rigor coverage` (precision), not `--protection`.
49
47
  - **A project with no Rigor config yet** — onboard first with