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
@@ -28,7 +28,7 @@ module Rigor
28
28
  class InvalidLabelError < Error
29
29
  end
30
30
 
31
- DATA_PATH = File.expand_path("../../../data/effects/registry.yml", __dir__)
31
+ DATA_PATH = File.expand_path("../../../data/effects/registry.yml", __dir__).freeze
32
32
 
33
33
  # How far a misspelling may be from a known label before {#suggest} declines to guess. Two
34
34
  # edits catches a transposition or a dropped segment character ("io.nte", "nondet.tim")
@@ -4,7 +4,9 @@ require "prism"
4
4
 
5
5
  require_relative "../source/constant_path"
6
6
  require_relative "../source/node_children"
7
+ require_relative "ancestry_recorder"
7
8
  require_relative "attribution"
9
+ require_relative "definition_context"
8
10
  require_relative "envelope_index"
9
11
  require_relative "file_collection"
10
12
  require_relative "local_ownership"
@@ -13,6 +15,7 @@ require_relative "framework_units"
13
15
  require_relative "plugin_facts"
14
16
  require_relative "summary"
15
17
  require_relative "unit_scan"
18
+ require_relative "visibility"
16
19
 
17
20
  module Rigor
18
21
  module Effects
@@ -21,8 +24,10 @@ module Rigor
21
24
  # The scanner owns **identity** — which effect units the file defines and what each is keyed as — and
22
25
  # delegates each unit's body to {UnitScan}. Keys follow the existing symbol tables (ADR-103 WD14):
23
26
  # `Class#m` for an instance method, `Class.m` for a singleton one, and `<toplevel>#m` for a `def`
24
- # outside any class body. Reopenings in one file join here; reopenings across files join when the
25
- # runner merges the collections.
27
+ # outside any class body. The side is the one Ruby defines the method on, which the enclosing method
28
+ # does not decide: {DefinitionContext} carries it down the walk, and a definition it cannot place is
29
+ # no unit. Reopenings in one file join here; reopenings across files join when the runner merges the
30
+ # collections.
26
31
  #
27
32
  # Three kinds of unit exist beyond a plain `def`:
28
33
  #
@@ -30,7 +35,9 @@ module Rigor
30
35
  # skip it, so this is the minimal extension WD14 calls for, made here rather than in `ScopeIndexer`
31
36
  # because nothing outside effects needs it yet.
32
37
  # - `attr_reader` / `attr_writer` / `attr_accessor` — synthesised: a reader is ∅, a writer is
33
- # `mutate.self`. Without them a caller's edge into an accessor would read as unresolved.
38
+ # `mutate.self` (`mutate.static` where `self` is the singleton class, since it writes the class
39
+ # object's ivar).
40
+ # Without them a caller's edge into an accessor would read as unresolved.
34
41
  # - a nested `def` — its own unit under the same class, never contained in the enclosing method.
35
42
  #
36
43
  # **This walk exists only when collection is on.** ADR-103 WD13 prefers riding `ScopeIndexer`'s
@@ -47,77 +54,121 @@ module Rigor
47
54
  # its own that no slice models yet.
48
55
  DECLARATION_MACROS = %i[include prepend attr_reader attr_writer attr_accessor define_method].to_set.freeze
49
56
 
50
- MUTATE_SELF = LabelSet.new(["mutate.self"])
51
- private_constant :MUTATE_SELF
52
-
53
57
  # A synthesised writer's summary is the same value at every `attr_accessor` in the project, so it is
54
- # built once rather than per accessor.
55
- WRITER_SUMMARY = Summary.new(bundles: { Origin.construct("attr-writer") => MUTATE_SELF })
56
- private_constant :WRITER_SUMMARY
57
-
58
+ # built once rather than per accessor. A singleton writer sets an ivar on the class object, and is
59
+ # labelled as an ivar write in a body that runs on a class is.
60
+ WRITER_SUMMARY = Summary.new(bundles: { Origin.construct("attr-writer") => UnitScan::MUTATE_SELF })
61
+ SINGLETON_WRITER_SUMMARY = Summary.new(bundles: { Origin.construct("attr-writer") => UnitScan::MUTATE_STATIC })
62
+ private_constant :WRITER_SUMMARY, :SINGLETON_WRITER_SUMMARY
63
+
64
+ # Every argument is one collection input the scan reads; a context object would move the same list
65
+ # one call further out.
66
+ # rubocop:disable Metrics/ParameterLists
58
67
  def self.scan(root:, path:, calls:, attribution: Attribution.empty, envelopes: EnvelopeIndex.empty,
59
- plugin_facts: PluginFacts.empty)
68
+ plugin_facts: PluginFacts.empty, unit_key: nil, unit_owner: nil)
60
69
  new(path: path, calls: calls, attribution: attribution, envelopes: envelopes,
61
- plugin_facts: plugin_facts).scan(root)
70
+ plugin_facts: plugin_facts, unit_key: unit_key, unit_owner: unit_owner).scan(root)
62
71
  end
63
72
 
64
73
  def initialize(path:, calls:, attribution: Attribution.empty, envelopes: EnvelopeIndex.empty,
65
- plugin_facts: PluginFacts.empty)
74
+ plugin_facts: PluginFacts.empty, unit_key: nil, unit_owner: nil)
75
+ # rubocop:enable Metrics/ParameterLists
66
76
  @path = path
77
+ # #392 — a template unit: the WHOLE file is one effect unit, keyed `view:<logical_name>`. A
78
+ # template has no `def` to key on and no owner class of its own, so the ordinary walk — which only
79
+ # ever mints a unit at a `def` — would report nothing for a file that calls into the project all
80
+ # the way down.
81
+ @unit_key = unit_key
82
+ # The declared `self` the unit's implicit-self calls resolve against — the plugin's `self_type:`.
83
+ # Without it a helper call in a template reads as an unresolved self-call and taints the unit.
84
+ @unit_owner = unit_owner
67
85
  @calls = calls
68
86
  @attribution = attribution
69
87
  @envelopes = envelopes
70
88
  @plugin_facts = plugin_facts
71
89
  @summaries = {}
72
90
  @edges = {}
73
- @superclasses = {}
74
- @includes = {}
91
+ @ancestry = AncestryRecorder.new
92
+ # #1048 — `{class name => Set[method name]}` for the `private` / `protected` members each class
93
+ # body declared. Read only by a UNIT callee rule, and only to decline.
94
+ @non_public = {}
75
95
  # ADR-103 WD10 / #387 — the class-body facts the framework-edge strategies read, harvested only
76
96
  # when a loaded plugin declared one. A run with no `effect_edges:` never allocates them.
77
97
  @harvest = plugin_facts.edges? ? {} : nil
78
98
  end
79
99
 
80
100
  def scan(root)
81
- walk(root, [], false)
101
+ return scan_template_unit(root) if @unit_key
102
+
103
+ walk(root, [], DefinitionContext::TOP_LEVEL)
82
104
  synthesize_framework_units
83
105
  FileCollection.new(
84
106
  path: @path, summaries: @summaries, edges: @edges,
85
- superclasses: @superclasses, includes: @includes
107
+ superclasses: @ancestry.superclasses, includes: @ancestry.includes
86
108
  )
87
109
  end
88
110
 
89
111
  private
90
112
 
91
- def walk(node, prefix, singleton)
113
+ # The whole file body as one unit under {@unit_key}. Deliberately NOT combined with the ordinary
114
+ # walk: a compiled template is straight-line statements, a `def` inside one would be a method the
115
+ # render site cannot call, and a second keying rule over the same nodes would double-count every
116
+ # origin in the file.
117
+ def scan_template_unit(root)
118
+ summary, edges = UnitScan.new(
119
+ context: DefinitionContext::INSTANCE_METHOD_BODY, parameters: [], block_parameter: nil,
120
+ owned_locals: LocalOwnership.owned(root, [], singleton: false), calls: @calls,
121
+ attribution: @attribution, envelopes: @envelopes, plugin_facts: @plugin_facts,
122
+ owner_class: @unit_owner, method_name: @unit_key
123
+ ).run(root)
124
+ merge_unit(@unit_key, summary, edges)
125
+ FileCollection.new(path: @path, summaries: @summaries, edges: @edges)
126
+ rescue StandardError
127
+ FileCollection.new(path: @path, summaries: { @unit_key => Summary.tainted("collector-error", @unit_key) })
128
+ end
129
+
130
+ # `context` is the {DefinitionContext} of the class-body position `node` sits at. A `class <<` body
131
+ # and a block that rebinds `self` move it; a nested namespace starts afresh.
132
+ def walk(node, prefix, context)
92
133
  return unless node.is_a?(Prism::Node)
93
134
 
94
135
  case node
95
136
  when Prism::ClassNode, Prism::ModuleNode
96
137
  return walk_namespace(node, prefix)
97
- when Prism::SingletonClassNode
98
- return walk(node.body, prefix, true) if node.body
99
138
  when Prism::DefNode
100
- return enter_def(node, prefix, singleton)
139
+ return enter_def(node, prefix, context)
140
+ when Prism::ConstantWriteNode, Prism::ConstantPathWriteNode
141
+ @ancestry.record_constant_class(node, prefix)
142
+ when Prism::AliasMethodNode
143
+ return record_initialize_alias(prefix) if @ancestry.alias_to_initialize?(node, context)
101
144
  when Prism::CallNode
102
145
  harvest_class_body_macro(node, prefix)
103
- return record_declaration(node, prefix) if declaration?(node)
146
+ return record_initialize_alias(prefix) if @ancestry.alias_to_initialize?(node, context)
147
+ return record_declaration(node, prefix, context) if declaration?(node)
104
148
  end
105
149
 
106
- node.rigor_each_child { |child| walk(child, prefix, singleton) }
150
+ rebinds = DefinitionContext.rebinds?(node)
151
+ node.rigor_each_child { |child| walk(child, prefix, rebinds ? context.for_child(node, child) : context) }
107
152
  end
108
153
 
109
154
  def walk_namespace(node, prefix)
110
155
  nested = Source::ConstantPath.declaration_prefix(prefix, node.constant_path)
111
- return node.rigor_each_child { |child| walk(child, prefix, false) } if nested.nil?
156
+ return node.rigor_each_child { |child| walk(child, prefix, DefinitionContext::CLASS_BODY) } if nested.nil?
157
+
158
+ @ancestry.record_superclass(nested.join("::"), node, prefix) if node.is_a?(Prism::ClassNode)
159
+ return if node.body.nil?
112
160
 
113
- record_superclass(nested.join("::"), node, prefix) if node.is_a?(Prism::ClassNode)
114
- walk(node.body, nested, false) if node.body
161
+ @non_public[nested.join("::")] = Visibility.non_public_names(node.body)
162
+ walk(node.body, nested, DefinitionContext::CLASS_BODY)
115
163
  end
116
164
 
117
- def enter_def(node, prefix, singleton)
118
- own_singleton = singleton || !node.receiver.nil?
119
- scan = add_unit(class_name_for(prefix), node.name.to_s, own_singleton, node.body, node.parameters)
120
- harvest_def(prefix, node.name.to_s, own_singleton, scan) if @harvest && !prefix.empty?
165
+ def enter_def(node, prefix, context)
166
+ body_context = context.def_body(node)
167
+ return if body_context.nil?
168
+
169
+ scan = add_unit(class_name_for(prefix), node.name.to_s, body_context, node.body, node.parameters,
170
+ non_public: non_public?(prefix, node.name.to_s))
171
+ harvest_def(prefix, node.name.to_s, body_context.singleton?, scan) if @harvest && !prefix.empty?
121
172
  end
122
173
 
123
174
  # What the framework strategies need to know about a `def` the class body spelled out itself: that it
@@ -162,6 +213,10 @@ module Rigor
162
213
  end || false
163
214
  end
164
215
 
216
+ def non_public?(prefix, name)
217
+ prefix.empty? ? false : @non_public[class_name_for(prefix)]&.include?(name) || false
218
+ end
219
+
165
220
  def harvest_for(prefix)
166
221
  @harvest[class_name_for(prefix)] ||= { defs: [], units: {}, macros: {}, uniqueness: false }
167
222
  end
@@ -184,21 +239,28 @@ module Rigor
184
239
  # per unit (ADR-103 WD13): a unit the scanner cannot finish is recorded as non-exhaustive with
185
240
  # `collector-error` and its siblings are unaffected.
186
241
  #
187
- # @return [UnitScan, nil] the finished scan, or nil when the unit failed soft
188
- def add_unit(class_name, method_name, singleton, body, parameters)
189
- key = "#{class_name}#{singleton ? '.' : '#'}#{method_name}"
242
+ # `context` is the {DefinitionContext} the body runs under, and the unit is keyed singleton exactly
243
+ # when that body runs on a class. The enclosing unit's own bit does not decide where the units nested
244
+ # in it land: `def self.outer` is scanned as a singleton method, and a `def inner` inside it is
245
+ # `Class#inner`.
246
+ #
247
+ # @return the finished scan, or nil when the unit failed soft
248
+ def add_unit(class_name, method_name, context, body, parameters, non_public: false, shared_slot: false)
249
+ key = "#{class_name}#{context.singleton? ? '.' : '#'}#{method_name}"
190
250
  names = parameter_names(parameters)
191
251
  scan = UnitScan.new(
192
- singleton: singleton, parameters: names,
252
+ context: context, parameters: names,
193
253
  block_parameter: block_parameter_name(parameters),
194
- owned_locals: LocalOwnership.owned(body, names), calls: @calls,
254
+ owned_locals: LocalOwnership.owned(body, names, singleton: context.singleton?), calls: @calls,
195
255
  attribution: @attribution, envelopes: @envelopes, plugin_facts: @plugin_facts,
196
- owner_class: class_name, method_name: method_name
256
+ owner_class: class_name, method_name: method_name, non_public: non_public, shared_slot: shared_slot
197
257
  )
198
258
  summary, edges = scan.run(body)
199
259
  merge_unit(key, summary, edges)
200
- scan.nested.each do |name, nested_singleton, nested_body, nested_parameters|
201
- add_unit(class_name, name, singleton || nested_singleton, nested_body, nested_parameters)
260
+ scan.nested.each do |name, nested_context, nested_body, nested_parameters, nested_shared_slot|
261
+ # A `def` inside a method is never an action, whatever the enclosing body's visibility.
262
+ add_unit(class_name, name, nested_context, nested_body, nested_parameters,
263
+ non_public: true, shared_slot: nested_shared_slot)
202
264
  end
203
265
  scan
204
266
  rescue StandardError
@@ -213,51 +275,42 @@ module Rigor
213
275
  node.receiver.nil? && DECLARATION_MACROS.include?(node.name)
214
276
  end
215
277
 
216
- def record_declaration(node, prefix)
278
+ def record_declaration(node, prefix, context)
217
279
  class_name = class_name_for(prefix)
218
280
  case node.name
219
- when :include, :prepend then record_includes(class_name, node, prefix)
220
- when :define_method then declare_define_method(class_name, node)
221
- else synthesize_accessors(class_name, node)
281
+ when :include, :prepend then @ancestry.record_includes(class_name, node, prefix, context)
282
+ when :define_method then declare_define_method(class_name, node, context)
283
+ else synthesize_accessors(class_name, node, context.module_call_body)
222
284
  end
223
285
  end
224
286
 
225
- def declare_define_method(class_name, node)
226
- unit = UnitScan.define_method_unit(node)
227
- return if unit.nil?
287
+ def declare_define_method(class_name, node, context)
288
+ name, body, parameters = UnitScan.define_method_unit(node)
289
+ body_context = context.module_call_body
290
+ return if name.nil? || body_context.nil?
228
291
 
229
- name, singleton, body, parameters = unit
230
- add_unit(class_name, name, singleton, body, parameters)
292
+ # The block runs on the special-variable slot of the body that calls `define_method`, shared by every sibling.
293
+ add_unit(class_name, name, body_context, body, parameters, shared_slot: true)
231
294
  end
232
295
 
233
- def synthesize_accessors(class_name, node)
296
+ # `attr_*` is a call on `self`, as `define_method` is, so it defines on the side `define_method` would:
297
+ # the class's own accessors inside `class << self`, and none where no key names the owner.
298
+ def synthesize_accessors(class_name, node, body_context)
299
+ return if body_context.nil?
300
+
301
+ singleton = body_context.singleton?
302
+ separator = singleton ? "." : "#"
234
303
  symbol_arguments(node).each do |name|
235
- merge_unit("#{class_name}##{name}", Summary.empty, []) unless node.name == :attr_writer
304
+ merge_unit("#{class_name}#{separator}#{name}", Summary.empty, []) unless node.name == :attr_writer
236
305
  next if node.name == :attr_reader
237
306
 
238
- merge_unit("#{class_name}##{name}=", WRITER_SUMMARY, [])
307
+ merge_unit("#{class_name}#{separator}#{name}=", singleton ? SINGLETON_WRITER_SUMMARY : WRITER_SUMMARY, [])
239
308
  end
240
309
  end
241
310
 
242
- def record_includes(class_name, node, prefix)
243
- names = constant_arguments(node).flat_map { |name| lexical_candidates(name, prefix) }
244
- (@includes[class_name] ||= []).concat(names) unless names.empty?
245
- end
246
-
247
- def record_superclass(full_name, node, prefix)
248
- superclass = node.superclass && Source::ConstantPath.qualified_name(node.superclass)
249
- @superclasses[full_name] = lexical_candidates(superclass, prefix) if superclass
250
- end
251
-
252
- # An ancestry name is recorded AS WRITTEN — `class Loud < Base` inside `module Tracer` names
253
- # `Base`, not `Tracer::Base` — and a single file cannot say which constant that resolves to. So the
254
- # scanner records the candidates Ruby's own lexical lookup would try, most-qualified first, and the
255
- # propagator picks the one the merged project actually defines. Same shape as `ScopeIndexer`'s
256
- # as-written superclass table, resolved at the same point: when the whole project is in view.
257
- def lexical_candidates(name, prefix)
258
- return [name] if prefix.empty? || name.start_with?("#{prefix.join('::')}::")
259
-
260
- prefix.length.downto(1).map { |depth| "#{prefix.first(depth).join('::')}::#{name}" } + [name]
311
+ # The opaque-ancestry recordings the walk routes here; {AncestryRecorder} owns what they mean.
312
+ def record_initialize_alias(prefix)
313
+ @ancestry.record_initialize_alias(class_name_for(prefix))
261
314
  end
262
315
 
263
316
  def merge_unit(key, summary, edges)
@@ -274,10 +327,6 @@ module Rigor
274
327
  []
275
328
  end
276
329
 
277
- def constant_arguments(node)
278
- node.arguments&.arguments&.filter_map { |argument| Source::ConstantPath.qualified_name(argument) } || []
279
- end
280
-
281
330
  def parameter_names(parameters)
282
331
  return Set.new unless parameters.is_a?(Prism::ParametersNode)
283
332
 
@@ -35,7 +35,7 @@ module Rigor
35
35
  ANNOTATION_BRACKETS.map do |opener, closer|
36
36
  /%a#{Regexp.escape(opener)}\s*(?:pure\s*#{Regexp.escape(closer)}|rigor:v1:effect\b)/
37
37
  end
38
- )
38
+ ).freeze
39
39
 
40
40
  # A `virtual:<plugin-id>:<source path>` buffer is rbs-inline's (or a plugin's) synthesized RBS for
41
41
  # a Ruby file the author actually wrote in. Naming that file is what a reader can act on, so the
@@ -52,9 +52,8 @@ module Rigor
52
52
  stripped.start_with?(root) ? stripped[root.length..] : stripped
53
53
  end
54
54
 
55
- # @param signature_paths [Array<String>, nil] the configured roots; empty / nil takes the default.
56
- # @param virtual_rbs [Array<Array(String, String)>, nil] the loader's virtual entries.
57
- # @return [Array<Array(String, String)>]
55
+ # @param signature_paths — the configured roots; empty / nil takes the default.
56
+ # @param virtual_rbs — the loader's virtual entries.
58
57
  def collect(signature_paths:, virtual_rbs: nil)
59
58
  files = roots(signature_paths).flat_map { |root| Dir.glob(File.join(root.to_s, "**", "*.rbs")).sort }
60
59
  sources = files.filter_map do |path|
@@ -76,8 +75,7 @@ module Rigor
76
75
  # residual reports one annotation per run, so the carrier a run holds past the environment's own
77
76
  # lifetime is a single synthesized buffer rather than the project's whole virtual tree.
78
77
  #
79
- # @param virtual_rbs [Array<Array(String, String)>, nil]
80
- # @return [Array<Array(String, String)>] zero or one pair.
78
+ # @return zero or one pair.
81
79
  def annotated_carrier(virtual_rbs)
82
80
  Array(virtual_rbs).each do |name, content|
83
81
  return [[name.to_s, content.to_s]] if ANNOTATION_HINT.match?(content)
@@ -117,12 +117,12 @@ module Rigor
117
117
  class << self
118
118
  # Builds the snapshot a run's effect table describes.
119
119
  #
120
- # @param table [EffectTable] the propagated graph
121
- # @param configuration [Rigor::Configuration] supplies the reach globs and the digested block
122
- # @param sources [Hash{String=>Array<String>}] `Runner#effect_sources` — where each unit is defined
123
- # @param full [Boolean] keep the rows {#omit?} drops
124
- # @param registry [Registry] the vocabulary whose version the header carries
125
- # @param project_root [String] what `sources` paths are relativised against for glob matching
120
+ # @param table — the propagated graph
121
+ # @param configuration — supplies the reach globs and the digested block
122
+ # @param sources — `Runner#effect_sources` — where each unit is defined
123
+ # @param full — keep the rows {#omit?} drops
124
+ # @param registry — the vocabulary whose version the header carries
125
+ # @param project_root — what `sources` paths are relativised against for glob matching
126
126
  def build(table:, configuration:, sources: {}, full: false, registry: Registry.default,
127
127
  project_root: Dir.pwd)
128
128
  globs = expand_reach(configuration.effects_snapshot_reach)
@@ -146,10 +146,10 @@ module Rigor
146
146
  # It is built beside the snapshot and never inside it: the file stays flat and undischarged, and
147
147
  # this index exists only for the duration of one comparison ({SnapshotDiff}).
148
148
  #
149
- # @param snapshot [Snapshot] the current side, already built
150
- # @param table [EffectTable] the run that produced it
151
- # @param discharge [Discharge] the policy
152
- # @return [Hash, nil] nil when the policy discharges nothing, which is the common case
149
+ # @param snapshot — the current side, already built
150
+ # @param table — the run that produced it
151
+ # @param discharge — the policy
152
+ # @return nil when the policy discharges nothing, which is the common case
153
153
  def undischarged_index(snapshot:, table:, discharge:)
154
154
  return nil if discharge.inert?
155
155
 
@@ -306,7 +306,8 @@ module Rigor
306
306
  )
307
307
  end
308
308
 
309
- # A synthesised default summary — an `attr_*` writer's `mutate.self`, and the `Struct` / `Data`
309
+ # A synthesised default summary — an `attr_*` writer's `mutate.self` (`mutate.static` for a
310
+ # singleton one), and the `Struct` / `Data`
310
311
  # accessors that join it when discovery synthesises them — carries no information a reviewer acts
311
312
  # on: it restates the `attr_accessor` line. Recognised by its origins being exactly the synthesised
312
313
  # constructs, so a hand-written `def name=` keeps its row.
@@ -86,7 +86,7 @@ module Rigor
86
86
  # Compares `current` against `recorded`. `recorded` may be nil — no snapshot on disk at all, which
87
87
  # is drift with a routed message rather than an error.
88
88
  #
89
- # @param undischarged [Hash] `{table name => {symbol => [label]}}` — the current side's labels that
89
+ # @param undischarged — `{table name => {symbol => [label]}}` — the current side's labels that
90
90
  # survive per-origin discharge ({Snapshot.undischarged_index}). Optional; omitting it judges every
91
91
  # event by label.
92
92
  def self.compare(recorded:, current:, tolerated: [], gate: :symmetric, strict_tolerated: false,
@@ -20,6 +20,8 @@ module Rigor
20
20
  # from {#bundles} because nothing declared is ever proven: diagnostics read {#proven} only, so an
21
21
  # attributed label can never manufacture an `effect.envelope-exceeded`.
22
22
  # - {#exhaustive} — false when some call this method makes could not be resolved.
23
+ # - {#unclaimed} — true when some call it makes was described by nothing at all. A separate question
24
+ # from exhaustiveness, with a single reader; see {#unclaimed?}.
23
25
  # - {#causes} — why, from {TaintCause}'s closed enum, as `[cause, detail]` pairs. Empty when exhaustive.
24
26
  #
25
27
  # {#proven} is the flat projection of {#bundles}: the join of every bundle. It is computed once at
@@ -53,10 +55,12 @@ module Rigor
53
55
 
54
56
  attr_reader :bundles, :declared_bundles, :declared, :causes, :proven
55
57
 
56
- def initialize(bundles: NO_BUNDLES, declared_bundles: NO_BUNDLES, exhaustive: true, causes: NO_CAUSES)
58
+ def initialize(bundles: NO_BUNDLES, declared_bundles: NO_BUNDLES, exhaustive: true, causes: NO_CAUSES,
59
+ unclaimed: false)
57
60
  @bundles = normalize_bundles(bundles)
58
61
  @declared_bundles = normalize_bundles(declared_bundles)
59
62
  @exhaustive = exhaustive ? true : false
63
+ @unclaimed = unclaimed ? true : false
60
64
  @causes = normalize_causes(causes)
61
65
  @proven = flatten(@bundles)
62
66
  @declared = flatten(@declared_bundles)
@@ -69,6 +73,24 @@ module Rigor
69
73
  @exhaustive
70
74
  end
71
75
 
76
+ # Whether this method made a call NOTHING described — no catalogue row, no plugin row, no imported
77
+ # envelope, and no project definition for the edge to land on (#391).
78
+ #
79
+ # It is deliberately **not** a taint. "Every call was resolved" and "every callee's footprint is
80
+ # known" are different questions, and the model answers the first one everywhere: an unresolved
81
+ # ordinary edge is dropped rather than tainted, because most such calls are inherited or gem calls
82
+ # the catalogue has no row for, and tainting them would make almost every method in almost every
83
+ # project non-exhaustive. That posture is right for a report and for a judgment, both of which read
84
+ # the proven lane.
85
+ #
86
+ # It is wrong for EMISSION. `%a{pure}` written onto a method that calls an undescribed gem method
87
+ # is a contract nothing re-derives, enforced on that method's callers from then on. So the second
88
+ # question gets its own bit, read by `rigor sig-gen` and by nothing else: not by the report, not by
89
+ # the snapshot, not by `effect.envelope-exceeded`, and not by `trivial?`.
90
+ def unclaimed?
91
+ @unclaimed
92
+ end
93
+
72
94
  # Whether this summary is worth showing at all: an exhaustive method whose whole proven footprint is
73
95
  # frame-local mutation and which claims nothing. `mutate.local` is tolerated by every envelope
74
96
  # (`%a{pure}` included), so a method that only mutates what its own frame allocated reads as pure
@@ -93,18 +115,19 @@ module Rigor
93
115
  bundles: merge_bundles(@bundles, other.bundles),
94
116
  declared_bundles: merge_bundles(@declared_bundles, other.declared_bundles),
95
117
  exhaustive: @exhaustive && other.exhaustive?,
96
- causes: @causes + other.causes
118
+ causes: @causes + other.causes,
119
+ unclaimed: @unclaimed || other.unclaimed?
97
120
  )
98
121
  end
99
122
 
100
123
  def ==(other)
101
124
  other.is_a?(Summary) && other.bundles == @bundles && other.declared_bundles == @declared_bundles &&
102
- other.exhaustive? == @exhaustive && other.causes == @causes
125
+ other.exhaustive? == @exhaustive && other.causes == @causes && other.unclaimed? == @unclaimed
103
126
  end
104
127
  alias eql? ==
105
128
 
106
129
  def hash
107
- [self.class, @bundles, @declared_bundles, @exhaustive, @causes].hash
130
+ [self.class, @bundles, @declared_bundles, @exhaustive, @causes, @unclaimed].hash
108
131
  end
109
132
 
110
133
  def inspect