rigortype 0.3.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (634) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/core_overlay/enumerable.rbs +51 -0
  5. data/data/core_overlay/enumerator.rbs +84 -0
  6. data/data/core_overlay/hash_rbs3.rbs +41 -0
  7. data/data/core_overlay/process.rbs +40 -0
  8. data/data/core_overlay/string_io.rbs +33 -0
  9. data/data/effects/core.yml +3 -3
  10. data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
  11. data/docs/handbook/02-everyday-types.md +17 -14
  12. data/docs/handbook/03-narrowing.md +132 -14
  13. data/docs/handbook/04-tuples-and-shapes.md +8 -6
  14. data/docs/handbook/05-methods-and-blocks.md +1 -1
  15. data/docs/handbook/06-classes.md +2 -2
  16. data/docs/handbook/07-rbs-and-extended.md +19 -1
  17. data/docs/handbook/10-sorbet.md +9 -10
  18. data/docs/handbook/11-sig-gen.md +525 -13
  19. data/docs/handbook/README.md +1 -1
  20. data/docs/handbook/appendix-elixir.md +2 -2
  21. data/docs/handbook/appendix-go.md +2 -2
  22. data/docs/handbook/appendix-java-csharp.md +2 -2
  23. data/docs/handbook/appendix-mypy.md +2 -2
  24. data/docs/handbook/appendix-phpstan.md +1 -1
  25. data/docs/handbook/appendix-rust.md +1 -1
  26. data/docs/handbook/appendix-type-theory.md +4 -4
  27. data/docs/handbook/appendix-typescript.md +1 -1
  28. data/docs/llms.txt +2 -0
  29. data/docs/manual/02-cli-reference.md +95 -6
  30. data/docs/manual/03-configuration.md +18 -0
  31. data/docs/manual/04-diagnostics.md +15 -0
  32. data/docs/manual/07-plugins.md +15 -5
  33. data/docs/manual/08-skills.md +23 -2
  34. data/docs/manual/10-mcp-server.md +3 -2
  35. data/docs/manual/11-ci.md +9 -0
  36. data/docs/manual/15-type-protection-coverage.md +8 -0
  37. data/docs/manual/16-rbs-extended-annotations.md +46 -8
  38. data/docs/manual/18-removing-dead-code.md +10 -8
  39. data/docs/manual/19-effect-labels.md +10 -0
  40. data/docs/manual/plugins/README.md +7 -0
  41. data/docs/manual/plugins/rigor-actioncable.md +8 -1
  42. data/docs/manual/plugins/rigor-actionmailer.md +7 -0
  43. data/docs/manual/plugins/rigor-actionpack.md +261 -0
  44. data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
  45. data/docs/manual/plugins/rigor-activejob.md +7 -0
  46. data/docs/manual/plugins/rigor-activerecord.md +138 -4
  47. data/docs/manual/plugins/rigor-activestorage.md +8 -1
  48. data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
  49. data/docs/manual/plugins/rigor-grape.md +106 -0
  50. data/docs/manual/plugins/rigor-graphql.md +23 -2
  51. data/docs/manual/plugins/rigor-pundit.md +8 -1
  52. data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
  53. data/docs/manual/plugins/rigor-rails-routes.md +7 -0
  54. data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
  55. data/docs/manual/plugins/rigor-sidekiq.md +8 -1
  56. data/docs/manual/plugins/rigor-sorbet.md +21 -5
  57. data/exe/rigor +19 -4
  58. data/lib/rigor/analysis/baseline.rb +3 -3
  59. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  60. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  61. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
  62. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  63. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
  64. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
  65. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  66. data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
  67. data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
  68. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  69. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  70. data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
  71. data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
  72. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
  73. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  74. data/lib/rigor/analysis/check_rules.rb +718 -79
  75. data/lib/rigor/analysis/crash_signature.rb +8 -24
  76. data/lib/rigor/analysis/dependency_recorder.rb +23 -0
  77. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  78. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  79. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  80. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  81. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  82. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  83. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  84. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  85. data/lib/rigor/analysis/fact_store.rb +15 -1
  86. data/lib/rigor/analysis/incremental.rb +42 -0
  87. data/lib/rigor/analysis/incremental_session.rb +117 -26
  88. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  89. data/lib/rigor/analysis/project_scan.rb +11 -1
  90. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  91. data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -5
  92. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  93. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  94. data/lib/rigor/analysis/reachability/scan_cache.rb +3 -2
  95. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  96. data/lib/rigor/analysis/result.rb +1 -3
  97. data/lib/rigor/analysis/rule_catalog.rb +131 -1
  98. data/lib/rigor/analysis/run_cache_key.rb +21 -12
  99. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  100. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  101. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  102. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +340 -33
  103. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  104. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
  105. data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
  106. data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
  107. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  108. data/lib/rigor/analysis/runner.rb +407 -51
  109. data/lib/rigor/analysis/template_unit_collector.rb +303 -0
  110. data/lib/rigor/analysis/template_unit_paths.rb +91 -0
  111. data/lib/rigor/analysis/template_unit_positions.rb +293 -0
  112. data/lib/rigor/analysis/template_units.rb +399 -0
  113. data/lib/rigor/analysis/worker_session.rb +62 -20
  114. data/lib/rigor/bleeding_edge.rb +10 -24
  115. data/lib/rigor/builtins/hkt_builtins.rb +3 -2
  116. data/lib/rigor/builtins/imported_refinements.rb +159 -21
  117. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  118. data/lib/rigor/builtins/regex_refinement.rb +21 -13
  119. data/lib/rigor/builtins/static_return_refinements.rb +7 -6
  120. data/lib/rigor/cache/annotation_location.rb +2 -4
  121. data/lib/rigor/cache/descriptor.rb +63 -20
  122. data/lib/rigor/cache/engine_source.rb +33 -5
  123. data/lib/rigor/cache/file_digest.rb +9 -1
  124. data/lib/rigor/cache/incremental_snapshot.rb +72 -6
  125. data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
  126. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  127. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  128. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  129. data/lib/rigor/cache/rbs_descriptor.rb +112 -14
  130. data/lib/rigor/cache/rbs_environment.rb +8 -4
  131. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  132. data/lib/rigor/cache/store.rb +76 -32
  133. data/lib/rigor/ci_detector.rb +1 -0
  134. data/lib/rigor/cli/annotate_command.rb +5 -6
  135. data/lib/rigor/cli/check_command.rb +57 -10
  136. data/lib/rigor/cli/check_invocation.rb +6 -11
  137. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  138. data/lib/rigor/cli/coverage_command.rb +1 -1
  139. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  140. data/lib/rigor/cli/coverage_scan.rb +1 -5
  141. data/lib/rigor/cli/diff_command.rb +1 -1
  142. data/lib/rigor/cli/doc_links.rb +4 -4
  143. data/lib/rigor/cli/docs_command.rb +6 -6
  144. data/lib/rigor/cli/doctor_command.rb +85 -38
  145. data/lib/rigor/cli/effects_command.rb +2 -2
  146. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  147. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  148. data/lib/rigor/cli/explain_command.rb +34 -1
  149. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  150. data/lib/rigor/cli/lsp_command.rb +1 -1
  151. data/lib/rigor/cli/mcp_command.rb +1 -1
  152. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  153. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  154. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  155. data/lib/rigor/cli/plugin_command.rb +4 -4
  156. data/lib/rigor/cli/plugins_command.rb +2 -1
  157. data/lib/rigor/cli/prism_colorizer.rb +1 -2
  158. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  159. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  160. data/lib/rigor/cli/sig_gen_command.rb +212 -31
  161. data/lib/rigor/cli/skill_command.rb +2 -2
  162. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  163. data/lib/rigor/cli/skill_describe.rb +35 -35
  164. data/lib/rigor/cli/trace_command.rb +4 -4
  165. data/lib/rigor/cli/trace_renderer.rb +5 -6
  166. data/lib/rigor/cli/triage_command.rb +1 -1
  167. data/lib/rigor/cli/type_of_command.rb +29 -11
  168. data/lib/rigor/cli/type_of_renderer.rb +20 -9
  169. data/lib/rigor/cli/type_of_template_probe.rb +189 -0
  170. data/lib/rigor/cli/type_scan_command.rb +4 -4
  171. data/lib/rigor/cli/unused_command.rb +11 -3
  172. data/lib/rigor/cli/upgrade_command.rb +1 -1
  173. data/lib/rigor/cli.rb +89 -9
  174. data/lib/rigor/config_audit.rb +30 -5
  175. data/lib/rigor/configuration/severity_profile.rb +25 -9
  176. data/lib/rigor/configuration.rb +109 -7
  177. data/lib/rigor/effects/ancestry_recorder.rb +191 -0
  178. data/lib/rigor/effects/attribution.rb +12 -5
  179. data/lib/rigor/effects/callee_rule.rb +368 -0
  180. data/lib/rigor/effects/catalog.rb +7 -4
  181. data/lib/rigor/effects/collector.rb +11 -5
  182. data/lib/rigor/effects/config_envelopes.rb +15 -10
  183. data/lib/rigor/effects/definition_context.rb +179 -0
  184. data/lib/rigor/effects/definition_lines.rb +25 -6
  185. data/lib/rigor/effects/effect_table.rb +11 -4
  186. data/lib/rigor/effects/entry_points.rb +0 -2
  187. data/lib/rigor/effects/envelope_check.rb +9 -9
  188. data/lib/rigor/effects/envelope_index.rb +20 -8
  189. data/lib/rigor/effects/file_collection.rb +59 -5
  190. data/lib/rigor/effects/framework_units.rb +5 -7
  191. data/lib/rigor/effects/identity.rb +20 -6
  192. data/lib/rigor/effects/inline_anchor.rb +7 -7
  193. data/lib/rigor/effects/label_intent.rb +3 -4
  194. data/lib/rigor/effects/liskov_check.rb +8 -8
  195. data/lib/rigor/effects/local_ownership.rb +38 -12
  196. data/lib/rigor/effects/method_key.rb +22 -1
  197. data/lib/rigor/effects/mutation_classifier.rb +23 -12
  198. data/lib/rigor/effects/plugin_facts.rb +47 -37
  199. data/lib/rigor/effects/propagator.rb +297 -19
  200. data/lib/rigor/effects/registry.rb +1 -1
  201. data/lib/rigor/effects/scanner.rb +122 -73
  202. data/lib/rigor/effects/signature_sources.rb +4 -6
  203. data/lib/rigor/effects/snapshot.rb +12 -11
  204. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  205. data/lib/rigor/effects/summary.rb +27 -4
  206. data/lib/rigor/effects/unit_scan.rb +393 -38
  207. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  208. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  209. data/lib/rigor/effects/visibility.rb +101 -0
  210. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  211. data/lib/rigor/environment/class_registry.rb +3 -1
  212. data/lib/rigor/environment/failure_slot.rb +2 -2
  213. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  214. data/lib/rigor/environment/lockfile_resolver.rb +68 -4
  215. data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
  216. data/lib/rigor/environment/member_consistency.rb +298 -0
  217. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  218. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  219. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  220. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  221. data/lib/rigor/environment/rbs_loader.rb +674 -79
  222. data/lib/rigor/environment.rb +174 -24
  223. data/lib/rigor/flow_contribution/merger.rb +0 -2
  224. data/lib/rigor/flow_contribution.rb +11 -13
  225. data/lib/rigor/hashing/xxh3.rb +264 -0
  226. data/lib/rigor/inference/acceptance.rb +292 -17
  227. data/lib/rigor/inference/block_auto_splat.rb +216 -0
  228. data/lib/rigor/inference/block_call_timing.rb +338 -0
  229. data/lib/rigor/inference/block_parameter_binder.rb +75 -30
  230. data/lib/rigor/inference/block_repetition.rb +71 -0
  231. data/lib/rigor/inference/body_fixpoint.rb +7 -6
  232. data/lib/rigor/inference/budget_trace.rb +3 -6
  233. data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
  234. data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
  235. data/lib/rigor/inference/captured_locals.rb +389 -18
  236. data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
  237. data/lib/rigor/inference/content_join.rb +200 -27
  238. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  239. data/lib/rigor/inference/def_return_typer.rb +11 -7
  240. data/lib/rigor/inference/define_method_block_self.rb +64 -0
  241. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  242. data/lib/rigor/inference/element_read_widening.rb +180 -0
  243. data/lib/rigor/inference/error_info.rb +196 -0
  244. data/lib/rigor/inference/expression_typer.rb +2124 -468
  245. data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
  246. data/lib/rigor/inference/fork_map.rb +5 -7
  247. data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
  248. data/lib/rigor/inference/global_write_census.rb +239 -0
  249. data/lib/rigor/inference/guard_rebinding.rb +447 -0
  250. data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
  251. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  252. data/lib/rigor/inference/hkt_registry.rb +4 -7
  253. data/lib/rigor/inference/index_write_widening.rb +16 -7
  254. data/lib/rigor/inference/indexed_narrowing.rb +61 -9
  255. data/lib/rigor/inference/jump_targets.rb +82 -0
  256. data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
  257. data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
  258. data/lib/rigor/inference/last_line.rb +340 -0
  259. data/lib/rigor/inference/last_status.rb +144 -0
  260. data/lib/rigor/inference/macro_block_self_type.rb +168 -21
  261. data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
  262. data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
  263. data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
  264. data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
  265. data/lib/rigor/inference/match_rebinding.rb +392 -0
  266. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  267. data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
  268. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  269. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  270. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  271. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
  272. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  273. data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
  274. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
  275. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  276. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  277. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
  278. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  279. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  280. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +165 -111
  281. data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
  282. data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
  283. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  284. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
  285. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  286. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  287. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  288. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
  289. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  290. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
  291. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  292. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  293. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -6
  294. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  295. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  296. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  297. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  298. data/lib/rigor/inference/method_dispatcher.rb +225 -25
  299. data/lib/rigor/inference/method_parameter_binder.rb +12 -9
  300. data/lib/rigor/inference/multi_target_binder.rb +341 -55
  301. data/lib/rigor/inference/mutation_rejoin.rb +171 -0
  302. data/lib/rigor/inference/mutation_widening.rb +156 -85
  303. data/lib/rigor/inference/narrowing.rb +835 -162
  304. data/lib/rigor/inference/operand_effects.rb +167 -0
  305. data/lib/rigor/inference/operand_walk.rb +88 -0
  306. data/lib/rigor/inference/optimistic_origin.rb +153 -16
  307. data/lib/rigor/inference/origin_lookup.rb +2 -3
  308. data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
  309. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  310. data/lib/rigor/inference/precision_scanner.rb +3 -4
  311. data/lib/rigor/inference/project_method_ownership.rb +136 -0
  312. data/lib/rigor/inference/project_patched_methods.rb +9 -4
  313. data/lib/rigor/inference/project_patched_scanner.rb +10 -6
  314. data/lib/rigor/inference/protection_scanner.rb +1 -2
  315. data/lib/rigor/inference/range_constant.rb +57 -0
  316. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  317. data/lib/rigor/inference/receiver_alias.rb +93 -4
  318. data/lib/rigor/inference/receiver_blind_block.rb +219 -0
  319. data/lib/rigor/inference/refinement_mutation.rb +76 -0
  320. data/lib/rigor/inference/repeated_or_writes.rb +463 -0
  321. data/lib/rigor/inference/return_barrier.rb +54 -0
  322. data/lib/rigor/inference/rewrite_mutation.rb +120 -0
  323. data/lib/rigor/inference/scope_indexer.rb +5228 -627
  324. data/lib/rigor/inference/statement_evaluator.rb +3452 -595
  325. data/lib/rigor/inference/stored_block_call.rb +54 -0
  326. data/lib/rigor/inference/string_mutation.rb +97 -0
  327. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  328. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  329. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  330. data/lib/rigor/inference/unknown_store_widening.rb +200 -0
  331. data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
  332. data/lib/rigor/inference/version_guard.rb +28 -21
  333. data/lib/rigor/inference/void_origin.rb +3 -3
  334. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  335. data/lib/rigor/language_server/buffer_table.rb +4 -4
  336. data/lib/rigor/language_server/completion_provider.rb +1 -1
  337. data/lib/rigor/language_server/debouncer.rb +0 -1
  338. data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
  339. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  340. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  341. data/lib/rigor/language_server/hover_provider.rb +1 -1
  342. data/lib/rigor/language_server/hover_renderer.rb +4 -4
  343. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  344. data/lib/rigor/language_server/project_context.rb +7 -8
  345. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  346. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  347. data/lib/rigor/language_server/server.rb +9 -9
  348. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  349. data/lib/rigor/language_server/uri.rb +1 -1
  350. data/lib/rigor/mcp/server.rb +2 -1
  351. data/lib/rigor/plugin/base.rb +258 -26
  352. data/lib/rigor/plugin/box_probe.rb +91 -0
  353. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  354. data/lib/rigor/plugin/effect_attribution.rb +65 -11
  355. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  356. data/lib/rigor/plugin/fact_store.rb +6 -6
  357. data/lib/rigor/plugin/io_boundary.rb +93 -8
  358. data/lib/rigor/plugin/isolation.rb +29 -6
  359. data/lib/rigor/plugin/loader.rb +33 -13
  360. data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
  361. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  362. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  363. data/lib/rigor/plugin/manifest.rb +94 -11
  364. data/lib/rigor/plugin/registry.rb +76 -12
  365. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
  366. data/lib/rigor/plugin/template_unit.rb +196 -0
  367. data/lib/rigor/plugin/trust_policy.rb +2 -4
  368. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  369. data/lib/rigor/plugin.rb +1 -0
  370. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  371. data/lib/rigor/project_environment.rb +138 -0
  372. data/lib/rigor/protection/analysis_guard.rb +93 -14
  373. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  374. data/lib/rigor/protection/dependency_closure.rb +5 -8
  375. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  376. data/lib/rigor/protection/discovery_seed.rb +14 -12
  377. data/lib/rigor/protection/kill_signature.rb +1 -4
  378. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  379. data/lib/rigor/protection/mutation_cache.rb +7 -11
  380. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  381. data/lib/rigor/protection/mutator.rb +2 -1
  382. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  383. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  384. data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
  385. data/lib/rigor/rbs_extended/reporter.rb +36 -10
  386. data/lib/rigor/rbs_extended.rb +88 -21
  387. data/lib/rigor/reflection/constant_ancestors.rb +97 -0
  388. data/lib/rigor/reflection/constant_path.rb +139 -0
  389. data/lib/rigor/reflection.rb +93 -110
  390. data/lib/rigor/runtime/jit.rb +6 -8
  391. data/lib/rigor/scope/discovery_index.rb +165 -2
  392. data/lib/rigor/scope.rb +1041 -32
  393. data/lib/rigor/sig_gen/alias_index.rb +289 -0
  394. data/lib/rigor/sig_gen/classification.rb +22 -5
  395. data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
  396. data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
  397. data/lib/rigor/sig_gen/generator.rb +673 -53
  398. data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
  399. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  400. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  401. data/lib/rigor/sig_gen/method_candidate.rb +47 -3
  402. data/lib/rigor/sig_gen/observation_collector.rb +18 -16
  403. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  404. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  405. data/lib/rigor/sig_gen/renderer.rb +128 -14
  406. data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -0
  407. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  408. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  409. data/lib/rigor/sig_gen/write_result.rb +19 -3
  410. data/lib/rigor/sig_gen/writer.rb +189 -41
  411. data/lib/rigor/sig_gen.rb +3 -0
  412. data/lib/rigor/signature_path_audit.rb +153 -6
  413. data/lib/rigor/source/literals.rb +0 -23
  414. data/lib/rigor/source/node_children.rb +0 -2
  415. data/lib/rigor/source/node_locator.rb +5 -11
  416. data/lib/rigor/source/node_walker.rb +0 -6
  417. data/lib/rigor/source/parameter_envelope.rb +72 -0
  418. data/lib/rigor/source.rb +1 -0
  419. data/lib/rigor/triage/catalogue.rb +1 -3
  420. data/lib/rigor/triage.rb +3 -5
  421. data/lib/rigor/type/accepts_result.rb +24 -6
  422. data/lib/rigor/type/combinator.rb +144 -5
  423. data/lib/rigor/type/data_class.rb +2 -2
  424. data/lib/rigor/type/data_instance.rb +4 -4
  425. data/lib/rigor/type/difference.rb +1 -0
  426. data/lib/rigor/type/float_range.rb +128 -0
  427. data/lib/rigor/type/hash_shape.rb +6 -6
  428. data/lib/rigor/type/integer_range.rb +13 -8
  429. data/lib/rigor/type/nominal.rb +8 -2
  430. data/lib/rigor/type/refined.rb +4 -3
  431. data/lib/rigor/type/struct_class.rb +3 -3
  432. data/lib/rigor/type/struct_instance.rb +4 -4
  433. data/lib/rigor/type.rb +1 -0
  434. data/lib/rigor/type_node/generic.rb +1 -1
  435. data/lib/rigor/type_node/range_literal.rb +25 -0
  436. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  437. data/lib/rigor/type_node.rb +1 -0
  438. data/lib/rigor/version.rb +1 -1
  439. data/lib/rigor.rb +1 -0
  440. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  441. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  442. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  443. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
  444. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
  445. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  446. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  447. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  448. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +14 -4
  449. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -30
  450. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  451. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  452. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
  453. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
  454. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
  455. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
  456. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
  457. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
  458. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
  459. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  460. data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
  461. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
  462. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
  463. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
  464. data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
  465. data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
  466. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  467. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -1
  468. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  469. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  470. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  471. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +10 -5
  472. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
  473. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
  474. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
  475. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
  476. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  477. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  478. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +322 -41
  479. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  480. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
  481. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
  482. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
  483. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  484. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +290 -19
  485. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  486. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -0
  487. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  488. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  489. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  490. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  491. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  492. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  493. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  494. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +2 -0
  495. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  496. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  497. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  498. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  499. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
  500. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  501. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  502. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
  503. data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
  504. data/plugins/rigor-grape/sig/grape.rbs +263 -0
  505. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  506. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
  507. data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
  508. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  509. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  510. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -3
  511. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  512. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  513. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  514. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  515. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  516. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +12 -7
  517. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  518. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  519. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  520. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +36 -37
  521. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  522. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -10
  523. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
  524. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -2
  525. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  526. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  527. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  528. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  529. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +10 -17
  530. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  531. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  532. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
  533. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
  534. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  535. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  536. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  537. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  538. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  539. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  540. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  541. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  542. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  543. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  544. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  545. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  546. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +2 -1
  547. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  548. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  549. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +10 -5
  550. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  551. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  552. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  553. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  554. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  555. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  556. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +3 -5
  557. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  558. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
  559. data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
  560. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  561. data/sig/rigor/analysis/baseline.rbs +69 -7
  562. data/sig/rigor/analysis/fact_store.rbs +1 -1
  563. data/sig/rigor/analysis/project_scan.rbs +74 -0
  564. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  565. data/sig/rigor/cache.rbs +2 -3
  566. data/sig/rigor/effects/config_envelopes.rbs +100 -0
  567. data/sig/rigor/effects/effect_table.rbs +60 -0
  568. data/sig/rigor/effects/envelope.rbs +97 -0
  569. data/sig/rigor/effects/envelope_index.rbs +33 -0
  570. data/sig/rigor/effects/file_collection.rbs +81 -0
  571. data/sig/rigor/effects/label.rbs +26 -0
  572. data/sig/rigor/effects/label_set.rbs +46 -0
  573. data/sig/rigor/effects/method_key.rbs +24 -0
  574. data/sig/rigor/effects/origin.rbs +50 -0
  575. data/sig/rigor/effects/plugin_facts.rbs +141 -0
  576. data/sig/rigor/effects/registry.rbs +68 -0
  577. data/sig/rigor/effects/summary.rbs +50 -0
  578. data/sig/rigor/effects/taint_cause.rbs +12 -0
  579. data/sig/rigor/environment.rbs +17 -10
  580. data/sig/rigor/inference/optimistic_origin.rbs +10 -0
  581. data/sig/rigor/inference.rbs +9 -14
  582. data/sig/rigor/plugin/additional_initializer.rbs +36 -0
  583. data/sig/rigor/plugin/base.rbs +30 -7
  584. data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
  585. data/sig/rigor/plugin/effect_attribution.rbs +66 -0
  586. data/sig/rigor/plugin/effect_edge.rbs +33 -0
  587. data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
  588. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  589. data/sig/rigor/plugin/loader.rbs +3 -3
  590. data/sig/rigor/plugin/manifest.rbs +51 -11
  591. data/sig/rigor/plugin/protocol_contract.rbs +68 -0
  592. data/sig/rigor/plugin/registry.rbs +62 -1
  593. data/sig/rigor/plugin.rbs +1 -1
  594. data/sig/rigor/rbs_extended.rbs +1 -1
  595. data/sig/rigor/reflection.rbs +9 -6
  596. data/sig/rigor/scope.rbs +148 -12
  597. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
  598. data/sig/rigor/source.rbs +4 -4
  599. data/sig/rigor/testing.rbs +10 -4
  600. data/sig/rigor/type.rbs +33 -1
  601. data/sig/rigor.rbs +39 -20
  602. data/skills/rigor-ask/SKILL.md +16 -11
  603. data/skills/rigor-baseline-reduce/SKILL.md +4 -6
  604. data/skills/rigor-ci-setup/SKILL.md +17 -21
  605. data/skills/rigor-doctor/SKILL.md +24 -22
  606. data/skills/rigor-doctor/references/01-checks.md +97 -33
  607. data/skills/rigor-editor-setup/SKILL.md +6 -4
  608. data/skills/rigor-mcp-setup/SKILL.md +5 -4
  609. data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
  610. data/skills/rigor-next-steps/SKILL.md +4 -2
  611. data/skills/rigor-plugin-author/SKILL.md +19 -23
  612. data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
  613. data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
  614. data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
  615. data/skills/rigor-plugin-review/SKILL.md +6 -4
  616. data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
  617. data/skills/rigor-plugin-tune/SKILL.md +4 -2
  618. data/skills/rigor-project-init/SKILL.md +15 -10
  619. data/skills/rigor-project-init/references/01-detect.md +13 -9
  620. data/skills/rigor-project-init/references/02-configure.md +43 -14
  621. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
  622. data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
  623. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  624. data/skills/rigor-protection-uplift/SKILL.md +4 -6
  625. data/skills/rigor-rbs-setup/SKILL.md +4 -2
  626. data/skills/rigor-type-oracle/SKILL.md +226 -0
  627. data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
  628. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  629. data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
  630. data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
  631. data/skills/rigor-upgrade/SKILL.md +13 -8
  632. metadata +133 -3
  633. data/lib/rigor/cli/probe_environment.rb +0 -85
  634. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -31,7 +31,7 @@
31
31
  #
32
32
  # Every method carries an effect annotation, and the split down
33
33
  # the middle of this file is the whole point of the Rails effect
34
- # layer: a **builder** is `%a{pure}` because it issues nothing —
34
+ # layer: a **query builder** is `%a{pure}` because it issues nothing —
35
35
  # `where` composes an Arel tree and returns a relation — while a
36
36
  # **materializer** is `%a{rigor:v1:effect io.db.read}` because it
37
37
  # is the call that finally runs the query. A presenter that builds
@@ -46,6 +46,136 @@
46
46
  # `Enumerable` surface, which materialise by delegating to `each`
47
47
  # — are covered by the plugin's `effect_attributions:` instead,
48
48
  # because declaring them here would change how they TYPE.
49
+ #
50
+ # ## Every bound here also bounds the association's relations
51
+ #
52
+ # A `has_many` reader returns an
53
+ # `ActiveRecord::Associations::CollectionProxy`, and a query
54
+ # builder called on the proxy (`user.posts.where(…)`) returns an
55
+ # `ActiveRecord::AssociationRelation`. Both are Relation subclasses
56
+ # that still hold the association, and the plugin types both as
57
+ # `Relation[Model]`. A call on either imports this file's bound, so
58
+ # each annotation has to hold for all three classes. The facts below
59
+ # are checked against activerecord 8.1.3.1.
60
+ #
61
+ # Where one of them adds records to the association's in-memory
62
+ # target or discards them from it, the bound carries `mutate.self`:
63
+ #
64
+ # - `build` / `new`, `create` / `create!` and the `find_or_*`,
65
+ # `create_or_find_by` and `first_or_*` builders reach
66
+ # `@association.build` / `create`, which call `add_to_target`. On
67
+ # the proxy they are overrides. On an AssociationRelation they are
68
+ # its private `_new` / `_create`.
69
+ # - `reset`, `reload`, `delete_all` and `destroy_all` on the proxy
70
+ # reset the association, which drops every record built there and
71
+ # not yet saved. So do `update_all` and `touch_all`: the proxy does
72
+ # not override them, and Relation's version ends with
73
+ # `.tap { reset }`, which reaches the proxy's `reset`.
74
+ # - `insert`, `insert!` and `upsert`, and their `_all` forms, on the
75
+ # proxy reset the target only when it holds no unsaved record.
76
+ # Rails 8.1 deprecates the other branch and says those records will
77
+ # be lost in 8.2. The bound is written for 8.2, and it over-states
78
+ # 8.1.
79
+ #
80
+ # So `user.posts.build` changes an object the caller can still reach
81
+ # through `user`: `user.posts.size` counts the new record, and
82
+ # `user.save` saves it. On a plain Relation, `new` and `build` change
83
+ # nothing, so `mutate.self` over-states them there. The type cannot
84
+ # tell the receivers apart, and an upper bound may be too wide but
85
+ # never too narrow.
86
+ #
87
+ # Loading is not counted as a change. `to_a` and `load` fill the
88
+ # target with what the database holds, which is the `io.db.read` they
89
+ # already carry.
90
+ #
91
+ # `mutate.self` is written from the callee's side, as any envelope
92
+ # is: the receiver changes itself. A caller imports the label
93
+ # unchanged, so in the caller's row it names the proxy it called,
94
+ # not the caller's own `self`.
95
+ #
96
+ # `insert`, `insert!` and `upsert` are declared here, beside their
97
+ # `_all` forms, because a plain Relation defines them too. The
98
+ # declaration changes no result type: an undeclared method on this
99
+ # open receiver already types as `untyped`. It adds the checks any
100
+ # declared method gets, `call.wrong-arity` and
101
+ # `call.possible-nil-receiver`, and its parameter list admits every
102
+ # call Rails accepts.
103
+ #
104
+ # The writers a plain Relation does not define (`<<`, `push`,
105
+ # `append`, `concat`, `replace`, `clear`) are rows in the plugin's
106
+ # `effect_attributions:` rather than declarations here. The same rows
107
+ # cover `delete` / `destroy`, which a Relation does define, as
108
+ # `delete(id_or_array)` and `destroy(id)`, but the proxy overrides as
109
+ # `delete(*records)` and `destroy(*records)`. A declaration here
110
+ # would need a parameter list and a return wide enough for both, and
111
+ # it would give the two methods a second effect channel next to
112
+ # their rows.
113
+ #
114
+ # ## Every parameter list here also admits the overrides
115
+ #
116
+ # The rule above holds for parameter lists as well as effect bounds.
117
+ # A call on the proxy or on an AssociationRelation is checked against
118
+ # the parameter list declared here, so a declaration must accept
119
+ # every argument an override accepts, or valid Rails draws
120
+ # `call.wrong-arity`. Of the overrides this file declares, only the
121
+ # proxy's `delete_all(dependent = nil)` accepts more than the
122
+ # Relation's `delete_all`. It takes `:nullify` or `:delete_all`, so
123
+ # `delete_all` declares an optional argument. The price is that
124
+ # `delete_all(:nullify)` on a plain Relation or on an
125
+ # AssociationRelation (`user.posts.where(…)`), neither of which
126
+ # overrides it, raises `ArgumentError` and goes unreported.
127
+ #
128
+ # Of the rest, the `(...)` methods of the `insert` and `insert_all`
129
+ # families and the proxy's `delegate … to: :scope` block (the query
130
+ # builders and `scoping`) forward their arguments unchanged to the
131
+ # Relation's own methods, and the proxy's other overrides (`find`,
132
+ # `last`, `take`, `build`, `create`, `calculate`, `pluck`, …) take no
133
+ # more than the declarations here. `delete` and `destroy`, whose
134
+ # overrides take more arguments than the Relation's, stay undeclared
135
+ # for the reasons above.
136
+ #
137
+ # ## A writer that also queries carries the read too
138
+ #
139
+ # `io.db.write` does not include `io.db.read`: they are sibling
140
+ # leaves. So a writer that queries before its write, or after a failed
141
+ # one, names both. In activerecord 8.1.3.1:
142
+ #
143
+ # - `find_or_create_by(!)` is `find_by || create_or_find_by`, and
144
+ # `first_or_create(!)` is `first || create`.
145
+ # - `create_or_find_by(!)` rescues `RecordNotUnique` with `find_by!`.
146
+ # - `destroy_all` is `records.each(&:destroy)`, which loads first, and
147
+ # `destroy_by` is `where(…).destroy_all`. `update(!)` walks `each`,
148
+ # or passes an id to the model's `update`, which calls `find`.
149
+ # - `update_all` and `delete_all`, and so `touch_all` and `delete_by`,
150
+ # first `SELECT` the distinct primary keys when the relation
151
+ # eager-loads, has a limit or offset and no `group`, and eager-loads
152
+ # or joins a collection association (`apply_join_dependency`). The
153
+ # proxy's `delete_all` on a `has_many :through` loads the target
154
+ # before it deletes.
155
+ # - `create(!)` on the proxy of a `has_many :through` a `has_one`
156
+ # builds the join record through the `has_one`, which loads the
157
+ # record it replaces.
158
+ #
159
+ # The `insert` and `insert_all` families stay `io.db.write` alone.
160
+ # Each compiles one `INSERT` statement and queries nothing else: the
161
+ # proxy's override reads only the target it already holds in memory.
162
+ #
163
+ # Two kinds of query are not counted. Schema reflection is one: the
164
+ # column and index lookups the schema cache serves would otherwise
165
+ # make `where` a read. The model's callbacks and validators are the
166
+ # other, including those an association option such as `dependent:`
167
+ # or `touch:` registers. They are not part of any bound here. The
168
+ # plugin's callback edge carries only symbol-argument callback macros
169
+ # and a uniqueness validator, and only on the model's own `save` /
170
+ # `destroy`. Nothing carries the reads a required `belongs_to`,
171
+ # `touch:` or `dependent:` registers.
172
+ #
173
+ # One case is not covered. On that same `has_many :through` a
174
+ # `has_one`, `new` / `build` also build the join record when the
175
+ # source reflection has an inverse, and so do `find_or_initialize_by`
176
+ # and `first_or_initialize`, which reach `new`. The `has_one` then
177
+ # replaces its record: it loads the old one, and even without a
178
+ # save it can delete, destroy or save it.
49
179
 
50
180
  module ActiveRecord
51
181
  class Relation[Elem]
@@ -101,6 +231,10 @@ module ActiveRecord
101
231
  def left_outer_joins: (*untyped) -> self
102
232
  %a{pure}
103
233
  def references: (*untyped) -> self
234
+ # With a block, `select` is Enumerable's and returns an Array. It
235
+ # stays `self`: on this open receiver a method the file does not
236
+ # declare is not reported, while on `Array[Elem]` any ActiveSupport
237
+ # method the bundled overlay leaves out would be.
104
238
  %a{pure}
105
239
  def select: (*untyped) -> self
106
240
  %a{pure}
@@ -145,9 +279,33 @@ module ActiveRecord
145
279
  def with: (*untyped) -> self
146
280
  def scoping: () { () -> untyped } -> untyped
147
281
 
148
- # --- finders (return an element) ---
149
- %a{rigor:v1:effect io.db.read}
150
- def find: (*untyped) -> Elem
282
+ # --- finders (return an element, or an Array of them; `find`
283
+ # with a block, the element or nil) ---
284
+ # Two or more ids return an Array, unless dropping `nil`s and
285
+ # duplicates leaves one. One argument returns an Array when it is an
286
+ # Array of ids, but one record when it is a composite key's tuple
287
+ # (`[shop_id, id]`), and nothing here can tell those apart. An
288
+ # overload keyed on an `Array` argument would be selected for every
289
+ # untyped id too (`find(params[:id])`). Its return would be joined
290
+ # with this one's into `Dynamic[Array[Elem] | Elem]`, on which a
291
+ # following `update` or `save` no longer resolves on the model and
292
+ # loses its `io.db.write`. So a single argument stays `Elem`. A
293
+ # splat and a `**` hash each count as one argument, so `find(*ids)`
294
+ # is `Elem`, while `find(id, *ids)` and `find(*args, **opts)` are
295
+ # Arrays even when the splat or the hash turns out empty.
296
+ #
297
+ # With a block `find` is `Enumerable#find` (`return super if
298
+ # block_given?`), so the id overloads do not apply: no argument
299
+ # returns the element or `nil`, and one argument is the `ifnone`
300
+ # callable, whose result joins the union. That form is rare, so
301
+ # it stays `untyped` rather than typing the callable. The
302
+ # class-side `Model.find { … }` reaches this same method through
303
+ # `all`.
304
+ %a{rigor:v1:effect io.db.read}
305
+ def find: () { (Elem) -> boolish } -> Elem?
306
+ | (untyped ifnone) { (Elem) -> boolish } -> untyped
307
+ | (untyped, untyped, *untyped) -> Array[Elem]
308
+ | (?untyped id) -> Elem
151
309
  %a{rigor:v1:effect io.db.read}
152
310
  def find_by: (*untyped) -> Elem?
153
311
  %a{rigor:v1:effect io.db.read}
@@ -243,9 +401,9 @@ module ActiveRecord
243
401
  def entries: () -> Array[Elem]
244
402
  %a{rigor:v1:effect io.db.read}
245
403
  def load: (?untyped) -> self
246
- %a{rigor:v1:effect io.db.read}
404
+ %a{rigor:v1:effect io.db.read, mutate.self}
247
405
  def reload: () -> self
248
- %a{pure}
406
+ %a{rigor:v1:effect mutate.self}
249
407
  def reset: () -> self
250
408
  %a{pure}
251
409
  def loaded?: () -> bool
@@ -272,56 +430,73 @@ module ActiveRecord
272
430
  %a{pure}
273
431
  def table: () -> untyped
274
432
  %a{pure}
275
- def arel: () -> untyped
433
+ def arel: (?untyped aliases) -> untyped
276
434
 
277
435
  # --- whole-relation persistence ---
278
- %a{rigor:v1:effect io.db.write}
436
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
279
437
  def update_all: (untyped) -> Integer
280
- %a{rigor:v1:effect io.db.write}
281
- def delete_all: () -> Integer
282
- %a{rigor:v1:effect io.db.write}
438
+ # The optional argument is the association proxy's `dependent`
439
+ # (see the header). A plain Relation's and an AssociationRelation's
440
+ # `delete_all` take none.
441
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
442
+ def delete_all: (?untyped dependent) -> Integer
443
+ # On a plain `has_many`, the proxy's `destroy_all` returns `nil`
444
+ # when a `before_remove` callback throws `:abort`. A `:through` one,
445
+ # which includes `has_and_belongs_to_many`, still returns the
446
+ # records. `Array[Elem]?` would report `call.possible-nil-receiver`
447
+ # on every `destroyed.each` for a case only such a callback reaches.
448
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
283
449
  def destroy_all: () -> Array[Elem]
284
- %a{rigor:v1:effect io.db.write}
450
+ %a{rigor:v1:effect io.db.read, io.db.write}
285
451
  def update: (*untyped) -> untyped
286
- %a{rigor:v1:effect io.db.write}
452
+ %a{rigor:v1:effect io.db.read, io.db.write}
287
453
  def update!: (*untyped) -> untyped
288
- %a{rigor:v1:effect io.db.write}
454
+ %a{rigor:v1:effect io.db.read, io.db.write}
289
455
  def delete_by: (*untyped) -> Integer
290
- %a{rigor:v1:effect io.db.write}
456
+ %a{rigor:v1:effect io.db.read, io.db.write}
291
457
  def destroy_by: (*untyped) -> Array[Elem]
292
- %a{rigor:v1:effect io.db.write}
458
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
293
459
  def touch_all: (*untyped) -> Integer
294
- %a{rigor:v1:effect io.db.write}
460
+ %a{rigor:v1:effect io.db.write, mutate.self}
461
+ def insert: (untyped, **untyped) -> untyped
462
+ %a{rigor:v1:effect io.db.write, mutate.self}
295
463
  def insert_all: (untyped, **untyped) -> untyped
296
- %a{rigor:v1:effect io.db.write}
464
+ %a{rigor:v1:effect io.db.write, mutate.self}
465
+ def insert!: (untyped, **untyped) -> untyped
466
+ %a{rigor:v1:effect io.db.write, mutate.self}
297
467
  def insert_all!: (untyped, **untyped) -> untyped
298
- %a{rigor:v1:effect io.db.write}
468
+ %a{rigor:v1:effect io.db.write, mutate.self}
469
+ def upsert: (untyped, **untyped) -> untyped
470
+ %a{rigor:v1:effect io.db.write, mutate.self}
299
471
  def upsert_all: (untyped, **untyped) -> untyped
300
472
 
301
473
  # --- instantiating builders (return an element) ---
302
- %a{pure}
474
+ # Given an Array of attribute hashes, `new`, `build`, `create` and
475
+ # `create!` return an Array of records. No overload says so, for
476
+ # the reason `find` gives: an untyped argument would select it too.
477
+ %a{rigor:v1:effect mutate.self}
303
478
  def new: (*untyped) ?{ (Elem) -> void } -> Elem
304
- %a{pure}
479
+ %a{rigor:v1:effect mutate.self}
305
480
  def build: (*untyped) ?{ (Elem) -> void } -> Elem
306
- %a{rigor:v1:effect io.db.write}
481
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
307
482
  def create: (*untyped) ?{ (Elem) -> void } -> Elem
308
- %a{rigor:v1:effect io.db.write}
483
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
309
484
  def create!: (*untyped) ?{ (Elem) -> void } -> Elem
310
- %a{rigor:v1:effect io.db.write}
485
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
311
486
  def find_or_create_by: (untyped) ?{ (Elem) -> void } -> Elem
312
- %a{rigor:v1:effect io.db.write}
487
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
313
488
  def find_or_create_by!: (untyped) ?{ (Elem) -> void } -> Elem
314
- %a{rigor:v1:effect io.db.read}
489
+ %a{rigor:v1:effect io.db.read, mutate.self}
315
490
  def find_or_initialize_by: (untyped) ?{ (Elem) -> void } -> Elem
316
- %a{rigor:v1:effect io.db.write}
491
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
317
492
  def create_or_find_by: (untyped) ?{ (Elem) -> void } -> Elem
318
- %a{rigor:v1:effect io.db.write}
493
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
319
494
  def create_or_find_by!: (untyped) ?{ (Elem) -> void } -> Elem
320
- %a{rigor:v1:effect io.db.write}
495
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
321
496
  def first_or_create: (?untyped) ?{ (Elem) -> void } -> Elem
322
- %a{rigor:v1:effect io.db.write}
497
+ %a{rigor:v1:effect io.db.read, io.db.write, mutate.self}
323
498
  def first_or_create!: (?untyped) ?{ (Elem) -> void } -> Elem
324
- %a{rigor:v1:effect io.db.read}
499
+ %a{rigor:v1:effect io.db.read, mutate.self}
325
500
  def first_or_initialize: (?untyped) ?{ (Elem) -> void } -> Elem
326
501
  end
327
502
  end
@@ -37,6 +37,7 @@ module Rigor
37
37
  class Activestorage < Rigor::Plugin::Base
38
38
  manifest(
39
39
  id: "activestorage",
40
+ target_gems: ["activestorage"],
40
41
  # Bumped 2026-09-02 (#621) — attachment owners are keyed by their de-rooted name and a reopened
41
42
  # class's attachments are UNIONed rather than clobbered, so a cached 0.1.0 index can be missing the
42
43
  # attachments the merge restores.
@@ -66,12 +67,15 @@ module Rigor
66
67
  def init(_services)
67
68
  @model_search_paths = Array(config.fetch("model_search_paths")).map(&:to_s)
68
69
  @attachment_index = nil
69
- @load_errors = []
70
+ @load_errors = {}
70
71
  end
71
72
 
72
73
  def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
73
74
  index = attachment_index
74
- return load_error_diagnostics(path) if index.nil?
75
+ if index.nil?
76
+ disclose_load_errors
77
+ return []
78
+ end
75
79
  return [] if index.empty?
76
80
 
77
81
  Analyzer.new(path: path, attachment_index: index).analyze(root).diagnostics
@@ -125,24 +129,32 @@ module Rigor
125
129
  # covers model-file additions.
126
130
  @attachment_index = cache_for(:attachment_index, params: {}).call
127
131
  rescue Plugin::AccessDeniedError => e
128
- @load_errors << "rigor-activestorage: #{e.message}"
132
+ @load_errors["1-read-refused"] ||= "rigor-activestorage: #{e.message}"
129
133
  nil
130
134
  rescue StandardError => e
131
- @load_errors << "rigor-activestorage: discovery failed: #{e.class}: #{e.message}"
135
+ @load_errors["2-discovery-failed"] ||=
136
+ "rigor-activestorage: discovery failed: #{e.class}: #{e.message}"
132
137
  nil
133
138
  end
134
139
 
135
- def load_error_diagnostics(path)
136
- @load_errors.uniq.map do |message|
137
- Rigor::Analysis::Diagnostic.new(
138
- path: path,
139
- line: 1,
140
- column: 1,
141
- message: message,
142
- severity: :warning,
143
- rule: "load-error"
144
- )
140
+ # Issue #1056 — "the attachment index did not load" is a fact about the run's INPUTS, not about the
141
+ # file being analysed, so both outcomes are registered rather than returned: see
142
+ # {Plugin::Base#disclose_once} for the channel and the position. Returned from the per-file hook they
143
+ # carried no once-guard at all — the `.uniq` collapsed repeats within one instance's list, but the
144
+ # whole list was re-emitted on every analysed file.
145
+ #
146
+ # The `key`s carry an ordinal prefix because the engine emits a plugin's disclosures in KEY order
147
+ # (#1051), and a refused read must still precede a discovery failure the way `@load_errors` records
148
+ # them — `@load_errors` is now keyed by that same string, which also caps it: `#attachment_index`
149
+ # re-attempts the load per call site, and the old Array appended a fresh interpolated string every
150
+ # time (the unbounded-growth shape #569 measured at 4.2 M retained strings on Redmine). The key, not
151
+ # the message, is the identity: a second refusal naming a different path collapses into the first row
152
+ # rather than adding one — the notice is "the index did not load", said once.
153
+ def disclose_load_errors
154
+ @load_errors.each do |key, message|
155
+ disclose_once(key, message: message, severity: :warning, rule: "load-error")
145
156
  end
157
+ nil
146
158
  end
147
159
  end
148
160
 
@@ -8,7 +8,7 @@ module Rigor
8
8
  # rigor-activesupport-core-ext's effect contract (ADR-103 WD10; design note § 11.2; issue #387).
9
9
  #
10
10
  # **Scope note.** This is the *impure* half of ActiveSupport only: the clock, the notification bus and
11
- # `CurrentAttributes`. The `%a{pure}` sweep over `blank?` / `present?` / `deep_dup` and the rest of
11
+ # `CurrentAttributes`. The `%a{pure}` sweep over `blank?` / `present?` / `presence` and the rest of
12
12
  # the core_ext predicate surface — the single cheapest purity win in a Rails app, per WD10 — is issue
13
13
  # #388 and lands in this plugin's shipped RBS, not here (`try` was audited and skipped: it dispatches
14
14
  # to whatever method the caller names, so its purity is a fact about the call site, not about `try`).
@@ -115,11 +115,18 @@ module Rigor
115
115
  # which is what earns `Date` its row here — the label is a fact about the module, not about the
116
116
  # receiver, and the three only ever differed in which spellings the bundle had got round to
117
117
  # declaring.
118
+ #
119
+ # `String#in_time_zone` is the exception: it has no instant to convert. It parses through
120
+ # `TimeZone#parse(str, now = now())`, whose default argument reads the clock on every call, or through
121
+ # `to_time` when no zone is set — so it takes `CLOCK`, not `ZONE_READ`.
118
122
  def in_time_zone_rows
119
123
  [TIME, DATE, DATETIME].map do |receiver|
120
124
  row(receiver, :in_time_zone, ZONE_READ,
121
125
  why: "the default argument reads `Time.zone` when the caller doesn't name one explicitly")
122
- end
126
+ end + [
127
+ row("String", :in_time_zone, CLOCK,
128
+ why: "parses in `Time.zone` (the default argument) and fills the parts from the zone's `now`")
129
+ ]
123
130
  end
124
131
 
125
132
  def current_rows
@@ -42,9 +42,13 @@ module Rigor
42
42
  class ActivesupportCoreExt < Rigor::Plugin::Base
43
43
  manifest(
44
44
  id: "activesupport-core-ext",
45
+ target_gems: ["activesupport"],
46
+ # Bumped 2026-09-10 (#534 item 7) — `ActiveSupport::Concern` is declared, so `extend
47
+ # ActiveSupport::Concern` resolves instead of reading as an unresolved constant.
48
+ #
45
49
  # Bumped 2026-09-02 (#632) — `ActiveSupport::Duration`'s reader surface (`ago`/`to_i`/`iso8601`/…)
46
50
  # is now declared, open_receivers-protected; see the class comment above.
47
- version: "0.4.0",
51
+ version: "0.5.0",
48
52
  description: "RBS bundle for the most-frequently-flagged ActiveSupport core_ext extensions, " \
49
53
  "plus the `ActiveSupport::Duration` type of the numeric time multipliers and its " \
50
54
  "reader surface.",
@@ -55,7 +59,10 @@ module Rigor
55
59
  # `call.undefined-method` fire against it. Distinct from the RECEIVER-side FP the multiplier
56
60
  # `dynamic_return` gate guards (`Time#day` vs `Duration#day`, in the class comment above) — this is
57
61
  # the class's OWN unenumerable member set.
58
- open_receivers: ["ActiveSupport::Duration"],
62
+ # `ActiveSupport::Concern` (#534 item 7) is here for the same reason at a different scale: the
63
+ # declaration names `included` / `prepended` / `class_methods` so `extend ActiveSupport::Concern`
64
+ # resolves, and everything else the module really responds to must stay lenient.
65
+ open_receivers: ["ActiveSupport::Duration", "ActiveSupport::Concern"],
59
66
  # ADR-103 WD10 (#387) — the IMPURE half of ActiveSupport: the clock, the notification bus and
60
67
  # `CurrentAttributes`. The `%a{pure}` sweep over the predicate surface is issue #388 and lands in
61
68
  # `sig/`, not here. See {Effects}.