rigortype 0.3.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (634) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/core_overlay/enumerable.rbs +51 -0
  5. data/data/core_overlay/enumerator.rbs +84 -0
  6. data/data/core_overlay/hash_rbs3.rbs +41 -0
  7. data/data/core_overlay/process.rbs +40 -0
  8. data/data/core_overlay/string_io.rbs +33 -0
  9. data/data/effects/core.yml +3 -3
  10. data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
  11. data/docs/handbook/02-everyday-types.md +17 -14
  12. data/docs/handbook/03-narrowing.md +132 -14
  13. data/docs/handbook/04-tuples-and-shapes.md +8 -6
  14. data/docs/handbook/05-methods-and-blocks.md +1 -1
  15. data/docs/handbook/06-classes.md +2 -2
  16. data/docs/handbook/07-rbs-and-extended.md +19 -1
  17. data/docs/handbook/10-sorbet.md +9 -10
  18. data/docs/handbook/11-sig-gen.md +525 -13
  19. data/docs/handbook/README.md +1 -1
  20. data/docs/handbook/appendix-elixir.md +2 -2
  21. data/docs/handbook/appendix-go.md +2 -2
  22. data/docs/handbook/appendix-java-csharp.md +2 -2
  23. data/docs/handbook/appendix-mypy.md +2 -2
  24. data/docs/handbook/appendix-phpstan.md +1 -1
  25. data/docs/handbook/appendix-rust.md +1 -1
  26. data/docs/handbook/appendix-type-theory.md +4 -4
  27. data/docs/handbook/appendix-typescript.md +1 -1
  28. data/docs/llms.txt +2 -0
  29. data/docs/manual/02-cli-reference.md +95 -6
  30. data/docs/manual/03-configuration.md +18 -0
  31. data/docs/manual/04-diagnostics.md +15 -0
  32. data/docs/manual/07-plugins.md +15 -5
  33. data/docs/manual/08-skills.md +23 -2
  34. data/docs/manual/10-mcp-server.md +3 -2
  35. data/docs/manual/11-ci.md +9 -0
  36. data/docs/manual/15-type-protection-coverage.md +8 -0
  37. data/docs/manual/16-rbs-extended-annotations.md +46 -8
  38. data/docs/manual/18-removing-dead-code.md +10 -8
  39. data/docs/manual/19-effect-labels.md +10 -0
  40. data/docs/manual/plugins/README.md +7 -0
  41. data/docs/manual/plugins/rigor-actioncable.md +8 -1
  42. data/docs/manual/plugins/rigor-actionmailer.md +7 -0
  43. data/docs/manual/plugins/rigor-actionpack.md +261 -0
  44. data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
  45. data/docs/manual/plugins/rigor-activejob.md +7 -0
  46. data/docs/manual/plugins/rigor-activerecord.md +138 -4
  47. data/docs/manual/plugins/rigor-activestorage.md +8 -1
  48. data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
  49. data/docs/manual/plugins/rigor-grape.md +106 -0
  50. data/docs/manual/plugins/rigor-graphql.md +23 -2
  51. data/docs/manual/plugins/rigor-pundit.md +8 -1
  52. data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
  53. data/docs/manual/plugins/rigor-rails-routes.md +7 -0
  54. data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
  55. data/docs/manual/plugins/rigor-sidekiq.md +8 -1
  56. data/docs/manual/plugins/rigor-sorbet.md +21 -5
  57. data/exe/rigor +19 -4
  58. data/lib/rigor/analysis/baseline.rb +3 -3
  59. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  60. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  61. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
  62. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  63. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
  64. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
  65. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  66. data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
  67. data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
  68. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  69. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  70. data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
  71. data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
  72. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
  73. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  74. data/lib/rigor/analysis/check_rules.rb +718 -79
  75. data/lib/rigor/analysis/crash_signature.rb +8 -24
  76. data/lib/rigor/analysis/dependency_recorder.rb +23 -0
  77. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  78. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  79. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  80. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  81. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  82. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  83. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  84. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  85. data/lib/rigor/analysis/fact_store.rb +15 -1
  86. data/lib/rigor/analysis/incremental.rb +42 -0
  87. data/lib/rigor/analysis/incremental_session.rb +117 -26
  88. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  89. data/lib/rigor/analysis/project_scan.rb +11 -1
  90. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  91. data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
  92. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  93. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  94. data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
  95. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  96. data/lib/rigor/analysis/result.rb +1 -3
  97. data/lib/rigor/analysis/rule_catalog.rb +131 -1
  98. data/lib/rigor/analysis/run_cache_key.rb +21 -12
  99. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  100. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  101. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  102. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
  103. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  104. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
  105. data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
  106. data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
  107. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  108. data/lib/rigor/analysis/runner.rb +407 -51
  109. data/lib/rigor/analysis/template_unit_collector.rb +303 -0
  110. data/lib/rigor/analysis/template_unit_paths.rb +91 -0
  111. data/lib/rigor/analysis/template_unit_positions.rb +293 -0
  112. data/lib/rigor/analysis/template_units.rb +399 -0
  113. data/lib/rigor/analysis/worker_session.rb +62 -20
  114. data/lib/rigor/bleeding_edge.rb +10 -24
  115. data/lib/rigor/builtins/hkt_builtins.rb +3 -2
  116. data/lib/rigor/builtins/imported_refinements.rb +159 -21
  117. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  118. data/lib/rigor/builtins/regex_refinement.rb +21 -13
  119. data/lib/rigor/builtins/static_return_refinements.rb +7 -6
  120. data/lib/rigor/cache/annotation_location.rb +2 -4
  121. data/lib/rigor/cache/descriptor.rb +63 -20
  122. data/lib/rigor/cache/engine_source.rb +33 -5
  123. data/lib/rigor/cache/file_digest.rb +9 -1
  124. data/lib/rigor/cache/incremental_snapshot.rb +72 -6
  125. data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
  126. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  127. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  128. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  129. data/lib/rigor/cache/rbs_descriptor.rb +112 -14
  130. data/lib/rigor/cache/rbs_environment.rb +8 -4
  131. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  132. data/lib/rigor/cache/store.rb +76 -32
  133. data/lib/rigor/ci_detector.rb +1 -0
  134. data/lib/rigor/cli/annotate_command.rb +5 -6
  135. data/lib/rigor/cli/check_command.rb +57 -10
  136. data/lib/rigor/cli/check_invocation.rb +6 -11
  137. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  138. data/lib/rigor/cli/coverage_command.rb +1 -1
  139. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  140. data/lib/rigor/cli/coverage_scan.rb +1 -5
  141. data/lib/rigor/cli/diff_command.rb +1 -1
  142. data/lib/rigor/cli/doc_links.rb +4 -4
  143. data/lib/rigor/cli/docs_command.rb +6 -6
  144. data/lib/rigor/cli/doctor_command.rb +85 -38
  145. data/lib/rigor/cli/effects_command.rb +2 -2
  146. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  147. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  148. data/lib/rigor/cli/explain_command.rb +34 -1
  149. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  150. data/lib/rigor/cli/lsp_command.rb +1 -1
  151. data/lib/rigor/cli/mcp_command.rb +1 -1
  152. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  153. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  154. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  155. data/lib/rigor/cli/plugin_command.rb +4 -4
  156. data/lib/rigor/cli/plugins_command.rb +2 -1
  157. data/lib/rigor/cli/prism_colorizer.rb +1 -2
  158. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  159. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  160. data/lib/rigor/cli/sig_gen_command.rb +212 -31
  161. data/lib/rigor/cli/skill_command.rb +2 -2
  162. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  163. data/lib/rigor/cli/skill_describe.rb +35 -35
  164. data/lib/rigor/cli/trace_command.rb +4 -4
  165. data/lib/rigor/cli/trace_renderer.rb +5 -6
  166. data/lib/rigor/cli/triage_command.rb +1 -1
  167. data/lib/rigor/cli/type_of_command.rb +29 -11
  168. data/lib/rigor/cli/type_of_renderer.rb +20 -9
  169. data/lib/rigor/cli/type_of_template_probe.rb +189 -0
  170. data/lib/rigor/cli/type_scan_command.rb +4 -4
  171. data/lib/rigor/cli/unused_command.rb +11 -3
  172. data/lib/rigor/cli/upgrade_command.rb +1 -1
  173. data/lib/rigor/cli.rb +89 -9
  174. data/lib/rigor/config_audit.rb +30 -5
  175. data/lib/rigor/configuration/severity_profile.rb +25 -9
  176. data/lib/rigor/configuration.rb +109 -7
  177. data/lib/rigor/effects/ancestry_recorder.rb +191 -0
  178. data/lib/rigor/effects/attribution.rb +12 -5
  179. data/lib/rigor/effects/callee_rule.rb +368 -0
  180. data/lib/rigor/effects/catalog.rb +7 -4
  181. data/lib/rigor/effects/collector.rb +11 -5
  182. data/lib/rigor/effects/config_envelopes.rb +15 -10
  183. data/lib/rigor/effects/definition_context.rb +179 -0
  184. data/lib/rigor/effects/definition_lines.rb +25 -6
  185. data/lib/rigor/effects/effect_table.rb +11 -4
  186. data/lib/rigor/effects/entry_points.rb +0 -2
  187. data/lib/rigor/effects/envelope_check.rb +9 -9
  188. data/lib/rigor/effects/envelope_index.rb +20 -8
  189. data/lib/rigor/effects/file_collection.rb +59 -5
  190. data/lib/rigor/effects/framework_units.rb +5 -7
  191. data/lib/rigor/effects/identity.rb +20 -6
  192. data/lib/rigor/effects/inline_anchor.rb +7 -7
  193. data/lib/rigor/effects/label_intent.rb +3 -4
  194. data/lib/rigor/effects/liskov_check.rb +8 -8
  195. data/lib/rigor/effects/local_ownership.rb +38 -12
  196. data/lib/rigor/effects/method_key.rb +22 -1
  197. data/lib/rigor/effects/mutation_classifier.rb +23 -12
  198. data/lib/rigor/effects/plugin_facts.rb +47 -37
  199. data/lib/rigor/effects/propagator.rb +297 -19
  200. data/lib/rigor/effects/registry.rb +1 -1
  201. data/lib/rigor/effects/scanner.rb +122 -73
  202. data/lib/rigor/effects/signature_sources.rb +4 -6
  203. data/lib/rigor/effects/snapshot.rb +12 -11
  204. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  205. data/lib/rigor/effects/summary.rb +27 -4
  206. data/lib/rigor/effects/unit_scan.rb +393 -38
  207. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  208. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  209. data/lib/rigor/effects/visibility.rb +101 -0
  210. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  211. data/lib/rigor/environment/class_registry.rb +3 -1
  212. data/lib/rigor/environment/failure_slot.rb +2 -2
  213. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  214. data/lib/rigor/environment/lockfile_resolver.rb +68 -4
  215. data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
  216. data/lib/rigor/environment/member_consistency.rb +298 -0
  217. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  218. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  219. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  220. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  221. data/lib/rigor/environment/rbs_loader.rb +674 -79
  222. data/lib/rigor/environment.rb +174 -24
  223. data/lib/rigor/flow_contribution/merger.rb +0 -2
  224. data/lib/rigor/flow_contribution.rb +11 -13
  225. data/lib/rigor/hashing/xxh3.rb +264 -0
  226. data/lib/rigor/inference/acceptance.rb +292 -17
  227. data/lib/rigor/inference/block_auto_splat.rb +216 -0
  228. data/lib/rigor/inference/block_call_timing.rb +338 -0
  229. data/lib/rigor/inference/block_parameter_binder.rb +75 -30
  230. data/lib/rigor/inference/block_repetition.rb +71 -0
  231. data/lib/rigor/inference/body_fixpoint.rb +7 -6
  232. data/lib/rigor/inference/budget_trace.rb +3 -6
  233. data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
  234. data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
  235. data/lib/rigor/inference/captured_locals.rb +389 -18
  236. data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
  237. data/lib/rigor/inference/content_join.rb +200 -27
  238. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  239. data/lib/rigor/inference/def_return_typer.rb +11 -7
  240. data/lib/rigor/inference/define_method_block_self.rb +64 -0
  241. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  242. data/lib/rigor/inference/element_read_widening.rb +180 -0
  243. data/lib/rigor/inference/error_info.rb +196 -0
  244. data/lib/rigor/inference/expression_typer.rb +2124 -468
  245. data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
  246. data/lib/rigor/inference/fork_map.rb +5 -7
  247. data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
  248. data/lib/rigor/inference/global_write_census.rb +239 -0
  249. data/lib/rigor/inference/guard_rebinding.rb +447 -0
  250. data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
  251. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  252. data/lib/rigor/inference/hkt_registry.rb +4 -7
  253. data/lib/rigor/inference/index_write_widening.rb +16 -7
  254. data/lib/rigor/inference/indexed_narrowing.rb +61 -9
  255. data/lib/rigor/inference/jump_targets.rb +82 -0
  256. data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
  257. data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
  258. data/lib/rigor/inference/last_line.rb +340 -0
  259. data/lib/rigor/inference/last_status.rb +144 -0
  260. data/lib/rigor/inference/macro_block_self_type.rb +168 -21
  261. data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
  262. data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
  263. data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
  264. data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
  265. data/lib/rigor/inference/match_rebinding.rb +392 -0
  266. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  267. data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
  268. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  269. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  270. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  271. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
  272. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  273. data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
  274. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
  275. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  276. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  277. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
  278. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  279. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  280. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
  281. data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
  282. data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
  283. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  284. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
  285. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  286. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  287. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  288. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
  289. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  290. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
  291. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  292. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  293. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
  294. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  295. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  296. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  297. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  298. data/lib/rigor/inference/method_dispatcher.rb +225 -25
  299. data/lib/rigor/inference/method_parameter_binder.rb +12 -9
  300. data/lib/rigor/inference/multi_target_binder.rb +341 -55
  301. data/lib/rigor/inference/mutation_rejoin.rb +171 -0
  302. data/lib/rigor/inference/mutation_widening.rb +156 -85
  303. data/lib/rigor/inference/narrowing.rb +835 -162
  304. data/lib/rigor/inference/operand_effects.rb +167 -0
  305. data/lib/rigor/inference/operand_walk.rb +88 -0
  306. data/lib/rigor/inference/optimistic_origin.rb +153 -16
  307. data/lib/rigor/inference/origin_lookup.rb +2 -3
  308. data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
  309. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  310. data/lib/rigor/inference/precision_scanner.rb +3 -4
  311. data/lib/rigor/inference/project_method_ownership.rb +136 -0
  312. data/lib/rigor/inference/project_patched_methods.rb +9 -4
  313. data/lib/rigor/inference/project_patched_scanner.rb +10 -6
  314. data/lib/rigor/inference/protection_scanner.rb +1 -2
  315. data/lib/rigor/inference/range_constant.rb +57 -0
  316. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  317. data/lib/rigor/inference/receiver_alias.rb +93 -4
  318. data/lib/rigor/inference/receiver_blind_block.rb +219 -0
  319. data/lib/rigor/inference/refinement_mutation.rb +76 -0
  320. data/lib/rigor/inference/repeated_or_writes.rb +463 -0
  321. data/lib/rigor/inference/return_barrier.rb +54 -0
  322. data/lib/rigor/inference/rewrite_mutation.rb +120 -0
  323. data/lib/rigor/inference/scope_indexer.rb +5228 -627
  324. data/lib/rigor/inference/statement_evaluator.rb +3452 -595
  325. data/lib/rigor/inference/stored_block_call.rb +54 -0
  326. data/lib/rigor/inference/string_mutation.rb +97 -0
  327. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  328. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  329. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  330. data/lib/rigor/inference/unknown_store_widening.rb +200 -0
  331. data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
  332. data/lib/rigor/inference/version_guard.rb +28 -21
  333. data/lib/rigor/inference/void_origin.rb +3 -3
  334. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  335. data/lib/rigor/language_server/buffer_table.rb +4 -4
  336. data/lib/rigor/language_server/completion_provider.rb +1 -1
  337. data/lib/rigor/language_server/debouncer.rb +0 -1
  338. data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
  339. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  340. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  341. data/lib/rigor/language_server/hover_provider.rb +1 -1
  342. data/lib/rigor/language_server/hover_renderer.rb +4 -4
  343. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  344. data/lib/rigor/language_server/project_context.rb +7 -8
  345. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  346. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  347. data/lib/rigor/language_server/server.rb +9 -9
  348. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  349. data/lib/rigor/language_server/uri.rb +1 -1
  350. data/lib/rigor/mcp/server.rb +2 -1
  351. data/lib/rigor/plugin/base.rb +258 -26
  352. data/lib/rigor/plugin/box_probe.rb +91 -0
  353. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  354. data/lib/rigor/plugin/effect_attribution.rb +65 -11
  355. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  356. data/lib/rigor/plugin/fact_store.rb +6 -6
  357. data/lib/rigor/plugin/io_boundary.rb +93 -8
  358. data/lib/rigor/plugin/isolation.rb +29 -6
  359. data/lib/rigor/plugin/loader.rb +33 -13
  360. data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
  361. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  362. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  363. data/lib/rigor/plugin/manifest.rb +94 -11
  364. data/lib/rigor/plugin/registry.rb +76 -12
  365. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
  366. data/lib/rigor/plugin/template_unit.rb +196 -0
  367. data/lib/rigor/plugin/trust_policy.rb +2 -4
  368. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  369. data/lib/rigor/plugin.rb +1 -0
  370. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  371. data/lib/rigor/project_environment.rb +138 -0
  372. data/lib/rigor/protection/analysis_guard.rb +93 -14
  373. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  374. data/lib/rigor/protection/dependency_closure.rb +5 -8
  375. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  376. data/lib/rigor/protection/discovery_seed.rb +14 -12
  377. data/lib/rigor/protection/kill_signature.rb +1 -4
  378. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  379. data/lib/rigor/protection/mutation_cache.rb +7 -11
  380. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  381. data/lib/rigor/protection/mutator.rb +2 -1
  382. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  383. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  384. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  385. data/lib/rigor/rbs_extended/reporter.rb +36 -10
  386. data/lib/rigor/rbs_extended.rb +88 -21
  387. data/lib/rigor/reflection/constant_ancestors.rb +97 -0
  388. data/lib/rigor/reflection/constant_path.rb +139 -0
  389. data/lib/rigor/reflection.rb +93 -110
  390. data/lib/rigor/runtime/jit.rb +6 -8
  391. data/lib/rigor/scope/discovery_index.rb +165 -2
  392. data/lib/rigor/scope.rb +1041 -32
  393. data/lib/rigor/sig_gen/alias_index.rb +289 -0
  394. data/lib/rigor/sig_gen/classification.rb +22 -5
  395. data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
  396. data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
  397. data/lib/rigor/sig_gen/generator.rb +673 -53
  398. data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
  399. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  400. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  401. data/lib/rigor/sig_gen/method_candidate.rb +47 -3
  402. data/lib/rigor/sig_gen/observation_collector.rb +18 -16
  403. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  404. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  405. data/lib/rigor/sig_gen/renderer.rb +128 -14
  406. data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
  407. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  408. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  409. data/lib/rigor/sig_gen/write_result.rb +19 -3
  410. data/lib/rigor/sig_gen/writer.rb +189 -41
  411. data/lib/rigor/sig_gen.rb +3 -0
  412. data/lib/rigor/signature_path_audit.rb +153 -6
  413. data/lib/rigor/source/literals.rb +0 -23
  414. data/lib/rigor/source/node_children.rb +0 -2
  415. data/lib/rigor/source/node_locator.rb +5 -11
  416. data/lib/rigor/source/node_walker.rb +0 -6
  417. data/lib/rigor/source/parameter_envelope.rb +72 -0
  418. data/lib/rigor/source.rb +1 -0
  419. data/lib/rigor/triage/catalogue.rb +1 -3
  420. data/lib/rigor/triage.rb +3 -5
  421. data/lib/rigor/type/accepts_result.rb +24 -6
  422. data/lib/rigor/type/combinator.rb +144 -5
  423. data/lib/rigor/type/data_class.rb +2 -2
  424. data/lib/rigor/type/data_instance.rb +4 -4
  425. data/lib/rigor/type/difference.rb +1 -0
  426. data/lib/rigor/type/float_range.rb +128 -0
  427. data/lib/rigor/type/hash_shape.rb +6 -6
  428. data/lib/rigor/type/integer_range.rb +13 -8
  429. data/lib/rigor/type/nominal.rb +8 -2
  430. data/lib/rigor/type/refined.rb +4 -3
  431. data/lib/rigor/type/struct_class.rb +3 -3
  432. data/lib/rigor/type/struct_instance.rb +4 -4
  433. data/lib/rigor/type.rb +1 -0
  434. data/lib/rigor/type_node/generic.rb +1 -1
  435. data/lib/rigor/type_node/range_literal.rb +25 -0
  436. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  437. data/lib/rigor/type_node.rb +1 -0
  438. data/lib/rigor/version.rb +1 -1
  439. data/lib/rigor.rb +1 -0
  440. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  441. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  442. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  443. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
  444. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
  445. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  446. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  447. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  448. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
  449. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
  450. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  451. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  452. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
  453. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
  454. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
  455. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
  456. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
  457. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
  458. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
  459. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  460. data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
  461. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
  462. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
  463. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
  464. data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
  465. data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
  466. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  467. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
  468. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  469. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  470. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  471. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -5
  472. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
  473. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
  474. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
  475. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
  476. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  477. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  478. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
  479. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  480. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
  481. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
  482. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
  483. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  484. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
  485. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  486. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
  487. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  488. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  489. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  490. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  491. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  492. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  493. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  494. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
  495. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  496. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  497. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  498. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  499. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
  500. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  501. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  502. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
  503. data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
  504. data/plugins/rigor-grape/sig/grape.rbs +263 -0
  505. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  506. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
  507. data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
  508. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  509. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  510. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
  511. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  512. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  513. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  514. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  515. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  516. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
  517. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  518. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  519. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  520. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
  521. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  522. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
  523. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
  524. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
  525. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  526. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  527. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  528. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  529. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
  530. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  531. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  532. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
  533. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
  534. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  535. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  536. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  537. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  538. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  539. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  540. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  541. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  542. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  543. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  544. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  545. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  546. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
  547. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  548. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  549. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
  550. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  551. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  552. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  553. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  554. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  555. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  556. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
  557. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  558. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
  559. data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
  560. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  561. data/sig/rigor/analysis/baseline.rbs +69 -7
  562. data/sig/rigor/analysis/fact_store.rbs +1 -1
  563. data/sig/rigor/analysis/project_scan.rbs +74 -0
  564. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  565. data/sig/rigor/cache.rbs +2 -3
  566. data/sig/rigor/effects/config_envelopes.rbs +100 -0
  567. data/sig/rigor/effects/effect_table.rbs +60 -0
  568. data/sig/rigor/effects/envelope.rbs +97 -0
  569. data/sig/rigor/effects/envelope_index.rbs +33 -0
  570. data/sig/rigor/effects/file_collection.rbs +81 -0
  571. data/sig/rigor/effects/label.rbs +26 -0
  572. data/sig/rigor/effects/label_set.rbs +46 -0
  573. data/sig/rigor/effects/method_key.rbs +24 -0
  574. data/sig/rigor/effects/origin.rbs +50 -0
  575. data/sig/rigor/effects/plugin_facts.rbs +141 -0
  576. data/sig/rigor/effects/registry.rbs +68 -0
  577. data/sig/rigor/effects/summary.rbs +50 -0
  578. data/sig/rigor/effects/taint_cause.rbs +12 -0
  579. data/sig/rigor/environment.rbs +17 -10
  580. data/sig/rigor/inference/optimistic_origin.rbs +10 -0
  581. data/sig/rigor/inference.rbs +9 -14
  582. data/sig/rigor/plugin/additional_initializer.rbs +36 -0
  583. data/sig/rigor/plugin/base.rbs +30 -7
  584. data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
  585. data/sig/rigor/plugin/effect_attribution.rbs +66 -0
  586. data/sig/rigor/plugin/effect_edge.rbs +33 -0
  587. data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
  588. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  589. data/sig/rigor/plugin/loader.rbs +3 -3
  590. data/sig/rigor/plugin/manifest.rbs +51 -11
  591. data/sig/rigor/plugin/protocol_contract.rbs +68 -0
  592. data/sig/rigor/plugin/registry.rbs +62 -1
  593. data/sig/rigor/plugin.rbs +1 -1
  594. data/sig/rigor/rbs_extended.rbs +1 -1
  595. data/sig/rigor/reflection.rbs +9 -6
  596. data/sig/rigor/scope.rbs +148 -12
  597. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
  598. data/sig/rigor/source.rbs +4 -4
  599. data/sig/rigor/testing.rbs +10 -4
  600. data/sig/rigor/type.rbs +33 -1
  601. data/sig/rigor.rbs +39 -20
  602. data/skills/rigor-ask/SKILL.md +16 -11
  603. data/skills/rigor-baseline-reduce/SKILL.md +4 -6
  604. data/skills/rigor-ci-setup/SKILL.md +17 -21
  605. data/skills/rigor-doctor/SKILL.md +24 -22
  606. data/skills/rigor-doctor/references/01-checks.md +97 -33
  607. data/skills/rigor-editor-setup/SKILL.md +6 -4
  608. data/skills/rigor-mcp-setup/SKILL.md +5 -4
  609. data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
  610. data/skills/rigor-next-steps/SKILL.md +4 -2
  611. data/skills/rigor-plugin-author/SKILL.md +19 -23
  612. data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
  613. data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
  614. data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
  615. data/skills/rigor-plugin-review/SKILL.md +6 -4
  616. data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
  617. data/skills/rigor-plugin-tune/SKILL.md +4 -2
  618. data/skills/rigor-project-init/SKILL.md +15 -10
  619. data/skills/rigor-project-init/references/01-detect.md +13 -9
  620. data/skills/rigor-project-init/references/02-configure.md +43 -14
  621. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
  622. data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
  623. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  624. data/skills/rigor-protection-uplift/SKILL.md +4 -6
  625. data/skills/rigor-rbs-setup/SKILL.md +4 -2
  626. data/skills/rigor-type-oracle/SKILL.md +226 -0
  627. data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
  628. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  629. data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
  630. data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
  631. data/skills/rigor-upgrade/SKILL.md +13 -8
  632. metadata +133 -3
  633. data/lib/rigor/cli/probe_environment.rb +0 -85
  634. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -292,7 +292,7 @@ class refinement carriers, produced automatically by narrowing.
292
292
  | --- | --- | --- |
293
293
  | `non-empty-string` | a `NonEmptyString` value class wrapping validation | Rigor produces it from `unless s.empty?`, no wrapper type. |
294
294
  | `positive-int` | a `PositiveInt` value object, or a runtime guard | Rigor narrows from `n > 0`. |
295
- | `int<1, 9>` | an `enum` of nine constants, or a range check | Rigor's range carrier handles arbitrary bounds without enumerating them. |
295
+ | `Integer[1..9]` | an `enum` of nine constants, or a range check | Rigor's range carrier handles arbitrary bounds without enumerating them. |
296
296
  | `numeric-string` | `string` + `int.TryParse` discipline | No type-level analogue in either language. |
297
297
  | `non-empty-array[T]` | a non-empty-collection value class | Rigor produces it from `unless arr.empty?`. |
298
298
 
@@ -379,7 +379,7 @@ longer than you might expect, because neither has literal types:
379
379
  `Constant<"FOO">`, not `String`. Rigor catalogues which
380
380
  built-in methods are pure and folds through them.
381
381
  - **First-class refinements.** `non-empty-string`, `positive-int`,
382
- `int<1, 9>`, `numeric-string` — invariants on ordinary types,
382
+ `Integer[1..9]`, `numeric-string` — invariants on ordinary types,
383
383
  no value-class wrapper.
384
384
  - **Structural facets without a declaration.** A Ruby object that
385
385
  has the right methods satisfies an RBS `interface` (a
@@ -71,7 +71,7 @@ features one at a time (`Literal`, `LiteralString`, `TypeIs`,
71
71
  | `non-empty-string` | (no built-in; PEP 675's `LiteralString` is closest in spirit but different in semantics) |
72
72
  | `literal-string` | `LiteralString` (PEP 675) — provably built from source-code literals. **Direct match.** |
73
73
  | `positive-int` | (no built-in; convention is `Annotated[int, Gt(0)]` with third-party validators) |
74
- | `int<min, max>` | (no built-in; same `Annotated[int, Range(...)]` convention) |
74
+ | `Integer[min..max]` | (no built-in; same `Annotated[int, Range(...)]` convention) |
75
75
  | `numeric-string` | (no built-in) |
76
76
  | `non-empty-array[T]` | (no built-in; some libraries use `tuple[T, *tuple[T, ...]]`) |
77
77
  | `Constant<42>` | `Literal[42]` |
@@ -286,7 +286,7 @@ draws the distinction in full.
286
286
  `Hash`.
287
287
  - **First-class refinement carriers with narrowing.**
288
288
  `non-empty-string`, `positive-int`, `numeric-string`,
289
- `int<min, max>` — values restricted by predicate, narrowed
289
+ `Integer[min..max]` — values restricted by predicate, narrowed
290
290
  by the corresponding Ruby predicate methods.
291
291
  - **No-false-positives stance.** mypy will warn about dynamic
292
292
  code unless `--no-warn-unused-ignores` or `--ignore-missing-imports`
@@ -53,7 +53,7 @@ is the closest match of any peer.
53
53
  | `numeric-string` | `numeric-string` | Identical. |
54
54
  | `lowercase-string` | `lowercase-string` | Identical. |
55
55
  | `class-string` | `Singleton[T]` | Equivalent shape. |
56
- | `int<1, 9>` | `int<1, 9>` | **Identical syntax.** |
56
+ | `int<1, 9>` | `Integer[1..9]` | Same carrier; Rigor spells the bound as the Ruby range literal `Range#cover?` reads (`Integer[1..]`, `Integer[..9]`). |
57
57
  | `positive-int` | `positive-int` | Identical. |
58
58
  | `negative-int` | `negative-int` | Identical. |
59
59
  | `non-zero-int` | `non-zero-int` | Identical. |
@@ -274,7 +274,7 @@ by narrowing.
274
274
  | --- | --- | --- |
275
275
  | `non-empty-string` | `struct NonEmptyString(String)` newtype | Rigor produces it from `unless s.empty?`, no wrapper. |
276
276
  | `positive-int` | `struct PositiveInt(u32)` newtype | Rigor narrows from `n > 0`. |
277
- | `int<1, 9>` | newtype + range check, or const generics gymnastics | Rigor's range carrier handles arbitrary bounds directly. |
277
+ | `Integer[1..9]` | newtype + range check, or const generics gymnastics | Rigor's range carrier handles arbitrary bounds directly. |
278
278
  | `numeric-string` | newtype wrapping validated parse | No type-level analogue. |
279
279
  | `non-empty-array[T]` | newtype over `Vec<T>` | Rigor produces it from `unless arr.empty?`. |
280
280
 
@@ -19,7 +19,7 @@ specification](../type-specification/README.md), the spec binds.
19
19
  | What about types that may or may not match? | Gradual consistency (`~`) | The `Dynamic[T]` carrier and the trinary certainty `yes / no / maybe` |
20
20
  | How are user types identified? | Nominal vs structural | **Nominal-first hybrid** — classes by name, plus structural facets (`interface`, `HashShape`, capability roles) |
21
21
  | How are generics expressed? | Parametric polymorphism (System F-style, but predicative) | RBS generics `class Array[Elem]`, method generics `def map: [U] () { (Elem) -> U } -> Array[U]` |
22
- | How is "x is a non-empty string" expressed? | Refinement / predicate subtyping | First-class refinement carriers (`non-empty-string`, `int<min, max>`, …) |
22
+ | How is "x is a non-empty string" expressed? | Refinement / predicate subtyping | First-class refinement carriers (`non-empty-string`, `Integer[min..max]`, …) |
23
23
  | How does `if x.is_a?(String)` change `x`'s type? | Occurrence typing / flow-sensitive narrowing | Edge-aware narrowing with trinary certainty |
24
24
  | What about side effects? | Effect systems | The engine's effect model (mutation, exception, escape) — internal, not user-visible |
25
25
  | Soundness or completeness? | Pick one (or neither) | **Neither in full** — Rigor optimises for no-false-positives, with a robustness-principle bias |
@@ -528,7 +528,7 @@ refinements with reserved names:
528
528
  | `non-empty-string` | `s : String, s.size >= 1` | refinement on `String` |
529
529
  | `numeric-string` | `s : String, s =~ /\A[+-]?\d+(\.\d+)?\z/` | refinement on `String` |
530
530
  | `literal-string` | "provably built from literals" | refinement on `String` |
531
- | `int<min, max>` | `n : Integer, min <= n <= max` | range carrier |
531
+ | `Integer[min..max]` | `n : Integer, min <= n <= max` | range carrier |
532
532
  | `non-zero-int` | `n : Integer, n != 0` | refinement on `Integer` |
533
533
  | `positive-int` | `n : Integer, n > 0` | refinement on `Integer` |
534
534
  | `non-empty-array[T]` | `arr : Array[T], arr.size >= 1` | refinement on `Array[T]` |
@@ -1491,7 +1491,7 @@ here so you can stop looking:
1491
1491
  and Rigor does not synthesise it.
1492
1492
  - **Full dependent types.** No `Vec[n, T]` with `n : Integer`.
1493
1493
  Type-checking is decidable but inference is not; integer-range
1494
- refinements (`int<min, max>`) cover the most common practical
1494
+ refinements (`Integer[min..max]`) cover the most common practical
1495
1495
  need without crossing the line.
1496
1496
  - **Row polymorphism as a user-quantifiable axis.** `HashShape`
1497
1497
  carries open-vs-closed semantics internally but does not
@@ -1581,7 +1581,7 @@ they map to the sections of this appendix:
1581
1581
  matching and exhaustiveness."
1582
1582
  - Rondon, Kawaguchi & Jhala. "Liquid Types." *PLDI 2008.* The
1583
1583
  refinement-types-with-SMT framework that informs the
1584
- `int<min, max>` carrier (Rigor uses a much weaker, decidable
1584
+ `Integer[min..max]` carrier (Rigor uses a much weaker, decidable
1585
1585
  fragment).
1586
1586
  - Lucassen & Gifford. "Polymorphic Effect Systems."
1587
1587
  *POPL 1988.* Origin of effect systems.
@@ -101,7 +101,7 @@ positive, an array that is provably non-empty.
101
101
  | --- | --- | --- |
102
102
  | `non-empty-string` | `\`${string}${string}\`` (template literal trick) or branded `NonEmptyString` | Awkward in TS; Rigor produces it from `unless s.empty?` automatically. |
103
103
  | `positive-int` | branded `PositiveInt` | TS users tend to skip the brand — Rigor narrows from `n > 0`. |
104
- | `int<1, 9>` | union of literal types `1 \| 2 \| 3 \| ... \| 9` | Rigor's range carrier handles arbitrary bounds without exploding. |
104
+ | `Integer[1..9]` | union of literal types `1 \| 2 \| 3 \| ... \| 9` | Rigor's range carrier handles arbitrary bounds without exploding. |
105
105
  | `numeric-string` | (none useful) | TS has no equivalent; Rigor narrows from regex matches against numeric patterns. |
106
106
  | `non-empty-array[T]` | `[T, ...T[]]` (tuple-with-rest) | TS has the encoding but few APIs use it; Rigor produces it from `unless arr.empty?`. |
107
107
 
data/docs/llms.txt CHANGED
@@ -42,6 +42,8 @@ Skill with `rigor skill <name>`. (The canonical web copy is
42
42
  - `15-type-protection-coverage` — `rigor coverage --protection`.
43
43
  - `16-rbs-extended-annotations` — the `%a{rigor:v1:…}` annotations.
44
44
  - `17-driving-improvement` — the `rigor-next-steps`-driven improvement loop.
45
+ - `18-removing-dead-code` — working `rigor unused` as a campaign on an old codebase.
46
+ - `19-effect-labels` — `rigor effects`, the `.rigor-effects.yml` snapshot, and `%a{pure}`.
45
47
 
46
48
  ## Handbook (read with `rigor docs <name>`)
47
49
 
@@ -45,6 +45,7 @@ the `paths:` list from the configuration file.
45
45
  | `--baseline=PATH` | Load a baseline file, overriding config. |
46
46
  | `--no-baseline` | Ignore any configured baseline. |
47
47
  | `--baseline-strict` | Fail the run on any baseline drift — a CI gate. |
48
+ | `--fail-on=SEVERITY` | Exit non-zero when a diagnostic at or above `SEVERITY` (`error`, the default; `warning`; or `info`) survives baseline filtering — raises the exit-status bar above the default `:error`-only reading for CI gates that want it, without changing `--format json`'s `success` / `error_count` fields. |
48
49
  | `--treat-all-as-inline-rbs` | Force-load `rigor-rbs-inline` with `require_magic_comment: false`, so every analysed file is treated as inline-RBS without the `# rbs_inline: enabled` comment (ADR-32). |
49
50
  | `--bleeding-edge[=ids]` | Adopt the bleeding-edge overlay for this run, overriding the configured [`bleeding_edge:`](03-configuration.md) selection (ADR-50 § WD2). Bare adopts every queued feature; `--bleeding-edge=a,b` adopts only the named feature ids. Inspect it with [`rigor show-bleedingedge`](#rigor-show-bleedingedge). |
50
51
  | `--no-bleeding-edge` | Ignore any configured `bleeding_edge:` selection for this run (adopt none). |
@@ -52,7 +53,9 @@ the `paths:` list from the configuration file.
52
53
  | `--tmp-file=PATH --instead-of=PATH` | Editor mode: analyse `PATH` using the buffer in `--tmp-file`. Both required together. Alone, only the buffer's own file produces diagnostics; add `--incremental` for whole-project scope (see below). |
53
54
 
54
55
  Exit `0` when no error-severity diagnostics remain, `1` when
55
- any are reported, `64` on a usage error.
56
+ any are reported, `64` on a usage error. `--fail-on` raises that
57
+ bar to `warning` or `info` for callers (CI gates, `make check`)
58
+ that want the stricter reading.
56
59
 
57
60
  ### Editor mode scope
58
61
 
@@ -135,6 +138,39 @@ truncation explicit. `--trace` records fail-soft fallbacks,
135
138
  after the rows of a line table in text output. The editor-mode
136
139
  `--tmp-file` / `--instead-of` pair is accepted as on `check`.
137
140
 
141
+ A template a plugin compiles into Ruby (an ERB view under
142
+ `rigor-actionpack`) is probed the way `rigor check` analyses
143
+ it: the compiled Ruby is typed under the view's declared
144
+ `self`, locals and instance variables, and `LINE:COL` are the
145
+ template's own. A column answers only when it lies in Ruby the
146
+ compiler copied verbatim — the `@user.name` in
147
+ `<%= @user.name %>` — and denotes exactly one compiled
148
+ expression. A column in HTML text, in code the plugin rewrote
149
+ (a layout's `<%= yield %>`), or in bytes copied to more than
150
+ one place prints `no expression found at …` with the reason
151
+ and exits `1`; the command never answers about a nearby
152
+ node instead. Two tags that name the same thing (`<%= v %>`
153
+ beside `<% if "a" <= v %>`, or `<%= v %> <%= v.to_s %>`)
154
+ decline for that reason as well; a name repeated inside ONE
155
+ tag still answers. A bare `FILE:LINE` lists only the expressions
156
+ that map back to a template column. `--trace` fallbacks are
157
+ reported at the template line, and a JSON fallback carries a
158
+ `column` only when its position maps back to one. A template
159
+ the plugin declined to compile prints
160
+ `plugin declined the template; probing its bytes as Ruby`
161
+ and is probed as plain Ruby, parse error included.
162
+
163
+ The four probe commands — `type-of`, `type-scan`, `trace` and
164
+ `annotate` — build their environment fresh on every invocation
165
+ and never read or write the persistent cache. That is why none
166
+ of them takes `--no-cache`: the flag would have nothing to skip.
167
+ A probe therefore types against the environment `rigor check
168
+ --no-cache` analyses with. The environment a cached (default)
169
+ `rigor check` builds is meant to be identical, and Rigor gates
170
+ the two builds against each other — but if you are chasing a
171
+ disagreement between a probe and a `check` run, comparing
172
+ against `rigor check --no-cache` removes that variable.
173
+
138
174
  ## `rigor trace`
139
175
 
140
176
  Replay HOW the engine typed a file, step by step, as a
@@ -429,6 +465,12 @@ rigor explain [rule]
429
465
  or a family wildcard (`call`, `flow`, `def`, `assert`, `dump`).
430
466
  `--format=json` is available. Exit `64` for an unknown rule.
431
467
 
468
+ It also answers the `sig.skipped.*` identifiers `rigor sig-gen`
469
+ prints when it declines to write a signature — `rigor explain
470
+ sig.skipped.untyped-return` says what the skip means and what to do
471
+ instead. Those are not diagnostic rules, so they are rendered without
472
+ a severity or a suppression line.
473
+
432
474
  ## `rigor diff`
433
475
 
434
476
  Compare the current diagnostics against a saved baseline JSON
@@ -457,13 +499,52 @@ rigor sig-gen [paths]
457
499
  | `--print` | Write RBS to stdout. Default. |
458
500
  | `--diff` | Write a unified diff against existing RBS. |
459
501
  | `--write` | Write RBS to `sig/<path>.rbs` files. |
460
- | `--overwrite` | Allow tighter-return updates to replace user-authored RBS. |
502
+ | `--check` | Write nothing; print what `--write` with the same options would change, and exit `1` if anything would. The CI freshness gate. |
503
+ | `--overwrite` | Allow tighter-return updates, and inline declarations that disagree with `sig/`, to replace user-authored RBS. |
461
504
  | `--include-private` | Emit private and protected methods too. |
462
505
  | `--params=untyped\|observed\|observed-strict` | Parameter-typing policy. Default `untyped`. |
463
- | `--observe=PATH` | Scan `PATH` for call-site observations. Repeatable. |
506
+ | `--observe=PATH` | Scan `PATH` for call-site observations. Repeatable. Default: the configured `test_paths:` (unset: whichever of `spec/` and `test/` exist). |
464
507
  | `--new-files` / `--new-methods` / `--tighter-returns` | Emit only that classification. |
508
+ | `--effect-envelopes` | Also emit `%a{rigor:v1:effect …}` for effectful methods. Needs the `effects:` opt-in. |
509
+ | `--no-cache` | Do not read or write the analysis cache. Only effect collection uses it. |
465
510
  | `--format=text\|json` | Output format. |
466
511
 
512
+ `--print`, `--diff`, `--write` and `--check` are mutually exclusive.
513
+ `--check` fails exactly when `--write` would create, change or
514
+ refuse a file, so a tighter return `--write` declines without
515
+ `--overwrite` does not fail it; `--check --overwrite` counts one.
516
+ See [handbook chapter 11](../handbook/11-sig-gen.md#keeping-sig-current-in-ci).
517
+
518
+ A method declared inline with `# @rbs` / `#:` is written as that
519
+ declaration, not as what its body infers; a parameter-only
520
+ annotation keeps its parameters and takes the return from the body,
521
+ and `initialize` is always `-> void`. When `sig/` already declares
522
+ the method (a `def` or an `attr_*`) and the two disagree as types —
523
+ parameter names and union spelling do not count, overload order
524
+ does — sig-gen changes neither: the method is refused
525
+ (`sig.skipped.inline-differs`, listed under `refused` in `--format=json`),
526
+ and `--write` / `--check` exit `1` until you make them agree or pass
527
+ `--overwrite`, which replaces the whole `sig/` member with the inline
528
+ declaration. For a parameter-only annotation only the parameters are
529
+ compared; the return follows the ordinary proposal rules. A class
530
+ made generic inline is not written unless `sig/` declares it with the
531
+ same type parameters. A project whose Steep reads the same
532
+ annotations sets `sig_gen.inline_declared: skip` in `.rigor.yml` to
533
+ keep those methods out of `sig/`. See
534
+ [handbook chapter 11](../handbook/11-sig-gen.md#methods-declared-inline).
535
+
536
+ When `.rigor.yml` carries an `effects:` block, sig-gen also writes
537
+ `%a{pure}` above a method whose effect summary is **exhaustive**
538
+ (every call it reaches was resolved), **undischarged** (nothing in
539
+ its footprint is only invisible because `effects.tolerated:` says
540
+ so), **claimed** (every callee is described by a catalogue row, a
541
+ plugin, an envelope or a project definition), carries no authored
542
+ bound of its own, and is free of surviving labels in the `≤` lane. Nothing else is annotated, and
543
+ `--effect-envelopes` adds Rigor's own labelled spelling for methods
544
+ that do have a footprint. With no `effects:` block the output is
545
+ byte-for-byte what it was before. See
546
+ [handbook chapter 11](../handbook/11-sig-gen.md#emitting-effect-annotations).
547
+
467
548
  Every signature is parsed before it is emitted. A method whose
468
549
  generated RBS does not parse is **skipped** (`sig.skipped.unrenderable-rbs`)
469
550
  and reported on stderr rather than written — an unparseable
@@ -481,6 +562,8 @@ not overwrite) is never a silent absence: under `--format=json`
481
562
  it is a `skipped` row of the `candidates` array with its
482
563
  `sig.skipped.*` identifier in `skip_reason`, and in text mode a
483
564
  one-line stderr summary counts the skipped methods per reason.
565
+ `rigor explain sig.skipped.untyped-return` (or any of the other skip
566
+ identifiers) explains what the reason means and what to do about it.
484
567
 
485
568
  ## `rigor lsp`
486
569
 
@@ -577,6 +660,12 @@ see. That is why this is a separate command and never a `rigor check`
577
660
  diagnostic — see
578
661
  [ADR-102](../adr/102-unused-code-reachability-report.md).
579
662
 
663
+ `rigor unused` refuses `--incremental` and exits non-zero rather
664
+ than quietly running a full pass. Reachability is only sound over a
665
+ whole-project run: with files served from the incremental cache a
666
+ constant would be reported as unused merely because the file that
667
+ references it was not re-scanned. Re-run without the flag.
668
+
580
669
  Reachability is computed from **roots**, not by counting references,
581
670
  so a cluster of classes that only reference each other is still
582
671
  reported. Roots are the declarations in files matching
@@ -1112,7 +1201,7 @@ operational knobs read the environment instead.
1112
1201
  | `RIGOR_RACTOR_WORKERS=N` | Worker count for parallel analysis. Sits between the CLI flag and the config key in precedence: `--workers=N` > `RIGOR_RACTOR_WORKERS` > `parallel.workers:` > `0` (sequential). |
1113
1202
  | `RIGOR_POOL_BACKEND=ractor` | Opt back into the (off-by-default) Ractor worker pool instead of the active fork-based pool ([ADR-15](../adr/15-ractor-concurrency.md)). Only relevant with a non-zero worker count; the fork pool is the supported backend. |
1114
1203
  | `RIGOR_LSP_POOL_MIN_BATCH=N` | Fewest buffers an [`rigor lsp`](#rigor-lsp) batch must carry before analysis is dispatched across the worker pool rather than run in-process (default `16`). Lower it if your project's per-file analysis is expensive enough that pooling pays off sooner. |
1115
- | `RIGOR_PLUGIN_ISOLATION=none\|process\|ruby_box` | How a plugin's direct calls into its target library are isolated. Default `process`. See [Using plugins § Isolation strategy](07-plugins.md). `RIGOR_BOX` is a legacy alias for `ruby_box`. |
1204
+ | `RIGOR_PLUGIN_ISOLATION=none\|process\|ruby_box` | How a plugin's direct calls into its target library are isolated. Overrides the `plugins_isolation:` configuration key. Default `process`. See [Using plugins § Isolation strategy](07-plugins.md). `RIGOR_BOX` is a legacy alias for `ruby_box`. |
1116
1205
  | `RIGOR_STRICT_VALIDATION=1` | Force full-content cache validation for one run (the same as `cache.validation: digest`, and winning over it) — re-hash every file's content instead of trusting its stat metadata. Use it if a filesystem's timestamps or inode numbers cannot be trusted. See [Caching § How a file is checked for changes](12-caching.md#how-a-file-is-checked-for-changes). |
1117
1206
  | `RIGOR_DISABLE_YJIT=1` | Opt out of Rigor's deferred YJIT enablement. Rigor turns YJIT on partway through any long run so short runs never pay the JIT warm-up; this variable leaves it off entirely. Diagnostics and allocations are identical either way — the effect is wall-time only. |
1118
1207
  | `RIGOR_YJIT_DEADLINE=<seconds>` | Advanced: tune how long a run must last before deferred YJIT enables (default `5.0`). Lower it if your runs are long and you want the JIT sooner; raise it to protect short runs. Ignored when `RIGOR_DISABLE_YJIT=1` is set or YJIT is unavailable. |
@@ -1126,8 +1215,8 @@ diagnostics about Rigor's own inference cutoffs and memory — see
1126
1215
 
1127
1216
  | Code | Meaning |
1128
1217
  | --- | --- |
1129
- | `0` | Success — no error-severity diagnostics. |
1130
- | `1` | Diagnostics found, or a per-command failure (parse error, missing file, new diagnostics on `diff`, effect drift on `effects check`). |
1218
+ | `0` | Success — no diagnostic at or above the exit threshold (`error` by default; `rigor check --fail-on=SEVERITY` lowers it to `warning` or `info`). |
1219
+ | `1` | Diagnostics found at or above the threshold, or a per-command failure (parse error, missing file, new diagnostics on `diff`, effect drift on `effects check`). |
1131
1220
  | `64` | Usage error — unknown command, bad flag, malformed argument, or a value in `.rigor.yml` the loader cannot proceed on. |
1132
1221
 
1133
1222
  `rigor triage` is the exception: it is advisory and always
@@ -60,6 +60,7 @@ cache:
60
60
  | `target_ruby` | String | `"4.0"` | The Ruby version *your* project runs — `"X.Y"`, `"X.Y.Z"`, or `"latest"`. Independent of the Ruby Rigor itself runs on. |
61
61
  | `paths` | Array | `["lib"]` | Directories or files to analyse. |
62
62
  | `exclude` | Array | `[]` | Glob patterns to skip. `vendor/bundle`, `.bundle`, and `node_modules` are always excluded. |
63
+ | `test_paths` | Array | `nil` | The project's test roots: the directories (or files) holding its tests. Relative entries resolve against the config file's directory. Unset auto-detects whichever of `spec/` and `test/` exist; `[]` declares none. `rigor sig-gen --params=observed` reads call sites there to type parameters, and names on stderr a declared root that does not exist. Test roots are not analysed unless `paths:` also lists them, and changing them invalidates no cache. |
63
64
  | `includes` | Array | `[]` | Other config files to layer underneath this one. |
64
65
  | `fold_platform_specific_paths` | Boolean | `false` | Resolve Ruby-version-conditional load paths when discovering sources. |
65
66
  | `parameter_inference` | Boolean | `false` | Opt-in call-site parameter type inference on the `check` walk ([ADR-67](../adr/67-parameter-type-inference.md) WD6). When `true`, an undeclared `def` / `initialize` / setter parameter is typed to the union of its resolved call-site argument types, sharpening downstream ivar reads, folds, and protection coverage. Precision-additive only — the negative rules never fire against an inferred parameter. Cannot be combined with `--incremental`. |
@@ -73,6 +74,12 @@ cache:
73
74
  | `pre_eval` | Array | `[]` | Files (or globs) walked before per-file analysis, to register project monkey-patches and publish their top-level constants project-wide. |
74
75
  | `plugins` | Array | `[]` | Plugins to activate — see [Using plugins](07-plugins.md). |
75
76
 
77
+ ### Signature generation
78
+
79
+ | Key | Type | Default | Meaning |
80
+ | --- | --- | --- | --- |
81
+ | `sig_gen.inline_declared` | String | `"write"` | What `rigor sig-gen` does with a method already declared inline by `# @rbs` / `#:`. `write` copies the inline declaration into `sig/`, so the generated signature is the complete contract a gem ships, and refuses (exit 1) a `sig/` declaration that later disagrees with the inline one until the two agree or `--overwrite` replaces it. `skip` leaves every method the inline reader declares out of `sig/` (`sig.skipped.inline-declared`): set it when Steep reads the same annotations (`inline: true` beside `signature "sig"`), where a copy would declare each method twice (`DuplicatedMethodDefinition`). The cost of `skip`: a consumer reading only your shipped `sig/` never sees those methods, and an annotation on one — a Rigor refinement included — takes effect only where the source itself is analysed. Any other value, or any other key under `sig_gen:`, is a load error. Only `rigor sig-gen` reads this key, and changing it invalidates no cache. See [handbook chapter 11](../handbook/11-sig-gen.md#methods-declared-inline). |
82
+
76
83
  ### Config validation warnings
77
84
 
78
85
  `rigor check` warns on STDERR when a configured value silently resolves to
@@ -93,6 +100,16 @@ rigor: severity_overrides: "flow.bogus" is not a recognized rule id; the overrid
93
100
  rigor: bundler.lockfile: "./missing/Gemfile.lock" does not exist
94
101
  ```
95
102
 
103
+ One warning covers the mirror-image mistake — a path that loads, but only
104
+ half of what you wanted. A bundled plugin ships its RBS *and* a manifest
105
+ recording which of those classes it declares only partially; `plugins:`
106
+ loads both, while pointing `signature_paths:` at the plugin's `sig/` loads
107
+ only the RBS, so calls your own code defines get reported as undefined:
108
+
109
+ ```
110
+ rigor: signature_paths: "…/plugins/rigor-activerecord/sig" loads the signatures of the bundled plugin "rigor-activerecord", which `plugins:` does not name — … Add "rigor-activerecord" to `plugins:` instead of naming its `sig/` in `signature_paths:`.
111
+ ```
112
+
96
113
  The unrecognised-key check covers **top-level** keys, and skips
97
114
  the namespaces reserved for other implementations (see below).
98
115
  A typo *inside* a group — `cache: { pth: … }` — is caught by
@@ -170,6 +187,7 @@ explicitly with `bundler.bundle_path:`, or supply signatures another way:
170
187
  | `cache.max_bytes` | Integer or `null` | `268435456` (256 MB) | LRU eviction cap for the cache directory; `null` disables eviction. See [Caching § Size and eviction](12-caching.md#size-and-eviction). |
171
188
  | `cache.validation` | String | `"auto"` | How the cache checks whether a file is unchanged: `auto` behaves as `digest` when a CI environment is detected and as `stat` otherwise; `stat` compares size + nanosecond timestamps + inode and only re-hashes a file whose stat moved; `digest` re-hashes every file's content every run. Both keep the content hash as the sole change authority — `stat` just skips the hash when the stat proves a file untouched. See [Caching § How a file is checked for changes](12-caching.md#how-a-file-is-checked-for-changes). The `RIGOR_STRICT_VALIDATION=1` environment variable forces `digest` for one run and wins over this key; `RIGOR_CI_DETECT=0` disables the CI detection. |
172
189
  | `parallel.workers` | Integer | `0` | Parallel worker processes for per-file analysis (fork-based pool today; ADR-15); `0` is sequential. CLI `--workers` and `RIGOR_RACTOR_WORKERS` take precedence. Applies to `--incremental` re-checks as well as full runs. |
190
+ | `plugins_isolation` | String | `null` | How a plugin's call into its target library is isolated — `process` (default) or `none`. `RIGOR_PLUGIN_ISOLATION` overrides it for one invocation; `ruby_box` is that variable only. See [Using plugins](07-plugins.md). |
173
191
  | `plugins_io.network` | String | `"disabled"` | Plugin network policy — `disabled` or `allowlist`. |
174
192
  | `plugins_io.allowed_paths` | Array | `[]` | Filesystem paths plugins may read. |
175
193
  | `plugins_io.allowed_url_hosts` | Array | `[]` | URL hosts plugins may fetch from when `network: allowlist`. |
@@ -16,6 +16,7 @@ Every rule has a two-segment `family.rule` identifier:
16
16
  | `call` | Call sites — undefined methods, arity, argument types, nil receivers. |
17
17
  | `flow` | Control-flow proofs — always-raises, dead branches, constant conditions. |
18
18
  | `def` | Method definitions — return types, ivar writes, visibility. |
19
+ | `global` | Writes to special globals — a value the setter rejects, a read-only variable. |
19
20
  | `assert` | `assert_type` checks. |
20
21
  | `dump` | `dump_type` notices. |
21
22
 
@@ -70,11 +71,14 @@ carries no `documentation_url`.
70
71
  | <a id="rule-def-override-visibility-reduced"></a>`def.override-visibility-reduced` | An override reduces the visibility it inherits from a project-defined ancestor. | high |
71
72
  | <a id="rule-def-override-return-widened"></a>`def.override-return-widened` | An override's declared return type widens the inherited return (covariance). | high |
72
73
  | <a id="rule-def-override-param-narrowed"></a>`def.override-param-narrowed` | An override narrows an inherited parameter type (contravariance). | high |
74
+ | <a id="rule-global-write-type-mismatch"></a>`global.write-type-mismatch` | A special global is assigned a literal its setter rejects, so the write raises `TypeError` every time it runs. `$/`, `$-0`, `$,` and `$\` take only a String or nil (`$/ = 1` and `$/ = /x/` report). `$;` and `$-F` take a String, a Regexp, nil, or an object with `to_str`. `$~` takes a MatchData or nil. `$0` and `$PROGRAM_NAME` take a String or an object with `to_str`, so `$0 = nil` and `$0 = :name` report. `$.` takes an Integer, a Float, or an object with `to_int`, so `$. = "3"` reports and `$. = 3r` does not. `$-i` takes a String, nil, false, or an object with `to_str`. `$stdout`, `$>` and `$stderr` take anything that responds to `write`: `$stdout = 1` reports, `$stdout = StringIO.new` does not. Ruby's setter decides, not the global's RBS type. Only a value written as a literal is judged — a number, a string, a symbol, an array, a hash or a regexp literal (interpolated or not), or `nil`, `true` or `false` — so a variable, a method call or a constant never reports, whatever its type. A literal stays silent when your program defines the method the setter asks for (`write`, `to_str`, `to_int`) or a `method_missing` / `respond_to_missing?` / `respond_to?` anywhere, in any spelling and on any class — Rigor cannot always tell which objects such a definition reaches, so it does not try — or when a top-level `include` / `extend` or an ancestor's `include` names a module Rigor has no RBS for. For `$stdout`, `$>` and `$stderr` it also stays silent where a `using` is in effect for a refinement that may add `write`. `$/`, `$-0`, `$,`, `$\` and `$~` accept only their classes, so none of that silences them. A write to `$stdin` is never checked, and a special any file aliases (`alias $stdout $out`, including a `pre_eval:` file) is exempt. `warning` under `lenient`. | high |
75
+ | <a id="rule-global-readonly-write"></a>`global.readonly-write` | A read-only special global is written — `$!`, `$$`, `$?`, `$<`, `$FILENAME`, `$*`, `$:` / `$LOAD_PATH` / `$-I`, `$"` / `$LOADED_FEATURES`, `$-W`, `$-p`, `$-l` or `$-a` — which raises `NameError` whatever the value. Covers `$g = value`, `$g += value` and a multiple-assignment target. `$LOAD_PATH ||= []` never writes and `$LOAD_PATH << dir` is a method call, so neither fires. A special any file aliases (`alias $! $err`) is exempt. An error in every profile. | high |
73
76
  | <a id="rule-static-value-use-void"></a>`static.value-use.void` | A value recovered from an author-declared `-> void` return is used in value context (an assignment right-hand side, a call receiver, or a call argument). Off by default; reaches a run only through the `use-of-void-value` bleeding-edge feature (ADR-100). A bare-statement `void` call and a legitimate `top` value both stay silent. | high |
74
77
  | <a id="rule-effect-envelope-exceeded"></a>`effect.envelope-exceeded` | A method performs an effect its declared envelope does not admit — its proven effect labels (its own body plus everything it calls) are not covered by the `%a{pure}` or `%a{rigor:v1:effect …}` bound written on it or on its class. Opt-in twice over: it needs an `effects:` block in `.rigor.yml` and an envelope you wrote. Positioned at the Ruby `def`. Unproven ("and possibly more") effects never fire, and `mutate.local` is tolerated by every envelope. | high |
75
78
  | <a id="rule-effect-liskov-widened"></a>`effect.liskov-widened` | An override escapes the envelope written on the method it overrides. A `PgRepo` is usable wherever a `Repo` is, so a `%a{rigor:v1:effect io.db}` on `Repo#find` binds `PgRepo#find` too: an implementation may be purer than the bound it inherits, never less pure. Either what the override *does* exceeds the inherited bound, or the envelope the override *declares for itself* is wider than it. Both sides must be authored — nothing fires unless someone wrote an envelope on the ancestor — and only subclassing counts, not `include`. Positioned at the override's `def`. Needs an `effects:` block. | high |
76
79
  | <a id="rule-effect-unknown-label"></a>`effect.unknown-label` | An effect declaration names a label the registry does not know — a typo in an envelope (`%a{rigor:v1:effect io.bd}`), or a member of `effects.tolerated:`. The whole tag then reads as unbounded, so the declaration quietly stops doing anything; this says so. Positioned at the declaration: the `.rbs` line, the `.rb` line for an rbs-inline annotation, or `.rigor.yml` for a config value. `# rigor:disable` comments are not read out of `.rbs` or `.rigor.yml`, so use `disable:` or the baseline there. Only fires where the spelling is evidently meant to be a label (close to a known one, next to a known one, dotted, or retired) — a word nothing resembles stays silent, because you may be opening your own vocabulary. Needs an `effects:` block. | high |
77
80
  | <a id="rule-effect-annotations-unchecked"></a>`effect.annotations-unchecked` | Your signatures carry `%a{pure}` / `%a{rigor:v1:effect …}` but `.rigor.yml` has no `effects:` block, so nothing checks them. One `:info` per run, positioned at the first annotation. An annotation never turns effect collection on by itself — that would make one line in one file more expensive for every run — so this is how it tells you instead. Add `effects: {}` to opt in, or `disable:` it to keep the annotations documentary. Reads both annotation lanes — `sig/*.rbs` and rbs-inline comments — on every run, a warm `--incremental` with nothing changed included. | — |
81
+ | <a id="rule-rbs-contradicting-signature"></a>`rbs.contradicting-signature` | A method is declared both in your `sig/` and by an inline `# @rbs` / `#:` annotation, and the two provably contradict — no value or call satisfies both: in the return or a parameter every call must pass, the two types share no value (`::String` against `::Integer`). Only absolutely written Ruby core or stdlib classes count: a module such as `Comparable`, a class your project declares, a gem's class, or a relative name like plain `String` never does, or two keyword-free declarations accept positional counts that cannot meet, or one requires a keyword the other cannot take in any form. Overloads are paired by correspondence, not order. Also fires when a member-level `%a{rigor:v1:return: …}` / `%a{rigor:v1:param: …}` refinement shares no value with the type its own member declares. Positioned at the `.rbs` member; Rigor reads that declaration. A stale generated signature is the usual cause: regenerate it, or fix the annotation. Two declarations where one refines the other merge to the more precise one without a word, and a pair Rigor cannot rank drops the inline side with a `source-rbs-annotation-not-honoured` `:info` instead ([Precedence](plugins/rigor-rbs-inline.md#precedence)). An error in every profile. | high |
78
82
  | <a id="rule-suppression-unknown-rule"></a>`suppression.unknown-rule` | A `# rigor:disable[-file]` comment names a rule that does not exist (typically a typo), so the suppression silently does nothing. `plugin.`-prefixed tokens are never flagged. | high |
79
83
  | <a id="rule-suppression-empty"></a>`suppression.empty` | A `# rigor:disable[-file]` comment lists no rules, so it suppresses nothing. | high |
80
84
  | <a id="rule-suppression-unknown-marker"></a>`suppression.unknown-marker` | A comment uses a suppression marker Rigor does not recognise — typically the RuboCop reflex `# rigor:disable-next-line <rule>` or `# rigor:enable <rule>`. Rigor's only markers are `# rigor:disable <rules>` (suppresses on its own line) and `# rigor:disable-file <rules>`, so the comment suppresses nothing. | high |
@@ -85,6 +89,17 @@ carries no `documentation_url`.
85
89
  Plugins may contribute further families and rules; `rigor
86
90
  explain` lists whatever the active configuration loads.
87
91
 
92
+ `flow.unreachable-branch` and `flow.always-truthy-condition` fold
93
+ version guards — `RUBY_VERSION` / `RUBY_ENGINE` comparisons, and
94
+ `X::VERSION` for a default gem of the running Ruby — against the
95
+ Ruby interpreter running `rigor`, never `target_ruby`. The
96
+ diagnostic set is therefore host-dependent: the same file can fold
97
+ a different arm on Ruby 3.3 than on Ruby 4.0, and a project whose
98
+ CI pins a different Ruby than your workstation should expect the CI
99
+ run's result, not yours. See
100
+ [Version-guard condition folding](../type-specification/control-flow-analysis.md#version-guard-condition-folding)
101
+ for the exact foldable set.
102
+
88
103
  ## Evidence tier
89
104
 
90
105
  Every rule in the catalogue above carries an **evidence tier** —
@@ -74,23 +74,33 @@ A plugin may want to read a file (a schema dump) or reach the
74
74
  network. Those are gated by the `plugins_io:` config keys —
75
75
  the network is `disabled` by default, and a plugin can read
76
76
  only the paths you list. See
77
- [Configuration](03-configuration.md).
77
+ [Configuration](03-configuration.md). If a plugin's read falls
78
+ outside every configured path — a path spelled through a symlink
79
+ alias where the read roots hold the real path (macOS' `/tmp` is
80
+ one), or a genuinely out-of-tree file — Rigor surfaces a
81
+ `plugin_trust.read-refused` `:info` diagnostic naming the plugin,
82
+ the refused path and the nearest read root instead of failing
83
+ silently. Spell the path the way the diagnostic's read root spells
84
+ it, or add it under `plugins_io.allowed_paths:`.
78
85
 
79
86
  ### Isolation strategy
80
87
 
81
88
  A few plugins call into their target library directly (for
82
89
  example to ask ActiveSupport's real inflector how to pluralise a
83
90
  class name). That call runs under an **isolation strategy**, set
84
- with the `RIGOR_PLUGIN_ISOLATION` environment variable:
91
+ with the `plugins_isolation:` configuration key or the
92
+ `RIGOR_PLUGIN_ISOLATION` environment variable:
85
93
 
86
94
  | Value | Behaviour |
87
95
  | --- | --- |
88
96
  | `process` (default) | Run the call in a forked, crash-contained worker, so the target library's monkey-patches and any crash never contaminate Rigor. Falls back to `none` where `fork` is unavailable (Windows / JRuby). |
89
97
  | `none` | Load the library into Rigor's own process and call it directly. |
90
- | `ruby_box` | Run inside an experimental `Ruby::Box` sandbox. This needs the `RUBY_BOX=1` start flag, so the `rigor` launcher re-execs itself with it set when you select this strategy. |
98
+ | `ruby_box` | Run inside an experimental `Ruby::Box` sandbox. This needs the `RUBY_BOX=1` start flag, so the `rigor` launcher re-execs itself with it set when you select this strategy. It also needs a Ruby that fixes [Ruby Bug #22260](https://bugs.ruby-lang.org/issues/22260), which no release up to 4.0.7 does. The launcher checks for the fix first, and without it prints a warning and uses the configured strategy instead. **Environment variable only** — the configuration file is read long after Ruby has booted, so `plugins_isolation: ruby_box` is reported as a configuration error instead. |
91
99
 
92
- The legacy `RIGOR_BOX` environment variable is a back-compat
93
- alias for `RIGOR_PLUGIN_ISOLATION=ruby_box`. The default
100
+ The environment variable wins over `plugins_isolation:`, so you can
101
+ override a project's committed choice for one invocation. The legacy
102
+ `RIGOR_BOX` environment variable is a back-compat alias for
103
+ `RIGOR_PLUGIN_ISOLATION=ruby_box`. The default
94
104
  (`process`) is the right choice for almost everyone; the variable
95
105
  exists for the rare platform where forking is unavailable or
96
106
  where you want stronger containment.
@@ -8,9 +8,9 @@ agent works inside a project that has Rigor available.
8
8
  Skills are optional. Everything they do, you can do by hand with the
9
9
  commands in this manual; a skill drives the workflow end to end.
10
10
 
11
- ## Start here — two skills to remember
11
+ ## Start here — three skills to remember
12
12
 
13
- You only ever need to remember two skills; the rest are reached through
13
+ You only ever need to remember three skills; the rest are reached through
14
14
  them.
15
15
 
16
16
  - **`rigor-next-steps`** — *"what should we do next?"* The single entry
@@ -31,6 +31,20 @@ them.
31
31
  code, runs `rigor check` / `annotate` / `type-of` — then answers from
32
32
  the page or the inferred type. You never have to remember the command,
33
33
  just the question. Available at any point.
34
+ - **`rigor-type-oracle`** — *"before you write a type, ask Rigor."* The
35
+ one to remember while **writing** rather than while planning. Any time
36
+ a type is about to be written or asserted — RBS under `sig/`, an inline
37
+ `#:` / `# @rbs` annotation, a Sorbet `sig`, a YARD `@param` /
38
+ `@return`, a type named in a doc sentence or a review comment, a nil
39
+ check justified by "this should be an `X`" — the type comes from
40
+ [`rigor type-of`](02-cli-reference.md#rigor-type-of) /
41
+ [`annotate`](02-cli-reference.md#rigor-annotate) /
42
+ [`sig-gen`](02-cli-reference.md#rigor-sig-gen), not from reading the
43
+ code. A type nobody obtained from Rigor is a guess, and where Rigor has
44
+ no answer (`Dynamic[top]`, `untyped`, a skipped method) the gap is
45
+ reported rather than filled in. It also ships the paragraph to keep in
46
+ your `AGENTS.md` / `CLAUDE.md`, so the rule holds in every agent
47
+ session and not only when the skill triggers.
34
48
 
35
49
  If you do not know which skill you need, start with `rigor-next-steps`.
36
50
 
@@ -72,6 +86,13 @@ is not repeated here.)
72
86
  - **`rigor-monkeypatch-resolve`** — resolves an `undefined-method`
73
87
  cluster that is really your project's own monkey-patches by wiring the
74
88
  defining files into `pre_eval:`.
89
+ - **`rigor-type-oracle`** — sources every type an agent writes from
90
+ Rigor (`type-of` / `annotate` / `sig-gen`) instead of from reading the
91
+ code, and reports the gaps rather than filling them. It is triggered by
92
+ the *event* of a type being about to be written, so `rigor skill
93
+ describe` lists it but never routes to it — reach for it (or install
94
+ its `AGENTS.md` paragraph) whenever an agent documents or annotates
95
+ your code. Introduced above under "Start here".
75
96
 
76
97
  ### Integration and operations
77
98
 
@@ -358,8 +358,9 @@ Generate RBS skeleton signatures inferred from Ruby source files.
358
358
  | `params` | `"untyped"` \| `"observed"` | no | `"untyped"` |
359
359
  | `config` | `string` | no | session default |
360
360
 
361
- `params: "observed"` harvests call-site argument types from `spec/`
362
- (or a directory named via `--observe=PATH` in the underlying CLI).
361
+ `params: "observed"` harvests call-site argument types from the
362
+ project's test roots: the configured `test_paths:`, or whichever of
363
+ `spec/` and `test/` exist.
363
364
 
364
365
  **Returns:** JSON — the same as `rigor sig-gen --print --format json`.
365
366
 
data/docs/manual/11-ci.md CHANGED
@@ -187,6 +187,15 @@ Rigor's severities map per format ([ADR-51](../adr/51-ci-diagnostic-output-forma
187
187
  The exit code is unchanged by `--format` — `0` when there are no errors,
188
188
  `1` otherwise — so the job still gates the pipeline. `--format json`
189
189
  remains available for any other tool that wants the raw diagnostic stream.
190
+ A job that wants warnings (or `info` notes) to gate the pipeline too, not
191
+ just errors, raises the bar with `rigor check --fail-on=warning` (or
192
+ `--fail-on=info`) instead of parsing the diagnostic stream itself.
193
+
194
+ A third status, `70`, means Rigor itself died mid-run — a
195
+ `SystemStackError` or `NoMemoryError` inside the analysis — and the report
196
+ it left behind (an empty `triage.json`, say) is not an account of your
197
+ code. Treat it as a failed job and report it as a Rigor defect, never as a
198
+ clean check.
190
199
 
191
200
  ## Gating effect drift
192
201
 
@@ -337,6 +337,14 @@ ones a type actually catches.
337
337
  > point Rigor at it with `bundler.bundle_path:`. Until you do, these
338
338
  > holes keep the generic `engine_gap` cause instead of `add_rbs` —
339
339
  > the label is missing, never wrong.
340
+ >
341
+ > A project with **no `Gemfile.lock`** is not left out: Rigor falls
342
+ > back to the gems it can see installed — the project's Bundler
343
+ > install tree if one resolves, otherwise the running Ruby's gems —
344
+ > and attributes constants against those. Ownership is still
345
+ > established by reading the gem's own entry file, so the fallback
346
+ > widens which gems can be claimed and never whether an unowned
347
+ > constant is.
340
348
 
341
349
  Provenance is precision-additive only: it never changes a type, fires
342
350
  no diagnostic, and never affects severity or the protection ratio.
@@ -22,25 +22,56 @@ The plain `() -> String` stays the compatibility contract; the
22
22
  annotation tells Rigor the return is a non-empty string.
23
23
 
24
24
  You may also write any of them **in a `.rb` file**, as an
25
- rbs-inline `# @rbs %a{…}` comment — `%a{}` is rbs-inline's own
26
- upstream grammar, and the annotation reaches Rigor on the same
27
- path the generated signature does:
25
+ inline-RBS comment — `%a{}` is RBS's own annotation grammar, and
26
+ the annotation reaches Rigor on the same path the generated
27
+ signature does. Rigor reads three spellings:
28
28
 
29
29
  ```rb
30
30
  # rbs_inline: enabled
31
31
 
32
32
  class Reader
33
+ # Own line: the annotation, then the type on its own tag.
33
34
  # @rbs %a{rigor:v1:return: non-empty-string}
34
35
  # @rbs return: String
35
36
  def read_name = "x"
37
+
38
+ # Same line, `@rbs` method type.
39
+ # @rbs %a{rigor:v1:return: non-empty-string} () -> String
40
+ def title = "x"
41
+
42
+ # Same line, `#:` method type.
43
+ #: %a{rigor:v1:return: non-empty-string} () -> String
44
+ def label = "x"
36
45
  end
37
46
  ```
38
47
 
48
+ Several annotations may stand before the method type
49
+ (`#: %a{pure} %a{rigor:v1:return: non-empty-string} () -> String`).
50
+ The other inline-RBS readers do not accept all three:
51
+
52
+ | spelling | rbs's built-in inline parser, Steep with `inline: true` | the `rbs-inline` gem's own `--output` |
53
+ | --- | --- | --- |
54
+ | own line | syntax error (`expected a token pARROW`), annotation lost | annotation kept |
55
+ | same line | annotation and method type kept | annotation kept, method type **dropped** |
56
+
57
+ Rigor keeps both halves of every row. If you also run Steep in
58
+ inline mode, use the same-line spelling. The measurement is in
59
+ [ADR-111](../adr/111-inline-refinement-carrier.md). If the method type after a same-line annotation
60
+ does not parse, the method is left untyped and Rigor reports it as
61
+ [`plugin.rbs-inline.source-rbs-annotation-not-honoured`](plugins/rigor-rbs-inline.md#same-line-annotations).
62
+
39
63
  This needs the `rbs-inline` library installed; Rigor ingests
40
64
  inline annotations by default when it is
41
- ([ADR-93](../adr/93-default-rbs-inline-ingestion.md)). There is
42
- no Rigor-only comment dialect: `# rigor:` comments remain
43
- suppression-only.
65
+ ([ADR-93](../adr/93-default-rbs-inline-ingestion.md)). `# rigor:`
66
+ comments remain suppression-only.
67
+
68
+ A dedicated `# @extrbs` comment for what RBS cannot spell is
69
+ accepted in [ADR-112](../adr/112-extrbs-comment-channel.md) but
70
+ not implemented yet ([#1073](https://github.com/rigortype/rigor/issues/1073)).
71
+ Until it ships, the `%a{}` forms above are the inline route. A type
72
+ plain RBS can spell, such as `:asc | :desc`, belongs in `# @rbs` or
73
+ `#:` either way.
74
+
44
75
  This page is the *operational* reference — the directives you can
45
76
  write and their syntax. For the normative rules (conflict
46
77
  handling, merging, provenance) see
@@ -122,7 +153,12 @@ The right-hand side of `return:`, `param:`, `assert*`, and
122
153
 
123
154
  Refinement payloads support the parameterised forms
124
155
  `non-empty-array[Integer]`, `non-empty-hash[Symbol, Integer]`,
125
- and the bounded-integer form `int<min, max>`. Type-argument
156
+ and the bounded numeric forms `Integer[1..10]` and
157
+ `Float[0.0...1.0]`, written with a Ruby range literal (`1...10`,
158
+ `1..`, `..10`; the PHPStan-style `int<1, 10>` still parses but is
159
+ deprecated and reports `dynamic.rbs-extended.deprecated-form` with
160
+ the spelling to write), plus the Float names `non-nan-float` and
161
+ `finite-float`. Type-argument
126
162
  positions also accept Symbol / String literal tokens and unions
127
163
  of them — `pick_of[T, :name | :email]`,
128
164
  `Pick[T, "name" | "email"]` — each lifted to a `Constant<value>`.
@@ -178,7 +214,9 @@ class UserRepository
178
214
  end
179
215
  ```
180
216
 
181
- The same two work as rbs-inline comments in a `.rb` file:
217
+ The same two work as rbs-inline comments in a `.rb` file, in any of
218
+ the three spellings shown above — the own-line
219
+ one here:
182
220
 
183
221
  ```rb
184
222
  # rbs_inline: enabled