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
@@ -13,8 +13,10 @@ require_relative "environment/constant_type_cache_holder"
13
13
  require_relative "environment/missing_gem_constant_index"
14
14
  require_relative "environment/bundle_sig_discovery"
15
15
  require_relative "environment/lockfile_resolver"
16
+ require_relative "environment/installed_gem_set"
16
17
  require_relative "environment/rbs_collection_discovery"
17
18
  require_relative "plugin/isolation"
19
+ require_relative "cache/engine_source"
18
20
  require_relative "environment/rbs_coverage_report"
19
21
  require_relative "inference/synthetic_method_index"
20
22
  require_relative "inference/project_patched_methods"
@@ -48,16 +50,15 @@ module Rigor
48
50
  :reporters, :name_scope,
49
51
  :synthetic_method_index, :project_patched_methods
50
52
 
51
- # @param class_registry [Rigor::Environment::ClassRegistry]
52
- # @param rbs_loader [Rigor::Environment::RbsLoader, nil] when nil the environment is "RBS-blind"; useful
53
+ # @param rbs_loader — when nil the environment is "RBS-blind"; useful
53
54
  # in tests that want to assert how the engine behaves without RBS data. The default Environment wires
54
55
  # the shared core loader, which is itself lazy: requesting an environment instance does NOT load RBS
55
56
  # until a method or class query actually consults the loader.
56
- # @param plugin_registry [Rigor::Plugin::Registry, nil] v0.1.1 Track 2 slice 7. The per-run plugin
57
+ # @param plugin_registry — v0.1.1 Track 2 slice 7. The per-run plugin
57
58
  # registry the inference engine consults at call sites for plugin `dynamic_return` rules. When nil
58
59
  # (the default), no plugin-level return-type contribution participates — useful for tests, the
59
60
  # `Environment.default` facade, and analyses that don't load plugins.
60
- # @param dependency_source_index [Rigor::Analysis::DependencySourceInference::Index, nil] ADR-10 slice
61
+ # @param dependency_source_index — ADR-10 slice
61
62
  # 2b-ii. The per-run index of opt-in gem sources the dispatcher consults BELOW RBS dispatch. When nil
62
63
  # (the default), no dep-source contribution participates and the dispatcher tier is a no-op.
63
64
  def initialize(class_registry: ClassRegistry.default, rbs_loader: nil, # rubocop:disable Metrics/ParameterLists
@@ -151,7 +152,7 @@ module Rigor
151
152
  # defect. First-write-wins on the slot means a run whose overlay AND scan both raise reports the
152
153
  # overlay — the actionable one, since the plugin is what a user can remove.
153
154
  #
154
- # @return [Rigor::Inference::HktRegistry] the registry the RBS scan is layered on top of.
155
+ # @return the registry the RBS scan is layered on top of.
155
156
  def pre_scan_hkt_registry
156
157
  return @hkt_registry_base unless @plugin_registry.respond_to?(:hkt_overlay_registry)
157
158
 
@@ -171,8 +172,6 @@ module Rigor
171
172
  # worker (the coordinator never analyses a file under the pool, so the drain is how its snapshot learns
172
173
  # the outcome: the #696 lesson), and from a resolved environment when the analyze set is empty — so the
173
174
  # row never depends on which files happened to be analysed.
174
- #
175
- # @return [Array(String, String, String, Symbol), Array(String, String, nil, Symbol), nil]
176
175
  def hkt_scan_failure
177
176
  @hkt_scan_failure.value
178
177
  end
@@ -255,18 +254,17 @@ module Rigor
255
254
  # Builds an Environment that consults the project's local signatures and any opt-in stdlib libraries on
256
255
  # top of RBS core.
257
256
  #
258
- # @param root [String, Pathname] project root used to auto-detect the default signature path. Defaults
257
+ # @param root — project root used to auto-detect the default signature path. Defaults
259
258
  # to the current working directory.
260
- # @param libraries [Array<String, Symbol>] additional stdlib libraries to load on top of
259
+ # @param libraries — additional stdlib libraries to load on top of
261
260
  # {DEFAULT_LIBRARIES}. The final list is the union of the two, de-duplicated while preserving order.
262
261
  # Pass an empty array (the default) to load only the defaults.
263
- # @param signature_paths [Array<String, Pathname>, nil] explicit list of `sig/`-style directories. When
262
+ # @param signature_paths — explicit list of `sig/`-style directories. When
264
263
  # `nil` (the default), the canonical project layout `<root>/sig` is used if it exists, otherwise no
265
264
  # signature path is loaded.
266
- # @param cache_store [Rigor::Cache::Store, nil] persistent cache threaded into the underlying
265
+ # @param cache_store — persistent cache threaded into the underlying
267
266
  # {Environment::RbsLoader} so constant lookups (and, in later v0.0.9 slices, other reflection
268
267
  # artefacts) consult the cache. Pass `nil` (the default) to skip caching for this environment.
269
- # @return [Rigor::Environment]
270
268
  # rubocop:disable-next Metrics/MethodLength, Metrics/ParameterLists
271
269
  def for_project(root: Dir.pwd, libraries: [], signature_paths: nil, cache_store: nil,
272
270
  plugin_registry: nil, dependency_source_index: nil,
@@ -276,7 +274,7 @@ module Rigor
276
274
  bundler_lockfile: nil,
277
275
  rbs_collection_lockfile: nil, rbs_collection_auto_detect: false,
278
276
  synthetic_method_index: nil, project_patched_methods: nil,
279
- source_files: [])
277
+ source_files: [], locked_gems: nil)
280
278
  resolved_paths = signature_paths || default_signature_paths(root)
281
279
  # O4 MVP — append per-gem `sig/` directories discovered under the target project's bundler install
282
280
  # root. Empty array when neither an explicit path nor auto-detection finds a bundle. Order: user
@@ -287,7 +285,10 @@ module Rigor
287
285
  # to the project root), use the locked gem set to filter the discovered `sig/` directories. Stale
288
286
  # gems in the bundle install tree (out-of-band installs, version drift after a `bundle update`) are
289
287
  # silently dropped so only gems the project actually declares contribute RBS.
290
- locked = LockfileResolver.locked_gems(
288
+ #
289
+ # #1064 — `locked_gems:` is that same map resolved by the caller. The Ractor pool passes it because
290
+ # Bundler's parser cannot run in a non-main Ractor; every other caller leaves it nil and resolves here.
291
+ locked = locked_gems || LockfileResolver.locked_gems(
291
292
  lockfile_path: bundler_lockfile,
292
293
  project_root: root,
293
294
  auto_detect: bundler_auto_detect
@@ -336,7 +337,7 @@ module Rigor
336
337
  missing_gems, overlay_paths = missing_gems_and_overlay_paths(
337
338
  locked: locked, default_libraries: merged_libraries, bundle_sig_paths: gem_sig_paths,
338
339
  rbs_collection_paths: collection_paths, plugin_registry: plugin_registry,
339
- signature_paths: resolved_paths
340
+ signature_paths: resolved_paths, bundle_root: bundle_root
340
341
  )
341
342
  loader_signature_paths = resolved_paths + plugin_sig_paths + gem_sig_paths +
342
343
  collection_paths + overlay_paths
@@ -361,6 +362,14 @@ module Rigor
361
362
  # the bundle walk and `rbs collection install` all stay authoritative.
362
363
  deferred_signature_paths: plugin_sig_paths
363
364
  )
365
+ # Issues #824 / #1075 — the rows ADR-112 WD5's consistency rule reports for members the project's
366
+ # own `sig/` and an inline source both declare: an error per contradiction (or per refinement that
367
+ # exceeds its own declared type), an `:info` per inline member dropped undecided. Recorded HERE
368
+ # because this is where the reporter is: the run's `source_rbs_synthesis` stream already carries
369
+ # WD12's "parsed but not honoured" rows, drains out of pool workers, and is regenerated (never cached)
370
+ # on every run, so the member-level outcome needs no plumbing of its own. Costs nothing for a project
371
+ # with no inline RBS: the loader's reader short-circuits on an empty `virtual_rbs`.
372
+ record_member_consistency(loader, source_rbs_synthesis_reporter, root)
364
373
  # ADR-20 slice 2c + 2e — seed hkt_registry with the bundled builtins. The Environment's
365
374
  # `#hkt_registry` getter then LAZILY merges in the RBS env scan on first call so fast paths that
366
375
  # don't consult HKT (e.g. `rigor check --cache-stats --no-stats`) don't pay the eager env-build cost
@@ -412,13 +421,13 @@ module Rigor
412
421
  # and consumed twice: as `[gem_name, version]` pairs for the ADR-82 WD9 constant-ownership index, and
413
422
  # as the eligible-gem set for the ADR-72 overlay resolution. Returns `[pairs, overlay_paths]`.
414
423
  def missing_gems_and_overlay_paths(locked:, default_libraries:, bundle_sig_paths:,
415
- rbs_collection_paths:, plugin_registry:, signature_paths: [])
416
- return [[], []] if locked.empty?
424
+ rbs_collection_paths:, plugin_registry:, signature_paths: [],
425
+ bundle_root: nil)
426
+ if locked.empty?
427
+ return unlocked_missing_gems(default_libraries, bundle_sig_paths, rbs_collection_paths, bundle_root)
428
+ end
417
429
 
418
- rows = RbsCoverageReport.classify(
419
- locked_gems: locked, default_libraries: default_libraries,
420
- bundle_sig_paths: bundle_sig_paths, rbs_collection_paths: rbs_collection_paths
421
- ).select { |row| row.source == :missing }
430
+ rows = missing_rows(locked, default_libraries, bundle_sig_paths, rbs_collection_paths)
422
431
  overlays = gem_overlay_paths(
423
432
  missing_gem_names: rows.map(&:gem_name), plugin_registry: plugin_registry,
424
433
  signature_paths: signature_paths
@@ -426,6 +435,27 @@ module Rigor
426
435
  [rows.map { |row| [row.gem_name, row.version] }, overlays]
427
436
  end
428
437
 
438
+ # Issue #530 — a project with no `Gemfile.lock` still has gems on disk, and before this it handed the
439
+ # ADR-82 WD9 index nothing, so every constant reaching into an RBS-less gem kept the generic cause and
440
+ # the whole boundary reported as `engine_gap` rather than `add_rbs`. The installed set stands in for
441
+ # the locked one ({InstalledGemSet} says why that stays honest), and only for the provenance index:
442
+ # the second element is always empty because the ADR-72 overlays change what type-checks and so remain
443
+ # gated on the project's own declaration.
444
+ def unlocked_missing_gems(default_libraries, bundle_sig_paths, rbs_collection_paths, bundle_root)
445
+ installed = InstalledGemSet.gems(bundle_path: bundle_root)
446
+ return [[], []] if installed.empty?
447
+
448
+ rows = missing_rows(installed, default_libraries, bundle_sig_paths, rbs_collection_paths)
449
+ [rows.map { |row| [row.gem_name, row.version] }, []]
450
+ end
451
+
452
+ def missing_rows(gems, default_libraries, bundle_sig_paths, rbs_collection_paths)
453
+ RbsCoverageReport.classify(
454
+ locked_gems: gems, default_libraries: default_libraries,
455
+ bundle_sig_paths: bundle_sig_paths, rbs_collection_paths: rbs_collection_paths
456
+ ).select { |row| row.source == :missing }
457
+ end
458
+
429
459
  # ADR-72 — resolve the bundled RBS overlay directories to load for this project. A gem is eligible when
430
460
  # it is classified `:missing` (see {missing_rbs_gem_rows}) and its conflicting opt-in plugin twin (if
431
461
  # any) is NOT already reachable. Returns `[Pathname]`, deterministically ordered, or `[]` when no
@@ -467,7 +497,10 @@ module Rigor
467
497
  #
468
498
  # WD5 — when `cache_store` is supplied, each (file, plugin) synthesizer call is memoised through
469
499
  # `Cache::Store`. The cache key composes the file's content SHA with the plugin's `PluginEntry` (id +
470
- # version + config_hash) so a config change or content change invalidates the entry automatically.
500
+ # version + config_hash) so a config change or content change invalidates the entry automatically, and
501
+ # with the engine's source identity (issue #1009): a plugin's manifest version does not move when a
502
+ # checkout edits its synthesizer, and a stale string here also keeps the `rbs.virtual_rbs` env key warm,
503
+ # so the new build's rules would read the old build's RBS.
471
504
  def collect_virtual_rbs(plugin_registry, source_files, cache_store, reporter)
472
505
  return [] if plugin_registry.nil?
473
506
 
@@ -498,6 +531,8 @@ module Rigor
498
531
  return invoke_synthesizer_safely(callable, path) unless File.file?(path)
499
532
 
500
533
  descriptor = build_synthesizer_cache_descriptor(plugin, path)
534
+ return invoke_synthesizer_safely(callable, path) if descriptor.nil?
535
+
501
536
  cache_store.fetch_or_compute(
502
537
  producer_id: SYNTHESIZER_CACHE_PRODUCER_ID,
503
538
  params: {},
@@ -540,6 +575,93 @@ module Rigor
540
575
  reporter&.record(plugin_id: plugin.manifest.id, path: path, message: message.to_s, kind: kind)
541
576
  end
542
577
 
578
+ # ADR-112 WD5 / issue #1075 — turn each reported {MemberConsistency::Record} into a row on the
579
+ # synthesis stream. A contradiction, or a refinement outside its own declared type, is a `:contradiction`
580
+ # entry ({Analysis::CheckRules::RULE_CONTRADICTING_SIGNATURE}, an error), positioned at the `.rbs`
581
+ # member when there is one: that file is real and its line is exact, where the inline member's position
582
+ # lives in the synthesized RBS buffer, a document nobody has. An undecided pair keeps ADR-32 WD12's
583
+ # `:not_honoured` row at the annotated `.rb` (line 1, as every entry on that stream is): the inline
584
+ # signature was parsed and dropped, and WD12 forbids swallowing that. The silent outcomes — equal, or
585
+ # merged to the more precise side — record nothing.
586
+ #
587
+ # The records are derived with a {MemberConsistency::RbsProof}, so a contradiction is proven from the
588
+ # RBS hierarchy of core and stdlib classes, never from the analyzer process's loaded constants.
589
+ def record_member_consistency(loader, reporter, root)
590
+ return if reporter.nil?
591
+
592
+ proof = MemberConsistency::RbsProof.new(loader)
593
+ loader.member_consistency(proof: proof).each do |record|
594
+ case record.outcome
595
+ when :contradiction then record_member_contradiction(reporter, record, root)
596
+ when :refinement then record_refinement_contradiction(reporter, record, root)
597
+ when :undecided then record_member_undecided(reporter, record, root)
598
+ end
599
+ end
600
+ end
601
+
602
+ def record_member_contradiction(reporter, record, root)
603
+ plugin_id, path = split_virtual_source_name(record.virtual_name)
604
+ return if path.nil?
605
+
606
+ reporter.record(
607
+ plugin_id: plugin_id, path: path_relative_to(record.signature_path, root), line: record.signature_line,
608
+ kind: :contradiction,
609
+ message: "`#{member_label(record)}` is declared here and by an inline annotation in " \
610
+ "`#{path_relative_to(path, root)}`, and the two contradict: #{record.detail}. Rigor reads " \
611
+ "this `.rbs` declaration. Regenerate the signature, or fix the annotation, so that one " \
612
+ "of the two refines the other."
613
+ )
614
+ end
615
+
616
+ def record_refinement_contradiction(reporter, record, root)
617
+ plugin_id, path = split_virtual_source_name(record.virtual_name)
618
+ path = record.signature_path if path.nil?
619
+ return if path.nil?
620
+
621
+ reporter.record(
622
+ plugin_id: plugin_id.to_s, path: path_relative_to(path, root), line: record.signature_line,
623
+ kind: :contradiction,
624
+ message: "`#{member_label(record)}` refines its own signature to a type outside it: " \
625
+ "#{record.detail}. A `rigor:v1:` refinement may narrow the ordinary RBS contract, never " \
626
+ "contradict it, so one of the two is wrong."
627
+ )
628
+ end
629
+
630
+ def record_member_undecided(reporter, record, root)
631
+ plugin_id, path = split_virtual_source_name(record.virtual_name)
632
+ return if path.nil?
633
+
634
+ reporter.record(
635
+ plugin_id: plugin_id, path: path, kind: :not_honoured,
636
+ message: "`#{member_label(record)}` is also declared in " \
637
+ "`#{path_relative_to(record.signature_path, root)}`, and Rigor cannot tell which of the two " \
638
+ "is the more precise (#{record.detail}), so the `.rbs` declaration binds and the inline " \
639
+ "signature was dropped. Left to collide the two would fail the class's definition build, " \
640
+ "and every call on it — real methods and typos alike — would read `Dynamic[top]`. Make " \
641
+ "one of the two a refinement of the other, or remove one, to settle which binds."
642
+ )
643
+ end
644
+
645
+ def member_label(record)
646
+ separator = record.kind == :singleton ? "." : "#"
647
+ "#{record.class_name}#{separator}#{record.method_name}"
648
+ end
649
+
650
+ # Splits a virtual buffer name back into the pair {.collect_virtual_rbs} composed it from
651
+ # (`virtual:<plugin id>:<source path>`). Limited to three fields so a path carrying a colon survives.
652
+ # Returns `[nil, nil]` for a name in any other shape — a synthesizer channel this method does not own.
653
+ def split_virtual_source_name(virtual_name)
654
+ prefix, plugin_id, path = virtual_name.to_s.split(":", 3)
655
+ return [nil, nil] unless prefix == "virtual" && plugin_id && !plugin_id.empty? && path && !path.empty?
656
+
657
+ [plugin_id, path]
658
+ end
659
+
660
+ def path_relative_to(path, root)
661
+ prefix = "#{root}#{File::SEPARATOR}"
662
+ path.to_s.start_with?(prefix) ? path.to_s.delete_prefix(prefix) : path.to_s
663
+ end
664
+
543
665
  SYNTHESIZER_CACHE_PRODUCER_ID = "plugin.source_rbs_synthesizer"
544
666
  private_constant :SYNTHESIZER_CACHE_PRODUCER_ID
545
667
 
@@ -558,8 +680,13 @@ module Rigor
558
680
  comparator: :digest,
559
681
  value: synthesizer_input_digest(path)
560
682
  )],
561
- plugins: [plugin.plugin_entry]
683
+ plugins: [plugin.plugin_entry],
684
+ configs: Cache::EngineSource.key_config_entries
562
685
  )
686
+ rescue Cache::EngineSource::Unavailable
687
+ # An engine that cannot be identified must not be keyed by its inputs alone (issue #1009): nil runs the
688
+ # synthesizer uncached rather than serving an entry another build may have written.
689
+ nil
563
690
  end
564
691
 
565
692
  def synthesizer_input_digest(path)
@@ -623,6 +750,17 @@ module Rigor
623
750
  end
624
751
  end
625
752
 
753
+ # Issue #1362 — the translated type of the RBS declaration of the global variable `name` (`$stdout: IO` answers
754
+ # `Nominal[IO]`), or nil when no signature declares it or the environment carries no RBS loader. With
755
+ # `builtin: true` it answers only for a declaration in Ruby's own core or stdlib signatures, a global the
756
+ # interpreter sets; a project's or a gem's declaration then answers nil.
757
+ def global_for_name(name, builtin: false)
758
+ return nil if rbs_loader.nil?
759
+ return nil if builtin && !rbs_loader.core_or_stdlib_global?(name)
760
+
761
+ rbs_loader.global_type(name)
762
+ end
763
+
626
764
  # Returns true when the constant name is known to either the static registry or the RBS loader. Useful
627
765
  # for callers that only need a presence check without materialising a type carrier.
628
766
  def class_known?(name)
@@ -653,6 +791,15 @@ module Rigor
653
791
  rbs_loader.rbs_module?(name)
654
792
  end
655
793
 
794
+ # Issue #915 — the module names RBS puts in `name`'s SINGLETON ancestry, the declaration-side twin of
795
+ # `Scope#singleton_extends_of`. `Inference::Narrowing` reads it the same one-directional way: presence
796
+ # withholds a `Bot`, absence asserts nothing (a class the environment does not know answers `[]`).
797
+ def singleton_extended_modules(name)
798
+ return [] unless rbs_loader
799
+
800
+ rbs_loader.singleton_extended_module_names(normalize_class_name(name))
801
+ end
802
+
656
803
  # Compares two class/module names using analyzer-owned class data. Returns `:equal`, `:subclass`,
657
804
  # `:superclass`, `:disjoint`, or `:unknown`. The static registry handles built-ins cheaply; the RBS
658
805
  # loader handles project/stdlib classes without relying on host Ruby constants being loaded.
@@ -677,8 +824,11 @@ module Rigor
677
824
  rbs_loader.class_known?(name)
678
825
  end
679
826
 
827
+ # Allocates only when there is a prefix to strip: `String#delete_prefix` copies unconditionally, and
828
+ # this runs once per class-registry / hierarchy probe (~320k times on the lib self-check).
680
829
  def normalize_class_name(name)
681
- name.to_s.delete_prefix("::")
830
+ name = name.to_s
831
+ name.start_with?("::") ? name.delete_prefix("::") : name
682
832
  end
683
833
 
684
834
  # ADR-13 slice 3b — composes the per-run plugin-supplied {Rigor::TypeNode::ResolverChain} into a single
@@ -53,8 +53,6 @@ module Rigor
53
53
 
54
54
  module_function
55
55
 
56
- # @param contributions [Array<FlowContribution>]
57
- # @return [MergeResult]
58
56
  def merge(contributions)
59
57
  contributions = Array(contributions)
60
58
  return MergeResult.new if contributions.empty?
@@ -49,24 +49,24 @@ module Rigor
49
49
 
50
50
  attr_reader(*SLOT_NAMES, :provenance)
51
51
 
52
- # @param return_type [Object, nil] normal-edge return type. Use `nil` when the contribution does not
52
+ # @param return_type — normal-edge return type. Use `nil` when the contribution does not
53
53
  # refine the return type selected from the RBS contract.
54
- # @param truthy_facts [Array, nil] facts that hold only on the truthy control-flow edge. Edge-local: a
54
+ # @param truthy_facts — facts that hold only on the truthy control-flow edge. Edge-local: a
55
55
  # truthy-edge fact does NOT imply its falsey-edge complement (ADR-2 § "Plugin Contribution Merging").
56
- # @param falsey_facts [Array, nil] dual of `truthy_facts`.
57
- # @param post_return_facts [Array, nil] facts that hold after the call returns normally on every edge —
56
+ # @param falsey_facts — dual of `truthy_facts`.
57
+ # @param post_return_facts — facts that hold after the call returns normally on every edge —
58
58
  # the carrier for assertion-style contributions.
59
- # @param mutations [Array, nil] receiver and argument mutation effects.
60
- # @param invalidations [Array, nil] targeted fact invalidations beyond what mutation effects already
59
+ # @param mutations — receiver and argument mutation effects.
60
+ # @param invalidations — targeted fact invalidations beyond what mutation effects already
61
61
  # imply.
62
- # @param exceptional [Object, nil] non-returning, raising, or unreachable effect.
63
- # @param role_conformance [Array, nil] capability-role conformance facts the contribution provides.
64
- # @param effects [Rigor::Effects::LabelSet, nil] ADR-103 WD5 — the effect labels this call edge
62
+ # @param exceptional — non-returning, raising, or unreachable effect.
63
+ # @param role_conformance — capability-role conformance facts the contribution provides.
64
+ # @param effects — ADR-103 WD5 — the effect labels this call edge
65
65
  # attributes to its callee, as an upper bound. `nil` means "says nothing about effects", which is
66
66
  # NOT the same as the empty set (which asserts the call performs none). Merged by union, the
67
67
  # conservative direction: two sources that each name part of a call's footprint together name
68
68
  # more of it, and neither can shrink the other's claim.
69
- # @param provenance [Provenance] source-family, plugin-id, node, and cache-descriptor metadata. Defaults
69
+ # @param provenance — source-family, plugin-id, node, and cache-descriptor metadata. Defaults
70
70
  # to `Provenance.builtin`.
71
71
  # rubocop:disable Metrics/ParameterLists
72
72
  def initialize(return_type: nil, truthy_facts: nil, falsey_facts: nil,
@@ -87,7 +87,7 @@ module Rigor
87
87
  freeze
88
88
  end
89
89
 
90
- # @return [Boolean] true when every content slot is unset (nil or an empty collection). Provenance does
90
+ # @return true when every content slot is unset (nil or an empty collection). Provenance does
91
91
  # not count toward emptiness — an empty bundle still carries source attribution.
92
92
  def empty?
93
93
  SLOT_NAMES.all? { |slot| slot_empty?(slot, public_send(slot)) }
@@ -116,8 +116,6 @@ module Rigor
116
116
  # | exceptional | exceptional | exception | :raise |
117
117
  # | role_conformance | normal | role | (per-role target) |
118
118
  # | effects | normal | effects | :effects |
119
- #
120
- # @return [Array<Element>]
121
119
  def to_element_list # rubocop:disable Metrics/AbcSize
122
120
  elements = []
123
121
  elements << element_for(:return, :normal, :return_type, return_type) unless return_type.nil?
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rigor
4
+ module Hashing
5
+ # Pure-Ruby XXH3-64 (seed 0, default secret) — the content hash for the `rigor lens` anchors of
6
+ # ADR-113 WD6, landed ahead of `rigor lens` itself (#1083). A port of the upstream xxHash scalar
7
+ # path (`xxhash.h`'s `XXH3_64bits` with `XXH3_kSecret`), pinned by the upstream sanity-check
8
+ # vectors and cross-checked against lisplens's `xxhash-rust` output; see
9
+ # `spec/rigor/hashing/xxh3_spec.rb`.
10
+ #
11
+ # Pure Ruby per ADR-31: a native `xxhash` gem is a supply-chain addition to hash a few hundred
12
+ # bytes per call. Only the seedless/default-secret variant is implemented — the `rigor lens`
13
+ # anchors are the sole consumer, and they match lisplens ADR-0008's form exactly:
14
+ #
15
+ # - {.anchor_hash} is `xxh3_64 & 0xffff` printed as 4 lowercase hex digits (a per-row anchor).
16
+ # - {.file_hash} is the full 64-bit value printed as 16 lowercase hex digits (a file-level guard).
17
+ #
18
+ # Every arithmetic op below is modulo 2^64; `& MASK64` marks the spots where the C code wraps.
19
+ module XXH3
20
+ # Constant and helper names keep upstream's `xxhash.h` identifiers (PRIME64_1, accumulate_512,
21
+ # …) so the port stays diffable against the reference scalar path.
22
+ # rubocop:disable Naming/VariableNumber
23
+ MASK64 = (1 << 64) - 1
24
+ MASK32 = (1 << 32) - 1
25
+
26
+ PRIME32_1 = 0x9E3779B1
27
+ PRIME32_2 = 0x85EBCA77
28
+ PRIME32_3 = 0xC2B2AE3D
29
+ PRIME64_1 = 0x9E3779B185EBCA87
30
+ PRIME64_2 = 0xC2B2AE3D27D4EB4F
31
+ PRIME64_3 = 0x165667B19E3779F9
32
+ PRIME64_4 = 0x85EBCA77C2B2AE63
33
+ PRIME64_5 = 0x27D4EB2F165667C5
34
+ PRIME_MX1 = 0x165667919E3779F9
35
+ PRIME_MX2 = 0x9FB21C651E98DF25
36
+
37
+ STRIPE_LEN = 64
38
+ SECRET_CONSUME_RATE = 8
39
+ ACC_NB = STRIPE_LEN / 8
40
+ MIDSIZE_MAX = 240
41
+ SECRET_SIZE_MIN = 136
42
+ MIDSIZE_STARTOFFSET = 3
43
+ MIDSIZE_LASTOFFSET = 17
44
+ SECRET_LASTACC_START = 7
45
+ SECRET_MERGEACCS_START = 11
46
+
47
+ # `XXH3_kSecret` — the 192-byte default secret taken from FARSH, verbatim from `xxhash.h`
48
+ # (one hex pair per upstream byte literal).
49
+ SECRET = [
50
+ "b8fe6c3923a44bbe7c01812cf721ad1cded46de9839097db7240a4a4b7b3671f" \
51
+ "cb79e64eccc0e578825ad07dccff7221b8084674f743248ee03590e6813a264c" \
52
+ "3c2852bb91c300cb88d0658b1b532ea371644897a20df94e3819ef46a9deacd8" \
53
+ "a8fa763fe39c343ff9dcbbc7c70b4f1d8a51e04bcdb45931c89f7ec9d9787364" \
54
+ "eac5ac8334d3ebc3c581a0fffa1363eb170ddd51b7f0da49d316552629d4689e" \
55
+ "2b16be587d47a1fc8ff8b8d17ad031ce45cb3a8f95160428afd7fbcabb4b407e"
56
+ ].pack("H*").freeze
57
+
58
+ # XXH3_INIT_ACC
59
+ INIT_ACC = [PRIME32_3, PRIME64_1, PRIME64_2, PRIME64_3, PRIME64_4, PRIME32_2, PRIME64_5, PRIME32_1].freeze
60
+
61
+ private_constant :MASK64, :MASK32, :PRIME32_1, :PRIME32_2, :PRIME32_3, :PRIME64_1, :PRIME64_2, :PRIME64_3,
62
+ :PRIME64_4, :PRIME64_5, :PRIME_MX1, :PRIME_MX2, :STRIPE_LEN, :SECRET_CONSUME_RATE,
63
+ :ACC_NB, :MIDSIZE_MAX, :SECRET_SIZE_MIN, :MIDSIZE_STARTOFFSET, :MIDSIZE_LASTOFFSET,
64
+ :SECRET_LASTACC_START, :SECRET_MERGEACCS_START, :SECRET, :INIT_ACC
65
+
66
+ class << self
67
+ # XXH3_64bits of `bytes`, hashed as raw bytes — the string's encoding is ignored.
68
+ def digest64(bytes)
69
+ len = bytes.bytesize
70
+ if len <= 16 then len_0to16(bytes, len)
71
+ elsif len <= 128 then len_17to128(bytes, len)
72
+ elsif len <= MIDSIZE_MAX then len_129to240(bytes, len)
73
+ else hash_long(bytes, len)
74
+ end
75
+ end
76
+
77
+ # lisplens ADR-0008's `anchor_hash`: the low 16 bits of the digest as 4 lowercase hex digits.
78
+ def anchor_hash(bytes)
79
+ format("%04x", digest64(bytes) & 0xffff)
80
+ end
81
+
82
+ # lisplens ADR-0008's `file_hash`: the full digest as 16 lowercase hex digits.
83
+ def file_hash(bytes)
84
+ format("%016x", digest64(bytes))
85
+ end
86
+
87
+ private
88
+
89
+ # --- short keys: len <= 16 ---
90
+
91
+ def len_0to16(bytes, len)
92
+ if len > 8 then len_9to16(bytes, len)
93
+ elsif len >= 4 then len_4to8(bytes, len)
94
+ elsif len.positive? then len_1to3(bytes, len)
95
+ else
96
+ xxh64_avalanche(u64(SECRET, 56) ^ u64(SECRET, 64))
97
+ end
98
+ end
99
+
100
+ def len_1to3(bytes, len)
101
+ c1 = bytes.getbyte(0)
102
+ c2 = bytes.getbyte(len >> 1)
103
+ c3 = bytes.getbyte(len - 1)
104
+ combined = (c1 << 16) | (c2 << 24) | c3 | (len << 8)
105
+ bitflip = u32(SECRET, 0) ^ u32(SECRET, 4)
106
+ xxh64_avalanche(combined ^ bitflip)
107
+ end
108
+
109
+ def len_4to8(bytes, len)
110
+ input1 = u32(bytes, 0)
111
+ input2 = u32(bytes, len - 4)
112
+ bitflip = u64(SECRET, 8) ^ u64(SECRET, 16)
113
+ keyed = (input2 + (input1 << 32)) ^ bitflip
114
+ rrmxmx(keyed, len)
115
+ end
116
+
117
+ def len_9to16(bytes, len)
118
+ bitflip1 = u64(SECRET, 24) ^ u64(SECRET, 32)
119
+ bitflip2 = u64(SECRET, 40) ^ u64(SECRET, 48)
120
+ input_lo = u64(bytes, 0) ^ bitflip1
121
+ input_hi = u64(bytes, len - 8) ^ bitflip2
122
+ acc = (len + swap64(input_lo) + input_hi + mul128_fold64(input_lo, input_hi)) & MASK64
123
+ xxh3_avalanche(acc)
124
+ end
125
+
126
+ # --- mid-size keys: 17 <= len <= 240 (mum-hash on 16-byte chunks) ---
127
+
128
+ def mix16b(bytes, in_off, sec_off)
129
+ input_lo = u64(bytes, in_off)
130
+ input_hi = u64(bytes, in_off + 8)
131
+ mul128_fold64(input_lo ^ u64(SECRET, sec_off), input_hi ^ u64(SECRET, sec_off + 8))
132
+ end
133
+
134
+ def len_17to128(bytes, len)
135
+ acc = len * PRIME64_1
136
+ if len > 32
137
+ if len > 64
138
+ if len > 96
139
+ acc += mix16b(bytes, 48, 96)
140
+ acc += mix16b(bytes, len - 64, 112)
141
+ end
142
+ acc += mix16b(bytes, 32, 64)
143
+ acc += mix16b(bytes, len - 48, 80)
144
+ end
145
+ acc += mix16b(bytes, 16, 32)
146
+ acc += mix16b(bytes, len - 32, 48)
147
+ end
148
+ acc += mix16b(bytes, 0, 0)
149
+ acc += mix16b(bytes, len - 16, 16)
150
+ xxh3_avalanche(acc & MASK64)
151
+ end
152
+
153
+ def len_129to240(bytes, len)
154
+ acc = len * PRIME64_1
155
+ nb_rounds = len / 16
156
+ 8.times { |i| acc += mix16b(bytes, 16 * i, 16 * i) }
157
+ acc_end = mix16b(bytes, len - 16, SECRET_SIZE_MIN - MIDSIZE_LASTOFFSET)
158
+ acc = xxh3_avalanche(acc & MASK64)
159
+ (8...nb_rounds).each do |i|
160
+ acc_end += mix16b(bytes, 16 * i, (16 * (i - 8)) + MIDSIZE_STARTOFFSET)
161
+ end
162
+ xxh3_avalanche((acc + acc_end) & MASK64)
163
+ end
164
+
165
+ # --- long keys: len > 240 (striped accumulate + scramble, then merge) ---
166
+
167
+ def hash_long(bytes, len)
168
+ acc = INIT_ACC.dup
169
+ secret_size = SECRET.bytesize
170
+ nb_stripes_per_block = (secret_size - STRIPE_LEN) / SECRET_CONSUME_RATE
171
+ block_len = STRIPE_LEN * nb_stripes_per_block
172
+ nb_blocks = (len - 1) / block_len
173
+
174
+ nb_blocks.times do |n|
175
+ accumulate_stripes(acc, bytes, n * block_len, nb_stripes_per_block)
176
+ scramble(acc, secret_size - STRIPE_LEN)
177
+ end
178
+
179
+ last_off = nb_blocks * block_len
180
+ accumulate_stripes(acc, bytes, last_off, (len - 1 - last_off) / STRIPE_LEN)
181
+ accumulate_512(acc, bytes, len - STRIPE_LEN, secret_size - STRIPE_LEN - SECRET_LASTACC_START)
182
+
183
+ merge_accs(acc, len * PRIME64_1)
184
+ end
185
+
186
+ def accumulate_stripes(acc, bytes, base_off, nb_stripes)
187
+ nb_stripes.times do |s|
188
+ accumulate_512(acc, bytes, base_off + (s * STRIPE_LEN), s * SECRET_CONSUME_RATE)
189
+ end
190
+ end
191
+
192
+ # XXH3_accumulate_512_scalar: each 64-byte stripe folds into the 8-lane accumulator — the
193
+ # data value is added to the swapped adjacent lane, and the secret-keyed value contributes a
194
+ # 32x32->64 product to its own lane.
195
+ def accumulate_512(acc, bytes, in_off, sec_off)
196
+ ACC_NB.times do |i|
197
+ data_val = u64(bytes, in_off + (8 * i))
198
+ data_key = data_val ^ u64(SECRET, sec_off + (8 * i))
199
+ acc[i ^ 1] = (acc[i ^ 1] + data_val) & MASK64
200
+ acc[i] = (acc[i] + ((data_key & MASK32) * (data_key >> 32))) & MASK64
201
+ end
202
+ end
203
+
204
+ # XXH3_scrambleAcc_scalar
205
+ def scramble(acc, sec_off)
206
+ ACC_NB.times do |i|
207
+ acc[i] = ((acc[i] ^ (acc[i] >> 47) ^ u64(SECRET, sec_off + (8 * i))) * PRIME32_1) & MASK64
208
+ end
209
+ end
210
+
211
+ # XXH3_mergeAccs over the default secret, at SECRET_MERGEACCS_START.
212
+ def merge_accs(acc, start)
213
+ result = start
214
+ 4.times do |i|
215
+ result += mul128_fold64(acc[2 * i] ^ u64(SECRET, SECRET_MERGEACCS_START + (16 * i)),
216
+ acc[(2 * i) + 1] ^ u64(SECRET, SECRET_MERGEACCS_START + (16 * i) + 8))
217
+ end
218
+ xxh3_avalanche(result & MASK64)
219
+ end
220
+
221
+ # --- primitives ---
222
+
223
+ def u32(bytes, off) = bytes.unpack1("L<", offset: off)
224
+
225
+ def u64(bytes, off) = bytes.unpack1("Q<", offset: off)
226
+
227
+ def swap64(value) = [value].pack("Q>").unpack1("Q<")
228
+
229
+ def rotl64(value, shift) = ((value << shift) | (value >> (64 - shift))) & MASK64
230
+
231
+ def mul128_fold64(lhs, rhs)
232
+ product = lhs * rhs
233
+ (product & MASK64) ^ (product >> 64)
234
+ end
235
+
236
+ # XXH3_avalanche — the fast finalizer used when the input is already partially mixed.
237
+ def xxh3_avalanche(hash)
238
+ hash = (hash ^ (hash >> 37)) & MASK64
239
+ hash = (hash * PRIME_MX1) & MASK64
240
+ hash ^ (hash >> 32)
241
+ end
242
+
243
+ # XXH64_avalanche — the stronger finalizer for the shortest keys.
244
+ def xxh64_avalanche(hash)
245
+ hash ^= hash >> 33
246
+ hash = (hash * PRIME64_2) & MASK64
247
+ hash ^= hash >> 29
248
+ hash = (hash * PRIME64_3) & MASK64
249
+ hash ^ (hash >> 32)
250
+ end
251
+
252
+ # XXH3_rrmxmx — the len 4-8 finalizer (Pelle Evensen's mix).
253
+ def rrmxmx(hash, len)
254
+ hash = (hash ^ rotl64(hash, 49) ^ rotl64(hash, 24)) & MASK64
255
+ hash = (hash * PRIME_MX2) & MASK64
256
+ hash ^= (hash >> 35) + len
257
+ hash = (hash * PRIME_MX2) & MASK64
258
+ hash ^ (hash >> 28)
259
+ end
260
+ end
261
+ # rubocop:enable Naming/VariableNumber
262
+ end
263
+ end
264
+ end