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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30b66e3d183999cbd85557c5af6b770ac78bf82507a2e587e5878c1a411b4326
4
- data.tar.gz: 2c3cee80d75ea9769d7b965dd84ac60eb4af89c27fc63d3ec35a1cdce3b45338
3
+ metadata.gz: 857b76366640c2aa188649c7fe6af483d599bc05e5f185c4d8285b093c092fdd
4
+ data.tar.gz: 8e281f8668221704dd74c0b8d942830f6e58b252135db54142a70543bcfdf839
5
5
  SHA512:
6
- metadata.gz: 9986a78b908e05fd017f772b0c26d6bc3d654e665cbf59d85cdf0176b76735403d644238ca7072268f9e47ba3937073002de47a308deda6cab77311459f6f008
7
- data.tar.gz: 66df68005471dc8df33c9a1881659644fca80c4e406830b7ab9c1fe5b641e3872d13a70ab39cccdf66fba3d256e46026ab13963a70a76be1362ac86b8e823f1d
6
+ metadata.gz: aaea4bc6f1073c80307375365f111ba58c198cef0852a63abe5476c1ffa937754b0e45a506b69a052e685724d24fa44689f4f8bfb375f902b7e53521eea7ae4d
7
+ data.tar.gz: 846d488914e0046afce19772fa8295ab99b068acf8e94a04d57590f9feb7718ebca29858ca78f47435a1542e7c1c8cbf000ca983619b3ebdb684650f8c8fc7aa
data/README.md CHANGED
@@ -231,7 +231,7 @@ rigor docs --list # list every bundled page
231
231
 
232
232
  ## Status
233
233
 
234
- Current release: **`v0.3.8`** (2026-09-08) — on the
234
+ Current release: **`v0.3.9`** (2026-09-12) — on the
235
235
  `0.3.x` line, continuing the evaluation era opened by `v0.2.0`, the first
236
236
  publicly-announced (general / evaluation) release. The line publishes
237
237
  an enumerated [compatibility surface](docs/compatibility.md) as a
@@ -0,0 +1,36 @@
1
+ # Rigor's capability-role catalog (ADR-1 § "Capability roles",
2
+ # docs/type-specification/structural-interfaces-and-object-shapes.md).
3
+ #
4
+ # These are the roles Rigor itself ships, as opposed to the stdlib interfaces (`_Reader`, `_ToStr`, …)
5
+ # it reuses from upstream RBS. They exist so a `%a{rigor:v1:conforms-to _ClosableStream}` directive
6
+ # written by following the specification resolves in a default run instead of failing soft.
7
+ #
8
+ # Parameter and return types are deliberately the loosest honest reading: a role says which members a
9
+ # value answers, and narrowing them here would reject conforming classes rather than describe them.
10
+
11
+ # The specification's running example: the one-member lifetime contract.
12
+ interface _Closable
13
+ def close: () -> untyped
14
+ end
15
+
16
+ interface _RewindableStream
17
+ def read: (*untyped) -> String?
18
+
19
+ def rewind: () -> untyped
20
+ end
21
+
22
+ interface _ClosableStream
23
+ def close: () -> untyped
24
+
25
+ def closed?: () -> bool
26
+ end
27
+
28
+ interface _FileDescriptorBacked
29
+ def fileno: () -> Integer
30
+ end
31
+
32
+ # Not generic: `_Callable[**A, R]` as ADR-1 first drafted it is not RBS grammar (`**A` has no meaning
33
+ # in a type-parameter list). A generic form is a future extension, gated on RBS gaining the syntax.
34
+ interface _Callable
35
+ def call: (*untyped) -> untyped
36
+ end
@@ -68,6 +68,31 @@ class Object
68
68
 
69
69
  # `core_ext/object/inclusion` — `x.in?([a, b])` / `x.in?(1..10)`.
70
70
  def in?: (untyped) -> bool
71
+
72
+ # Issue #673 — `core_ext/object/to_query`, `core_ext/object/duplicable` and
73
+ # `core_ext/object/instance_variables` all hang off `Object`, so every
74
+ # RBS-known receiver reaches them; declaring them only on `Hash` (as
75
+ # `to_param` / `to_query` were) or only on `NilClass` (as `duplicable?` /
76
+ # `instance_values` were) left `"x".to_param`, `1.duplicable?` and friends a
77
+ # `call.undefined-method` on ordinary Rails code.
78
+ #
79
+ # `to_param` is `to_s`; `to_query(key)` interpolates two `CGI.escape` results.
80
+ # `key` is `untyped` because the body only calls `to_param` on it, and both
81
+ # `:name` and `"user[name]"` are ordinary call sites.
82
+ def to_param: () -> String
83
+ def to_query: (untyped key) -> String
84
+
85
+ # `bool` and not `true`: `Object#duplicable?` answers true, but the
86
+ # `Singleton` (and, on a Ruby where methods are not duplicable, `Method` /
87
+ # `UnboundMethod`) overrides answer false. `-> true` on the universal
88
+ # receiver would make every `if x.duplicable?` an always-truthy branch —
89
+ # a false positive on the only shape anybody writes.
90
+ def duplicable?: () -> bool
91
+
92
+ # Keys without the leading `@` for `instance_values`, with it for
93
+ # `instance_variable_names`.
94
+ def instance_values: () -> Hash[String, untyped]
95
+ def instance_variable_names: () -> Array[String]
71
96
  end
72
97
 
73
98
  # `nil.blank?` / `nil.present?` / `nil.try` are the most frequent
@@ -80,16 +105,24 @@ class NilClass
80
105
  def try!: (*untyped) -> nil
81
106
  def duplicable?: () -> true
82
107
  def instance_values: () -> Hash[String, untyped]
108
+ # `core_ext/object/to_query` overrides `to_param` on the three singleton
109
+ # values to return `self` rather than `Object#to_param`'s `to_s`, so the
110
+ # `Object` row above would carry a wrong return here. `NilClass#to_query`
111
+ # is its own method (`CGI.escape(key.to_param)`), still a String.
112
+ def to_param: () -> nil
113
+ def to_query: (untyped key) -> String
83
114
  end
84
115
 
85
116
  class TrueClass
86
117
  def blank?: () -> false
87
118
  def present?: () -> true
119
+ def to_param: () -> true
88
120
  end
89
121
 
90
122
  class FalseClass
91
123
  def blank?: () -> true
92
124
  def present?: () -> false
125
+ def to_param: () -> false
93
126
  end
94
127
 
95
128
  # ---------------------------------------------------------------
@@ -263,6 +296,16 @@ end
263
296
  # `spec/rigor/environment/activesupport_overlay_parity_spec.rb`); every method not listed here stays
264
297
  # undeclared and silently `Dynamic` rather than guessed.
265
298
  module ActiveSupport
299
+ # Issue #534 item 7 — the `ActiveSupport::Concern` twin of the plugin's declaration. The plugin file
300
+ # carries the reasoning; the short version is that naming the module makes `extend ActiveSupport::Concern`
301
+ # resolve, and the three members have to come with it so that `included do … end` does not become a false
302
+ # `call.undefined-method` on a now-RBS-known module.
303
+ module Concern
304
+ def included: (?untyped base) ?{ (?) -> untyped } -> untyped
305
+ def prepended: (?untyped base) ?{ (?) -> untyped } -> untyped
306
+ def class_methods: () { (?) -> untyped } -> untyped
307
+ end
308
+
266
309
  class Duration
267
310
  # The `ago` family (issue #659). `since(time = ::Time.current)` and `ago(time = ::Time.current)` are
268
311
  # the only two real methods; `from_now` / `after` alias the first and `until` / `before` the second,
@@ -272,23 +315,25 @@ module ActiveSupport
272
315
  # `GEM_OVERLAY_OPEN_RECEIVERS` cannot suppress since it protects a Duration receiver, not a `Time`
273
316
  # one. #658 declared that surface and #670 the `Date` / `DateTime` twins, so the chains resolve now.
274
317
  #
275
- # `Time` is the right class rather than a compromise: under a zone these answer an
276
- # `ActiveSupport::TimeWithZone`, and Rails overrides `TimeWithZone#is_a?` to answer true for
277
- # `::Time`. A `Time | ActiveSupport::TimeWithZone` union was measured and rejected it types every
278
- # downstream call `Dynamic[top]`. The with-an-argument form stays `untyped` because the runtime
279
- # return depends on the argument's class AND on whether the duration carries a date-scale part
280
- # (`(1.month).ago(Date.today)` answers a `Date`; `(30.minutes).ago(Date.today)` a `TimeWithZone`).
281
- def ago: () -> Time
318
+ # Since #673 the no-argument form answers `ActiveSupport::TimeWithZone`, the class these really
319
+ # build off `::Time.current` under a zone, declared below as a `::Time` SUBCLASS. That keeps every
320
+ # `Time`-typed chain and parameter working while `1.hour.ago.time_zone` stops being a false
321
+ # `call.undefined-method`. A `Time | ActiveSupport::TimeWithZone` union was measured and rejected
322
+ # it types every downstream call `Dynamic[top]`. The with-an-argument form stays `untyped` because
323
+ # the runtime return depends on the argument's class AND on whether the duration carries a
324
+ # date-scale part (`(1.month).ago(Date.today)` answers a `Date`; `(30.minutes).ago(Date.today)` a
325
+ # `TimeWithZone`).
326
+ def ago: () -> TimeWithZone
282
327
  | (untyped time) -> untyped
283
- def until: () -> Time
328
+ def until: () -> TimeWithZone
284
329
  | (untyped time) -> untyped
285
- def before: () -> Time
330
+ def before: () -> TimeWithZone
286
331
  | (untyped time) -> untyped
287
- def since: () -> Time
332
+ def since: () -> TimeWithZone
288
333
  | (untyped time) -> untyped
289
- def from_now: () -> Time
334
+ def from_now: () -> TimeWithZone
290
335
  | (untyped time) -> untyped
291
- def after: () -> Time
336
+ def after: () -> TimeWithZone
292
337
  | (untyped time) -> untyped
293
338
 
294
339
  # `to_i` / its `in_seconds` alias return `@value.to_i` — always Integer.
@@ -315,6 +360,54 @@ module ActiveSupport
315
360
  # Float` is what stays honest without costing the common `parts[:days] * 2` chain anything.
316
361
  def parts: () -> Hash[Symbol, Integer | Float]
317
362
  end
363
+
364
+ # ---------------------------------------------------------------
365
+ # ActiveSupport::TimeWithZone — issue #673
366
+ # ---------------------------------------------------------------
367
+ #
368
+ # `Time.current` and the `Duration#ago` family really answer a `TimeWithZone`
369
+ # under a zone, and this class was not modelled at all, so the four readers a
370
+ # TWZ has and a `Time` does not — `time_zone`, `time`, `period`,
371
+ # `comparable_time` — were `call.undefined-method` on every `Time.current`
372
+ # receiver. Declaring them on `Time` would have been wrong: a plain `Time`
373
+ # genuinely lacks them.
374
+ #
375
+ # `< ::Time` rather than a `Time | TimeWithZone` union at the producers. The
376
+ # union was measured during #632 and rejected — it types every downstream
377
+ # call `Dynamic[top]`. Inheritance is the cheaper shape AND the honest one
378
+ # here: Rails overrides `TimeWithZone#is_a?` to answer true for `::Time`, so
379
+ # `Time` is the class Rails itself tells callers a TWZ is, and TWZ's
380
+ # `method_missing` forwards every name it does not define to the wrapped
381
+ # `::Time` — which is exactly what inheriting `Time`'s surface models. A TWZ
382
+ # therefore also satisfies every `Time`-typed parameter, and a chain through
383
+ # `Time.current` keeps resolving instead of collapsing.
384
+ #
385
+ # The cost, taken deliberately: `Time.current` answers a plain `Time` when
386
+ # `Time.zone` is nil, so `Time.current.time_zone` in a zone-less
387
+ # activesupport-only project is a real `NoMethodError` this now types. Rails
388
+ # sets `config.time_zone` ("UTC") by default, so the shape is rare, and the
389
+ # false positive it replaces fired on correct Rails code — which AGENTS.md
390
+ # ranks above the worst-case static reading.
391
+ class TimeWithZone < ::Time
392
+ # `Time`'s own `initialize` takes the `(year, month, …)` parts; inheriting
393
+ # it would make `TimeWithZone.new(utc, zone)` — how Rails constructs one —
394
+ # a `call.argument-type-mismatch`. `untyped` because the zone and period
395
+ # carriers (`ActiveSupport::TimeZone`, `TZInfo::TimezonePeriod`) are not
396
+ # declared anywhere in this overlay.
397
+ def initialize: (untyped utc_time, untyped time_zone, ?untyped local_time, ?untyped period) -> void
398
+
399
+ # The `attr_reader`; an `ActiveSupport::TimeZone`, undeclared here.
400
+ def time_zone: () -> untyped
401
+
402
+ # The wall-clock `::Time` in the zone. `utc` / `getutc` / `getgm` /
403
+ # `gmtime` are `Time`'s own names and stay inherited; `comparable_time` is
404
+ # the alias of `utc` that `Time` does not carry.
405
+ def time: () -> ::Time
406
+ def comparable_time: () -> ::Time
407
+
408
+ # A `TZInfo::TimezonePeriod`, undeclared here.
409
+ def period: () -> untyped
410
+ end
318
411
  end
319
412
 
320
413
  # ---------------------------------------------------------------
@@ -322,7 +415,10 @@ end
322
415
  # ---------------------------------------------------------------
323
416
 
324
417
  class Time
325
- def self.current: () -> Time
418
+ # `Time.zone ? Time.zone.now : Time.now` an `ActiveSupport::TimeWithZone` under a zone, which
419
+ # since #673 is declared as a `::Time` subclass so the four TWZ-only readers resolve without the
420
+ # rest of the chain losing its `Time` typing. See the class's own note for the zone-less cost.
421
+ def self.current: () -> ActiveSupport::TimeWithZone
326
422
  def self.zone: () -> untyped # ActiveSupport::TimeZone | nil
327
423
  def self.zone=: (String | Symbol | untyped) -> untyped
328
424
 
@@ -25,7 +25,7 @@ n = ARGV.size
25
25
  ```
26
26
 
27
27
  A vanilla checker says: `n: Integer`. Rigor says:
28
- `n: int<0, max>` (a non-negative integer — `Array#size` cannot
28
+ `n: Integer[0..]` (a non-negative integer — `Array#size` cannot
29
29
  return a negative count).
30
30
 
31
31
  The reason this matters: most diagnostics Rigor wants to fire
@@ -40,7 +40,7 @@ or narrow (`Constant<3>`, `non-empty-string`). The rest of
40
40
  this chapter is the carrier zoo.
41
41
 
42
42
  One note on notation before the zoo: angle brackets hold a
43
- concrete value or bound — `Constant<3>`, `int<0, max>`
43
+ concrete value or bound — `Constant<3>`, `Integer[0..]`
44
44
  while square brackets hold type parameters, exactly as in RBS
45
45
  — `Nominal[String]`, `Hash[K, V]`, `Dynamic[top]`. A type
46
46
  parameter names the type of a *part*: `Hash[K, V]` is a Hash
@@ -124,27 +124,30 @@ nominal carrier or `Dynamic[top]`.
124
124
 
125
125
  Some integer-valued expressions produce a known range without
126
126
  producing a single literal value. Rigor describes those with
127
- `Type::IntegerRange`, displayed as `int<min, max>`:
127
+ `Type::IntegerRange`, displayed as `Integer[…]` around the Ruby
128
+ range literal that covers the values, `Integer[1..10]`:
128
129
 
129
130
  ```ruby
130
- n = ARGV.size #=> dump_type: int<0, max>
131
- m = n + 1 #=> dump_type: int<1, max>
132
- double = n * 2 #=> dump_type: int<0, max>
131
+ n = ARGV.size #=> dump_type: Integer[0..]
132
+ m = n + 1 #=> dump_type: Integer[1..]
133
+ double = n * 2 #=> dump_type: Integer[0..]
133
134
  ```
134
135
 
135
- `max` here means "positive infinity" the upper bound is
136
- unbounded; `min`, which appears in the table below, is its
137
- mirror, "negative infinity." Multiplication preserves the
138
- floor, so `n * 2` stays `int<0, max>`.
136
+ The literal means what it means in Ruby: `Integer[0..]` is
137
+ every integer `(0..).cover?` accepts, an endless range is
138
+ unbounded above, a beginless one (`Integer[..-1]`) is unbounded
139
+ below, and `Integer[1...10]` is written back as `Integer[1..9]`
140
+ because for integers the two are the same set. Multiplication
141
+ preserves the floor, so `n * 2` stays `Integer[0..]`.
139
142
 
140
143
  A handful of common ranges have shorter names:
141
144
 
142
145
  | Spelling | Meaning |
143
146
  | --- | --- |
144
- | `positive-int` | `int<1, max>` |
145
- | `non-negative-int` | `int<0, max>` |
146
- | `negative-int` | `int<min, -1>` |
147
- | `non-positive-int` | `int<min, 0>` |
147
+ | `positive-int` | `Integer[1..]` |
148
+ | `non-negative-int` | `Integer[0..]` |
149
+ | `negative-int` | `Integer[..-1]` |
150
+ | `non-positive-int` | `Integer[..0]` |
148
151
 
149
152
  `Array#size`, `Array#length`, `Hash#size`, `String#size`, …
150
153
  all carry `non-negative-int`. `Array#count` does too. Adding
@@ -107,7 +107,7 @@ matched member:
107
107
  ```ruby
108
108
  case n
109
109
  when 0 then :zero # Constant<0>
110
- when 1..9 then :small # int<1, 9>
110
+ when 1..9 then :small # Integer[1..9]
111
111
  when 10 then :ten # Constant<10>
112
112
  else :large # everything else
113
113
  end
@@ -153,7 +153,7 @@ end
153
153
  ```
154
154
 
155
155
  The `is_a?` narrowed `x` to `Integer`, then the integer
156
- comparison narrowed it further to `int<1, max>`.
156
+ comparison narrowed it further to `Integer[1..]`.
157
157
 
158
158
  ## Integer comparisons
159
159
 
@@ -165,7 +165,7 @@ integer ranges:
165
165
  def safe_index(arr, n)
166
166
  return :empty if arr.empty?
167
167
  return :out_of_range if n < 0 || n >= arr.size
168
- # n: int<0, arr.size - 1> (in practice: int<0, max>
168
+ # n: Integer[0..arr.size - 1] (in practice: Integer[0..]
169
169
  # tightened against `n >= arr.size`)
170
170
  arr.fetch(n)
171
171
  end
@@ -176,10 +176,43 @@ Range comparisons compose with literals:
176
176
  ```ruby
177
177
  n = some_input
178
178
  if n.between?(1, 9)
179
- # n: int<1, 9>
179
+ # n: Integer[1..9]
180
180
  end
181
181
  ```
182
182
 
183
+ ## Float comparisons
184
+
185
+ A `Float` narrows on the *true* edge only. `x > 0.0` is false for
186
+ `NaN`, so the values that pass are a range, `Float[0.0..]`, but the
187
+ values that fail are everything else *plus* `NaN`, which no range
188
+ spells, so the else branch keeps `Float`:
189
+
190
+ ```ruby
191
+ x = Float(input)
192
+ if x > 0.0
193
+ # x: Float[0.0..]
194
+ else
195
+ # x: Float
196
+ end
197
+ if x < 1.0
198
+ # x: Float[...1.0] (Ruby's exclusive end)
199
+ end
200
+ if x.between?(0.0, 1.0)
201
+ # x: Float[0.0..1.0]
202
+ end
203
+ ```
204
+
205
+ `x.nan?` narrows its *false* edge to `non-nan-float`, and
206
+ `x.finite?` its *true* edge to `finite-float`. The range is the
207
+ one Ruby's `Range#cover?` would accept, so `Float[0.0..]` contains
208
+ `Float::INFINITY` and never `Float::NAN`.
209
+
210
+ Some calls produce a bounded Float on their own: `rand(0.0...1.0)`
211
+ and `Random.rand(1.0..2.0)` are the range they name, `Math.sqrt(n)` of a `non-negative-int` is
212
+ `Float[0.0..]`, and `x.clamp(0.0, 1.0)` or `x.clamp(0.0..1.0)` on a
213
+ bounded `x` is the bracket. A bounded Float knows it is not `NaN`,
214
+ so `u.nan?` folds to `false` and `u.round` to an integer range.
215
+
183
216
  ## Predicate methods on refinements
184
217
 
185
218
  Rigor recognises a small set of "type-carrier predicate
@@ -21,7 +21,7 @@ first one that produces a result:
21
21
  2. **Shape dispatch.** The receiver carries a `Tuple` /
22
22
  `HashShape` / `IntegerRange` / refinement and the method
23
23
  has a per-shape rule. `Tuple[A, B, C].size` →
24
- `Constant<3>`; `int<0, max>.zero?` → `Constant<true> |
24
+ `Constant<3>`; `Integer[0..].zero?` → `Constant<true> |
25
25
  Constant<false>`.
26
26
  3. **RBS dispatch.** The class has an RBS sig for the method.
27
27
  Argument types are checked against the parameter contract
@@ -113,11 +113,11 @@ class Counter
113
113
  end
114
114
 
115
115
  def bump
116
- @count += 1 # @count rebound to int<1, max>
116
+ @count += 1 # @count rebound to Integer[1..]
117
117
  end
118
118
 
119
119
  def value
120
- @count # int<0, max> (union of seen writes)
120
+ @count # Integer[0..] (union of seen writes)
121
121
  end
122
122
  end
123
123
  ```
@@ -128,7 +128,8 @@ A short reference:
128
128
  | Family | Names |
129
129
  | --- | --- |
130
130
  | Empty / non-empty | `non-empty-string`, `non-empty-array[T]`, `non-empty-hash[K, V]` |
131
- | Integer ranges | `positive-int`, `non-negative-int`, `negative-int`, `non-positive-int`, `non-zero-int`, `int<min, max>` |
131
+ | Integer ranges | `positive-int`, `non-negative-int`, `negative-int`, `non-positive-int`, `non-zero-int`, `Integer[min..max]` |
132
+ | Float ranges | `Float[0.0..1.0]`, `Float[0.0...1.0]`, `Float[0.0..]`, `non-nan-float`, `finite-float` |
132
133
  | String predicates | `lowercase-string`, `uppercase-string`, `numeric-string`, `decimal-int-string`, `octal-int-string`, `hex-int-string`, `literal-string` |
133
134
  | Paired complements | `non-lowercase-string`, `non-uppercase-string`, `non-numeric-string` |
134
135
  | Composed | `non-empty-lowercase-string`, `non-empty-uppercase-string`, `non-empty-literal-string` |
@@ -465,6 +466,18 @@ Notes:
465
466
  `source-rbs-synthesis-failed` `:info` diagnostic; the file
466
467
  falls back to no inline-RBS contribution and analysis
467
468
  continues.
469
+ - When a method is declared **both** in `sig/` and by an
470
+ inline annotation, the `.rbs` wins for that one method and
471
+ the inline signature is dropped — reported as a
472
+ `source-rbs-annotation-not-honoured` `:info` naming both
473
+ files. Only the overlapping member stands down; every other
474
+ annotation in the file still binds, and the class keeps its
475
+ method surface. This matters while migrating in either
476
+ direction: rbs and Steep merge the two sources without
477
+ ranking them, so left to collide they fail the class's
478
+ definition build and every call on it — real methods and
479
+ typos alike — reads `Dynamic[top]`. Delete one of the two
480
+ declarations to make the inline one take effect.
468
481
 
469
482
  Full plugin documentation, configuration options (including
470
483
  the `require_magic_comment: false` host-context override the
@@ -90,7 +90,7 @@ states:
90
90
  | `new-file` | No RBS file declares the receiver class at all. |
91
91
  | `new-method` | RBS file declares the class but not this method. |
92
92
  | `tighter-return` | RBS file declares the method, but the inferred return is a strict subtype of the declared return. |
93
- | `equivalent` | The inferred return is not a strict subtype of the declared one identical, wider, or unrelated so there is nothing to tighten. Silently skipped. |
93
+ | `equivalent` | Nothing for `sig-gen` to propose: the inferred return is identical, wider or unrelated, or it is a narrowing the generator declines (a literal under a wider declaration, anything under a declared `void`). Silently skipped. |
94
94
  | `skipped` | Disqualified for one of the reasons below. |
95
95
 
96
96
  The `sig.skipped.*` reasons are:
@@ -129,6 +129,77 @@ its reason next to the rows that did emit. In text mode a
129
129
  one-line stderr summary counts the skipped methods per
130
130
  reason instead; stdout stays paste-clean.
131
131
 
132
+ ## Recording a gap the generator cannot close
133
+
134
+ `sig-gen --diff` answers "is this declaration what the
135
+ implementation proves?" for every method it can type. The
136
+ answers it cannot give are the interesting ones: a
137
+ `tighter-return` you decided not to apply, a method it
138
+ skipped, a declaration with no `def` behind it at all. Each
139
+ of those is a hand-written type, and a hand-written type
140
+ that nobody wrote down a reason for is indistinguishable
141
+ from one nobody has looked at since.
142
+
143
+ The convention Rigor uses on its own `sig/` is a line in the
144
+ member's RBS comment:
145
+
146
+ ```rbs
147
+ class Registry
148
+ # sig-gen gap: #1234 — sig-gen types the body `untyped`
149
+ # (the ivar has no inferred field type yet), so this
150
+ # return is hand-written until it can prove one.
151
+ def resolve: (String name) -> Entry
152
+ end
153
+ ```
154
+
155
+ A `void` return needs no marker. `sig-gen` never proposes a
156
+ value for one: `void` says the return is not part of the
157
+ contract, and no inference synthesizes it, so a `void`
158
+ declaration is authored intent the way a parameter type is
159
+ ([ADR-14](../adr/14-rbs-sig-generation.md) § "The
160
+ inference-vs-RBS contradiction rule").
161
+
162
+ Neither does a declaration the body proves as a literal.
163
+ `sig-gen` will not propose `"bot"` for a `def describe: ()
164
+ -> String` whose body is the string `"bot"`: the declared
165
+ type is your abstraction over that body, and pinning the
166
+ literal would rewrite a contract every sibling class shares.
167
+ A method no `.rbs` declares is unaffected — the literal is
168
+ still the strictest thing the body proves, and that is what
169
+ gets written.
170
+
171
+ A comment rather than a `%a{…}` annotation, for three
172
+ reasons: it stays out of the `rigor:v1:` directive namespace
173
+ (`docs/type-specification/rbs-extended.md`), no engine path
174
+ reads it, and `RBS::Parser` binds it to the member, so a
175
+ check can find it on the AST instead of scanning lines. The
176
+ issue number is the load-bearing half — it points at the
177
+ engine work that would let the generator answer, which is
178
+ the [ADR-14](../adr/14-rbs-sig-generation.md) rule that a
179
+ gap pushing you toward hand-written RBS is the more valuable
180
+ signal.
181
+
182
+ Nothing in the CLI requires this. It is a convention you can
183
+ gate in your own suite: parse `sig/**/*.rbs`, run the
184
+ generator, and fail on a declaration that is neither
185
+ generated-equivalent nor marked. Rigor's own gate is
186
+ `spec/rigor/sig_gen/provenance_spec.rb`
187
+ ([ADR-107](../adr/107-checked-types-and-typeless-comments.md)
188
+ G3).
189
+
190
+ One case in that list is not a marker case at all. A
191
+ declaration with no `def` behind it may simply describe a
192
+ method Ruby generates — an `attr_*`, a `Data` member, one a
193
+ class macro defines at load — or it may be left over from a
194
+ `def` that was deleted or renamed, which nothing catches,
195
+ because `rigor check` and Steep both ask whether the
196
+ implementation matches `sig/` and never the converse.
197
+ Rigor's gate separates the two by asking its own
198
+ project index and then the loaded tree
199
+ ([#839](https://github.com/rigortype/rigor/issues/839)).
200
+ That check requires the code under `sig/`, so it stays a
201
+ repository gate: `sig-gen` in your project is unchanged.
202
+
132
203
  ## What method shapes the generator covers
133
204
 
134
205
  Slice-by-slice (each shipped via a CHANGELOG entry — this
@@ -105,7 +105,7 @@ choices.
105
105
  conditional / mapped types do not have a Rigor analogue.
106
106
  - [**Coming from PHPStan**](appendix-phpstan.md) — the
107
107
  closest peer in spirit. Identical refinement vocabulary
108
- (`non-empty-string`, `int<min, max>`, `numeric-string`,
108
+ (`non-empty-string`, `Integer[min..max]`, `numeric-string`,
109
109
  `literal-string`), `@phpstan-assert*` ↔ `RBS::Extended`,
110
110
  Type-Specifying Extensions ↔ plugins, baseline diffing.
111
111
  - [**Coming from mypy / Pyright**](appendix-mypy.md) — gradual
@@ -224,7 +224,7 @@ end
224
224
  | --- | --- | --- |
225
225
  | `positive-int` | `when n > 0` | Rigor names and carries the result. |
226
226
  | `non-empty-string` | `when s != ""` / `byte_size(s) > 0` | Rigor produces it from `unless s.empty?`. |
227
- | `int<1, 9>` | `when x in 1..9` | Rigor's range carrier handles arbitrary bounds. |
227
+ | `Integer[1..9]` | `when x in 1..9` | Rigor's range carrier handles arbitrary bounds. |
228
228
  | `non-empty-array[T]` | `when xs != []` | Rigor produces it from `unless arr.empty?`. |
229
229
  | `numeric-string` | `Integer.parse/1` + match | No direct Elixir analogue. |
230
230
 
@@ -283,7 +283,7 @@ The other direction:
283
283
  and Elixir's types do not fold call results to singleton
284
284
  types this way.
285
285
  - **Named refinement carriers.** `non-empty-string`,
286
- `positive-int`, `int<1, 9>`, `numeric-string` — first-class,
286
+ `positive-int`, `Integer[1..9]`, `numeric-string` — first-class,
287
287
  named, and flowed onward from a guard.
288
288
  - **Inferred object shapes and capability roles.** Beyond
289
289
  behaviours and protocols, Rigor infers anonymous structural
@@ -243,7 +243,7 @@ carriers that encode an invariant on the value.
243
243
  | --- | --- | --- |
244
244
  | `non-empty-string` | runtime `if len(s) == 0` check | Rigor produces it from `unless s.empty?`. |
245
245
  | `positive-int` | runtime `if n <= 0` check | Rigor narrows from `n > 0`. |
246
- | `int<1, 9>` | runtime range check | Rigor's range carrier handles arbitrary bounds. |
246
+ | `Integer[1..9]` | runtime range check | Rigor's range carrier handles arbitrary bounds. |
247
247
  | `numeric-string` | `strconv.Atoi` + error check | No type-level analogue in Go. |
248
248
  | `non-empty-array[T]` | runtime `len(xs) == 0` check | Rigor produces it from `unless arr.empty?`. |
249
249
 
@@ -311,7 +311,7 @@ is deliberately minimal:
311
311
  - **Constant folding through method calls.** `"foo".upcase` is
312
312
  `Constant<"FOO">`, not `string`.
313
313
  - **Refinements.** `non-empty-string`, `positive-int`,
314
- `int<1, 9>` — invariants on the value, no runtime check needed
314
+ `Integer[1..9]` — invariants on the value, no runtime check needed
315
315
  to know them statically.
316
316
  - **Inferred shapes beyond interfaces.** Rigor infers anonymous
317
317
  object shapes and capability roles, not just satisfaction of a
@@ -292,7 +292,7 @@ class refinement carriers, produced automatically by narrowing.
292
292
  | --- | --- | --- |
293
293
  | `non-empty-string` | a `NonEmptyString` value class wrapping validation | Rigor produces it from `unless s.empty?`, no wrapper type. |
294
294
  | `positive-int` | a `PositiveInt` value object, or a runtime guard | Rigor narrows from `n > 0`. |
295
- | `int<1, 9>` | an `enum` of nine constants, or a range check | Rigor's range carrier handles arbitrary bounds without enumerating them. |
295
+ | `Integer[1..9]` | an `enum` of nine constants, or a range check | Rigor's range carrier handles arbitrary bounds without enumerating them. |
296
296
  | `numeric-string` | `string` + `int.TryParse` discipline | No type-level analogue in either language. |
297
297
  | `non-empty-array[T]` | a non-empty-collection value class | Rigor produces it from `unless arr.empty?`. |
298
298
 
@@ -379,7 +379,7 @@ longer than you might expect, because neither has literal types:
379
379
  `Constant<"FOO">`, not `String`. Rigor catalogues which
380
380
  built-in methods are pure and folds through them.
381
381
  - **First-class refinements.** `non-empty-string`, `positive-int`,
382
- `int<1, 9>`, `numeric-string` — invariants on ordinary types,
382
+ `Integer[1..9]`, `numeric-string` — invariants on ordinary types,
383
383
  no value-class wrapper.
384
384
  - **Structural facets without a declaration.** A Ruby object that
385
385
  has the right methods satisfies an RBS `interface` (a
@@ -71,7 +71,7 @@ features one at a time (`Literal`, `LiteralString`, `TypeIs`,
71
71
  | `non-empty-string` | (no built-in; PEP 675's `LiteralString` is closest in spirit but different in semantics) |
72
72
  | `literal-string` | `LiteralString` (PEP 675) — provably built from source-code literals. **Direct match.** |
73
73
  | `positive-int` | (no built-in; convention is `Annotated[int, Gt(0)]` with third-party validators) |
74
- | `int<min, max>` | (no built-in; same `Annotated[int, Range(...)]` convention) |
74
+ | `Integer[min..max]` | (no built-in; same `Annotated[int, Range(...)]` convention) |
75
75
  | `numeric-string` | (no built-in) |
76
76
  | `non-empty-array[T]` | (no built-in; some libraries use `tuple[T, *tuple[T, ...]]`) |
77
77
  | `Constant<42>` | `Literal[42]` |
@@ -286,7 +286,7 @@ draws the distinction in full.
286
286
  `Hash`.
287
287
  - **First-class refinement carriers with narrowing.**
288
288
  `non-empty-string`, `positive-int`, `numeric-string`,
289
- `int<min, max>` — values restricted by predicate, narrowed
289
+ `Integer[min..max]` — values restricted by predicate, narrowed
290
290
  by the corresponding Ruby predicate methods.
291
291
  - **No-false-positives stance.** mypy will warn about dynamic
292
292
  code unless `--no-warn-unused-ignores` or `--ignore-missing-imports`
@@ -53,7 +53,7 @@ is the closest match of any peer.
53
53
  | `numeric-string` | `numeric-string` | Identical. |
54
54
  | `lowercase-string` | `lowercase-string` | Identical. |
55
55
  | `class-string` | `Singleton[T]` | Equivalent shape. |
56
- | `int<1, 9>` | `int<1, 9>` | **Identical syntax.** |
56
+ | `int<1, 9>` | `Integer[1..9]` | Same carrier; Rigor spells the bound as the Ruby range literal `Range#cover?` reads (`Integer[1..]`, `Integer[..9]`). |
57
57
  | `positive-int` | `positive-int` | Identical. |
58
58
  | `negative-int` | `negative-int` | Identical. |
59
59
  | `non-zero-int` | `non-zero-int` | Identical. |
@@ -274,7 +274,7 @@ by narrowing.
274
274
  | --- | --- | --- |
275
275
  | `non-empty-string` | `struct NonEmptyString(String)` newtype | Rigor produces it from `unless s.empty?`, no wrapper. |
276
276
  | `positive-int` | `struct PositiveInt(u32)` newtype | Rigor narrows from `n > 0`. |
277
- | `int<1, 9>` | newtype + range check, or const generics gymnastics | Rigor's range carrier handles arbitrary bounds directly. |
277
+ | `Integer[1..9]` | newtype + range check, or const generics gymnastics | Rigor's range carrier handles arbitrary bounds directly. |
278
278
  | `numeric-string` | newtype wrapping validated parse | No type-level analogue. |
279
279
  | `non-empty-array[T]` | newtype over `Vec<T>` | Rigor produces it from `unless arr.empty?`. |
280
280