rigortype 0.3.8 → 0.3.9

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 (447) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
  5. data/docs/handbook/02-everyday-types.md +17 -14
  6. data/docs/handbook/03-narrowing.md +37 -4
  7. data/docs/handbook/05-methods-and-blocks.md +1 -1
  8. data/docs/handbook/06-classes.md +2 -2
  9. data/docs/handbook/07-rbs-and-extended.md +14 -1
  10. data/docs/handbook/11-sig-gen.md +72 -1
  11. data/docs/handbook/README.md +1 -1
  12. data/docs/handbook/appendix-elixir.md +2 -2
  13. data/docs/handbook/appendix-go.md +2 -2
  14. data/docs/handbook/appendix-java-csharp.md +2 -2
  15. data/docs/handbook/appendix-mypy.md +2 -2
  16. data/docs/handbook/appendix-phpstan.md +1 -1
  17. data/docs/handbook/appendix-rust.md +1 -1
  18. data/docs/handbook/appendix-type-theory.md +4 -4
  19. data/docs/handbook/appendix-typescript.md +1 -1
  20. data/docs/llms.txt +2 -0
  21. data/docs/manual/02-cli-reference.md +32 -4
  22. data/docs/manual/03-configuration.md +11 -0
  23. data/docs/manual/04-diagnostics.md +11 -0
  24. data/docs/manual/07-plugins.md +15 -5
  25. data/docs/manual/08-skills.md +23 -2
  26. data/docs/manual/11-ci.md +9 -0
  27. data/docs/manual/15-type-protection-coverage.md +8 -0
  28. data/docs/manual/16-rbs-extended-annotations.md +6 -1
  29. data/docs/manual/18-removing-dead-code.md +10 -8
  30. data/docs/manual/plugins/rigor-actionpack.md +18 -0
  31. data/docs/manual/plugins/rigor-activerecord.md +28 -0
  32. data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
  33. data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
  34. data/lib/rigor/analysis/baseline.rb +2 -2
  35. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  36. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  37. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  38. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  40. data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
  41. data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
  42. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  43. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  44. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  45. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  46. data/lib/rigor/analysis/check_rules.rb +200 -32
  47. data/lib/rigor/analysis/crash_signature.rb +8 -24
  48. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  49. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  50. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  51. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  52. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  53. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  54. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  55. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  56. data/lib/rigor/analysis/fact_store.rb +6 -1
  57. data/lib/rigor/analysis/incremental.rb +16 -0
  58. data/lib/rigor/analysis/incremental_session.rb +81 -22
  59. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  60. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  61. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
  62. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  63. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  64. data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
  65. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  66. data/lib/rigor/analysis/result.rb +1 -3
  67. data/lib/rigor/analysis/rule_catalog.rb +4 -1
  68. data/lib/rigor/analysis/run_cache_key.rb +1 -1
  69. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  70. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  71. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  72. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +177 -26
  73. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  74. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
  75. data/lib/rigor/analysis/runner/pool_coordinator.rb +256 -88
  76. data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
  77. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  78. data/lib/rigor/analysis/runner.rb +133 -35
  79. data/lib/rigor/analysis/worker_session.rb +9 -10
  80. data/lib/rigor/bleeding_edge.rb +12 -24
  81. data/lib/rigor/builtins/hkt_builtins.rb +2 -2
  82. data/lib/rigor/builtins/imported_refinements.rb +155 -21
  83. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  84. data/lib/rigor/builtins/regex_refinement.rb +4 -4
  85. data/lib/rigor/builtins/static_return_refinements.rb +5 -6
  86. data/lib/rigor/cache/annotation_location.rb +2 -4
  87. data/lib/rigor/cache/descriptor.rb +58 -20
  88. data/lib/rigor/cache/engine_source.rb +4 -4
  89. data/lib/rigor/cache/file_digest.rb +9 -1
  90. data/lib/rigor/cache/incremental_snapshot.rb +40 -6
  91. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  92. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  93. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  94. data/lib/rigor/cache/rbs_descriptor.rb +79 -11
  95. data/lib/rigor/cache/rbs_environment.rb +8 -4
  96. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  97. data/lib/rigor/cache/store.rb +77 -32
  98. data/lib/rigor/cli/annotate_command.rb +5 -6
  99. data/lib/rigor/cli/check_command.rb +57 -10
  100. data/lib/rigor/cli/check_invocation.rb +6 -11
  101. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  102. data/lib/rigor/cli/coverage_command.rb +1 -1
  103. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  104. data/lib/rigor/cli/coverage_scan.rb +1 -5
  105. data/lib/rigor/cli/diff_command.rb +1 -1
  106. data/lib/rigor/cli/doc_links.rb +3 -3
  107. data/lib/rigor/cli/docs_command.rb +2 -2
  108. data/lib/rigor/cli/doctor_command.rb +85 -38
  109. data/lib/rigor/cli/effects_command.rb +2 -2
  110. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  111. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  112. data/lib/rigor/cli/explain_command.rb +34 -1
  113. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  114. data/lib/rigor/cli/lsp_command.rb +1 -1
  115. data/lib/rigor/cli/mcp_command.rb +1 -1
  116. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  117. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  118. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  119. data/lib/rigor/cli/plugin_command.rb +1 -1
  120. data/lib/rigor/cli/plugins_command.rb +2 -1
  121. data/lib/rigor/cli/prism_colorizer.rb +2 -2
  122. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  123. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  124. data/lib/rigor/cli/sig_gen_command.rb +2 -2
  125. data/lib/rigor/cli/skill_command.rb +1 -1
  126. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  127. data/lib/rigor/cli/skill_describe.rb +36 -35
  128. data/lib/rigor/cli/trace_command.rb +4 -4
  129. data/lib/rigor/cli/trace_renderer.rb +5 -6
  130. data/lib/rigor/cli/triage_command.rb +1 -1
  131. data/lib/rigor/cli/type_of_command.rb +10 -6
  132. data/lib/rigor/cli/type_scan_command.rb +4 -4
  133. data/lib/rigor/cli/unused_command.rb +11 -3
  134. data/lib/rigor/cli/upgrade_command.rb +1 -1
  135. data/lib/rigor/cli.rb +68 -6
  136. data/lib/rigor/config_audit.rb +30 -5
  137. data/lib/rigor/configuration/severity_profile.rb +6 -6
  138. data/lib/rigor/configuration.rb +43 -4
  139. data/lib/rigor/effects/attribution.rb +1 -3
  140. data/lib/rigor/effects/config_envelopes.rb +6 -8
  141. data/lib/rigor/effects/definition_lines.rb +25 -6
  142. data/lib/rigor/effects/effect_table.rb +0 -1
  143. data/lib/rigor/effects/entry_points.rb +0 -2
  144. data/lib/rigor/effects/envelope_check.rb +8 -8
  145. data/lib/rigor/effects/envelope_index.rb +5 -8
  146. data/lib/rigor/effects/framework_units.rb +4 -6
  147. data/lib/rigor/effects/identity.rb +4 -6
  148. data/lib/rigor/effects/inline_anchor.rb +7 -7
  149. data/lib/rigor/effects/label_intent.rb +3 -4
  150. data/lib/rigor/effects/liskov_check.rb +8 -8
  151. data/lib/rigor/effects/method_key.rb +1 -1
  152. data/lib/rigor/effects/plugin_facts.rb +4 -6
  153. data/lib/rigor/effects/propagator.rb +2 -3
  154. data/lib/rigor/effects/scanner.rb +1 -1
  155. data/lib/rigor/effects/signature_sources.rb +3 -5
  156. data/lib/rigor/effects/snapshot.rb +10 -10
  157. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  158. data/lib/rigor/effects/unit_scan.rb +10 -10
  159. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  160. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  161. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  162. data/lib/rigor/environment/class_registry.rb +3 -1
  163. data/lib/rigor/environment/failure_slot.rb +2 -2
  164. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  165. data/lib/rigor/environment/lockfile_resolver.rb +51 -4
  166. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  167. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  168. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  169. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  170. data/lib/rigor/environment/rbs_loader.rb +388 -59
  171. data/lib/rigor/environment.rb +105 -20
  172. data/lib/rigor/flow_contribution/merger.rb +0 -2
  173. data/lib/rigor/flow_contribution.rb +11 -13
  174. data/lib/rigor/inference/acceptance.rb +153 -8
  175. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  176. data/lib/rigor/inference/body_fixpoint.rb +5 -5
  177. data/lib/rigor/inference/budget_trace.rb +1 -5
  178. data/lib/rigor/inference/captured_locals.rb +2 -3
  179. data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
  180. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  181. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  182. data/lib/rigor/inference/element_read_widening.rb +168 -0
  183. data/lib/rigor/inference/expression_typer.rb +763 -143
  184. data/lib/rigor/inference/fork_map.rb +5 -7
  185. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  186. data/lib/rigor/inference/hkt_registry.rb +4 -7
  187. data/lib/rigor/inference/index_write_widening.rb +1 -5
  188. data/lib/rigor/inference/macro_block_self_type.rb +1 -4
  189. data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
  190. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  191. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  192. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  193. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  194. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
  195. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  196. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  197. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  198. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  199. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
  200. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  201. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
  202. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  203. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  204. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  205. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  206. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
  207. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  208. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  209. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
  210. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  211. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  212. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  213. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  214. data/lib/rigor/inference/method_dispatcher.rb +12 -13
  215. data/lib/rigor/inference/method_parameter_binder.rb +4 -6
  216. data/lib/rigor/inference/multi_target_binder.rb +1 -3
  217. data/lib/rigor/inference/mutation_rejoin.rb +168 -0
  218. data/lib/rigor/inference/mutation_widening.rb +100 -51
  219. data/lib/rigor/inference/narrowing.rb +299 -46
  220. data/lib/rigor/inference/optimistic_origin.rb +1 -7
  221. data/lib/rigor/inference/origin_lookup.rb +2 -3
  222. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  223. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  224. data/lib/rigor/inference/precision_scanner.rb +3 -4
  225. data/lib/rigor/inference/project_patched_methods.rb +2 -2
  226. data/lib/rigor/inference/project_patched_scanner.rb +3 -3
  227. data/lib/rigor/inference/protection_scanner.rb +1 -2
  228. data/lib/rigor/inference/range_constant.rb +57 -0
  229. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  230. data/lib/rigor/inference/receiver_alias.rb +3 -3
  231. data/lib/rigor/inference/refinement_mutation.rb +72 -0
  232. data/lib/rigor/inference/scope_indexer.rb +688 -151
  233. data/lib/rigor/inference/statement_evaluator.rb +221 -50
  234. data/lib/rigor/inference/string_mutation.rb +60 -0
  235. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  236. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  237. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  238. data/lib/rigor/inference/version_guard.rb +28 -21
  239. data/lib/rigor/inference/void_origin.rb +3 -3
  240. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  241. data/lib/rigor/language_server/buffer_table.rb +4 -4
  242. data/lib/rigor/language_server/completion_provider.rb +1 -1
  243. data/lib/rigor/language_server/debouncer.rb +1 -1
  244. data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
  245. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  246. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  247. data/lib/rigor/language_server/hover_provider.rb +1 -1
  248. data/lib/rigor/language_server/hover_renderer.rb +2 -2
  249. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  250. data/lib/rigor/language_server/project_context.rb +2 -5
  251. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  252. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  253. data/lib/rigor/language_server/server.rb +9 -9
  254. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  255. data/lib/rigor/language_server/uri.rb +1 -1
  256. data/lib/rigor/plugin/base.rb +90 -21
  257. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  258. data/lib/rigor/plugin/effect_attribution.rb +7 -7
  259. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  260. data/lib/rigor/plugin/fact_store.rb +6 -6
  261. data/lib/rigor/plugin/io_boundary.rb +93 -8
  262. data/lib/rigor/plugin/isolation.rb +29 -6
  263. data/lib/rigor/plugin/loader.rb +31 -12
  264. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  265. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  266. data/lib/rigor/plugin/manifest.rb +23 -1
  267. data/lib/rigor/plugin/registry.rb +42 -12
  268. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
  269. data/lib/rigor/plugin/trust_policy.rb +2 -4
  270. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  271. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  272. data/lib/rigor/project_environment.rb +138 -0
  273. data/lib/rigor/protection/analysis_guard.rb +93 -14
  274. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  275. data/lib/rigor/protection/dependency_closure.rb +5 -8
  276. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  277. data/lib/rigor/protection/discovery_seed.rb +11 -11
  278. data/lib/rigor/protection/kill_signature.rb +2 -4
  279. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  280. data/lib/rigor/protection/mutation_cache.rb +6 -9
  281. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  282. data/lib/rigor/protection/mutator.rb +2 -1
  283. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  284. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  285. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  286. data/lib/rigor/rbs_extended/reporter.rb +36 -10
  287. data/lib/rigor/rbs_extended.rb +61 -21
  288. data/lib/rigor/reflection/constant_path.rb +126 -0
  289. data/lib/rigor/reflection.rb +26 -10
  290. data/lib/rigor/runtime/jit.rb +6 -8
  291. data/lib/rigor/scope/discovery_index.rb +30 -0
  292. data/lib/rigor/scope.rb +203 -4
  293. data/lib/rigor/sig_gen/generator.rb +115 -22
  294. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  295. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  296. data/lib/rigor/sig_gen/observation_collector.rb +17 -15
  297. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  298. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  299. data/lib/rigor/sig_gen/renderer.rb +4 -5
  300. data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
  301. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  302. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  303. data/lib/rigor/sig_gen/writer.rb +23 -6
  304. data/lib/rigor/signature_path_audit.rb +153 -6
  305. data/lib/rigor/source/literals.rb +0 -23
  306. data/lib/rigor/source/node_children.rb +0 -2
  307. data/lib/rigor/source/node_locator.rb +5 -11
  308. data/lib/rigor/source/node_walker.rb +2 -5
  309. data/lib/rigor/triage/catalogue.rb +1 -3
  310. data/lib/rigor/triage.rb +3 -5
  311. data/lib/rigor/type/accepts_result.rb +24 -6
  312. data/lib/rigor/type/combinator.rb +68 -5
  313. data/lib/rigor/type/data_class.rb +2 -2
  314. data/lib/rigor/type/data_instance.rb +4 -4
  315. data/lib/rigor/type/float_range.rb +128 -0
  316. data/lib/rigor/type/hash_shape.rb +5 -5
  317. data/lib/rigor/type/integer_range.rb +13 -8
  318. data/lib/rigor/type/nominal.rb +8 -2
  319. data/lib/rigor/type/refined.rb +3 -3
  320. data/lib/rigor/type/struct_class.rb +3 -3
  321. data/lib/rigor/type/struct_instance.rb +4 -4
  322. data/lib/rigor/type.rb +1 -0
  323. data/lib/rigor/type_node/generic.rb +1 -1
  324. data/lib/rigor/type_node/range_literal.rb +25 -0
  325. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  326. data/lib/rigor/type_node.rb +1 -0
  327. data/lib/rigor/version.rb +1 -1
  328. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  329. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  330. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  331. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
  332. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  333. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  334. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  335. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
  336. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
  337. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  338. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  339. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
  340. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  341. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  342. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  343. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  344. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  345. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  346. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
  347. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
  348. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  349. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  350. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
  351. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  352. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
  353. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  354. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
  355. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  356. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
  357. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  358. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  359. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  360. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  361. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  362. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  363. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  364. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  365. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  366. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  367. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  368. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  369. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  370. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  371. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  372. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
  373. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  374. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  375. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
  376. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  377. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  378. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  379. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  380. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  381. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
  382. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  383. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  384. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  385. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
  386. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  387. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
  388. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
  389. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
  390. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  391. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  392. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  393. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  394. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
  395. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  396. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  397. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
  398. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  399. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  400. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  401. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  402. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  403. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  404. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  405. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  406. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  407. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  408. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  409. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  410. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
  411. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  412. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  413. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
  414. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  415. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  416. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  417. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  418. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  419. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  420. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
  421. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  422. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
  423. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  424. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  425. data/sig/rigor/cache.rbs +2 -3
  426. data/sig/rigor/environment.rbs +5 -0
  427. data/sig/rigor/inference.rbs +3 -10
  428. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  429. data/sig/rigor/plugin/manifest.rbs +2 -1
  430. data/sig/rigor/reflection.rbs +3 -1
  431. data/sig/rigor/scope.rbs +24 -3
  432. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
  433. data/sig/rigor/type.rbs +27 -1
  434. data/sig/rigor.rbs +1 -0
  435. data/skills/rigor-ask/SKILL.md +8 -6
  436. data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
  437. data/skills/rigor-project-init/SKILL.md +6 -3
  438. data/skills/rigor-project-init/references/02-configure.md +10 -6
  439. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  440. data/skills/rigor-type-oracle/SKILL.md +228 -0
  441. data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
  442. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  443. data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
  444. data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
  445. metadata +26 -3
  446. data/lib/rigor/cli/probe_environment.rb +0 -85
  447. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -0,0 +1,228 @@
1
+ ---
2
+ name: rigor-type-oracle
3
+ description: |
4
+ Before writing or asserting ANY Ruby type, get it from Rigor rather than from reading the code: `rigor type-of FILE:LINE:COL` / `rigor annotate FILE` for an expression, `rigor sig-gen --print FILE` for a method signature, call-site observation for a parameter. A type you did not obtain from Rigor is a guess, and a guessed type is never written anywhere. Triggers: writing RBS under `sig/`, an inline `#:` / `# @rbs` annotation, a Sorbet `sig do … end`, a YARD `@param` / `@return`, a type stated in a doc sentence or a review comment, a nil check / `is_a?` / `respond_to?` guard justified by "this should be an X", "add types to this class / file", "document this method", "what type is this / what does this return?". Applies to Rigor's own tree too. When Rigor answers `Dynamic[top]` or `untyped`, or `sig-gen` skips the method, report the gap — never fill it in from inference of your own. NOT for setting Rigor up (use rigor-next-steps) or working a baseline down (use rigor-baseline-reduce).
5
+ license: MPL-2.0
6
+ metadata:
7
+ version: 0.1.0
8
+ homepage: https://github.com/rigortype/rigor
9
+ ---
10
+
11
+ # Rigor Type Oracle
12
+
13
+ You are about to write a type. Stop and ask Rigor first.
14
+
15
+ This project has a deterministic type oracle installed. Reading the
16
+ source and concluding "`currency` is a String, `pattern` is a Regexp" is
17
+ *plausible-guess* behaviour: it is right often enough to feel safe and
18
+ wrong often enough to poison a signature file, a doc comment, or a
19
+ reviewer's mental model. Rigor already knows the answer — for every
20
+ expression, at every line, from the same engine that will check the code
21
+ tomorrow. Asking it costs one command.
22
+
23
+ **The rule: a type you did not obtain from Rigor is a guess, and a
24
+ guessed type is never written anywhere.** Not in `sig/`, not in an inline
25
+ annotation (`#:`, `# @rbs`), not in a Sorbet `sig`, not in a YARD tag,
26
+ not in a doc sentence, not in a review comment, and not as the stated
27
+ reason for a nil check or an `is_a?` guard.
28
+
29
+ ## First: load the version-current copy
30
+
31
+ This skill's exact commands, flags, output spellings, and skip-reason ids
32
+ drift between Rigor releases, so follow the copy that ships with the
33
+ **installed** Rigor rather than any vendored or frozen copy of this file.
34
+ Get the complete current procedure in one call:
35
+
36
+ ```sh
37
+ rigor skill --full rigor-type-oracle # this body + all its references/, inline
38
+ ```
39
+
40
+ If you already loaded this skill *via* `rigor skill` you have the current
41
+ copy — just proceed (read any `references/NN-*.md` from the directory the
42
+ header names). If `rigor` is not on `PATH`, this task needs it: run
43
+ **`rigor-next-steps`** to install Rigor first, then come back.
44
+
45
+ ## When to use
46
+
47
+ Trigger on the *act*, not on the user saying the word "type". You are in
48
+ scope the moment you are about to write or assert one:
49
+
50
+ - Writing or editing RBS under `sig/`.
51
+ - Writing an inline annotation — rbs-inline `#:`, `# @rbs`, a
52
+ `%a{rigor:v1:…}` override.
53
+ - Writing a Sorbet `sig do … end`, or any other type DSL.
54
+ - Writing a YARD / RDoc `@param` / `@return` / `@raise`, or any doc
55
+ sentence that names a type ("returns an Array of entries").
56
+ - Stating a type in a review comment, a PR description, or a chat answer.
57
+ - Justifying a nil check, an `is_a?` / `respond_to?` guard, or a
58
+ defensive `to_s` with "this should be an X".
59
+ - Being asked "what type is this?", "what does this method return?",
60
+ "add types to this class", "document this file".
61
+
62
+ It applies to **Rigor's own tree** as well: `lib/`, the bundled plugins,
63
+ and the examples are held to the same rule, and a gap found there is
64
+ engine signal worth more than the annotation you would have written.
65
+
66
+ ## When NOT to use
67
+
68
+ - **Setting Rigor up on a project that has none** → `rigor-next-steps`
69
+ (which routes to `rigor-project-init`). You cannot ask an oracle that
70
+ is not installed.
71
+ - **Working an existing `.rigor-baseline.yml` down** →
72
+ `rigor-baseline-reduce`. That is fixing diagnostics, not sourcing a
73
+ type.
74
+ - **Raising type protection from a `coverage --protection` list** →
75
+ `rigor-protection-uplift`. It owns the "where should a type go, and how
76
+ do I verify it" loop; this skill owns "where does the type itself come
77
+ from".
78
+
79
+ ## The three allowed sources
80
+
81
+ Every type you write comes from exactly one of these. There is no fourth.
82
+
83
+ | What you need | Ask Rigor with | What you write |
84
+ | --- | --- | --- |
85
+ | The type of an **expression** at a point | `rigor type-of FILE:LINE:COL` — or `rigor annotate FILE` for every line at once | the `type:` it prints, verbatim |
86
+ | The **signature of a method** | `rigor sig-gen --print FILE` | the RBS it prints, verbatim — never what you expected it to print |
87
+ | A **parameter's intended type** | `rigor sig-gen --observe PATH --params=observed` (call-site derivation) | the observed type, *reviewed and widened*, kept only while `rigor check` stays green |
88
+
89
+ The parameter row is the one genuine gap. Inference reads a method
90
+ *body*, and a body does not state what its callers are allowed to pass —
91
+ so Rigor spells parameters `untyped` by design ([ADR-5](https://github.com/rigortype/rigor/blob/master/docs/adr/5-robustness-principle.md):
92
+ strict on returns, lenient on parameters). `--params=observed` derives
93
+ them from the call sites instead, which is evidence rather than
94
+ invention — but it is *narrow* evidence (it can emit literal types such
95
+ as `("JPY")`), so it is the one place you review and widen before
96
+ adopting, under the gate that `rigor check` gains no new diagnostic.
97
+
98
+ Exact command forms, flags, position syntax, JSON shapes, and how to read
99
+ each output: [`references/01-oracle-commands.md`](references/01-oracle-commands.md).
100
+
101
+ ## The gap protocol — a gap is a finding, not a blank to fill
102
+
103
+ `Dynamic[top]`, `untyped`, and a `sig.skipped.*` classification are
104
+ **answers**. They mean "Rigor cannot prove a type here", which is
105
+ information about the project or about the engine. Filling that hole with
106
+ your own reading converts a known unknown into a confident falsehood, and
107
+ it does so in a file the next reader will trust.
108
+
109
+ So when the oracle comes back empty:
110
+
111
+ 1. **Report it.** Give the exact command and its exact output.
112
+ 2. **Find out why**, when it matters: `rigor trace --format=json --line=N FILE`
113
+ replays how the type was built; `rigor explain <rule>` documents a
114
+ diagnostic that fired nearby.
115
+ 3. **Route it.** A project-side gap has a sibling skill that closes it
116
+ (missing gem RBS → `rigor-rbs-setup`; an unconfigured framework →
117
+ `rigor-plugin-tune`; the project's own monkey-patches →
118
+ `rigor-monkeypatch-resolve`; a project DSL → `rigor-plugin-author`).
119
+ An engine-side gap is a Rigor issue.
120
+
121
+ Which output means which gap, the full routing table, and how to word the
122
+ issue: [`references/03-gap-protocol.md`](references/03-gap-protocol.md).
123
+
124
+ ## Provenance — every type you state carries its command
125
+
126
+ When you tell a human a type, tell them how to re-derive it. One line is
127
+ enough:
128
+
129
+ > `entries_matching` returns `Array[untyped] | []`
130
+ > (`rigor sig-gen --print lib/demo/budget_ledger.rb`).
131
+
132
+ This is not ceremony. It is the difference between an assertion the
133
+ reader must trust and a claim they can re-run in three seconds — and it
134
+ is what makes a wrong answer *findable* instead of permanent.
135
+
136
+ ## With the MCP server connected, use the tools
137
+
138
+ If the Rigor MCP server is wired up (`rigor-mcp-setup`), `rigor_type_of`,
139
+ `rigor_annotate`, `rigor_sig_gen`, `rigor_check`, and `rigor_explain` are
140
+ the same oracle as tool calls — prefer them over shelling out, and treat
141
+ their results exactly as this skill treats CLI output. Argument shapes:
142
+ [`references/01-oracle-commands.md`](references/01-oracle-commands.md)
143
+ § "The MCP tools".
144
+
145
+ ## Worked example
146
+
147
+ Asked to document `Demo::BudgetLedger`, the guessing path writes
148
+ `@param currency [String]`, `@return [Numeric]`, `@param pattern
149
+ [Regexp]`. Here is the oracle path.
150
+
151
+ ```sh
152
+ rigor annotate lib/demo/budget_ledger.rb
153
+ ```
154
+
155
+ ```ruby
156
+ def initialize(currency, opening_balance: 0) #=> Dynamic[top]
157
+ @currency = currency #=> Dynamic[top]
158
+ @entries = [] #=> []
159
+ def balance(as_of: nil) #=> Dynamic[top]
160
+ def entries_matching(pattern) #=> Array[Dynamic[top]] | []
161
+ ```
162
+
163
+ ```sh
164
+ rigor sig-gen --print lib/demo/budget_ledger.rb
165
+ ```
166
+
167
+ ```
168
+ rigor sig-gen: skipped 2 method(s) it could not type or would not overwrite
169
+ (sig.skipped.untyped-return: 2). Run with --format=json to see each one with
170
+ its skip_reason.
171
+ class Demo::BudgetLedger
172
+ def initialize: (untyped, ?opening_balance: untyped) -> void
173
+ def record: (untyped, ?memo: untyped, ?at: untyped) -> Demo::BudgetLedger
174
+ def entries_matching: (untyped) -> (Array[untyped] | [])
175
+ end
176
+ ```
177
+
178
+ What you now know, and may write: `record` returns
179
+ `Demo::BudgetLedger`; `entries_matching` returns `Array[untyped] | []`.
180
+ What you must **report rather than write**: `balance` and `overdrawn?`
181
+ were skipped as `sig.skipped.untyped-return` — `@opening_balance` is
182
+ `Dynamic[top]`, so the arithmetic proves nothing. `currency` is not
183
+ `String` on any evidence Rigor has; it is `untyped`, and the YARD tag is
184
+ `@param currency — the ledger's currency` with no type at all.
185
+
186
+ Call-site derivation closes the parameter half where specs exist:
187
+
188
+ ```sh
189
+ rigor sig-gen --print --params=observed --observe spec lib/demo/budget_ledger.rb
190
+ # def initialize: ("JPY", ?opening_balance: 100) -> void
191
+ # def entries_matching: (Regexp) -> (Array[untyped] | [])
192
+ ```
193
+
194
+ `entries_matching: (Regexp)` is now *derived*, and adoptable. The
195
+ `("JPY")` / `100` literals are the narrowness warned about above — widen
196
+ them to `String` / `Integer` before adopting, and keep the change only
197
+ while `rigor check` stays green.
198
+
199
+ ## When the user insists on a hand-written type anyway
200
+
201
+ They may. It is their code, and a human can know an intended contract
202
+ that no static reading can prove. Do not argue past one exchange:
203
+
204
+ 1. Say once what Rigor actually reports for that site, with the command.
205
+ 2. Write what they asked for.
206
+ 3. Run `rigor check` on the touched paths and report the result.
207
+ 4. **Say which lines the check covers.** A green `rigor check` proves
208
+ that the annotation contradicts nothing Rigor can currently see — it
209
+ does not prove the contract. Where the surrounding types are
210
+ `untyped`, there is nothing to contradict, and you must say so rather
211
+ than let a green run read as confirmation.
212
+
213
+ If the check *does* go red, the annotation modeled the wrong contract:
214
+ report it and revert, never suppress the diagnostic.
215
+
216
+ The same holds for an inline annotation a human wrote as documentation —
217
+ `#: void`, `#: :asc | :desc`, a parameter contract. It is their intent,
218
+ checked by `rigor check`, not a guess for you to "correct" to what inference
219
+ shows. Leave it; if the check finds a contradiction, report that.
220
+
221
+ ## Installing the rule in the project
222
+
223
+ An agent that never loads this skill still guesses. The durable fix is
224
+ one paragraph in the project's `AGENTS.md` / `CLAUDE.md`, so the rule is
225
+ in context every session rather than only when a skill happens to
226
+ trigger. The text to paste:
227
+ [`references/02-agents-md-fragment.md`](references/02-agents-md-fragment.md).
228
+ `rigor-project-init` installs it during onboarding.
@@ -0,0 +1,261 @@
1
+ # 01 — The oracle commands, exactly
2
+
3
+ Every command form here was taken from `rigor <cmd> --help` on the
4
+ version this file ships with. If a flag below is missing from your
5
+ `--help`, trust `--help`.
6
+
7
+ All five commands are **read-only**: they analyse and print. None of them
8
+ writes to your source tree. (`rigor sig-gen --write` does, and is the one
9
+ form this skill never reaches for on its own — see § "Writing, not just
10
+ reading".)
11
+
12
+ ## `rigor type-of` — the type of one expression
13
+
14
+ ```sh
15
+ rigor type-of FILE:LINE[:COL] [FILE:LINE[:COL] ...]
16
+ ```
17
+
18
+ | Option | Effect |
19
+ | --- | --- |
20
+ | `--format=text\|json` | `text` (default) or a JSON object per position. |
21
+ | `--trace` | Also record fail-soft fallbacks via the tracer. |
22
+ | `--config=PATH` | Explicit `.rigor.yml`. |
23
+ | `--tmp-file=PATH` / `--instead-of=PATH` | Editor mode: analyse an unsaved buffer as if it were the project path. Paired. |
24
+
25
+ **Position syntax.** `FILE:LINE:COL`, both **1-based**. The column may be
26
+ omitted (`FILE:LINE`). Several positions in one invocation — that is the
27
+ cheap way to ask about a whole method.
28
+
29
+ Text output:
30
+
31
+ ```
32
+ lib/demo/budget_ledger.rb:19:7
33
+ node: Prism::LocalVariableWriteNode
34
+ type: Array[Dynamic[top]]
35
+ erased: Array[untyped]
36
+ ```
37
+
38
+ - `node` — the Prism node the position resolved to. Check it: if it is
39
+ not the expression you meant, your column is off, and the `type` below
40
+ it answers a different question.
41
+ - `type` — Rigor's internal type. **This is the answer.**
42
+ - `erased` — the same type spelled as RBS. **This is what you write into
43
+ a `.rbs` file or an annotation.** `Dynamic[top]` erases to `untyped`.
44
+
45
+ JSON output is the same five fields:
46
+
47
+ ```json
48
+ { "file": "…", "line": 19, "column": 7,
49
+ "node": "Prism::LocalVariableWriteNode",
50
+ "type": "Array[Dynamic[top]]", "erased": "Array[untyped]" }
51
+ ```
52
+
53
+ ## `rigor annotate` — every line of a file at once
54
+
55
+ ```sh
56
+ rigor annotate FILE
57
+ ```
58
+
59
+ | Option | Effect |
60
+ | --- | --- |
61
+ | `--format=text\|json` | `text` (default), or JSON as a `{ line => type }` map. |
62
+ | `--[no-]color` | Force / disable ANSI colour (auto-detects a tty; honours `NO_COLOR`). |
63
+ | `--[no-]bat` | Force / disable highlighting through `bat`. |
64
+ | `--config=PATH` | Explicit `.rigor.yml`. |
65
+
66
+ Output is the source with each line's **last-expression type** appended
67
+ after `#=>`:
68
+
69
+ ```ruby
70
+ def initialize(currency, opening_balance: 0) #=> Dynamic[top]
71
+ @currency = currency #=> Dynamic[top]
72
+ @entries = [] #=> []
73
+ end #=> :initialize
74
+ ```
75
+
76
+ Two reading rules that catch people out:
77
+
78
+ - `#=>` is the type of the line's **last expression**, not of the
79
+ variable being assigned and not of the method being defined. A `def`
80
+ line's `#=>` is the *return* type of the method; the `end` line's
81
+ `#=>` is the symbol the `def` expression itself evaluates to
82
+ (`:initialize`) — that is Ruby, not a Rigor quirk. Ignore it.
83
+ - One `#=>` per line. For a multi-expression line, or for a
84
+ sub-expression in the middle, go back to `type-of` with a column.
85
+
86
+ Reach for `annotate` first when the task is "document this file" or "add
87
+ types to this class": one call types everything, and the lines that come
88
+ back `Dynamic[top]` are your gap list before you have written a word.
89
+
90
+ ## `rigor sig-gen` — the signature of a method
91
+
92
+ ```sh
93
+ rigor sig-gen [paths]
94
+ ```
95
+
96
+ | Option | Effect |
97
+ | --- | --- |
98
+ | `--print` | RBS to stdout. **Default** — you may omit it. |
99
+ | `--diff` | Unified diff against the existing RBS. Read-only. |
100
+ | `--write` | Write to `sig/<path>.rbs`. The only mode that touches the filesystem. |
101
+ | `--overwrite` | Allow a tighter return to replace user-authored RBS. |
102
+ | `--include-private` | Emit private / protected instance methods too (default: public only). |
103
+ | `--params=untyped\|observed\|observed-strict` | Parameter policy. Default `untyped`. `observed-strict` is reserved and currently a usage error. |
104
+ | `--observe=PATH` | Directory / file to scan for call-site observations. Repeatable. Defaults to `spec/` when present. |
105
+ | `--new-files` / `--new-methods` / `--tighter-returns` | Emit only that classification. |
106
+ | `--format=text\|json` | Text RBS, or the structured candidate report. |
107
+ | `--config=PATH` | Explicit `.rigor.yml`. |
108
+
109
+ Text output is RBS you paste as-is:
110
+
111
+ ```
112
+ class Demo::BudgetLedger
113
+ # [new]
114
+ def record: (untyped, ?memo: untyped, ?at: untyped) -> Demo::BudgetLedger
115
+ # [new]
116
+ def entries_matching: (untyped) -> (Array[untyped] | [])
117
+ end
118
+ ```
119
+
120
+ **Read stderr.** A skip summary goes there, not to stdout:
121
+
122
+ ```
123
+ rigor sig-gen: skipped 2 method(s) it could not type or would not overwrite
124
+ (sig.skipped.untyped-return: 2). Run with --format=json to see each one with
125
+ its skip_reason.
126
+ ```
127
+
128
+ A method that is missing from the printed RBS was **skipped**, and a
129
+ skip is a finding. `--format=json` names each one:
130
+
131
+ ```json
132
+ { "class": "Demo::BudgetLedger", "method": "balance", "kind": "instance",
133
+ "classification": "skipped", "skip_reason": "sig.skipped.untyped-return" }
134
+ ```
135
+
136
+ Classifications: `new-file`, `new-method`, `tighter-return`, `equivalent`
137
+ (nothing to tighten; silently dropped), `skipped`. Skip reasons and what
138
+ each one means for you: [`03-gap-protocol.md`](03-gap-protocol.md).
139
+
140
+ ### Deriving a parameter type from call sites
141
+
142
+ ```sh
143
+ rigor sig-gen --print --params=observed --observe spec lib/demo/budget_ledger.rb
144
+ ```
145
+
146
+ `observed` collects argument types from every call site under the
147
+ `--observe` paths, unions them per parameter position, and emits the
148
+ union. The recogniser understands RSpec shapes — `RSpec.describe Foo`,
149
+ bare `describe Foo`, `subject { … }` / `subject(:name) { … }`,
150
+ `let(:name)` / `let!(:name)`, and `described_class.new(...)` — so a
151
+ normal spec suite is already an observation corpus. No plugin needed.
152
+
153
+ The result is *evidence*, and it is narrow evidence:
154
+
155
+ ```
156
+ def initialize: ("JPY", ?opening_balance: 100) -> void
157
+ def entries_matching: (Regexp) -> (Array[untyped] | [])
158
+ ```
159
+
160
+ `(Regexp)` is a real derivation and adoptable. `("JPY")` and `100` are
161
+ literal types — the union of what today's callers happen to pass, frozen
162
+ as a contract. Widen those to the class before adopting
163
+ (`String`, `Integer`), per [ADR-5](https://github.com/rigortype/rigor/blob/master/docs/adr/5-robustness-principle.md):
164
+ lenient on parameters. The gate on any widening is that `rigor check`
165
+ gains no new diagnostic.
166
+
167
+ ### Writing, not just reading
168
+
169
+ This skill's job ends at *knowing* the type. `--write` is an edit to the
170
+ project and belongs to whoever asked for it — propose `--diff` first, and
171
+ run `rigor check` after. `--write` only ever writes inside the configured
172
+ signature paths (`sig/` by default).
173
+
174
+ ## `rigor trace` — why the type is what it is
175
+
176
+ ```sh
177
+ rigor trace FILE
178
+ ```
179
+
180
+ | Option | Effect |
181
+ | --- | --- |
182
+ | `--format=text\|json` | `text` is an interactive animation; **use `json`** for the raw event stream. |
183
+ | `--line=N` | Only replay events whose source range starts on line `N`. |
184
+ | `--verbose` | Include every expression enter/result frame. |
185
+ | `--delay=SECONDS` | Autoplay the text animation (default: step on key press). |
186
+ | `--config=PATH` | Explicit `.rigor.yml`. |
187
+
188
+ An agent wants `--format=json --line=N`: the text mode waits on
189
+ keystrokes. Each event carries `kind`, `depth`, a `location`, the node
190
+ `stack`, and a `data` payload — a `bind` event, for example, names the
191
+ local and the type it received:
192
+
193
+ ```json
194
+ { "kind": "bind", "depth": 2,
195
+ "location": { "start_line": 20, "start_column": 25, "…": "…" },
196
+ "stack": ["CallNode", "CallNode"],
197
+ "data": { "name": "amount", "type": "Dynamic[top]" } }
198
+ ```
199
+
200
+ Reach for it when `type-of` gave you `Dynamic[top]` and you need to name
201
+ *where* the precision was lost before reporting the gap.
202
+
203
+ ## `rigor explain` — what a diagnostic rule means
204
+
205
+ ```sh
206
+ rigor explain [<rule>]
207
+ ```
208
+
209
+ | Option | Effect |
210
+ | --- | --- |
211
+ | `--format=text\|json` | Default `text`. |
212
+
213
+ With no argument it lists every rule. With a rule id, a legacy alias, or
214
+ a family prefix (`call`, `flow`, `assert`, `dump`, `def`) it prints the
215
+ rule's firing conditions, the severity per profile, the evidence tier,
216
+ and how to suppress it.
217
+
218
+ **`explain` covers diagnostic rules only.** A `sig.skipped.*` id is a
219
+ sig-gen *skip reason*, not a diagnostic rule — `rigor explain
220
+ sig.skipped.untyped-return` answers `Unknown rule`. Skip reasons are
221
+ documented in [`03-gap-protocol.md`](03-gap-protocol.md).
222
+
223
+ ## `rigor check` — the gate, not the oracle
224
+
225
+ ```sh
226
+ rigor check [paths]
227
+ ```
228
+
229
+ You need one flag family here: `--format=text|json|sarif|github|gitlab|checkstyle|junit|teamcity`,
230
+ plus `--explain` to surface fail-soft fallback events as `:info`
231
+ diagnostics. Everything else (`--baseline*`, `--workers`, `--incremental`,
232
+ the cache flags) belongs to other skills.
233
+
234
+ `check` never *sources* a type. Its role here is the verification half:
235
+ after any annotation lands, `rigor check PATHS` must gain **no new
236
+ diagnostic** versus before. Green is a non-contradiction proof, not a
237
+ correctness proof — say which lines it actually covers.
238
+
239
+ ## The MCP tools
240
+
241
+ With the Rigor MCP server wired up (`rigor-mcp-setup`), the same oracle
242
+ is available as tool calls. Prefer them over shelling out; the results
243
+ are the CLI's, so read them exactly as above.
244
+
245
+ | Tool | Required arguments | Optional | Returns |
246
+ | --- | --- | --- | --- |
247
+ | `rigor_type_of` | `file` (string), `line` (integer, 1-based), `col` (integer, 1-based) | `config` | The inferred type at that location. |
248
+ | `rigor_annotate` | `file` (string) | `config` | The source with each line's last-expression type appended. |
249
+ | `rigor_sig_gen` | — (defaults to the configured paths) | `paths` (array of strings), `params` (`"untyped"` \| `"observed"`), `config` | The JSON candidate report — classifications, `rbs`, `inferred_return`, `skip_reason`. |
250
+ | `rigor_check` | `paths` (array of strings) | `config` | The JSON diagnostic report. |
251
+ | `rigor_explain` | — (omit `rule` to list every rule) | `rule` (string) | The rule catalogue entry, as JSON. |
252
+
253
+ Two shape differences from the CLI worth knowing:
254
+
255
+ - `rigor_sig_gen` returns the **JSON candidate report**, always — there
256
+ is no `--print` text mode and no `--diff`. Read `rbs` per candidate.
257
+ - `rigor_sig_gen` exposes `params` but **not** `observe`; observation
258
+ falls back to `spec/` when present. Point it elsewhere from the CLI.
259
+
260
+ `rigor_triage` and `rigor_coverage` are also served, and belong to
261
+ `rigor-baseline-reduce` / `rigor-protection-uplift` rather than here.
@@ -0,0 +1,52 @@
1
+ # 02 — The paragraph to keep in the project's agent contract
2
+
3
+ A skill only binds an agent that loaded it. The rule below binds every
4
+ session, because it lives in the file the agent reads at startup —
5
+ `AGENTS.md`, or `CLAUDE.md` for Claude Code (which pulls `AGENTS.md` in
6
+ with `@AGENTS.md` when both exist).
7
+
8
+ Paste it verbatim. `rigor-project-init` installs it during onboarding; if
9
+ the project is already onboarded, add it now.
10
+
11
+ ## Where it goes
12
+
13
+ - **`AGENTS.md` exists** → append the section at the end. Do not
14
+ reorganise the file.
15
+ - **Only `CLAUDE.md` exists** → append it there.
16
+ - **Both exist** → put it in `AGENTS.md`; add `@AGENTS.md` to `CLAUDE.md`
17
+ only if it is not already pulled in.
18
+ - **Neither exists** → create `AGENTS.md` with this as its first section.
19
+ - **A "Types" / "Type checking" section already exists** → merge into it
20
+ rather than adding a second one, and keep the five bullets intact.
21
+
22
+ Never overwrite unrelated content, and never rewrite a rule the project
23
+ already wrote for itself — show the user the conflict instead.
24
+
25
+ ## The fragment
26
+
27
+ ```markdown
28
+ ## Types come from Rigor, not from reading code
29
+
30
+ This project is type-checked by [Rigor](https://github.com/rigortype/rigor). A type you did not obtain from Rigor is a guess, and a guessed type is never written anywhere: not in `sig/`, not in an inline annotation (`#:`, `# @rbs`), not in a doc comment, not in a review comment, and not as the reason for a nil check or an `is_a?` guard.
31
+
32
+ - The type of an expression: `rigor type-of FILE:LINE:COL`, or `rigor annotate FILE` for a whole file.
33
+ - The signature of a method: `rigor sig-gen --print FILE`; paste what it prints, never what you expect.
34
+ - A parameter type is the one thing inference does not give you: derive it from the call sites with `rigor sig-gen --observe PATH` and keep it only while `rigor check` stays green.
35
+ - When Rigor answers `Dynamic[top]` or `untyped`, or `sig-gen` skips the method, do not fill the gap. Report the exact command and its output; the gap is the finding.
36
+ - Every type you state to a human carries the command that produced it, so it can be re-run.
37
+
38
+ The `rigor-type-oracle` skill (`rigor skill --full rigor-type-oracle`) has the full procedure. With the Rigor MCP server connected, `rigor_type_of`, `rigor_annotate`, and `rigor_sig_gen` are the same oracle as tool calls.
39
+ ```
40
+
41
+ ## Two notes for whoever installs it
42
+
43
+ - **The single-long-line shape is deliberate.** Each bullet is one line,
44
+ however long. Some of these files are rendered as Markdown by tools
45
+ that turn a newline inside a paragraph into a line break, so a
46
+ column-wrapped bullet reads ragged. Do not re-wrap it to fit an editor
47
+ ruler.
48
+ - **Tell the user it landed.** It is a change to the file that governs
49
+ every future agent session in the repo — it belongs in the "here is
50
+ what I created, and whether to commit it" report, not in a silent diff.
51
+ It should be committed: the point is that every contributor's agent
52
+ reads the same rule.
@@ -0,0 +1,128 @@
1
+ # 03 — The gap protocol
2
+
3
+ Rigor answering "I don't know" is not the oracle failing. It is the
4
+ oracle telling you something the code alone would not have: precision is
5
+ lost *here*, for *this* reason. That is worth more than the annotation
6
+ you were about to write, and it is worth exactly nothing if you paper
7
+ over it.
8
+
9
+ **The rule: never convert a gap into a type.** Report it, locate it,
10
+ route it.
11
+
12
+ ## Which output means which gap
13
+
14
+ ### `Dynamic[top]` (erased: `untyped`)
15
+
16
+ Rigor has no class information for this expression at all. It is the
17
+ top type, and it is contagious: a `Dynamic[top]` receiver makes every
18
+ method call on it `Dynamic[top]` too, so one unresolved source can
19
+ account for a whole file of "unknown".
20
+
21
+ Common causes, in the order worth checking:
22
+
23
+ - A **method parameter** — parameters are `untyped` by design unless RBS
24
+ or observation says otherwise. Not a defect; use call-site derivation.
25
+ - An **ivar** assigned from a parameter (`@currency = currency`) — the
26
+ same gap, one hop later.
27
+ - A call into a **gem with no RBS** — the dominant cause on a real
28
+ project.
29
+ - A call into a **framework Rigor is not configured for** — Rails
30
+ without the Rails plugins enabled, say.
31
+ - The project's **own monkey-patch or DSL**, which the analyzer never saw
32
+ defined.
33
+
34
+ ### `untyped` inside an otherwise-precise type
35
+
36
+ `Array[untyped]`, `Hash[Symbol, untyped]`: the container is proven, the
37
+ element is not. Write the container. Do **not** invent the element — an
38
+ `Array[String]` you guessed is worse than the `Array[untyped]` Rigor
39
+ proved, because it will be believed.
40
+
41
+ ### A method missing from `sig-gen` output — the `sig.skipped.*` reasons
42
+
43
+ Read stderr for the count, `--format=json` for the per-method reason.
44
+
45
+ | Reason | What it means | What you do |
46
+ | --- | --- | --- |
47
+ | `sig.skipped.untyped-return` | The body's last expression types as `Dynamic[top]`; emitting `untyped` would be noise. | The commonest gap. Trace the return expression back to its `Dynamic[top]` source and route *that*. Never write a return type here. |
48
+ | `sig.skipped.user-authored` | An RBS declaration already exists and `--overwrite` was not given. | Not a gap. The hand-written type is the project's answer — read `sig/`, and if it disagrees with inference, raise the disagreement rather than silently retyping. |
49
+ | `sig.skipped.unrenderable-rbs` | Rigor rendered a signature that does not parse as RBS, so it was dropped rather than written. | **A bug in Rigor**, not in the code. Report it with the method and the file. |
50
+ | `sig.skipped.complex-shape` | Reserved; the generator does not produce it today. | If you ever see one, it is worth reporting as a surprise. |
51
+ | `skipped_outside_sig_root` | A `--write` target outside the configured signature paths. | Configuration, not inference. |
52
+
53
+ `rigor explain <id>` answers each of these ids directly (the command
54
+ carries a second catalogue for the skip reasons alongside the
55
+ diagnostic rules); this table is the summary.
56
+
57
+ ### `equivalent`, and silence
58
+
59
+ A `tighter-return` candidate that never appears was classified
60
+ `equivalent`: the inferred return is not a strict subtype of what `sig/`
61
+ already declares. The existing declaration stands. Silence here means
62
+ "nothing to change", not "nothing is known" — check `sig/` before calling
63
+ it a gap.
64
+
65
+ ## Locating the gap before you report it
66
+
67
+ Two commands turn "somewhere upstream" into a line number.
68
+
69
+ ```sh
70
+ rigor annotate FILE # walk the #=> column upward to the first Dynamic[top]
71
+ rigor trace --format=json --line=N FILE
72
+ ```
73
+
74
+ `annotate` is usually enough: the first line in the chain that reports
75
+ `Dynamic[top]` is the source, and everything below it is cascade. Use
76
+ `trace` when the loss happens *within* a line — its `bind` events name
77
+ each local and the type it received, so you can see which argument
78
+ arrived unknown.
79
+
80
+ If a diagnostic fired near the site, `rigor explain <rule>` gives its
81
+ firing conditions, which frequently name the cause outright.
82
+
83
+ ## Routing table — project-side gaps have an owner
84
+
85
+ Once you know the source, the fix is almost never "write the type here".
86
+
87
+ | The gap's source | Route to | Why |
88
+ | --- | --- | --- |
89
+ | A dependency gem ships no RBS | **`rigor-rbs-setup`** | `rbs collection install` brings in community RBS; this is the single biggest `Dynamic` reduction on most projects. |
90
+ | A framework Rigor isn't configured for (Rails, RSpec, dry-rb…) | **`rigor-plugin-tune`** | The bundled plugin for it is probably just not enabled in `.rigor.dist.yml`. |
91
+ | `undefined-method` on the project's own monkey-patches | **`rigor-monkeypatch-resolve`** | Wiring the defining files into `pre_eval:` makes them visible. |
92
+ | The project's own DSL / `define_method` factory / `method_missing` | **`rigor-plugin-author`** | Rigor does not bundle per-application plugins; a project-owned plugin is the durable fix. |
93
+ | The site is protected-coverage work, not a documentation task | **`rigor-protection-uplift`** | It owns the "minimal true annotation + double gate" loop. |
94
+ | The setup itself looks wrong (zero RBS classes, config not taking) | **`rigor-doctor`** | Validate before concluding anything about inference. |
95
+ | None of the above — Rigor should have inferred this | A Rigor issue | See below. |
96
+
97
+ Offer the route; do not silently switch tasks. The user asked you to
98
+ document a class, and "the reason I cannot is X, and here is the skill
99
+ that fixes X" is the answer to that request.
100
+
101
+ ## Reporting an engine-side gap
102
+
103
+ When the type is derivable from the source and Rigor still says
104
+ `Dynamic[top]`, that is a completeness gap in the engine, and it is worth
105
+ more to the project than any annotation. File it at
106
+ <https://github.com/rigortype/rigor/issues> with these five parts:
107
+
108
+ 1. **The file and line** — the smallest reproduction you can get to,
109
+ ideally a standalone snippet rather than a pointer into a private
110
+ repo.
111
+ 2. **The exact command**, as run: `rigor type-of demo.rb:12:5`,
112
+ `rigor sig-gen --print demo.rb`.
113
+ 3. **The exact output**, pasted, including the stderr skip line.
114
+ 4. **What the correct type is, and what proves it** — the Ruby-level
115
+ reasoning a reader can verify. "`Array#sum` over `Integer` elements is
116
+ `Integer`", not "it should obviously be Integer".
117
+ 5. **What the report proves** — one sentence naming the *class* of gap,
118
+ not just the instance: "block-parameter destructuring loses the
119
+ element type", "a keyword default of `Time.now` does not seed the
120
+ parameter". This is the part that makes the issue actionable, because
121
+ it says what a fix would generalise to.
122
+
123
+ Also say `rigor --version`, and note whether `sig/` and the relevant
124
+ plugins were in play — a gap that only appears without community RBS is a
125
+ different bug from one that survives it.
126
+
127
+ Inside Rigor's own tree the same report is the deliverable: the gap is
128
+ the reason not to hand-write the RBS there.