rigortype 0.3.9 → 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 (363) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  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/10-sorbet.md +9 -10
  14. data/docs/handbook/11-sig-gen.md +454 -13
  15. data/docs/manual/02-cli-reference.md +63 -2
  16. data/docs/manual/03-configuration.md +7 -0
  17. data/docs/manual/04-diagnostics.md +4 -0
  18. data/docs/manual/07-plugins.md +1 -1
  19. data/docs/manual/10-mcp-server.md +3 -2
  20. data/docs/manual/16-rbs-extended-annotations.md +40 -7
  21. data/docs/manual/19-effect-labels.md +10 -0
  22. data/docs/manual/plugins/README.md +7 -0
  23. data/docs/manual/plugins/rigor-actioncable.md +8 -1
  24. data/docs/manual/plugins/rigor-actionmailer.md +7 -0
  25. data/docs/manual/plugins/rigor-actionpack.md +243 -0
  26. data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
  27. data/docs/manual/plugins/rigor-activejob.md +7 -0
  28. data/docs/manual/plugins/rigor-activerecord.md +110 -4
  29. data/docs/manual/plugins/rigor-activestorage.md +8 -1
  30. data/docs/manual/plugins/rigor-activesupport-core-ext.md +11 -5
  31. data/docs/manual/plugins/rigor-grape.md +106 -0
  32. data/docs/manual/plugins/rigor-graphql.md +23 -2
  33. data/docs/manual/plugins/rigor-pundit.md +8 -1
  34. data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
  35. data/docs/manual/plugins/rigor-rails-routes.md +7 -0
  36. data/docs/manual/plugins/rigor-rbs-inline.md +212 -25
  37. data/docs/manual/plugins/rigor-sidekiq.md +8 -1
  38. data/docs/manual/plugins/rigor-sorbet.md +21 -5
  39. data/exe/rigor +19 -4
  40. data/lib/rigor/analysis/baseline.rb +1 -1
  41. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
  42. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
  43. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -3
  44. data/lib/rigor/analysis/check_rules/rule_ids.rb +20 -5
  45. data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
  46. data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
  47. data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
  48. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +36 -2
  49. data/lib/rigor/analysis/check_rules.rb +520 -49
  50. data/lib/rigor/analysis/dependency_recorder.rb +23 -0
  51. data/lib/rigor/analysis/fact_store.rb +9 -0
  52. data/lib/rigor/analysis/incremental.rb +26 -0
  53. data/lib/rigor/analysis/incremental_session.rb +36 -4
  54. data/lib/rigor/analysis/project_scan.rb +11 -1
  55. data/lib/rigor/analysis/reachability/plugin_roots.rb +0 -1
  56. data/lib/rigor/analysis/reachability/scan_cache.rb +1 -0
  57. data/lib/rigor/analysis/rule_catalog.rb +127 -0
  58. data/lib/rigor/analysis/run_cache_key.rb +20 -11
  59. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +167 -11
  60. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
  61. data/lib/rigor/analysis/runner/pool_coordinator.rb +107 -15
  62. data/lib/rigor/analysis/runner/project_pre_passes.rb +26 -7
  63. data/lib/rigor/analysis/runner.rb +278 -20
  64. data/lib/rigor/analysis/template_unit_collector.rb +303 -0
  65. data/lib/rigor/analysis/template_unit_paths.rb +91 -0
  66. data/lib/rigor/analysis/template_unit_positions.rb +293 -0
  67. data/lib/rigor/analysis/template_units.rb +399 -0
  68. data/lib/rigor/analysis/worker_session.rb +53 -10
  69. data/lib/rigor/bleeding_edge.rb +0 -2
  70. data/lib/rigor/builtins/hkt_builtins.rb +1 -0
  71. data/lib/rigor/builtins/imported_refinements.rb +4 -0
  72. data/lib/rigor/builtins/regex_refinement.rb +17 -9
  73. data/lib/rigor/builtins/static_return_refinements.rb +2 -0
  74. data/lib/rigor/cache/descriptor.rb +6 -1
  75. data/lib/rigor/cache/engine_source.rb +29 -1
  76. data/lib/rigor/cache/incremental_snapshot.rb +33 -1
  77. data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
  78. data/lib/rigor/cache/rbs_descriptor.rb +37 -7
  79. data/lib/rigor/cache/store.rb +0 -1
  80. data/lib/rigor/ci_detector.rb +1 -0
  81. data/lib/rigor/cli/doc_links.rb +1 -1
  82. data/lib/rigor/cli/docs_command.rb +4 -4
  83. data/lib/rigor/cli/plugin_command.rb +3 -3
  84. data/lib/rigor/cli/prism_colorizer.rb +0 -1
  85. data/lib/rigor/cli/sig_gen_command.rb +210 -29
  86. data/lib/rigor/cli/skill_command.rb +1 -1
  87. data/lib/rigor/cli/skill_describe.rb +0 -1
  88. data/lib/rigor/cli/type_of_command.rb +19 -5
  89. data/lib/rigor/cli/type_of_renderer.rb +20 -9
  90. data/lib/rigor/cli/type_of_template_probe.rb +189 -0
  91. data/lib/rigor/cli.rb +21 -3
  92. data/lib/rigor/configuration/severity_profile.rb +19 -3
  93. data/lib/rigor/configuration.rb +66 -3
  94. data/lib/rigor/effects/ancestry_recorder.rb +191 -0
  95. data/lib/rigor/effects/attribution.rb +11 -2
  96. data/lib/rigor/effects/callee_rule.rb +368 -0
  97. data/lib/rigor/effects/catalog.rb +7 -4
  98. data/lib/rigor/effects/collector.rb +11 -5
  99. data/lib/rigor/effects/config_envelopes.rb +9 -2
  100. data/lib/rigor/effects/definition_context.rb +179 -0
  101. data/lib/rigor/effects/effect_table.rb +11 -3
  102. data/lib/rigor/effects/envelope_check.rb +1 -1
  103. data/lib/rigor/effects/envelope_index.rb +15 -0
  104. data/lib/rigor/effects/file_collection.rb +59 -5
  105. data/lib/rigor/effects/framework_units.rb +1 -1
  106. data/lib/rigor/effects/identity.rb +16 -0
  107. data/lib/rigor/effects/local_ownership.rb +38 -12
  108. data/lib/rigor/effects/method_key.rb +21 -0
  109. data/lib/rigor/effects/mutation_classifier.rb +23 -12
  110. data/lib/rigor/effects/plugin_facts.rb +43 -31
  111. data/lib/rigor/effects/propagator.rb +295 -16
  112. data/lib/rigor/effects/registry.rb +1 -1
  113. data/lib/rigor/effects/scanner.rb +121 -72
  114. data/lib/rigor/effects/signature_sources.rb +1 -1
  115. data/lib/rigor/effects/snapshot.rb +2 -1
  116. data/lib/rigor/effects/summary.rb +27 -4
  117. data/lib/rigor/effects/unit_scan.rb +385 -30
  118. data/lib/rigor/effects/visibility.rb +101 -0
  119. data/lib/rigor/environment/lockfile_resolver.rb +17 -0
  120. data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
  121. data/lib/rigor/environment/member_consistency.rb +298 -0
  122. data/lib/rigor/environment/rbs_loader.rb +399 -133
  123. data/lib/rigor/environment.rb +103 -38
  124. data/lib/rigor/hashing/xxh3.rb +264 -0
  125. data/lib/rigor/inference/acceptance.rb +139 -9
  126. data/lib/rigor/inference/block_auto_splat.rb +216 -0
  127. data/lib/rigor/inference/block_call_timing.rb +338 -0
  128. data/lib/rigor/inference/block_parameter_binder.rb +73 -27
  129. data/lib/rigor/inference/block_repetition.rb +71 -0
  130. data/lib/rigor/inference/body_fixpoint.rb +2 -1
  131. data/lib/rigor/inference/budget_trace.rb +2 -1
  132. data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
  133. data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
  134. data/lib/rigor/inference/captured_locals.rb +387 -15
  135. data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
  136. data/lib/rigor/inference/content_join.rb +200 -27
  137. data/lib/rigor/inference/def_return_typer.rb +11 -7
  138. data/lib/rigor/inference/define_method_block_self.rb +64 -0
  139. data/lib/rigor/inference/element_read_widening.rb +22 -10
  140. data/lib/rigor/inference/error_info.rb +196 -0
  141. data/lib/rigor/inference/expression_typer.rb +1532 -496
  142. data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
  143. data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
  144. data/lib/rigor/inference/global_write_census.rb +239 -0
  145. data/lib/rigor/inference/guard_rebinding.rb +447 -0
  146. data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
  147. data/lib/rigor/inference/index_write_widening.rb +16 -3
  148. data/lib/rigor/inference/indexed_narrowing.rb +61 -9
  149. data/lib/rigor/inference/jump_targets.rb +82 -0
  150. data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
  151. data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
  152. data/lib/rigor/inference/last_line.rb +340 -0
  153. data/lib/rigor/inference/last_status.rb +144 -0
  154. data/lib/rigor/inference/macro_block_self_type.rb +167 -17
  155. data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
  156. data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
  157. data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
  158. data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
  159. data/lib/rigor/inference/match_rebinding.rb +392 -0
  160. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  161. data/lib/rigor/inference/method_dispatcher/block_folding.rb +85 -24
  162. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
  163. data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
  164. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +4 -0
  165. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
  166. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +113 -73
  167. data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
  168. data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
  169. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +881 -38
  170. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
  171. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +128 -53
  172. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +1 -1
  173. data/lib/rigor/inference/method_dispatcher.rb +214 -13
  174. data/lib/rigor/inference/method_parameter_binder.rb +8 -3
  175. data/lib/rigor/inference/multi_target_binder.rb +340 -52
  176. data/lib/rigor/inference/mutation_rejoin.rb +4 -1
  177. data/lib/rigor/inference/mutation_widening.rb +70 -48
  178. data/lib/rigor/inference/narrowing.rb +540 -120
  179. data/lib/rigor/inference/operand_effects.rb +167 -0
  180. data/lib/rigor/inference/operand_walk.rb +88 -0
  181. data/lib/rigor/inference/optimistic_origin.rb +152 -9
  182. data/lib/rigor/inference/parameter_inference_collector.rb +3 -2
  183. data/lib/rigor/inference/project_method_ownership.rb +136 -0
  184. data/lib/rigor/inference/project_patched_methods.rb +7 -2
  185. data/lib/rigor/inference/project_patched_scanner.rb +7 -3
  186. data/lib/rigor/inference/receiver_alias.rb +90 -1
  187. data/lib/rigor/inference/receiver_blind_block.rb +219 -0
  188. data/lib/rigor/inference/refinement_mutation.rb +15 -11
  189. data/lib/rigor/inference/repeated_or_writes.rb +463 -0
  190. data/lib/rigor/inference/return_barrier.rb +54 -0
  191. data/lib/rigor/inference/rewrite_mutation.rb +120 -0
  192. data/lib/rigor/inference/scope_indexer.rb +4615 -551
  193. data/lib/rigor/inference/statement_evaluator.rb +3176 -490
  194. data/lib/rigor/inference/stored_block_call.rb +54 -0
  195. data/lib/rigor/inference/string_mutation.rb +44 -7
  196. data/lib/rigor/inference/unknown_store_widening.rb +200 -0
  197. data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
  198. data/lib/rigor/language_server/debouncer.rb +0 -1
  199. data/lib/rigor/language_server/diagnostic_publisher.rb +3 -2
  200. data/lib/rigor/language_server/hover_renderer.rb +3 -3
  201. data/lib/rigor/language_server/project_context.rb +5 -3
  202. data/lib/rigor/mcp/server.rb +2 -1
  203. data/lib/rigor/plugin/base.rb +168 -5
  204. data/lib/rigor/plugin/box_probe.rb +91 -0
  205. data/lib/rigor/plugin/bundled_catalog.rb +1 -1
  206. data/lib/rigor/plugin/effect_attribution.rb +58 -4
  207. data/lib/rigor/plugin/loader.rb +2 -1
  208. data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
  209. data/lib/rigor/plugin/manifest.rb +71 -10
  210. data/lib/rigor/plugin/registry.rb +35 -1
  211. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
  212. data/lib/rigor/plugin/template_unit.rb +196 -0
  213. data/lib/rigor/plugin.rb +1 -0
  214. data/lib/rigor/protection/discovery_seed.rb +3 -1
  215. data/lib/rigor/protection/kill_signature.rb +0 -1
  216. data/lib/rigor/protection/mutation_cache.rb +1 -2
  217. data/lib/rigor/rbs_extended.rb +27 -0
  218. data/lib/rigor/reflection/constant_ancestors.rb +97 -0
  219. data/lib/rigor/reflection/constant_path.rb +19 -6
  220. data/lib/rigor/reflection.rb +72 -105
  221. data/lib/rigor/scope/discovery_index.rb +135 -2
  222. data/lib/rigor/scope.rb +859 -49
  223. data/lib/rigor/sig_gen/alias_index.rb +289 -0
  224. data/lib/rigor/sig_gen/classification.rb +22 -5
  225. data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
  226. data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
  227. data/lib/rigor/sig_gen/generator.rb +558 -31
  228. data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
  229. data/lib/rigor/sig_gen/method_candidate.rb +47 -3
  230. data/lib/rigor/sig_gen/observation_collector.rb +1 -1
  231. data/lib/rigor/sig_gen/renderer.rb +124 -9
  232. data/lib/rigor/sig_gen/skip_reason_catalog.rb +44 -1
  233. data/lib/rigor/sig_gen/write_result.rb +19 -3
  234. data/lib/rigor/sig_gen/writer.rb +166 -35
  235. data/lib/rigor/sig_gen.rb +3 -0
  236. data/lib/rigor/signature_path_audit.rb +1 -1
  237. data/lib/rigor/source/node_walker.rb +0 -3
  238. data/lib/rigor/source/parameter_envelope.rb +72 -0
  239. data/lib/rigor/source.rb +1 -0
  240. data/lib/rigor/type/combinator.rb +88 -12
  241. data/lib/rigor/type/difference.rb +1 -0
  242. data/lib/rigor/type/hash_shape.rb +1 -1
  243. data/lib/rigor/type/refined.rb +1 -0
  244. data/lib/rigor/version.rb +1 -1
  245. data/lib/rigor.rb +1 -0
  246. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
  247. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +9 -5
  248. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -4
  249. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +0 -1
  250. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
  251. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
  252. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
  253. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
  254. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
  255. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
  256. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +239 -4
  257. data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
  258. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
  259. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
  260. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
  261. data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
  262. data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
  263. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -0
  264. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +9 -5
  265. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
  266. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
  267. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +380 -3
  268. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
  269. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +281 -35
  270. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
  271. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +25 -14
  272. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
  273. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +171 -5
  274. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +2 -0
  275. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  276. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
  277. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
  278. data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
  279. data/plugins/rigor-grape/sig/grape.rbs +263 -0
  280. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +72 -2
  281. data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
  282. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -0
  283. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +11 -7
  284. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +35 -37
  285. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -1
  286. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -0
  287. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +1 -0
  288. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +9 -17
  289. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
  290. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +217 -36
  291. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -0
  292. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +9 -5
  293. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +0 -1
  294. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +46 -1
  295. data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
  296. data/sig/rigor/analysis/baseline.rbs +69 -7
  297. data/sig/rigor/analysis/fact_store.rbs +1 -1
  298. data/sig/rigor/analysis/project_scan.rbs +74 -0
  299. data/sig/rigor/effects/config_envelopes.rbs +100 -0
  300. data/sig/rigor/effects/effect_table.rbs +60 -0
  301. data/sig/rigor/effects/envelope.rbs +97 -0
  302. data/sig/rigor/effects/envelope_index.rbs +33 -0
  303. data/sig/rigor/effects/file_collection.rbs +81 -0
  304. data/sig/rigor/effects/label.rbs +26 -0
  305. data/sig/rigor/effects/label_set.rbs +46 -0
  306. data/sig/rigor/effects/method_key.rbs +24 -0
  307. data/sig/rigor/effects/origin.rbs +50 -0
  308. data/sig/rigor/effects/plugin_facts.rbs +141 -0
  309. data/sig/rigor/effects/registry.rbs +68 -0
  310. data/sig/rigor/effects/summary.rbs +50 -0
  311. data/sig/rigor/effects/taint_cause.rbs +12 -0
  312. data/sig/rigor/environment.rbs +12 -10
  313. data/sig/rigor/inference/optimistic_origin.rbs +10 -0
  314. data/sig/rigor/inference.rbs +6 -4
  315. data/sig/rigor/plugin/additional_initializer.rbs +36 -0
  316. data/sig/rigor/plugin/base.rbs +30 -7
  317. data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
  318. data/sig/rigor/plugin/effect_attribution.rbs +66 -0
  319. data/sig/rigor/plugin/effect_edge.rbs +33 -0
  320. data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
  321. data/sig/rigor/plugin/io_boundary.rbs +1 -1
  322. data/sig/rigor/plugin/loader.rbs +3 -3
  323. data/sig/rigor/plugin/manifest.rbs +50 -11
  324. data/sig/rigor/plugin/protocol_contract.rbs +68 -0
  325. data/sig/rigor/plugin/registry.rbs +62 -1
  326. data/sig/rigor/plugin.rbs +1 -1
  327. data/sig/rigor/rbs_extended.rbs +1 -1
  328. data/sig/rigor/reflection.rbs +7 -6
  329. data/sig/rigor/scope.rbs +135 -20
  330. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -7
  331. data/sig/rigor/source.rbs +4 -4
  332. data/sig/rigor/testing.rbs +10 -4
  333. data/sig/rigor/type.rbs +6 -0
  334. data/sig/rigor.rbs +38 -20
  335. data/skills/rigor-ask/SKILL.md +8 -5
  336. data/skills/rigor-baseline-reduce/SKILL.md +4 -6
  337. data/skills/rigor-ci-setup/SKILL.md +17 -21
  338. data/skills/rigor-doctor/SKILL.md +24 -22
  339. data/skills/rigor-doctor/references/01-checks.md +97 -33
  340. data/skills/rigor-editor-setup/SKILL.md +6 -4
  341. data/skills/rigor-mcp-setup/SKILL.md +5 -4
  342. data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
  343. data/skills/rigor-next-steps/SKILL.md +4 -2
  344. data/skills/rigor-plugin-author/SKILL.md +19 -23
  345. data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
  346. data/skills/rigor-plugin-author/references/02-walker-and-types.md +12 -25
  347. data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
  348. data/skills/rigor-plugin-review/SKILL.md +6 -4
  349. data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
  350. data/skills/rigor-plugin-tune/SKILL.md +4 -2
  351. data/skills/rigor-project-init/SKILL.md +9 -7
  352. data/skills/rigor-project-init/references/01-detect.md +13 -9
  353. data/skills/rigor-project-init/references/02-configure.md +33 -8
  354. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
  355. data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
  356. data/skills/rigor-protection-uplift/SKILL.md +4 -6
  357. data/skills/rigor-rbs-setup/SKILL.md +4 -2
  358. data/skills/rigor-type-oracle/SKILL.md +4 -6
  359. data/skills/rigor-type-oracle/references/01-oracle-commands.md +12 -8
  360. data/skills/rigor-type-oracle/references/03-gap-protocol.md +0 -3
  361. data/skills/rigor-unused-adjudicate/SKILL.md +4 -2
  362. data/skills/rigor-upgrade/SKILL.md +13 -8
  363. metadata +108 -1
@@ -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) |
@@ -154,7 +156,7 @@ committed `sig/` directory.
154
156
  | 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
157
  | — (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
158
 
157
- ## Escalation paths (Phase 7 preview)
159
+ ## Escalation paths (Phase 8 preview)
158
160
 
159
161
  Some diagnostic clusters are neither a quick fix nor honest baseline
160
162
  material. Two of them have a dedicated answer this skill hands off to:
@@ -183,7 +185,7 @@ material. Two of them have a dedicated answer this skill hands off to:
183
185
  support, **open an issue on the Rigor project** asking for it:
184
186
  <https://github.com/rigortype/rigor/issues>.
185
187
 
186
- Neither is a Phase 7 obligation — they are options to *offer* the
188
+ Neither is a Phase 8 obligation — they are options to *offer* the
187
189
  user when the triage report points at one of these causes. The
188
190
  project-DSL handoff is detailed in
189
191
  [`references/03-baseline-and-bugs.md`](references/03-baseline-and-bugs.md)
@@ -223,7 +225,7 @@ only in acknowledge mode). For each, give the commit recommendation:
223
225
 
224
226
  | File | What it is | Commit? |
225
227
  | --- | --- | --- |
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. |
228
+ | `.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
229
  | `.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
230
  | `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
231
  | `.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
  ```
@@ -138,9 +143,28 @@ A strict-mode plain-Ruby gem is shorter:
138
143
  ```yaml
139
144
  paths:
140
145
  - lib
146
+ test_paths:
147
+ - test
141
148
  severity_profile: strict
142
149
  ```
143
150
 
151
+ ### Test roots — write `test_paths:` explicitly
152
+
153
+ Always write `test_paths:` with the test roots Phase 1 found, even
154
+ when they are the conventional `spec/` or `test/`. Left unset, Rigor
155
+ auto-detects whichever of `spec/` and `test/` exist; that covers the
156
+ common layouts, but the committed config is then silent about where
157
+ the tests are, and a suite anywhere else (a second tree, an
158
+ `integration/` directory) is never read. Declaring the key replaces
159
+ auto-detection, so list every root. A project with no tests yet
160
+ writes `test_paths: []`.
161
+
162
+ The key affects no diagnostic. Its reader today is `rigor sig-gen
163
+ --params=observed` (Phase 5), which types a parameter from the
164
+ arguments the tests pass. When there is no root to read, or a declared
165
+ root does not exist, sig-gen says so on stderr and the affected
166
+ parameters stay `untyped`.
167
+
144
168
  ### Key reference
145
169
 
146
170
  Only the keys this skill needs. `rigor --help` and the project
@@ -150,19 +174,20 @@ handbook document the full surface.
150
174
  | --- | --- |
151
175
  | `paths:` | Directories Rigor analyses. Source roots only — not `spec/` / `test/`. |
152
176
  | `exclude:` | Paths removed from the `paths:` walk. |
177
+ | `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
178
  | `plugins:` | Plugin ids to activate (the Phase 3 set). |
154
179
  | `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
180
  | `severity_profile:` | `lenient` / `balanced` / `strict`. See the table above. |
156
181
  | `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. |
182
+ | `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. |
183
+ | `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. |
184
+ | `dependencies.source_inference:` | Opt-in inference for gems shipping no RBS. Leave empty at init; Phase 8 may suggest it. |
160
185
 
161
186
  ## Do not write the baseline yet
162
187
 
163
188
  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
189
+ or absent**. The baseline file does not exist until Phase 7, and a
190
+ `baseline:` pointing at a missing file is an error. Phase 7 writes
166
191
  the file and uncomments / appends the line in one step.
167
192
 
168
193
  Strict mode never adds `baseline:` at all.
@@ -231,8 +256,8 @@ with the message text.
231
256
 
232
257
  ## Output of this module
233
258
 
234
- A committed `.rigor.dist.yml` with `paths:`, `exclude:`,
235
- `plugins:`, and `severity_profile:` set — and no active
259
+ A committed `.rigor.dist.yml` with `paths:`, `test_paths:`,
260
+ `exclude:`, `plugins:`, and `severity_profile:` set — and no active
236
261
  `baseline:` line. `rigor plugins` reports every entry loaded,
237
262
  zero load errors. No Gemfile changes; plugins are bundled inside
238
263
  `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
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  name: rigor-rbs-setup
3
- description: |
4
- Install community RBS for the project's gems with `rbs collection install`, so Rigor stops typing calls into RBS-less dependencies as `Dynamic` and gains real coverage (and real bug-catching) on them. Rigor auto-detects the resulting `rbs_collection.lock.yaml` — no Rigor config change needed. Triggers: "set up rbs collection", "my gems type as Dynamic", "rigor check says N gems have no RBS available", "reduce false positives from untyped gems". NOT for first-time Rigor setup (use rigor-project-init first) and NOT for a gem that has no entry in the community collection (that needs rigor-plugin-author or a Rigor issue).
3
+ description: >-
4
+ Install community RBS for a project's gems so Rigor can type dependencies that currently become
5
+ `Dynamic`. Use when `rbs collection` is missing or untyped gems limit coverage; not for first-time
6
+ Rigor setup or a gem with no community RBS entry.
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-type-oracle
3
- description: |
4
- Before writing or asserting ANY Ruby type, get it from Rigor rather than from reading the code: `rigor type-of FILE:LINE:COL` / `rigor annotate FILE` for an expression, `rigor sig-gen --print FILE` for a method signature, call-site observation for a parameter. A type you did not obtain from Rigor is a guess, and a guessed type is never written anywhere. Triggers: writing RBS under `sig/`, an inline `#:` / `# @rbs` annotation, a Sorbet `sig do … end`, a YARD `@param` / `@return`, a type stated in a doc sentence or a review comment, a nil check / `is_a?` / `respond_to?` guard justified by "this should be an X", "add types to this class / file", "document this method", "what type is this / what does this return?". Applies to Rigor's own tree too. When Rigor answers `Dynamic[top]` or `untyped`, or `sig-gen` skips the method, report the gap — never fill it in from inference of your own. NOT for setting Rigor up (use rigor-next-steps) or working a baseline down (use rigor-baseline-reduce).
3
+ description: >-
4
+ Obtain a Ruby type from Rigor before writing or asserting it, using `type-of`, `annotate`, or `sig-gen`
5
+ as appropriate. Use when adding RBS, inline annotations, Sorbet/YARD types, type-shaped docs, or
6
+ type-justified guards; not for Rigor setup or baseline reduction.
5
7
  license: MPL-2.0
6
8
  metadata:
7
9
  version: 0.1.0
@@ -59,10 +61,6 @@ scope the moment you are about to write or assert one:
59
61
  - Being asked "what type is this?", "what does this method return?",
60
62
  "add types to this class", "document this file".
61
63
 
62
- It applies to **Rigor's own tree** as well: `lib/`, the bundled plugins,
63
- and the examples are held to the same rule, and a gap found there is
64
- engine signal worth more than the annotation you would have written.
65
-
66
64
  ## When NOT to use
67
65
 
68
66
  - **Setting Rigor up on a project that has none** → `rigor-next-steps`
@@ -101,7 +101,7 @@ rigor sig-gen [paths]
101
101
  | `--overwrite` | Allow a tighter return to replace user-authored RBS. |
102
102
  | `--include-private` | Emit private / protected instance methods too (default: public only). |
103
103
  | `--params=untyped\|observed\|observed-strict` | Parameter policy. Default `untyped`. `observed-strict` is reserved and currently a usage error. |
104
- | `--observe=PATH` | Directory / file to scan for call-site observations. Repeatable. Defaults to `spec/` when present. |
104
+ | `--observe=PATH` | Directory / file to scan for call-site observations. Repeatable. Defaults to the configured `test_paths:` (unset: whichever of `spec/` and `test/` exist). |
105
105
  | `--new-files` / `--new-methods` / `--tighter-returns` | Emit only that classification. |
106
106
  | `--format=text\|json` | Text RBS, or the structured candidate report. |
107
107
  | `--config=PATH` | Explicit `.rigor.yml`. |
@@ -133,8 +133,9 @@ skip is a finding. `--format=json` names each one:
133
133
  "classification": "skipped", "skip_reason": "sig.skipped.untyped-return" }
134
134
  ```
135
135
 
136
- Classifications: `new-file`, `new-method`, `tighter-return`, `equivalent`
137
- (nothing to tighten; silently dropped), `skipped`. Skip reasons and what
136
+ Classifications (the JSON `classification` values): `new_file`,
137
+ `new_method`, `tighter_return`, `equivalent` (nothing to tighten;
138
+ silently dropped), `skipped`. Skip reasons and what
138
139
  each one means for you: [`03-gap-protocol.md`](03-gap-protocol.md).
139
140
 
140
141
  ### Deriving a parameter type from call sites
@@ -215,10 +216,11 @@ a family prefix (`call`, `flow`, `assert`, `dump`, `def`) it prints the
215
216
  rule's firing conditions, the severity per profile, the evidence tier,
216
217
  and how to suppress it.
217
218
 
218
- **`explain` covers diagnostic rules only.** A `sig.skipped.*` id is a
219
- sig-gen *skip reason*, not a diagnostic rule — `rigor explain
220
- sig.skipped.untyped-return` answers `Unknown rule`. Skip reasons are
221
- documented in [`03-gap-protocol.md`](03-gap-protocol.md).
219
+ **`explain` has two catalogues.** A `sig.skipped.*` id is a sig-gen
220
+ *skip reason*, not a diagnostic rule, but `rigor explain
221
+ sig.skipped.untyped-return` answers it from a second catalogue (no
222
+ severity, no profile, nothing to suppress). The table in
223
+ [`03-gap-protocol.md`](03-gap-protocol.md) is the summary.
222
224
 
223
225
  ## `rigor check` — the gate, not the oracle
224
226
 
@@ -255,7 +257,9 @@ Two shape differences from the CLI worth knowing:
255
257
  - `rigor_sig_gen` returns the **JSON candidate report**, always — there
256
258
  is no `--print` text mode and no `--diff`. Read `rbs` per candidate.
257
259
  - `rigor_sig_gen` exposes `params` but **not** `observe`; observation
258
- falls back to `spec/` when present. Point it elsewhere from the CLI.
260
+ reads the configured `test_paths:` (unset: whichever of `spec/` and
261
+ `test/` exist). To observe anything else, declare it in `test_paths:`
262
+ or use the CLI's `--observe`.
259
263
 
260
264
  `rigor_triage` and `rigor_coverage` are also served, and belong to
261
265
  `rigor-baseline-reduce` / `rigor-protection-uplift` rather than here.
@@ -123,6 +123,3 @@ more to the project than any annotation. File it at
123
123
  Also say `rigor --version`, and note whether `sig/` and the relevant
124
124
  plugins were in play — a gap that only appears without community RBS is a
125
125
  different bug from one that survives it.
126
-
127
- Inside Rigor's own tree the same report is the deliverable: the gap is
128
- the reason not to hand-write the RBS there.
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  name: rigor-unused-adjudicate
3
- description: |
4
- Find dead code in a Ruby project with `rigor unused` — establish what the report can see on THIS project first, then adjudicate every row before proposing any deletion. Use this whenever someone asks to find or remove dead code, unused classes, unused constants, or "code nobody calls", whenever they ask what a `rigor unused` report means or which rows are safe to delete, and whenever a dead-code cleanup, codebase inventory, or legacy audit comes up — even if they never say "rigor". The report is a review queue and not a defect list; on an adjudicated corpus target only 4 of 57 rows were genuinely dead, so acting on it directly produces mostly wrong deletions. NOT for deleting a specific class you already know is dead, and NOT for `rigor check` diagnostics (those are ordinary type errors).
3
+ description: >-
4
+ Adjudicate a `rigor unused` report safely before proposing dead-code removal. Use when interpreting or
5
+ reviewing Rigor's unused classes/modules/constants, or when a cleanup is based on that report; not for
6
+ a specific deletion already known to be safe or for ordinary `rigor check` diagnostics.
5
7
  license: MPL-2.0
6
8
  metadata:
7
9
  version: 0.2.0
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  name: rigor-upgrade
3
- description: |
4
- Adopt a new Rigor version cleanly: after upgrading the `rigortype` gem, re-run the analysis, diff the diagnostics against the committed baseline, and sort the changes into genuine new catches (sharper inference), known sig-quality false positives, and the baseline you should regenerate. Triggers: "I upgraded Rigor, what changed?", "new diagnostics after gem update rigortype", "adopt the new Rigor version", "rigor baseline drifted after upgrade". NOT for first-time setup (use rigor-project-init) or routine baseline work unrelated to an upgrade (use rigor-baseline-reduce).
3
+ description: >-
4
+ Adopt a new `rigortype` version by comparing its diagnostics with the committed baseline and separating
5
+ new catches from signature-quality false positives. Use after a Rigor gem upgrade; not for first-time
6
+ setup or routine baseline reduction.
5
7
  license: MPL-2.0
6
8
  metadata:
7
9
  version: 0.1.0
@@ -50,12 +52,15 @@ rigor --version
50
52
  ### Phase 2 — see the delta against the committed baseline
51
53
 
52
54
  ```sh
53
- rigor check
54
- rigor diff # compare current diagnostics to the saved baseline JSON
55
+ rigor check # everything outside the committed baseline's envelope
56
+ rigor baseline drift # per-bucket movement against .rigor-baseline.yml
55
57
  ```
56
58
 
57
- `rigor diff` shows what is **new** relative to the baseline — that set is
58
- what the upgrade changed.
59
+ With `baseline:` wired, `rigor check` already hides what the baseline
60
+ covers, so what it prints is **new** relative to the baseline — that set
61
+ is what the upgrade changed. `rigor baseline drift` adds the bucket view:
62
+ buckets now over their recorded count, and buckets the new version
63
+ cleared or shrank.
59
64
 
60
65
  ### Phase 3 — sort the new diagnostics
61
66
 
@@ -80,8 +85,8 @@ envelope so the regeneration does not bury what you just fixed:
80
85
  rigor baseline regenerate
81
86
  ```
82
87
 
83
- Commit the updated `.rigor-baseline.yml` together with any fixes, so the
84
- team adopts the same post-upgrade baseline.
88
+ Recommend committing the updated `.rigor-baseline.yml` together with any
89
+ fixes, so the team adopts the same post-upgrade baseline.
85
90
 
86
91
  ## Note
87
92