rigortype 0.4.6 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (550) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/data/core_overlay/bigdecimal.rbs +33 -0
  4. data/data/core_overlay/resolv.rbs +30 -0
  5. data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
  6. data/data/gem_overlay/addressable/addressable.rbs +40 -0
  7. data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
  8. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
  9. data/docs/handbook/01-getting-started.md +6 -3
  10. data/docs/handbook/11-sig-gen.md +85 -3
  11. data/docs/handbook/appendix-phpstan.md +4 -4
  12. data/docs/install.md +20 -0
  13. data/docs/manual/01-installation.md +67 -26
  14. data/docs/manual/02-cli-reference.md +455 -113
  15. data/docs/manual/03-configuration.md +76 -5
  16. data/docs/manual/04-diagnostics.md +5 -4
  17. data/docs/manual/06-baseline.md +6 -4
  18. data/docs/manual/08-skills.md +11 -0
  19. data/docs/manual/09-editor-integration.md +21 -2
  20. data/docs/manual/10-mcp-server.md +38 -2
  21. data/docs/manual/12-caching.md +4 -3
  22. data/docs/manual/14-rails-quickstart.md +1 -1
  23. data/docs/manual/19-effect-labels.md +3 -4
  24. data/docs/manual/plugins/rigor-actionpack.md +32 -15
  25. data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
  26. data/docs/manual/plugins/rigor-sorbet.md +17 -3
  27. data/lib/rigor/analysis/baseline.rb +24 -27
  28. data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
  29. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
  30. data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
  31. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
  32. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
  33. data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
  34. data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
  35. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
  36. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
  37. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
  38. data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
  40. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
  41. data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
  42. data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
  43. data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
  44. data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
  45. data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
  46. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
  47. data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
  48. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  49. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
  50. data/lib/rigor/analysis/check_rules.rb +720 -141
  51. data/lib/rigor/analysis/crash_signature.rb +45 -3
  52. data/lib/rigor/analysis/dependency_recorder.rb +237 -80
  53. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
  54. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
  55. data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
  56. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  57. data/lib/rigor/analysis/edit_replay.rb +93 -6
  58. data/lib/rigor/analysis/fact_store.rb +3 -3
  59. data/lib/rigor/analysis/incremental.rb +11 -1
  60. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
  61. data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
  62. data/lib/rigor/analysis/incremental_session.rb +306 -53
  63. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
  64. data/lib/rigor/analysis/rule_catalog.rb +39 -3
  65. data/lib/rigor/analysis/run_cache_key.rb +27 -3
  66. data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
  67. data/lib/rigor/analysis/run_stats.rb +2 -2
  68. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
  69. data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
  70. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
  71. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
  72. data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
  73. data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
  74. data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
  75. data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
  76. data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
  77. data/lib/rigor/analysis/runner.rb +298 -109
  78. data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
  79. data/lib/rigor/analysis/template_unit_positions.rb +2 -3
  80. data/lib/rigor/analysis/template_units.rb +9 -1
  81. data/lib/rigor/analysis/worker_session.rb +65 -71
  82. data/lib/rigor/bleeding_edge.rb +22 -10
  83. data/lib/rigor/builtins/imported_refinements.rb +6 -8
  84. data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
  85. data/lib/rigor/builtins/regex_refinement.rb +4 -6
  86. data/lib/rigor/cache/descriptor.rb +6 -5
  87. data/lib/rigor/cache/file_digest.rb +39 -31
  88. data/lib/rigor/cache/incremental_snapshot.rb +95 -17
  89. data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
  90. data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
  91. data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
  92. data/lib/rigor/cache/rbs_constant_table.rb +1 -1
  93. data/lib/rigor/cache/rbs_descriptor.rb +90 -4
  94. data/lib/rigor/cache/rbs_environment.rb +8 -1
  95. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
  96. data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
  97. data/lib/rigor/cache/store.rb +11 -10
  98. data/lib/rigor/ci_detector.rb +1 -1
  99. data/lib/rigor/cli/annotate_command.rb +24 -19
  100. data/lib/rigor/cli/baseline_command.rb +160 -57
  101. data/lib/rigor/cli/catalogue.rb +256 -0
  102. data/lib/rigor/cli/check_command.rb +112 -104
  103. data/lib/rigor/cli/check_runner_factory.rb +2 -2
  104. data/lib/rigor/cli/command.rb +15 -1
  105. data/lib/rigor/cli/coverage_command.rb +21 -8
  106. data/lib/rigor/cli/coverage_mutation.rb +5 -7
  107. data/lib/rigor/cli/coverage_renderer.rb +2 -1
  108. data/lib/rigor/cli/coverage_scan.rb +3 -5
  109. data/lib/rigor/cli/diff_command.rb +25 -20
  110. data/lib/rigor/cli/docs_command.rb +1 -2
  111. data/lib/rigor/cli/doctor_command.rb +12 -12
  112. data/lib/rigor/cli/effects_command.rb +50 -40
  113. data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
  114. data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
  115. data/lib/rigor/cli/effects_renderer.rb +2 -1
  116. data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
  117. data/lib/rigor/cli/explain_command.rb +18 -8
  118. data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
  119. data/lib/rigor/cli/json_document.rb +41 -0
  120. data/lib/rigor/cli/lens_command.rb +29 -10
  121. data/lib/rigor/cli/lsp_command.rb +250 -23
  122. data/lib/rigor/cli/mcp_command.rb +12 -7
  123. data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
  124. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
  125. data/lib/rigor/cli/options.rb +40 -1
  126. data/lib/rigor/cli/plugin_command.rb +93 -46
  127. data/lib/rigor/cli/plugins_command.rb +17 -18
  128. data/lib/rigor/cli/plugins_renderer.rb +10 -6
  129. data/lib/rigor/cli/protection_fork_scan.rb +2 -2
  130. data/lib/rigor/cli/protection_renderer.rb +2 -1
  131. data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
  132. data/lib/rigor/cli/sig_gen_command.rb +92 -27
  133. data/lib/rigor/cli/skill_command.rb +32 -3
  134. data/lib/rigor/cli/skill_deep_probe.rb +1 -1
  135. data/lib/rigor/cli/skill_describe.rb +2 -1
  136. data/lib/rigor/cli/trace_command.rb +14 -9
  137. data/lib/rigor/cli/triage_command.rb +10 -11
  138. data/lib/rigor/cli/triage_renderer.rb +2 -1
  139. data/lib/rigor/cli/type_of_command.rb +16 -11
  140. data/lib/rigor/cli/type_of_project_scope.rb +1 -4
  141. data/lib/rigor/cli/type_of_renderer.rb +4 -1
  142. data/lib/rigor/cli/type_scan_command.rb +14 -9
  143. data/lib/rigor/cli/type_scan_renderer.rb +2 -1
  144. data/lib/rigor/cli/unused_command.rb +27 -17
  145. data/lib/rigor/cli/upgrade_command.rb +5 -0
  146. data/lib/rigor/cli.rb +214 -56
  147. data/lib/rigor/config_audit.rb +33 -19
  148. data/lib/rigor/configuration/severity_profile.rb +14 -5
  149. data/lib/rigor/configuration.rb +104 -35
  150. data/lib/rigor/effects/collector.rb +53 -21
  151. data/lib/rigor/effects/definition_context.rb +0 -2
  152. data/lib/rigor/effects/envelope.rb +1 -1
  153. data/lib/rigor/effects/framework_units.rb +1 -1
  154. data/lib/rigor/effects/mutation_classifier.rb +4 -7
  155. data/lib/rigor/effects/propagator.rb +1 -1
  156. data/lib/rigor/effects/registry.rb +2 -2
  157. data/lib/rigor/effects/snapshot.rb +1 -1
  158. data/lib/rigor/effects/taint_cause.rb +0 -2
  159. data/lib/rigor/effects/unit_scan.rb +9 -13
  160. data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
  161. data/lib/rigor/environment/class_registry.rb +3 -3
  162. data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
  163. data/lib/rigor/environment/core_root.rb +77 -0
  164. data/lib/rigor/environment/default_libraries.rb +18 -1
  165. data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
  166. data/lib/rigor/environment/gated_signature_guard.rb +1 -1
  167. data/lib/rigor/environment/gated_signature_plan.rb +5 -2
  168. data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
  169. data/lib/rigor/environment/lockfile_resolver.rb +28 -12
  170. data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
  171. data/lib/rigor/environment/member_consistency.rb +56 -7
  172. data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
  173. data/lib/rigor/environment/project_path_gems.rb +124 -0
  174. data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
  175. data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
  176. data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
  177. data/lib/rigor/environment/rbs_loader.rb +772 -110
  178. data/lib/rigor/environment/reflection.rb +7 -7
  179. data/lib/rigor/environment/required_features.rb +0 -3
  180. data/lib/rigor/environment/ruby_library_roots.rb +1 -1
  181. data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
  182. data/lib/rigor/environment.rb +169 -42
  183. data/lib/rigor/flow_contribution/merger.rb +4 -4
  184. data/lib/rigor/inference/acceptance.rb +136 -40
  185. data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
  186. data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
  187. data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
  188. data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
  189. data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
  190. data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
  191. data/lib/rigor/inference/block_auto_splat.rb +1 -1
  192. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  193. data/lib/rigor/inference/block_run_count.rb +185 -0
  194. data/lib/rigor/inference/budget_trace.rb +8 -2
  195. data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
  196. data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
  197. data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
  198. data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
  199. data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
  200. data/lib/rigor/inference/callee_write_census.rb +815 -0
  201. data/lib/rigor/inference/captured_locals.rb +2 -0
  202. data/lib/rigor/inference/census_foreign_block.rb +354 -0
  203. data/lib/rigor/inference/clause_widening.rb +1 -1
  204. data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
  205. data/lib/rigor/inference/construction_escape.rb +258 -0
  206. data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
  207. data/lib/rigor/inference/declaration_walk.rb +4 -0
  208. data/lib/rigor/inference/deep_erasure.rb +39 -7
  209. data/lib/rigor/inference/def_handle.rb +1 -1
  210. data/lib/rigor/inference/def_node_resolver.rb +33 -15
  211. data/lib/rigor/inference/def_return_typer.rb +47 -5
  212. data/lib/rigor/inference/definer_resolution.rb +19 -9
  213. data/lib/rigor/inference/definers_index.rb +162 -0
  214. data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
  215. data/lib/rigor/inference/element_read_widening.rb +49 -3
  216. data/lib/rigor/inference/error_info.rb +16 -0
  217. data/lib/rigor/inference/expression_typer.rb +1559 -320
  218. data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
  219. data/lib/rigor/inference/flow_tracer.rb +31 -14
  220. data/lib/rigor/inference/fork_map.rb +3 -0
  221. data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
  222. data/lib/rigor/inference/global_write_census.rb +81 -5
  223. data/lib/rigor/inference/global_writer_reach.rb +260 -0
  224. data/lib/rigor/inference/hkt_body.rb +1 -2
  225. data/lib/rigor/inference/hkt_body_parser.rb +3 -5
  226. data/lib/rigor/inference/hkt_reducer.rb +2 -2
  227. data/lib/rigor/inference/hkt_registry.rb +3 -5
  228. data/lib/rigor/inference/in_effect_refinements.rb +7 -0
  229. data/lib/rigor/inference/indexed_narrowing.rb +61 -15
  230. data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
  231. data/lib/rigor/inference/ivar_writer_census.rb +154 -8
  232. data/lib/rigor/inference/key_presence_guard.rb +51 -24
  233. data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
  234. data/lib/rigor/inference/last_line.rb +5 -2
  235. data/lib/rigor/inference/loop_value_record.rb +66 -14
  236. data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
  237. data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
  238. data/lib/rigor/inference/match_rebinding.rb +28 -5
  239. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
  240. data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
  241. data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
  242. data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
  243. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
  244. data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
  245. data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
  246. data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
  247. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
  248. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
  249. data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
  250. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
  251. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
  252. data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
  253. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
  254. data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
  255. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
  256. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
  257. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
  258. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
  259. data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
  260. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
  261. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
  262. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
  263. data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
  264. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
  265. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
  266. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  267. data/lib/rigor/inference/method_dispatcher.rb +123 -25
  268. data/lib/rigor/inference/method_parameter_binder.rb +136 -56
  269. data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
  270. data/lib/rigor/inference/module_function_state.rb +24 -3
  271. data/lib/rigor/inference/multi_target_binder.rb +2 -2
  272. data/lib/rigor/inference/mutation_rejoin.rb +240 -4
  273. data/lib/rigor/inference/mutation_widening.rb +199 -29
  274. data/lib/rigor/inference/narrowing.rb +238 -50
  275. data/lib/rigor/inference/operand_effects.rb +43 -2
  276. data/lib/rigor/inference/optimistic_origin.rb +134 -9
  277. data/lib/rigor/inference/origin_lookup.rb +1 -1
  278. data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
  279. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  280. data/lib/rigor/inference/pre_eval_constants.rb +1 -1
  281. data/lib/rigor/inference/precision_scanner.rb +1 -1
  282. data/lib/rigor/inference/project_method_ownership.rb +30 -9
  283. data/lib/rigor/inference/project_patched_methods.rb +65 -13
  284. data/lib/rigor/inference/project_patched_scanner.rb +120 -25
  285. data/lib/rigor/inference/project_patched_visibility.rb +39 -3
  286. data/lib/rigor/inference/project_yielder.rb +11 -17
  287. data/lib/rigor/inference/rbs_type_translator.rb +68 -15
  288. data/lib/rigor/inference/receiver_alias.rb +20 -0
  289. data/lib/rigor/inference/receiver_writer_call.rb +67 -0
  290. data/lib/rigor/inference/recursion_rebinding.rb +157 -0
  291. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
  292. data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
  293. data/lib/rigor/inference/refined_dispatch.rb +13 -0
  294. data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
  295. data/lib/rigor/inference/remover_holder_scan.rb +287 -0
  296. data/lib/rigor/inference/remover_narrowing.rb +224 -0
  297. data/lib/rigor/inference/rewrite_mutation.rb +53 -2
  298. data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
  299. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
  300. data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
  301. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
  302. data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
  303. data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
  304. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
  305. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
  306. data/lib/rigor/inference/scope_indexer.rb +2213 -226
  307. data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
  308. data/lib/rigor/inference/self_preserving_block.rb +87 -0
  309. data/lib/rigor/inference/set_mutation.rb +189 -0
  310. data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
  311. data/lib/rigor/inference/singleton_object_constant.rb +32 -2
  312. data/lib/rigor/inference/statement_evaluator.rb +3268 -358
  313. data/lib/rigor/inference/string_mutation.rb +14 -0
  314. data/lib/rigor/inference/struct_fold_safety.rb +4 -6
  315. data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
  316. data/lib/rigor/inference/toplevel_def_census.rb +67 -0
  317. data/lib/rigor/inference/unknown_store_widening.rb +103 -4
  318. data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
  319. data/lib/rigor/inference/untyped_rejoin.rb +1 -1
  320. data/lib/rigor/inference/void_tail_summary.rb +29 -9
  321. data/lib/rigor/language_server/buffer_table.rb +3 -3
  322. data/lib/rigor/language_server/completion_provider.rb +26 -20
  323. data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
  324. data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
  325. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  326. data/lib/rigor/language_server/folding_range_provider.rb +1 -2
  327. data/lib/rigor/language_server/hover_provider.rb +1 -7
  328. data/lib/rigor/language_server/hover_renderer.rb +6 -8
  329. data/lib/rigor/language_server/project_context.rb +39 -1
  330. data/lib/rigor/language_server/publish_batcher.rb +3 -4
  331. data/lib/rigor/language_server/selection_range_provider.rb +2 -4
  332. data/lib/rigor/language_server/server.rb +7 -3
  333. data/lib/rigor/language_server/signature_help_provider.rb +4 -4
  334. data/lib/rigor/lens/claims/comparison.rb +78 -0
  335. data/lib/rigor/lens/claims/request.rb +131 -0
  336. data/lib/rigor/lens/claims/type_reader.rb +216 -0
  337. data/lib/rigor/lens/claims.rb +208 -0
  338. data/lib/rigor/lens/contract_reader.rb +5 -0
  339. data/lib/rigor/lens/document.rb +1 -1
  340. data/lib/rigor/lens/file_reader.rb +21 -8
  341. data/lib/rigor/lens/plugins.rb +1 -1
  342. data/lib/rigor/lens/run.rb +8 -0
  343. data/lib/rigor/lens.rb +1 -1
  344. data/lib/rigor/library_probe.rb +68 -0
  345. data/lib/rigor/mcp/server.rb +73 -2
  346. data/lib/rigor/mcp.rb +1 -1
  347. data/lib/rigor/memo_audit.rb +282 -0
  348. data/lib/rigor/plugin/access_denied_error.rb +1 -2
  349. data/lib/rigor/plugin/base.rb +15 -2
  350. data/lib/rigor/plugin/bundled_catalog.rb +1 -12
  351. data/lib/rigor/plugin/io_boundary.rb +26 -4
  352. data/lib/rigor/plugin/loader.rb +25 -8
  353. data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
  354. data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
  355. data/lib/rigor/plugin/manifest.rb +16 -16
  356. data/lib/rigor/plugin/registry.rb +16 -5
  357. data/lib/rigor/plugin/services.rb +2 -3
  358. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
  359. data/lib/rigor/plugin/trust_policy.rb +48 -2
  360. data/lib/rigor/plugin/type_node_resolver.rb +2 -4
  361. data/lib/rigor/plugin.rb +6 -8
  362. data/lib/rigor/plugin_gap_advisory.rb +5 -1
  363. data/lib/rigor/project_environment.rb +16 -2
  364. data/lib/rigor/protection/analysis_guard.rb +1 -1
  365. data/lib/rigor/protection/discovery_seed.rb +44 -5
  366. data/lib/rigor/protection/mutation_cache.rb +4 -1
  367. data/lib/rigor/protection/mutator.rb +1 -1
  368. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  369. data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
  370. data/lib/rigor/rbs_extended/reporter.rb +27 -3
  371. data/lib/rigor/rbs_extended.rb +2 -3
  372. data/lib/rigor/reflection/constant_ancestors.rb +81 -22
  373. data/lib/rigor/reflection/declaration_collectors.rb +55 -0
  374. data/lib/rigor/reflection/declarations.rb +58 -0
  375. data/lib/rigor/reflection/project_method.rb +177 -0
  376. data/lib/rigor/reflection.rb +2 -0
  377. data/lib/rigor/rooted_glob.rb +32 -1
  378. data/lib/rigor/run_scoped_slots.rb +38 -0
  379. data/lib/rigor/runtime/jit.rb +1 -1
  380. data/lib/rigor/scope/discovery_index.rb +168 -8
  381. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
  382. data/lib/rigor/scope/resolution_chain.rb +901 -54
  383. data/lib/rigor/scope.rb +644 -37
  384. data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
  385. data/lib/rigor/sig_gen/classification.rb +18 -1
  386. data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
  387. data/lib/rigor/sig_gen/generator.rb +588 -66
  388. data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
  389. data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
  390. data/lib/rigor/sig_gen/layout_index.rb +3 -3
  391. data/lib/rigor/sig_gen/method_candidate.rb +37 -4
  392. data/lib/rigor/sig_gen/observation_collector.rb +14 -7
  393. data/lib/rigor/sig_gen/observed_call.rb +1 -1
  394. data/lib/rigor/sig_gen/path_mapper.rb +6 -0
  395. data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
  396. data/lib/rigor/sig_gen/renderer.rb +15 -9
  397. data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
  398. data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
  399. data/lib/rigor/sig_gen/write_reporter.rb +63 -14
  400. data/lib/rigor/sig_gen/write_result.rb +23 -12
  401. data/lib/rigor/sig_gen/writer.rb +238 -37
  402. data/lib/rigor/sig_gen.rb +1 -0
  403. data/lib/rigor/source/forwardable_delegators.rb +122 -0
  404. data/lib/rigor/source/node_children.rb +13 -0
  405. data/lib/rigor/type/acceptance_router.rb +2 -2
  406. data/lib/rigor/type/accepts_result.rb +1 -1
  407. data/lib/rigor/type/app.rb +4 -6
  408. data/lib/rigor/type/combinator.rb +173 -21
  409. data/lib/rigor/type/constant.rb +4 -5
  410. data/lib/rigor/type/hash_shape.rb +21 -40
  411. data/lib/rigor/type/interface.rb +60 -0
  412. data/lib/rigor/type/union.rb +1 -1
  413. data/lib/rigor/type.rb +1 -0
  414. data/lib/rigor/type_node/identifier.rb +1 -2
  415. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  416. data/lib/rigor/type_node/string_literal.rb +1 -2
  417. data/lib/rigor/type_node/symbol_literal.rb +1 -2
  418. data/lib/rigor/type_node/union.rb +3 -6
  419. data/lib/rigor/type_of_memo/serving.rb +99 -0
  420. data/lib/rigor/type_of_memo/stats.rb +43 -0
  421. data/lib/rigor/type_of_memo.rb +337 -0
  422. data/lib/rigor/typing_state.rb +1094 -0
  423. data/lib/rigor/value_semantics.rb +1 -10
  424. data/lib/rigor/version.rb +1 -1
  425. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
  426. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
  427. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
  428. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
  429. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
  430. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
  431. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
  432. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
  433. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
  434. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
  435. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
  436. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
  437. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
  438. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
  439. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  440. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
  441. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
  442. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
  443. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
  444. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
  445. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
  446. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
  447. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
  448. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
  449. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
  450. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
  451. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
  452. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
  453. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
  454. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
  455. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
  456. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
  457. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
  458. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
  459. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
  460. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
  461. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
  462. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
  463. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
  464. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
  465. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
  466. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
  467. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
  468. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
  469. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
  470. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
  471. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
  472. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
  473. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
  474. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
  475. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
  476. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
  477. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
  478. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
  479. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
  480. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
  481. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
  482. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
  483. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
  484. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
  485. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
  486. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
  487. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
  488. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
  489. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
  490. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
  491. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
  492. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
  493. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
  494. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
  495. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
  496. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
  497. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
  498. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
  499. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
  500. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
  501. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
  502. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
  503. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
  504. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
  505. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
  506. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
  507. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
  508. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
  509. data/sig/rigor/analysis/baseline.rbs +0 -3
  510. data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
  511. data/sig/rigor/analysis/fact_store.rbs +2 -2
  512. data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
  513. data/sig/rigor/cache/descriptor.rbs +14 -0
  514. data/sig/rigor/cli/diff_command.rbs +1 -1
  515. data/sig/rigor/cli/explain_command.rbs +1 -1
  516. data/sig/rigor/effects/registry.rbs +2 -2
  517. data/sig/rigor/environment.rbs +4 -1
  518. data/sig/rigor/inference.rbs +4 -1
  519. data/sig/rigor/plugin/additional_initializer.rbs +0 -5
  520. data/sig/rigor/plugin/base.rbs +53 -18
  521. data/sig/rigor/plugin/effect_attribution.rbs +3 -3
  522. data/sig/rigor/plugin/effect_edge.rbs +1 -1
  523. data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
  524. data/sig/rigor/plugin/interfaces.rbs +51 -0
  525. data/sig/rigor/plugin/io_boundary.rbs +3 -3
  526. data/sig/rigor/plugin/manifest.rbs +9 -9
  527. data/sig/rigor/plugin/node_context.rbs +21 -0
  528. data/sig/rigor/plugin/protocol_contract.rbs +3 -7
  529. data/sig/rigor/reflection.rbs +37 -0
  530. data/sig/rigor/scope.rbs +43 -4
  531. data/sig/rigor/trinary.rbs +1 -0
  532. data/sig/rigor/type.rbs +9 -3
  533. data/sig/rigor.rbs +3 -2
  534. data/skills/rigor-ask/SKILL.md +1 -0
  535. data/skills/rigor-plugin-author/SKILL.md +4 -4
  536. data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
  537. data/skills/rigor-plugin-review/SKILL.md +3 -3
  538. data/skills/rigor-project-init/references/01-detect.md +1 -0
  539. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
  540. data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
  541. data/skills/rigor-rbs-contract/SKILL.md +147 -0
  542. data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
  543. data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
  544. data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
  545. data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
  546. data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
  547. data/skills/rigor-rbs-setup/SKILL.md +3 -1
  548. data/skills/rigor-type-oracle/SKILL.md +26 -1
  549. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
  550. metadata +82 -3
@@ -10,6 +10,8 @@ rigor <command> [options] [arguments]
10
10
  `rigor help` prints the command list; `rigor version` prints
11
11
  the installed version. An unknown command, or a malformed
12
12
  option, exits `64` — the conventional "usage error" code.
13
+ See [`rigor help`](#rigor-help) for a single command's help and
14
+ the machine-readable list of commands and flags.
13
15
 
14
16
  Every command that loads your project configuration also
15
17
  accepts `--config=PATH` to point at a specific config file
@@ -46,7 +48,7 @@ check their arity, since another file may redefine them.
46
48
  | `--no-cache` | Skip the persistent cache for this run. With `--incremental` it is a cold full analysis: the incremental snapshot and the run-result cache are neither read nor written, so the next plain `--incremental` run still uses the snapshot it had before. |
47
49
  | `--incremental` | Re-analyse only the files changed since the last run plus the files that depend on them, serving the rest from a cross-process disk snapshot (ADR-46). Diagnostics are identical to a full run; a config / gem / version change (or a file added or removed) transparently forces a full re-analysis. A run with nothing changed is answered from the cache without starting the analyzer. See [Caching](12-caching.md). |
48
50
  | `--verify-incremental` | Acceptance gate: run the incremental analyzer against a full `--no-cache` run and assert the diagnostics agree as a set and in the same order, then exit (0 on match, 1 on mismatch: the differing diagnostics for a set mismatch, or the first index where the order diverges for a same-set, different-order mismatch). It re-analyses half of an unchanged tree and serves the other half from cache, so it checks the cache-merge path but not that an edit re-checks every file it affects; `--verify-incremental-edits` adds that. |
49
- | `--verify-incremental-edits=N` | With `--verify-incremental` (and implying it): after the check above, replay two edits on each of `N` files spread over the analysed list, through an editor buffer, never on disk: the file emptied, then the file reduced to its `class` / `module` declarations with every body dropped (skipped when that is the file itself, or holds no declaration). After each edit, the warm recheck of the baseline must equal a full analysis of the edited tree, or the run fails naming the edit and the file. Each replay costs a recheck and a full analysis. A replay the recheck declines (it would have been a full analysis) is counted in the OK line, and the run fails if every replay declined. Only these two removals are replayed, never an addition. Default 0. |
51
+ | `--verify-incremental-edits=N` | With `--verify-incremental` (and implying it): after the check above, replay edits on each of `N` files spread over the analysed list, through an editor buffer, never on disk: the file emptied; the file reduced to its `class` / `module` declarations with every body dropped (skipped when that is the file itself, or holds no declaration); and up to two copies of the file each missing one statement of a `class` / `module` body, with everything else kept, so a finer dependency edge is the only thing that re-checks a reader (visibility calls such as `private` and `module_function` are dropped first, then other statements spread over the file). After each edit, the warm recheck of the baseline must equal a full analysis of the edited tree, or the run fails naming the edit and the file. Each replay costs a recheck and a full analysis. A replay the recheck declines (it would have been a full analysis) is counted in the OK line, and the run fails if every replay declined. Only removals are replayed, never an addition. Default 0. |
50
52
  | `--clear-cache` | Delete the cache directory before running. |
51
53
  | `--cache-stats` | Print the on-disk cache inventory when finished — on stdout under `--format text`, on stderr under every other format, so machine-readable output stays a parseable document. `--clear-cache`'s and `--verify-incremental`'s notes follow the same rule. |
52
54
  | `--[no-]stats` | Print a run summary (files, classes, memory, wall time) to stderr. Default on. |
@@ -60,7 +62,7 @@ check their arity, since another file may redefine them.
60
62
  | `--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). |
61
63
  | `--no-bleeding-edge` | Ignore any configured `bleeding_edge:` selection for this run (adopt none). |
62
64
  | `--no-tolerated-effects` | Check effect envelopes as if [`effects.tolerated:`](03-configuration.md) were empty — the audit switch for your discharge policy (ADR-103). Judgment only: the run, what it collects and its cache entry are identical either way, so this never costs a re-analysis. |
63
- | `--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). |
65
+ | `--tmp-file=PATH --instead-of=PATH` | Editor mode: analyse `PATH` using the buffer in `--tmp-file`. Both required together, once per run (a repeat is a usage error). Alone, only the buffer's own file produces diagnostics; add `--incremental` for whole-project scope (see below). |
64
66
 
65
67
  Exit `0` when no error-severity diagnostics remain, `1` when
66
68
  any are reported, `64` on a usage error. `--fail-on` raises that
@@ -70,14 +72,16 @@ that want the stricter reading.
70
72
  ### Editor mode scope
71
73
 
72
74
  `--tmp-file` / `--instead-of` on their own analyse **only** the
73
- buffer's file. It still reads the rest of the project's declarations
74
- (the classes and methods other files define), as `rigor check FILE`
75
+ buffer's file. The `--instead-of` logical path is analysed even when it
76
+ lies outside `paths:`, as it always has been. It still reads the rest of the
77
+ project's declarations (the classes and methods other files define), as `rigor check FILE`
75
78
  does, but it reports nothing about what the unsaved edit does to the
76
79
  rest of the project.
77
80
 
78
81
  Adding `--incremental` analyses the **whole project with the buffer
79
82
  substituted** — the edited file and the files that depend on it are
80
83
  re-analysed, everything else is served from the incremental snapshot.
84
+ A buffer for a claimed template is analysed as its template unit, once.
81
85
  An unsaved change to a method's return type therefore surfaces in its
82
86
  callers, not just in the file being edited.
83
87
 
@@ -91,6 +95,11 @@ Two things to know:
91
95
  exist only in your editor, and recording them would make the next
92
96
  ordinary run believe it had already analysed a file in a state it
93
97
  was never in.
98
+ - The buffer's file need not exist on disk yet: a new file is analysed
99
+ as an added file, with or without path arguments.
100
+ - Editor mode binds one buffer per run. Repeating `--tmp-file` or
101
+ `--instead-of` is a usage error (exit `64`), not a silent replacement
102
+ of the earlier pair.
94
103
 
95
104
  `--verify-incremental` refuses a buffer: it compares against a full
96
105
  analysis of the files on disk, which a buffer contradicts by
@@ -101,31 +110,82 @@ construction.
101
110
  Write a starter configuration file.
102
111
 
103
112
  ```sh
104
- rigor init [--path=PATH] [--force]
113
+ rigor init [--path=PATH] [--force] [--no-plugins]
105
114
  ```
106
115
 
107
116
  Writes `.rigor.dist.yml` by default. `--path` chooses a
108
117
  different target; `--force` overwrites an existing file.
109
- Exit `1` if the file exists and `--force` was not given.
118
+ Exit `1` if the file exists and `--force` was not given. A `--path` whose
119
+ directory does not exist, or that cannot be written, is a usage error: one
120
+ `rigor init: cannot write PATH: REASON` line on stderr and exit `64`. The
121
+ directory is not created.
122
+
123
+ When the project has a `Gemfile.lock` in the working directory,
124
+ `rigor init` suggests the bundled plugins whose gems the lockfile
125
+ declares (the same read as [`rigor doctor`](#rigor-doctor)'s
126
+ plugin-gap check). The suggestions are commented out under
127
+ `plugins: []`, so nothing loads until you enable one: delete the
128
+ `[]` on the `plugins:` line, uncomment the entries you want, then
129
+ run `rigor plugins --strict`. A Rails project's `rails` also brings
130
+ in the Rails family, so the list can be long. Without a lockfile,
131
+ or when no locked gem has a bundled plugin, the template has no
132
+ suggestions.
133
+
134
+ Suggesting rather than enabling follows ADR-96 Criterion 2: a
135
+ lockfile's presence does not by itself opt a project into a
136
+ plugin's code.
137
+
138
+ In a Rails application (a `config/application.rb` in the working
139
+ directory), `rigor init` also suggests the source directories that
140
+ exist, `app` and `lib`. The active `paths:` stays `lib`; the
141
+ suggestion is a commented `paths:` line under it, which names what
142
+ was detected. A bare `rigor check` analyses only `lib` until you
143
+ delete the `paths:` list above and uncomment the line below. Other
144
+ layouts get no directory suggestion.
145
+
146
+ `--no-plugins` writes the template with no plugin suggestions, even
147
+ when a lockfile is present.
110
148
 
111
149
  ## `rigor annotate`
112
150
 
113
- Reprint a file with every line tagged by the type of the
114
- expression it evaluates to, as a trailing `#=>` comment. See
115
- [Inspecting inferred types](05-inspecting-types.md).
151
+ Reprint a source file with every line tagged by the inferred type of the expression it evaluates to, as a trailing `#=>` comment (following the xmpfilter / seeing_is_believing convention). See [Inspecting inferred types](05-inspecting-types.md).
116
152
 
117
153
  ```sh
118
- rigor annotate [--[no-]color] [--[no-]bat] [--format=text|json] [--config=PATH] FILE
154
+ rigor annotate [options] FILE
119
155
  ```
120
156
 
121
- `FILE` is required. Colour is auto-detected for a tty and
122
- honours `NO_COLOR`; `--color` / `--no-color` override. When
123
- colour is on and [`bat`](https://github.com/sharkdp/bat) is on
124
- `PATH`, highlighting goes through bat (`--no-bat` opts out;
125
- `--bat` warns if bat is missing and falls back to the built-in
126
- colorizer). `--format=json` emits a `{ line => type }` map
127
- instead of the annotated source. Exit `1` on a parse error or a
128
- missing file.
157
+ | Flag | Purpose |
158
+ | --- | --- |
159
+ | `--config=PATH` | Path to `.rigor.yml`. Overrides auto-discovery. |
160
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
161
+ | `--[no-]color` | Force or disable ANSI color (default: auto-detected for a tty; honors `NO_COLOR`). |
162
+ | `--[no-]bat` | Force or disable syntax highlighting through [`bat`](https://github.com/sharkdp/bat) (default: enabled when color is on and `bat` is found on `PATH`). |
163
+
164
+ The positional `FILE` argument is required. For each line, the command locates the last expression evaluated on that line (e.g. the last statement in `1; 2; 3` reports `3`, and a conditional header `if nil` reports its condition) and annotates it with `#=> <type>`. On method definition headers (`def`), the line is annotated with the method's inferred return type rather than the header expression.
165
+
166
+ Annotations are idempotent: re-running `rigor annotate` matches existing `#=>` comments (as well as legacy `#=> dump_type:` comments) and replaces them in-place instead of stacking comments.
167
+
168
+ When color is enabled and `bat` is installed, output is syntax-highlighted via `bat --language=ruby --style=plain --paging=never`. Passing `--no-bat` disables `bat` integration in favor of Rigor's built-in Prism colorizer. `--bat` warns on stderr and falls back to the built-in colorizer if `bat` is not found on `PATH`.
169
+
170
+ In `--format=json`, the command emits a JSON object mapping line numbers to type strings conforming to schema version 1:
171
+
172
+ ```json
173
+ {
174
+ "schema_version": 1,
175
+ "annotations": {
176
+ "1": "Integer",
177
+ "2": "String | nil"
178
+ }
179
+ }
180
+ ```
181
+
182
+ Exit `0` on success, `1` if the file does not exist or contains a Prism syntax error, and `64` on usage errors (such as a missing `FILE` argument).
183
+
184
+ ## JSON documents
185
+
186
+ Every `--format=json` document Rigor defines whose top level is an object, except the three named below, starts with an integer `schema_version` (currently `1`). Where a flag selects which document is printed, `mode` follows it: `rigor coverage` prints `precision`, `protection`, `mutation` or `protection-fused`, and `rigor plugins` prints `status` or `capabilities`. The key set is contract from v1.0.0: a minor release may add a key, and removing or renaming one needs a major.
187
+
188
+ `rigor lens` (its own `schema` / `revision`), `rigor baseline dump` (`version`) and `rigor check`'s CI formats (`sarif`, `gitlab`, ...) keep their own markers. `rigor type-of`, `rigor explain` and `rigor trace` are the exceptions and still print their current top-level shape (an array; for `type-of` a flat object or `{ results }`; for `explain <skip-reason>` a flat object); the next minor release switches them to `{ "schema_version": 1, "results" | "rules" | "skip_reasons" | "events": [...] }`. Until then each prints a note on stderr, and the matching MCP tools append the same note as an extra text block.
129
189
 
130
190
  ## `rigor type-of`
131
191
 
@@ -148,7 +208,8 @@ wraps several results in a `results` array. Line queries add a
148
208
  `line_enumerations` array whose `shown` and `total` counts make
149
209
  truncation explicit. `--trace` records fail-soft fallbacks,
150
210
  after the rows of a line table in text output. The editor-mode
151
- `--tmp-file` / `--instead-of` pair is accepted as on `check`.
211
+ `--tmp-file` / `--instead-of` pair is accepted as on `check`, and a repeated flag is a usage error
212
+ (exit `64`) there too.
152
213
 
153
214
  A class or constant that another project file declares reads as
154
215
  `rigor check` types it (`singleton(User)`) when a bare
@@ -159,9 +220,13 @@ and never writes it. With no reusable snapshot (none yet, or one a
159
220
  change made unusable, for example a changed configuration, lockfile, `sig/`, `pre_eval:`
160
221
  file or required feature, an engine upgrade, or a snapshot written
161
222
  for other roots) the probe types the one file on its own, so such a name reads
162
- `Dynamic[top]`, and it prints nothing about it. Only those
163
- declarations are shared: the types `check` infers for an unannotated
164
- parameter are not.
223
+ `Dynamic[top]`, and it prints nothing about it. With
224
+ `parameter_inference: true`, the types `check` infers for an
225
+ unannotated parameter come from the same snapshot (its recorded
226
+ call-site table), so `@v = v` reads as `check` types it, but only
227
+ while no project file has changed since the snapshot (an editor
228
+ buffer whose bytes differ from the snapshot counts as a change); once any file differs, or with the setting off
229
+ or no snapshot, those types are not shared.
165
230
 
166
231
  A template a plugin compiles into Ruby (an ERB view under
167
232
  `rigor-actionpack`) is probed the way `rigor check` analyses
@@ -200,58 +265,144 @@ against `rigor check --no-cache` removes that variable.
200
265
 
201
266
  ## `rigor lens`
202
267
 
203
- Map the declarations of one or more files with their types and
204
- type provenance (experimental). See
205
- [Inspecting inferred types](05-inspecting-types.md).
268
+ Map the declarations of one or more files with their types and type provenance (experimental). See [Inspecting inferred types](05-inspecting-types.md).
206
269
 
207
270
  ```sh
208
- rigor lens [--format=text|json] [--config=PATH] [--workers=N] FILE...
271
+ rigor lens [options] FILE...
272
+ ```
273
+
274
+ | Flag | Purpose |
275
+ | --- | --- |
276
+ | `--config=PATH` | Path to `.rigor.yml`. Overrides auto-discovery. |
277
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
278
+ | `--workers=N` | Worker process count for a cold run. |
279
+ | `--claims=REQUEST` | Check documentation type claims against authored contracts (unreleased). Requires `--format=json`. |
280
+
281
+ `rigor lens` performs the same incremental analysis as `rigor check --incremental` and shares its cache (`.rigor/cache`). The first execution on a file costs a full check, while subsequent runs reuse the recorded cache.
282
+
283
+ `--claims=REQUEST` checks documentation type claims against authored contracts in the same run (unreleased). It requires `--format=json` and a UTF-8 JSON request following the [claims contract](../internal-spec/lens-claims.md). The `claims` section reports each claim's verdict; a conflict does not change the exit status. An invalid or stale request exits `64` without a document. Claims do not alter analysis, diagnostics or cached types.
284
+
285
+ ### Text representation
286
+
287
+ Text output lists each declaration preceded by an anchor identifier:
288
+
289
+ ```text
290
+ lib/shop.rb xxh3-64:845f7426b6acfe12 (2 omitted)
291
+ 4:7c90 class Shop
292
+ 8:e37e Shop#label: (::Integer n) -> ::String [contract inline]
293
+ 14:ce93 Shop#total: () -> Integer [single_site inferred]
294
+ 16:182b Shop#fetch [contested; also lib/legacy_shop.rb:2]
295
+ omitted constant Shop::LIMIT (line 5)
209
296
  ```
210
297
 
211
- A lens runs the same incremental analysis as
212
- `rigor check --incremental` and reads and writes its cache.
213
- `--workers` applies to a cold run. `--format=json` writes one
214
- `lens/v0` document ([specification](../internal-spec/lens.md)).
215
- Exit `0` when at least one file has a result, `1` when none
216
- does, `64` on a usage error; diagnostics in the code never
217
- change the exit status.
298
+ - **`LINE:HASH`**: Stable anchor hash identifying the declaration. It changes when the declaration signature or preceding type comments change, but remains unchanged across prose comment edits.
299
+ - **`provenance`**: The origin of the type in square brackets (e.g. `[contract sig]`, `[contract inline]`, `[single_site inferred]`, `[plugin <name>]`).
300
+ - **`contested`**: Denotes methods where multiple definitions exist in the project (e.g. reopened classes across files); Rigor prints no signature rather than guessing runtime dispatch.
301
+ - **`omitted`**: Constructs not yet modeled by the lens (constants, `alias`, `define_method`) are explicitly recorded as omitted rather than discarded.
302
+
303
+ In `--format=json`, the command outputs a `lens/v0` JSON document (specified in [the lens output contract](../internal-spec/lens.md)).
304
+
305
+ Exit `0` when at least one input file is successfully analysed (`status: "ok"`), `1` when no target achieves `status: "ok"` (the JSON document is still written to stdout), and `64` on usage errors (such as missing `FILE` arguments or an unreadable `--config` path). Diagnostics present in the analysed files do not affect the exit code.
218
306
 
219
307
  ## `rigor trace`
220
308
 
221
- Replay HOW the engine typed a file, step by step, as a
222
- terminal animation. It is a teaching probe over the same
223
- inference `rigor check` runs.
309
+ Replay how the engine typed a file step-by-step as an interactive terminal animation. It is a teaching and debugging probe operating over the inference engine. See [Inspecting inferred types](05-inspecting-types.md).
224
310
 
225
311
  ```sh
226
- rigor trace [--delay=SECONDS] [--line=N] [--verbose] [--format=json] FILE
312
+ rigor trace [options] FILE
227
313
  ```
228
314
 
229
- Each frame highlights the source range being evaluated next to
230
- the scope's locals and describes one inference moment: a local
231
- binding entering the scope (`bind`), branch types merging
232
- (`union`), or a method call resolving — or fail-softening to
233
- `Dynamic[top]` (`dispatch`). On a tty the replay steps on a key
234
- press (`q` quits); `--delay` autoplays. `--verbose` adds every
235
- expression enter/result frame, `--line=N` keeps only events on
236
- one line, and `--format=json` dumps the raw event stream for
237
- tooling or course material. See
238
- [Inspecting inferred types](05-inspecting-types.md).
315
+ | Flag | Purpose |
316
+ | --- | --- |
317
+ | `--config=PATH` | Path to `.rigor.yml`. Overrides auto-discovery. |
318
+ | `--delay=SECONDS` | Autoplay frames with `SECONDS` delay between steps (default: step manually on key press). |
319
+ | `--line=N` | Replay only inference events whose source range starts on line `N`. |
320
+ | `--verbose` | Include every expression `enter` and `result` frame (default: only teachable inference milestones). |
321
+ | `--format=FORMAT` | Output format: `text` (interactive terminal animation) or `json` (raw event stream). Default: `text`. |
322
+
323
+ ### Inference events
324
+
325
+ By default, the replay highlights the AST expression being evaluated alongside the current local variable environment, presenting three core inference event kinds:
326
+ - **`bind`**: A local variable binding enters the active scope.
327
+ - **`union`**: Types from distinct control-flow branches merge into a union type.
328
+ - **`dispatch`**: A method call resolves against its receiver or falls back to `Dynamic[top]`.
329
+
330
+ With `--verbose`, every AST node entry and evaluated type result is captured as a frame.
331
+
332
+ ### Interactive navigation
333
+
334
+ On a terminal TTY, the animation advances one frame per key press. Press `q` to exit playback early. Specifying `--delay=SECONDS` enables hands-free autoplay.
335
+
336
+ In `--format=json`, the raw stream of event objects is emitted as a top-level JSON array for external tooling or courseware integration. Like `rigor explain`, it prints a deprecation notice on stderr noting the upcoming `schema_version: 1` envelope migration (see [JSON documents](#json-documents)).
337
+
338
+ Exit `0` on successful playback or stream emission, `1` if the file does not exist or contains syntax errors (Prism parse errors), and `64` on usage errors (such as missing `FILE`, passing multiple files, or invalid flags).
239
339
 
240
340
  ## `rigor type-scan`
241
341
 
242
- Report `type_of` inference coverage across paths — a
243
- diagnostic-of-the-diagnoser, useful when investigating why a
244
- class infers poorly.
342
+ Report `Scope#type_of` inference coverage across paths — an engine diagnostic measuring how much of the target source is directly typed without falling back to `Dynamic[top]`.
245
343
 
246
344
  ```sh
247
- rigor type-scan PATH...
345
+ rigor type-scan [options] PATH...
248
346
  ```
249
347
 
250
- `--limit=N` caps the printed examples (default 10),
251
- `--show-recognized` includes fully-covered classes,
252
- `--threshold=RATIO` makes the command exit non-zero when the
253
- unrecognized-node ratio exceeds `RATIO`, and `--format=text|json`
254
- selects the output format. `--config=PATH` overrides config discovery.
348
+ | Flag | Purpose |
349
+ | --- | --- |
350
+ | `--config=PATH` | Path to `.rigor.yml`. Overrides auto-discovery. |
351
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
352
+ | `--limit=N` | Maximum unrecognized example events to display in text mode. Default: `10`. |
353
+ | `--show-recognized` | Include AST node classes with 0 unrecognized visits in the summary table. |
354
+ | `--threshold=RATIO` | Exit non-zero (`1`) when the ratio of unrecognized nodes (`unrecognized / visits`) exceeds `RATIO` (float). |
355
+
356
+ At least one `PATH` argument is required. The command traverses every Prism node across the specified paths and queries `Scope#type_of` against a discovery-seeded project environment (matching `rigor coverage`).
357
+
358
+ ### Output format
359
+
360
+ In text mode, `rigor type-scan` prints a summary block, class breakdown, and unrecognized examples:
361
+
362
+ ```text
363
+ Type-of scan: 1 file
364
+ - lib/user.rb
365
+
366
+ Summary:
367
+ files processed: 1
368
+ parse errors: 0
369
+ AST nodes visited: 165
370
+ unrecognized: 14 ( 8.5%)
371
+
372
+ Coverage by node class (unrecognized/visits):
373
+ Prism::CallNode 12/120 ( 10.0%)
374
+ Prism::ConstantReadNode 2/ 45 ( 4.4%)
375
+
376
+ Unrecognized examples (showing 10 of 14):
377
+ Prism::CallNode @ lib/user.rb:14:5
378
+ ```
379
+
380
+ Passing `--show-recognized` includes node classes with 0 unrecognized visits in the class breakdown. `--limit=N` caps the total number of examples shown in the unrecognized events section.
381
+
382
+ In `--format=json`, the command emits a JSON document conforming to schema version 1:
383
+
384
+ ```json
385
+ {
386
+ "schema_version": 1,
387
+ "files": [ "lib/user.rb" ],
388
+ "summary": {
389
+ "files_processed": 1,
390
+ "parse_errors": 0,
391
+ "visited": 165,
392
+ "unrecognized": 14,
393
+ "unrecognized_ratio": 0.0848
394
+ },
395
+ "by_class": {
396
+ "Prism::CallNode": { "visits": 120, "unrecognized": 12 }
397
+ },
398
+ "events": [
399
+ { "file": "lib/user.rb", "node_class": "Prism::CallNode", "family": "call", "line": 14, "column": 5 }
400
+ ],
401
+ "parse_errors": []
402
+ }
403
+ ```
404
+
405
+ Exit `0` if the unrecognized ratio is within `--threshold` (or when no threshold is set) and no parse errors occurred. Exit `1` if the ratio exceeds `--threshold` or if any scanned file contains parse errors. Exit `64` if no paths are provided, a specified path does not exist, or flags are invalid.
255
406
 
256
407
  ## `rigor effects`
257
408
 
@@ -384,6 +535,12 @@ origin/main:.rigor-effects.yml)` in a bot),
384
535
  `explain` additionally accepts `--symbol=KEY` to explain one
385
536
  specific unit instead of the changed ones.
386
537
 
538
+ `update` writes `effects.snapshot.path:` (default
539
+ `.rigor-effects.yml`). A path whose directory does not exist, or
540
+ that cannot be written, is a usage error: one
541
+ `rigor effects: cannot write PATH: REASON` line on stderr and
542
+ exit `64`. The directory is not created.
543
+
387
544
  The file records two tables. `methods:` holds each method's
388
545
  **direct** summary — what its own body does, block literals
389
546
  and catalogued callees included, but not what the project
@@ -501,38 +658,107 @@ reviewer's judgment, which is what makes it a review artefact.
501
658
 
502
659
  ## `rigor explain`
503
660
 
504
- Print the catalogue entry for a diagnostic rule, or list every
505
- rule when called with no argument.
661
+ Print the catalogue entry for a diagnostic rule, or list every rule when called with no argument.
506
662
 
507
663
  ```sh
508
- rigor explain [options] [rule]
664
+ rigor explain [options] [<rule>]
509
665
  ```
510
666
 
511
- `rule` is a rule ID (`call.undefined-method`), a legacy alias,
512
- or a family wildcard (`call`, `flow`, `def`, `assert`, `dump`, `global`,
513
- `suppression`, `static`, `effect`).
514
- `--format=text|json` selects the output format. Exit `64` for an unknown rule.
667
+ | Flag | Purpose |
668
+ | --- | --- |
669
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
670
+
671
+ ### Invocation modes
672
+
673
+ | Form | Purpose |
674
+ | --- | --- |
675
+ | (none) | Print the complete rule catalogue index (rule ID and one-line summary) followed by every `rigor sig-gen` skip reason. |
676
+ | `<rule>` | Print the full catalogue entry for a canonical rule ID (e.g. `call.undefined-method`) or a legacy alias (e.g. `undefined-method`). |
677
+ | `<family>` | Family wildcard (`call`, `flow`, `def`, `assert`, `dump`, `global`, `suppression`, `static`, `effect`). Prints every rule under that prefix. |
678
+ | `<skip-reason>` | Print the explanation and recommended next action for an exact `rigor sig-gen` skip reason token (e.g. `sig.skipped.untyped-return`). Does not accept wildcards. |
679
+
680
+ A rule entry includes its summary, legacy aliases, authored severity, severity mapped across configuration profiles (`lenient`, `balanced`, `strict`), evidence tier, `Fires when:` conditions, `Does not fire when:` exceptions, inline suppression syntax (`# rigor:disable <rule>`), documentation link, and the Rigor release where the rule was introduced.
681
+
682
+ A `<skip-reason>` token explains why `rigor sig-gen` declined to write a signature (such as dynamic returns or unbounded blocks) and suggests what to address instead. Because skip reasons report signatures that were not written rather than code findings, they carry no severity and nothing to suppress.
683
+
684
+ In `--format=json`, diagnostic rules (whether requested by a single rule ID, legacy alias, family wildcard, or with no arguments) always emit a JSON array of catalogue entry objects. In contrast, querying an individual `<skip-reason>` emits a single JSON object. In the index mode with no arguments, `--format=json` outputs only the diagnostic rule array; individual skip reasons must be queried by token. Top-level JSON shapes currently print a stderr deprecation notice regarding the future envelope migration (see [JSON documents](#json-documents)).
515
685
 
516
- It also answers the `sig.skipped.*` identifiers `rigor sig-gen`
517
- prints when it declines to write a signature — `rigor explain
518
- sig.skipped.untyped-return` says what the skip means and what to do
519
- instead. Those are not diagnostic rules, so they are rendered without
520
- a severity or a suppression line.
686
+ Exit `0` on success, `64` on an unknown rule name or invalid options.
521
687
 
522
688
  ## `rigor diff`
523
689
 
524
- Compare the current diagnostics against a saved baseline JSON
525
- and report only what is new.
690
+ Compare current diagnostics against a saved `rigor check --format=json` result and report the delta.
691
+
692
+ ```sh
693
+ rigor diff [options] <rigor-check.json> [paths...]
694
+ ```
695
+
696
+ | Flag | Purpose |
697
+ | --- | --- |
698
+ | `--current=PATH` | Compare against a saved diagnostics JSON file instead of executing a fresh `rigor check` run. |
699
+ | `--config=PATH` | Custom `.rigor.yml` configuration forwarded to the implicit `rigor check` run. Ignored when `--current` is supplied. |
700
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
701
+
702
+ The positional `<rigor-check.json>` file is required. Per ADR-126 § WD1, this file is a saved `rigor check --format=json` result or JSON snapshot, distinct from the rule-bucket `.rigor-baseline.yml` managed by `rigor baseline`.
703
+
704
+ When `[paths...]` are given, only those source paths are evaluated by the implicit check. Note that if `<rigor-check.json>` covers the entire repository, restricting the implicit check to a subset of paths causes all diagnostics in omitted files to be reported as `- FIXED`. When `--current=PATH` is provided, `[paths...]` are ignored.
705
+
706
+ ### Identity matching
707
+
708
+ Diagnostic identity is determined by the 6-element tuple:
709
+
710
+ ```
711
+ (path, line, column, rule, source_family, message)
712
+ ```
713
+
714
+ Diagnostics matching a saved tuple are considered pre-existing. Current diagnostics missing from the saved snapshot are classified as `+ NEW` (regressions). Saved diagnostics missing from the current run are classified as `- FIXED` (progress). Moving code to another line or changing a message produces one fixed and one new entry. Severity is not part of the identity tuple; changing a diagnostic's severity without moving its location does not register as a diff.
715
+
716
+ ### Output format
717
+
718
+ Text output reports the diff header, line-by-line additions and removals, and summary counts:
719
+
720
+ ```text
721
+ # diff against rigor-check.json (10 baseline / 9 current)
722
+ + NEW lib/user.rb:14:5 [call.undefined-method] Method `full_name` does not exist on `User`
723
+ - FIXED lib/auth.rb:22:9 [call.argument-type-mismatch] Expected `String`, got `Integer`
724
+
725
+ 1 new, 1 fixed
726
+ ```
727
+
728
+ With `--format=json`, the output conforms to schema version 1:
729
+
730
+ ```json
731
+ {
732
+ "schema_version": 1,
733
+ "baseline": "rigor-check.json",
734
+ "baseline_count": 10,
735
+ "current_count": 9,
736
+ "new": [ ... ],
737
+ "fixed": [ ... ]
738
+ }
739
+ ```
740
+
741
+ ### CI integration
742
+
743
+ `rigor diff` enables regression gating in repositories adopting Rigor incrementally by catching newly introduced diagnostics without requiring all legacy diagnostics to be fixed first.
744
+
745
+ In standard CI workflows, commit a saved snapshot in-tree (e.g. `rigor-check.json`) generated by `rigor check --format=json > rigor-check.json`. In CI, run:
526
746
 
527
747
  ```sh
528
- rigor diff [options] <baseline.json> [paths...]
748
+ rigor diff rigor-check.json
529
749
  ```
530
750
 
531
- `--current=PATH` compares against a saved diagnostics JSON
532
- instead of running a fresh check. `--format=text|json` selects the
533
- output format (default `text`), and `--config=PATH` passes a custom
534
- configuration to the implicit check. Exit `1` when new
535
- diagnostics appear.
751
+ When comparing against a dynamic base branch in a multi-step job (where `rigor check` on the base branch may exit non-zero due to pre-existing diagnostics), guard the exit code under `set -e`:
752
+
753
+ ```sh
754
+ # On base branch (or via git show / artifact)
755
+ rigor check --format=json > /tmp/base-check.json || true
756
+
757
+ # On PR branch
758
+ rigor diff /tmp/base-check.json
759
+ ```
760
+
761
+ Exit `0` when no new diagnostics appear (`new` is empty, even if `fixed` contains entries). Exit `1` when any new diagnostic of any severity (`error`, `warning`, or `info`) appears. Exit `64` if either JSON file is missing, contains invalid JSON, or command usage is incorrect.
536
762
 
537
763
  ## `rigor sig-gen`
538
764
 
@@ -571,10 +797,27 @@ refuse a file, so a tighter return `--write` declines without
571
797
  `--overwrite` does not fail it; `--check --overwrite` counts one.
572
798
  See [handbook chapter 11](../handbook/11-sig-gen.md#keeping-sig-current-in-ci).
573
799
 
800
+ `--write` also repairs a header in any file under the signature directory it writes to (the directory
801
+ under the project root named like the last path component of the first `signature_paths:` entry, `sig/` by
802
+ default, vendored or generated `.rbs` under it included) that opens a generic class such as
803
+ `class Array` or `class Hash` without its type parameters (an earlier release wrote them), reporting
804
+ `repaired N generic header(s)`, and `--check` fails until it is written. In `--format=json` the row of a file
805
+ no method of the run maps to has `"source": null`. A class is repaired in all of its bare headers or in none:
806
+ when the body of any of them names a type spelt like one of the parameters (`def old: () -> E`), which the
807
+ parameter would capture, or one of them is in another `signature_paths:` entry, which `--write` never
808
+ rewrites, none is repaired. A `generic header left bare` line (`left_bare` in `--format=json`) names the files
809
+ and the reason, and `--check` does not fail on it. Write the name as `::E`, or fix the other file, to let the
810
+ repair through.
811
+
574
812
  `--write` names each proposal it skipped (a member `sig/` already declares, an `alias` included, or one that
575
813
  routes to a signature path it does not write to, because it is declared there or its class lives there) on its own `skipped Class#method (reason)` line; the exit status is
576
814
  unchanged. See [handbook chapter 11](../handbook/11-sig-gen.md#safety-what---write-will-and-will-not-do).
577
815
 
816
+ A `sig/` target `--write` cannot write — a permission denial, a directory sitting where the `.rbs`
817
+ belongs, or a parent path that is a regular file — is a usage error: one `rigor sig-gen: cannot
818
+ write PATH: REASON` line on stderr and exit `64`. A missing parent directory is created as before.
819
+ The run stops at the first failure; targets already written stay written.
820
+
578
821
  A method declared inline with `# @rbs` / `#:` is written as that
579
822
  declaration, not as what its body infers; a parameter-only
580
823
  annotation keeps its parameters and takes the return from the body,
@@ -614,7 +857,13 @@ would take every other type in the file down with it. Under
614
857
  **refused** (the existing file is left untouched) and the command
615
858
  exits `1`: you asked for a write and did not get one. Such a skip
616
859
  is a bug in Rigor's RBS rendering, not in your code — please
617
- report it.
860
+ report it. An existing target that does not parse as RBS in the
861
+ first place, and that sig-gen would write into, is refused the same
862
+ way, named on stdout as `REFUSED` with the parse location (the JSON
863
+ action is `skipped_unparseable`); `--check` fails on it too, so such a
864
+ target is never reported as up to date. An unparseable `sig/` file that
865
+ no emittable candidate maps to is not refused and still passes
866
+ `--check`. Fix its syntax error and re-run.
618
867
 
619
868
  A method the generator declined for any other reason (a body
620
869
  that proves only `untyped`, a user-authored declaration it will
@@ -634,10 +883,20 @@ Run the Language Server over stdio. See
634
883
  rigor lsp [--transport=stdio] [--log=PATH] [--config=PATH]
635
884
  ```
636
885
 
637
- `stdio` is the only transport in v1. `--log=PATH` is accepted
638
- but not yet wired in this release — it is reserved for routing
639
- the server's wire log to a file; until then logging stays on
640
- stderr.
886
+ `stdio` is the only transport in v1. `--log=PATH` appends the LSP
887
+ wire log to PATH: each JSON-RPC message the client sends (`<-`) and
888
+ each one the server writes (`->`), one line per message with a
889
+ timestamp, plus the server's debug output from the start of the
890
+ server loop to its end. The file is opened for appending before the
891
+ server starts, so an unwritable PATH exits with a usage error. An
892
+ exception that escapes the loop is written to the file with its
893
+ backtrace. Output written before the loop starts (configuration
894
+ loading and server setup) and any crash outside the loop still go to
895
+ stderr, as does the server's output when the file becomes unwritable
896
+ mid-session. A message the JSON encoder rejects (raw invalid UTF-8) is
897
+ logged in a scrubbed form. Without `--log`, no wire log is written and
898
+ the server's debug output goes to stderr; stdout carries only the
899
+ protocol.
641
900
 
642
901
  ## `rigor baseline`
643
902
 
@@ -661,6 +920,18 @@ rigor baseline <generate|regenerate|dump|drift|prune> [options]
661
920
  `--baseline=PATH` to read a non-default baseline file. `dump`
662
921
  also accepts `--format=text|json` for machine-readable output.
663
922
 
923
+ A write target — `generate` and `regenerate`'s `--output`, or the
924
+ `--baseline` file `prune` rewrites — whose directory does not exist,
925
+ or that cannot be written, is a usage error: one
926
+ `rigor baseline: cannot write PATH: REASON` line on stderr and exit
927
+ `64`. The directory is not created.
928
+
929
+ No subcommand takes a positional path: `generate` and `regenerate`
930
+ analyse the configured `paths:`, `drift` and `prune` audit them, and
931
+ `dump` reads the baseline file and filters with `--rule`/`--file`. A path argument is a usage error
932
+ (exit `64`). Narrow the scope with `paths:` in the configuration, not on
933
+ the command line.
934
+
664
935
  ## `rigor triage`
665
936
 
666
937
  Summarise a diagnostic stream — rule distribution, **class/method
@@ -1011,30 +1282,50 @@ toolchain, so you can read a real, working plugin as a worked
1011
1282
  example when authoring your own.
1012
1283
 
1013
1284
  ```sh
1014
- rigor plugin <list|path|print|root> [name]
1285
+ rigor plugin [<name>] [--path <name>] [--list] [--root]
1015
1286
  ```
1016
1287
 
1017
- | Subcommand | Purpose |
1288
+ The positional slot is a plugin *name*; alternative outputs are
1289
+ flags, the same grammar as `rigor skill` and `rigor docs`.
1290
+
1291
+ | Form | Purpose |
1018
1292
  | --- | --- |
1019
- | `list` | Table of every bundled plugin and example, name + absolute directory path (default when no subcommand given). |
1020
- | `path <name>` | One-line absolute path to the plugin's directory. |
1021
- | `print <name>` | A header (dir / lib / sig / README paths) followed by the plugin's main source body inlined. |
1022
- | `root` | The `rigortype` gem root and its key subdirectories. |
1293
+ | `rigor plugin`, `--list` | Table of every bundled plugin and example, name + absolute directory path (the default). |
1294
+ | `<name>` | A header (dir / lib / sig / README paths) followed by the plugin's main source body inlined. |
1295
+ | `--path <name>` | One-line absolute path to the plugin's directory. |
1296
+ | `--root` | The `rigortype` gem root and its key subdirectories. |
1297
+
1298
+ The earlier verbs `list`, `path <name>`, `print <name>` and `root`
1299
+ still work for one minor release and print a deprecation notice on
1300
+ stderr naming the replacement; the output on stdout is the same.
1023
1301
 
1024
1302
  Paths resolve at runtime from the gem location (a documented
1025
1303
  caveat for container / cross-filesystem setups).
1026
1304
 
1027
1305
  ## `rigor playground`
1028
1306
 
1029
- Start the browser playground (a CodeMirror editor with
1030
- real-time diagnostics). Requires the separate `rigor-playground`
1031
- gem; if it is not installed the command prints an install hint
1032
- and exits `64`.
1307
+ Start a local browser playground (a CodeMirror editor with real-time Rigor diagnostics and type inspection).
1033
1308
 
1034
1309
  ```sh
1035
- rigor playground
1310
+ rigor playground [options]
1311
+ ```
1312
+
1313
+ | Flag | Purpose |
1314
+ | --- | --- |
1315
+ | `--port=PORT` | Port to bind the playground HTTP server to (default: `9393`). |
1316
+ | `--no-open` | Do not automatically open the playground URL in the default browser on launch. |
1317
+
1318
+ The playground requires the separate `rigor-playground` companion gem. When the gem is not installed in the active Ruby environment, the command prints an installation hint on stderr and exits `64` (`CLI::EXIT_USAGE`):
1319
+
1320
+ ```text
1321
+ rigor playground requires the rigor-playground gem.
1322
+ Install it with: gem install rigor-playground
1036
1323
  ```
1037
1324
 
1325
+ When installed, the command boots the local web server and opens an interactive browser playground where Ruby code can be written, edited, and analyzed with immediate feedback.
1326
+
1327
+ Exit `0` on clean shutdown, `1` if server startup fails (e.g. port already in use), and `64` if `rigor-playground` is missing or on usage errors.
1328
+
1038
1329
  ## `rigor skill`
1039
1330
 
1040
1331
  List or print the bundled Agent Skills shipped inside the
@@ -1092,24 +1383,27 @@ they now read as an unknown skill. Use the forms above.
1092
1383
 
1093
1384
  ## `rigor describe`
1094
1385
 
1095
- Top-level alias for [`rigor skill describe`](#rigor-skill) — the
1096
- onboarding entry point that recommends the next skill for this project.
1097
- A bare `rigor describe` is the intuitive guess most users reach for
1098
- first, so it is surfaced as its own command ([ADR-73](../adr/73-skill-driven-user-experience.md)
1099
- § WD2).
1386
+ Top-level alias for [`rigor skill describe`](#rigor-skill) — the onboarding entry point that assesses the project's state and recommends the next skill to run ([ADR-73](../adr/73-skill-driven-user-experience.md) § WD2).
1100
1387
 
1101
1388
  ```sh
1102
- rigor describe
1389
+ rigor describe [--deep]
1103
1390
  ```
1104
1391
 
1105
- It reports a presence-only project-state probe (does a `.rigor.yml`,
1106
- `.rigor-baseline.yml`, `sig/` directory, or CI integration exist?) and a
1107
- recommended next skill. It is read-only and side-effect-free — it never
1108
- runs `rigor check`. Identical output to `rigor skill describe`.
1392
+ | Flag | Purpose |
1393
+ | --- | --- |
1394
+ | `--deep` | Run `rigor check` first and route the headline recommendation on the diagnostic results. |
1395
+
1396
+ ### Audit modes
1397
+
1398
+ By default, `rigor describe` performs a presence-only probe checking for the existence of `.rigor.yml`, `.rigor-baseline.yml`, a `sig/` directory, and CI configurations. This default mode is read-only, fast, and side-effect-free — it never executes `rigor check` and never reads or writes `.rigor/cache`.
1109
1399
 
1110
- `rigor describe --deep` forwards to
1111
- [`rigor skill describe --deep`](#rigor-skill), which opts into running the
1112
- check first — slow, and it writes the cache.
1400
+ Passing `--deep` opts into running `rigor check` before generating recommendations, routing based on analysis health:
1401
+ - **`rigor-doctor`**: Selected if the RBS environment builds with 0 classes or any `configuration-error` diagnostic is raised.
1402
+ - **`rigor-monkeypatch-resolve`**: Selected if at least one third of remaining error diagnostics stem from calls resolving to project-side reopened core or gem definitions (addressable via `pre_eval:`).
1403
+ - **`rigor-baseline-reduce`**: Selected when remaining error diagnostics exist.
1404
+ - **Presence-only recommendation**: Retained when the analysis is completely clean or when no configuration exists yet.
1405
+
1406
+ Output is identical to `rigor skill describe`. Exit `0` on completion, or `64` on invalid options.
1113
1407
 
1114
1408
  ## `rigor docs`
1115
1409
 
@@ -1175,9 +1469,10 @@ Queued today:
1175
1469
  | --- | --- | --- |
1176
1470
  | `reject-unparseable-signatures` | severity | A broken RBS set **fails the run** instead of degrading it silently: an unparseable `.rbs` under `signature_paths:` (`rbs.coverage.quarantined-signature` → `error`), a declaration that collides on resolve and collapses the whole environment (`rbs.coverage.environment-build-failed` → `error`), and a duplicate method definition that collapses one class's method surface (`rbs.coverage.definition-build-failed` → `error`). |
1177
1471
  | `use-of-void-value` | severity | Using a value recovered from an author-declared `-> void` return in value context is reported as `static.value-use.void` (`warning`). |
1472
+ | `plugin-api-surface` | severity | In plugin source (a class that inherits `Rigor::Plugin::Base`), a call on a `Rigor::Plugin` interface value (a hook's `scope`, `scope.environment`, ...) to a member the interface does not declare is reported as `plugin.api-surface` (`warning`). |
1178
1473
  | `discovery-seeded-mutation-sites` | behaviour | [`rigor coverage --protection --mutation`](15-type-protection-coverage.md) measures against the same cross-file project discovery Tier 1 already uses — both when picking the sites and when deciding whether a breakage was caught — so a call on a project class declared in a *sibling* file is measured instead of dropped, and a breakage there can actually be caught. **Adds sites to the denominator, so the reported effectiveness ratio moves** — check it against any `--threshold` you pin in CI before adopting. |
1179
1474
  | `dependent-closure-kill-oracle` | behaviour | [`rigor coverage --protection --mutation`](15-type-protection-coverage.md) counts a breakage as caught when the diagnostic appears anywhere in the mutated file **or the files that depend on it**, instead of in the mutated file alone — so changing what a method returns counts as caught when the error lands in its callers. Can only **add** kills, so the ratio moves up or not at all; it costs about a third more wall time per mutant, and a ratio measured under it is not comparable with one measured without it. |
1180
- | `effects-on-by-default` | behaviour | A project whose `.rigor.yml` carries no [`effects:`](03-configuration.md#effect-labels) key at all is treated as if it had written `effects: {}` — [effect collection](03-configuration.md#effect-labels), the `rigor effects` verbs' cache sharing, and `effects.check` all turn on with every sub-key at its default. Writing `effects: false` explicitly still opts out. **Scheduled to graduate at v0.4.0** ([ADR-103](../adr/103-effect-labels.md) § WD15) rather than at the next major — an owner ruling specific to this feature, ahead of the general v1.0.0 majors-only cadence below. |
1475
+ | `effects-on-by-default` | behaviour | A project whose `.rigor.yml` carries no [`effects:`](03-configuration.md#effect-labels) key at all is treated as if it had written `effects: {}` — [effect collection](03-configuration.md#effect-labels), the `rigor effects` verbs' cache sharing, and `effects.check` all turn on with every sub-key at its default. Writing `effects: false` explicitly still opts out. **Scheduled to graduate at v0.5.0** ([ADR-103](../adr/103-effect-labels.md) § WD15, moved from v0.4.0 by § WD18) rather than at the next major — an owner ruling specific to this feature, ahead of the general v1.0.0 majors-only cadence below. |
1181
1476
 
1182
1477
  Once a feature **graduates** — it becomes the default at a major
1183
1478
  ([ADR-50](../adr/50-release-engineering-and-stability-strategy.md) § WD7)
@@ -1262,18 +1557,65 @@ Exits `1` when any check fails, `0` when all pass.
1262
1557
 
1263
1558
  ## `rigor upgrade`
1264
1559
 
1265
- Migration command skeleton ([ADR-50](../adr/50-release-engineering-and-stability-strategy.md)
1266
- WD7). The real body lands when a concrete backwards-compatibility
1267
- break gives it a target (e.g. re-running `baseline regenerate`
1268
- against a strengthened default profile, surfacing renamed
1269
- suppression ids, reporting `bleeding_edge:` graduations).
1560
+ Migration command skeleton for major version transitions and breaking changes ([ADR-50](../adr/50-release-engineering-and-stability-strategy.md) § WD7).
1270
1561
 
1271
1562
  ```sh
1272
1563
  rigor upgrade
1273
1564
  ```
1274
1565
 
1275
- Until then it prints the current version and notes that upgrade is
1276
- queued. Exits `0`.
1566
+ ### Design and future role
1567
+
1568
+ The upgrade command is designed to automate project migrations when major version transitions or breaking changes occur:
1569
+ - **Baseline regeneration**: Re-running `rigor baseline regenerate` against strengthened default rules or profiles.
1570
+ - **Suppression updates**: Surfacing and updating renamed or deprecated inline `# rigor:disable` rules.
1571
+ - **Bleeding-edge consolidation**: Reporting and removing graduated features from `bleeding_edge:` configurations.
1572
+
1573
+ ### Current status
1574
+
1575
+ Until a backward-incompatible release provides an active migration target, the command serves as a placeholder. It prints the current Rigor version and reports that migrations are queued:
1576
+
1577
+ ```text
1578
+ rigor upgrade: No migration target available yet (ADR-50 WD7, queued).
1579
+ Current version: <version>
1580
+ ```
1581
+
1582
+ Exit `0` on completion.
1583
+
1584
+ ## `rigor help`
1585
+
1586
+ ```sh
1587
+ rigor help # the command list
1588
+ rigor help <command> # the same as `rigor <command> --help`
1589
+ rigor help --format=json # every command, its flags and formats
1590
+ ```
1591
+
1592
+ `rigor help --format=json` prints the whole CLI surface as one JSON
1593
+ document, for tools and agents that would otherwise parse the help
1594
+ text:
1595
+
1596
+ ```json
1597
+ { "schema_version": 1, "version": "…", "commands": [ … ] }
1598
+ ```
1599
+
1600
+ Each entry of `commands` has:
1601
+
1602
+ | Key | Value |
1603
+ | --- | --- |
1604
+ | `name` | The command name. |
1605
+ | `summary` | The one-line description `rigor help` prints. |
1606
+ | `usage` | The usage line (`Usage: rigor …`). |
1607
+ | `flags` | The command's own switches, each `{ "name", "arg", "optional", "desc" }`. `arg` is the argument's name, `null` for a switch that takes none, and `optional` is `true` when the argument may be left out (`--bleeding-edge[=LIST]`). `--[no-]x` is listed as the two flags `--x` and `--no-x`. |
1608
+ | `formats` | The values the command's own `--format` accepts, empty when it has none. A command that only has subcommands with the flag (`baseline`) lists none here. |
1609
+ | `subcommands` | One `{ "name", "usage", "flags", "formats" }` object per subcommand (`baseline dump`, `effects check`, `skill describe`), with the same meaning as above and empty arrays where the subcommand has nothing to list. |
1610
+ | `mcp_tool` | The [MCP](10-mcp-server.md) tool that mirrors the command, or `null`. |
1611
+
1612
+ The list covers every command above plus `help` and `version`. The
1613
+ text help is rendered from the same list.
1614
+
1615
+ `rigor help <command>` takes one command: a second word
1616
+ (`rigor help baseline drift`) is refused with exit `64`, and an
1617
+ unknown command exits `64` too. Flags after the command name are
1618
+ ignored rather than parsed.
1277
1619
 
1278
1620
  ## Environment variables
1279
1621