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
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../check_rules/rule_ids"
3
4
  require_relative "../crash_signature"
4
5
  require_relative "../diagnostic"
5
6
  require_relative "../severity_stamp"
@@ -22,36 +23,39 @@ module Rigor
22
23
  # end-of-pass snapshots) is read through injected reader procs so this collaborator never calls back
23
24
  # into the {Runner} and the read happens at the exact point in the run the original inline read did.
24
25
  class DiagnosticAggregator # rubocop:disable Metrics/ClassLength
25
- # @param configuration [Rigor::Configuration]
26
- # @param rbs_extended_reporter [RbsExtended::Reporter]
27
- # @param boundary_cross_reporter [DependencySourceInference::BoundaryCrossReporter]
28
- # @param source_rbs_synthesis_reporter [Plugin::SourceRbsSynthesisReporter]
29
- # @param plugin_registry [#call] reader returning the current {Plugin::Registry} (varies per run).
30
- # @param dependency_source_index [#call] reader returning the current
26
+ # @param plugin_registry — reader returning the current {Plugin::Registry} (varies per run).
27
+ # @param dependency_source_index — reader returning the current
31
28
  # {DependencySourceInference::Index}.
32
- # @param pool_mode [#call] reader returning the pool-mode flag.
33
- # @param cached_plugin_prepare_diagnostics [#call] reader returning the prepare-diagnostic snapshot.
34
- # @param pre_eval_diagnostics_from_scanner [#call] reader returning the pre-eval scanner diagnostics.
35
- # @param synthesized_namespaces_snapshot [#call] reader.
36
- # @param quarantined_signatures_snapshot [#call] reader returning the `signature_paths:` files skipped
29
+ # @param pool_mode — reader returning the pool-mode flag.
30
+ # @param cached_plugin_prepare_diagnostics — reader returning the prepare-diagnostic snapshot.
31
+ # @param pre_eval_diagnostics_from_scanner — reader returning the pre-eval scanner diagnostics.
32
+ # @param synthesized_namespaces_snapshot — reader.
33
+ # @param quarantined_signatures_snapshot — reader returning the `signature_paths:` files skipped
37
34
  # because they do not parse (`[path, first_error_line]` pairs).
38
- # @param env_build_failure_snapshot [#call] reader returning the total RBS env-build failure tuple
35
+ # @param signature_standdowns_snapshot — reader returning the plugin-contributed signature files
36
+ # that stood down against a colliding generic arity (#610), as
37
+ # `[path, class_name, existing_arity, incoming_arity, existing_file]` tuples. Defaults to none so
38
+ # a caller that snapshots nothing of the kind need not say so.
39
+ # @param env_build_failure_snapshot — reader returning the total RBS env-build failure tuple
39
40
  # (`[error_class, first_error_line, conflicting_buffer_names]`) or nil when the env built.
40
- # @param definition_build_failures_snapshot [#call] issue #696 — reader returning the per-class
41
+ # @param definition_build_failures_snapshot — issue #696 — reader returning the per-class
41
42
  # `RBS::DefinitionBuilder` failures the run observed, as `[class_name, error_class, member,
42
- # conflicting_buffer_names]` tuples. Empty for a healthy sig set.
43
- # @param hkt_scan_failure_snapshot [#call] issue #784 — reader returning the `[error_class_name,
43
+ # conflicting_buffer_names, unresolved_type_name_detail]` tuples (issue #997 added the fifth
44
+ # element — `[token, location, refinement_hint]`, `nil` unless `error_class` is
45
+ # `RBS::NoTypeFoundError`). Empty for a healthy sig set.
46
+ # @param hkt_scan_failure_snapshot — issue #784 — reader returning the `[error_class_name,
44
47
  # first_message_line, raw_frame_or_nil, stage]` tuple whichever stage of the HKT-registry build
45
48
  # raised, or nil when both built (or were never demanded). `stage` is `:scan` (the RBS `type`-alias
46
49
  # scan) or `:overlay` (the plugin-manifest aggregation, #791), and picks the row's wording.
47
- # @param conformance_results_snapshot [#call] reader.
50
+ # @param conformance_results_snapshot — reader.
48
51
  def initialize(configuration:, rbs_extended_reporter:, boundary_cross_reporter:, # rubocop:disable Metrics/ParameterLists
49
52
  source_rbs_synthesis_reporter:, plugin_registry:, dependency_source_index:,
50
53
  pool_mode:, cached_plugin_prepare_diagnostics:,
51
54
  pre_eval_diagnostics_from_scanner:, synthesized_namespaces_snapshot:,
52
55
  quarantined_signatures_snapshot:, env_build_failure_snapshot:,
53
56
  definition_build_failures_snapshot:, hkt_scan_failure_snapshot:,
54
- conformance_results_snapshot:)
57
+ conformance_results_snapshot:, signature_standdowns_snapshot: -> { [] },
58
+ pooled_run_disclosures: -> { [] })
55
59
  @configuration = configuration
56
60
  @rbs_extended_reporter = rbs_extended_reporter
57
61
  @boundary_cross_reporter = boundary_cross_reporter
@@ -63,10 +67,14 @@ module Rigor
63
67
  @pre_eval_diagnostics_from_scanner_reader = pre_eval_diagnostics_from_scanner
64
68
  @synthesized_namespaces_snapshot_reader = synthesized_namespaces_snapshot
65
69
  @quarantined_signatures_snapshot_reader = quarantined_signatures_snapshot
70
+ @signature_standdowns_snapshot_reader = signature_standdowns_snapshot
66
71
  @env_build_failure_snapshot_reader = env_build_failure_snapshot
67
72
  @definition_build_failures_snapshot_reader = definition_build_failures_snapshot
68
73
  @hkt_scan_failure_snapshot_reader = hkt_scan_failure_snapshot
69
74
  @conformance_results_snapshot_reader = conformance_results_snapshot
75
+ # Issue #1051 — reader for the pool coordinator's de-duplicated run-scoped disclosure table.
76
+ # Empty on a sequential run, where `plugin_registry`'s own instances are the ones that registered.
77
+ @pooled_run_disclosures_reader = pooled_run_disclosures
70
78
  end
71
79
 
72
80
  # Pre-file diagnostic streams that fire once per run rather than per analyzed file: plugin load /
@@ -416,12 +424,33 @@ module Rigor
416
424
  [build_rbs_synthesized_namespace_diagnostic(synthesized)]
417
425
  end
418
426
 
427
+ # Issue #610 — the outcome that AVOIDED `rbs.coverage.definition-build-failed`'s rung: a signature
428
+ # file a loaded plugin contributes re-declared a class another loaded source (typically an `rbs
429
+ # collection install`) already declares at a DIFFERENT generic arity, so the plugin's file stood
430
+ # down rather than fail the class's definition build. One `:info` per file: the user is told what
431
+ # typing they are not getting and why, and — unlike the quarantine row this file used to be
432
+ # misreported as — is not sent to remove a declaration the plugin owns.
433
+ def rbs_plugin_signature_stood_down_diagnostics
434
+ standdowns = signature_standdowns_snapshot
435
+ return [] if standdowns.nil? || standdowns.empty?
436
+
437
+ standdowns.map { |entry| build_rbs_plugin_signature_stood_down_diagnostic(entry) }
438
+ end
439
+
440
+ # The two `:info` notices that close the `rbs.coverage.*` ladder, in this order: the namespace
441
+ # synthesis first, then the stand-down (#610) — the outcome that AVOIDED a definition-build failure
442
+ # and so the quietest row on it. One method so the runner's assembly reads them as one slot.
443
+ def rbs_coverage_notice_diagnostics
444
+ rbs_synthesized_namespace_diagnostics + rbs_plugin_signature_stood_down_diagnostics
445
+ end
446
+
419
447
  # Maps the per-run `rigor:v1:conforms-to` scan results into diagnostics (spec: `rbs-extended.md` §
420
448
  # "Explicit conformance directive"). A class that declares `conforms-to _Interface` but is missing
421
449
  # a required interface method surfaces as `rbs_extended.unsatisfied-conformance`; an unresolvable
422
450
  # interface name surfaces as `dynamic.rbs-extended.unresolved` `:info` (the same fail-soft channel
423
- # the other directive parsers use). Empty for a project with no directive, a well-formed
424
- # conformance, or a non-sequential pool run (the snapshot mirrors `synthesized_namespaces`).
451
+ # the other directive parsers use). Empty for a project with no directive or a well-formed
452
+ # conformance; every analysis path, pooled or degraded, takes the snapshot `synthesized_namespaces`
453
+ # comes from.
425
454
  def conforms_to_diagnostics
426
455
  results = conformance_results_snapshot
427
456
  return [] if results.nil? || results.empty?
@@ -436,16 +465,28 @@ module Rigor
436
465
  when RbsExtended::ConformanceChecker::IncompatibleSignature
437
466
  build_incompatible_signature_diagnostic(record)
438
467
  else # UnresolvedInterface
439
- build_reporter_diagnostic(
440
- record.location,
441
- rule: "dynamic.rbs-extended.unresolved",
442
- message: "`#{record.class_name}` declares `conforms-to #{record.interface_name}` but " \
443
- "interface `#{record.interface_name}` is not loaded. Check for a typo or add " \
444
- "the `sig`/library that declares it to the RBS load path."
445
- )
468
+ build_unresolved_conformance_diagnostic(record)
446
469
  end
447
470
  end
448
471
 
472
+ # Issue #928 — `:warning`, not the `:info` the other `dynamic.rbs-extended.unresolved` producers
473
+ # stamp. Those report that an inference fell back; this one reports that an assertion the author
474
+ # deliberately wrote is checking NOTHING, and the whole point of `conforms-to` is to be checked. It
475
+ # is not unsolicited either: only a project that wrote the directive can see it. Now that the
476
+ # capability-role catalog ships, the remaining way to reach this row is a name that does not exist.
477
+ def build_unresolved_conformance_diagnostic(record)
478
+ path, line, column = location_fields(record.location)
479
+ Diagnostic.new(
480
+ path: path, line: line, column: column,
481
+ message: "`#{record.class_name}` declares `conforms-to #{record.interface_name}` but " \
482
+ "interface `#{record.interface_name}` is not loaded. Check for a typo or add " \
483
+ "the `sig`/library that declares it to the RBS load path.",
484
+ severity: :warning,
485
+ rule: "dynamic.rbs-extended.unresolved",
486
+ source_family: :builtin
487
+ )
488
+ end
489
+
449
490
  def build_unsatisfied_conformance_diagnostic(record)
450
491
  path, line, column = location_fields(record.location)
451
492
  Diagnostic.new(
@@ -544,17 +585,58 @@ module Rigor
544
585
  "#{first_failure_clause(failures.first)}#{conflicting_files_clause(files, sample_size)} " \
545
586
  "Rigor still treats each class as KNOWN, so calls into it — real methods and typos " \
546
587
  "alike — silently read `Dynamic[top]` instead of resolving, and this run is quieter " \
547
- "than it should be rather than cleaner. Two signature sources declare the same " \
548
- "member; remove the duplicate declaration (`rbs validate`) to restore type coverage.",
588
+ "than it should be rather than cleaner. #{definition_build_advice(failures.first)}",
549
589
  severity: :warning,
550
590
  rule: "rbs.coverage.definition-build-failed",
551
591
  source_family: :builtin
552
592
  )
553
593
  end
554
594
 
555
- # `[class_name, error_class, member, buffers]`. The member is nil for the error classes that carry no
556
- # name at all (`RecursiveAncestorError`), and the clause then names the error class alone rather than
557
- # inventing a member.
595
+ # The closing advice follows the FIRST failure's error class: `GenericParameterMismatchError` is two
596
+ # declarations of one CLASS at different generic arity (#610); `NoTypeFoundError` (issue #997) is
597
+ # ONE declaration naming a type that does not exist, which "remove the duplicate declaration" sends
598
+ # its reader after a duplicate that is not there — the fifth tuple element
599
+ # ({RbsLoader#unresolved_type_name_detail}) is non-nil for exactly that case. Every other error class
600
+ # (the two duplicated-definition errors, `RecursiveAncestorError`, …) keeps the duplicate-declaration
601
+ # wording, which is accurate for them.
602
+ def definition_build_advice(failure)
603
+ _, error_class, _member, _buffers, unresolved = failure
604
+ if error_class.to_s.end_with?("GenericParameterMismatchError")
605
+ "Two signature sources declare the class with a different number of type parameters; make " \
606
+ "the declarations agree (`rbs validate`) to restore type coverage."
607
+ elsif unresolved
608
+ unresolved_type_name_advice(unresolved)
609
+ else
610
+ "Two signature sources declare the same member; remove the duplicate declaration " \
611
+ "(`rbs validate`) to restore type coverage."
612
+ end
613
+ end
614
+
615
+ # Issue #997 — `unresolved` is {RbsLoader#unresolved_type_name_detail}'s `[token, location,
616
+ # refinement_hint]`. `refinement_hint` turns the message into a fix: the token is not a typo to
617
+ # chase down but the start of a Rigor refinement name that is not valid in an RBS type position, and
618
+ # the reader is told the spelling that IS valid today. Without a hint, the token is named with its
619
+ # position and the reader is left with the same "not a duplicate" correction plus a typo/missing-load
620
+ # check — still narrower than the duplicate-declaration wording it replaces.
621
+ def unresolved_type_name_advice(unresolved)
622
+ token, location, refinement_hint = unresolved
623
+ position = location ? " at `#{location}`" : ""
624
+ if refinement_hint
625
+ "`#{token}`#{position} does not name a type RBS can resolve; it is the start of the Rigor " \
626
+ "refinement `#{refinement_hint}`, which is not valid in an RBS type position. Write it as " \
627
+ "an `%a{rigor:v1:param: name is #{refinement_hint}}` (or `return:` / `assert`) annotation " \
628
+ "alongside the plain RBS type instead — see docs/manual/16-rbs-extended-annotations.md — " \
629
+ "to restore type coverage."
630
+ else
631
+ "One signature source names `#{token}`#{position}, which is not a type RBS can resolve; fix " \
632
+ "the typo, or if it names a class, confirm it is loaded (`rbs validate`) to restore type " \
633
+ "coverage."
634
+ end
635
+ end
636
+
637
+ # `[class_name, error_class, member, buffers, unresolved_type_name_detail]`. The member is nil for
638
+ # the error classes that carry no name at all (`RecursiveAncestorError`), and the clause then names
639
+ # the error class alone rather than inventing a member.
558
640
  def first_failure_clause(failure)
559
641
  _, error_class, member, = failure
560
642
  return " First failure: #{error_class}." if member.nil? || member.empty?
@@ -665,6 +747,42 @@ module Rigor
665
747
  )
666
748
  end
667
749
 
750
+ def build_rbs_plugin_signature_stood_down_diagnostic(entry)
751
+ path, class_name, existing_arity, incoming_arity, existing_file = entry
752
+ displaced_by = displacing_source_phrase(existing_file)
753
+ Diagnostic.new(
754
+ path: ".rigor.yml",
755
+ line: 1,
756
+ column: 1,
757
+ message: "`#{relative_signature_path(path.to_s)}` (a signature file a plugin contributes) declares " \
758
+ "`#{class_name.to_s.delete_prefix('::')}` with #{type_parameter_phrase(incoming_arity)}, " \
759
+ "but #{displaced_by} already declares it with #{type_parameter_phrase(existing_arity)}. " \
760
+ "Two declarations of one class at different generic arity fail its definition build, so " \
761
+ "the plugin's file stood down: calls into the class resolve against the other " \
762
+ "declaration, and the plugin's element typing for it is unavailable while both are " \
763
+ "loaded. Expected when a bundled plugin and an `rbs collection install` both declare " \
764
+ "the class; nothing to fix unless you want the plugin's typing back, which needs the " \
765
+ "other source to stop declaring the class.",
766
+ severity: :info,
767
+ rule: "rbs.coverage.plugin-signature-stood-down",
768
+ source_family: :builtin
769
+ )
770
+ end
771
+
772
+ def displacing_source_phrase(existing_file)
773
+ return "another loaded signature source" if existing_file.nil?
774
+
775
+ "`#{relative_signature_path(existing_file.to_s)}`"
776
+ end
777
+
778
+ def type_parameter_phrase(arity)
779
+ case arity
780
+ when nil, 0 then "no type parameters"
781
+ when 1 then "1 type parameter"
782
+ else "#{arity} type parameters"
783
+ end
784
+ end
785
+
668
786
  def build_rbs_coverage_missing_diagnostic(missing)
669
787
  sample_size = 5
670
788
  sample = missing.first(sample_size).map(&:gem_name)
@@ -722,7 +840,22 @@ module Rigor
722
840
  )
723
841
  end
724
842
 
725
- unresolved + lossy + hkt_directive_diagnostics
843
+ unresolved + lossy + hkt_directive_diagnostics + deprecated_form_diagnostics
844
+ end
845
+
846
+ # ADR-109 WD3 — one row per annotation still written in a spelling the grammar accepts but no
847
+ # longer displays. The row names the replacement because the deprecation window exists for exactly
848
+ # this edit, and it stays `:info`: the annotation resolved, the run is as green as it was.
849
+ def deprecated_form_diagnostics
850
+ @rbs_extended_reporter.deprecated_forms.map do |entry|
851
+ build_positioned_reporter_diagnostic(
852
+ entry,
853
+ rule: "dynamic.rbs-extended.deprecated-form",
854
+ message: "`#{entry.payload}` is a deprecated spelling; write `#{entry.replacement}`. The " \
855
+ "angle-bracket integer range is accepted for one deprecation window and removed " \
856
+ "at the next compatibility break (ADR-109)."
857
+ )
858
+ end
726
859
  end
727
860
 
728
861
  # Issue #785 — one row per declined HKT directive. The consequence sentence is the point: the parser
@@ -752,10 +885,29 @@ module Rigor
752
885
  return [] if @source_rbs_synthesis_reporter.empty?
753
886
 
754
887
  @source_rbs_synthesis_reporter.entries.map do |entry|
755
- entry.kind == :not_honoured ? not_honoured_diagnostic(entry) : synthesis_failed_diagnostic(entry)
888
+ case entry.kind
889
+ when :not_honoured then not_honoured_diagnostic(entry)
890
+ when :contradiction then contradicting_signature_diagnostic(entry)
891
+ else synthesis_failed_diagnostic(entry)
892
+ end
756
893
  end
757
894
  end
758
895
 
896
+ # ADR-112 WD5 / issue #1075 — a member whose two declarations contradict, or whose refinement is
897
+ # outside its own declared type. An error in every profile: the false-positive discipline is carried
898
+ # by the rule's narrow definition of a contradiction, never by the severity (ADR-112 WD5). The message
899
+ # is composed where the record is read ({Environment.record_member_consistency}), since only there
900
+ # are both sources' paths at hand.
901
+ def contradicting_signature_diagnostic(entry)
902
+ Diagnostic.new(
903
+ path: entry.path, line: entry.line || 1, column: 1,
904
+ message: entry.message,
905
+ severity: :error,
906
+ rule: CheckRules::RULE_CONTRADICTING_SIGNATURE,
907
+ source_family: :builtin
908
+ )
909
+ end
910
+
759
911
  def synthesis_failed_diagnostic(entry)
760
912
  Diagnostic.new(
761
913
  path: entry.path, line: 1, column: 1,
@@ -811,6 +963,153 @@ module Rigor
811
963
  end
812
964
  end
813
965
 
966
+ # Issue #959 — surfaces the {Plugin::IoBoundary} refusal history each loaded plugin accumulated over
967
+ # the whole run (prepare AND every per-file call: `#io_boundary` is memoised per plugin instance, so
968
+ # one boundary sees both). `TrustPolicy#allow_read?` stays `File.expand_path`-only per ADR-2 — a
969
+ # project rooted under a symlink (macOS' `/tmp` → `/private/tmp` is the common case) can have every
970
+ # plugin read fall outside its own read roots with nothing said about it. One `:info` row per plugin
971
+ # that hit this, never one per path, naming the count and the first path so the row stays legible on
972
+ # a plugin that walked a whole out-of-scope subtree.
973
+ #
974
+ # Positioned here, in `#pre_file_diagnostics`'s post-analysis sibling list (called from
975
+ # `Runner#assemble_run_diagnostics`, which only runs on an ADR-45 cache MISS): the refusal history is
976
+ # a function of the plugin code and the resolved paths for THIS configuration, so it is deterministic
977
+ # across runs of the same inputs and the row that lands in a miss's cached diagnostics blob is the
978
+ # row a later warm HIT correctly re-serves — the same regeneration contract every other stream
979
+ # aggregated in `#pre_file_diagnostics` / `#assemble_run_diagnostics` already relies on (see
980
+ # `docs/type-specification/diagnostic-policy.md` § "Run-level rows and the record-and-validate
981
+ # cache"). It is also never routed through `IncrementalSession`'s per-file cache (`#788`): that cache
982
+ # holds only `Runner#per_file_diagnostics`, and this row is not part of that stream.
983
+ #
984
+ # Scope: `plugin_registry` here is the coordinator-side registry, which only actually RUNS `#prepare`
985
+ # / per-file analysis in sequential mode (`--workers` unset, the default) — a pooled run (`--workers
986
+ # N`) prepares and analyses on per-worker `WorkerSession` registries this reader never sees, so a
987
+ # refusal confined to a pooled worker's slice is not reported. Left out deliberately: draining it
988
+ # needs a fourth marshalled channel beside `drain_reporters` / `drain_dependencies` / `drain_effects`
989
+ # in `Runner::PoolCoordinator`, and the default (unpooled) path already covers the reported bug.
990
+ def plugin_trust_refusal_diagnostics
991
+ return [] if plugin_registry.empty?
992
+
993
+ plugin_registry.plugins.filter_map { |plugin| plugin_trust_refusal_diagnostic(plugin) }
994
+ end
995
+
996
+ # Issue #1051 — the run-scoped plugin disclosures ({Plugin::Base#disclose_once}), the run's
997
+ # project-global notices about its own inputs. Two sources feed one table:
998
+ #
999
+ # - the coordinator-side registry, which is where a SEQUENTIAL run's `#prepare` and per-file hooks
1000
+ # ran; and
1001
+ # - `pooled_run_disclosures`, the pool coordinator's already-de-duplicated collection from the
1002
+ # pre-fork parent session and every worker payload.
1003
+ #
1004
+ # Both are keyed `[plugin id, key]` and the first registration of a pair wins, so a run emits one
1005
+ # row per pair no matter how many plugin instances (workers) reached it — the #1051 bug, where a
1006
+ # per-instance `@emitted` flag produced one row per WORKER.
1007
+ #
1008
+ # The emitted order is `(registry position, key)`, NOT registration order: a worker's slice
1009
+ # determines which instance registers a key first, so registration order is a function of
1010
+ # `--workers N` and would make a pooled run's stream differ from a sequential one's even with the
1011
+ # duplicates gone. Registry position is the plugin load order (topological by `consumes:`), which
1012
+ # both modes share.
1013
+ #
1014
+ # Position is `.rigor.yml:1:1` for every row; see {Plugin::Base#disclose_once} for why that and not
1015
+ # the first analysed file. A positioned batch ({Plugin::Base#emit_once}) shares the table but not
1016
+ # this stream — see {#plugin_run_emission_diagnostics}.
1017
+ def plugin_run_disclosure_diagnostics
1018
+ ordered_run_disclosure_records
1019
+ .reject { |record| record.key?(:diagnostics) }
1020
+ .map { |record| run_disclosure_diagnostic(record) }
1021
+ end
1022
+
1023
+ # Issue #1060 — the positioned batches ({Plugin::Base#emit_once}) from the same table, de-duplicated
1024
+ # by the same `[plugin id, key]` identity over the same sources, so the first registration of a key
1025
+ # wins whole and a batch is never merged row by row across workers. Unlike a disclosure, every row
1026
+ # keeps the position the plugin gave it; the engine only stamps `source_family: "plugin.<id>"`, the
1027
+ # same stamp a `#diagnostics_for_file` row gets, so a baseline keyed to the row's own file and
1028
+ # qualified rule matches it before and after a plugin moves onto this channel.
1029
+ #
1030
+ # Order is `(registry position, key)` and then the batch's own row order — the same
1031
+ # `--workers`-independent order the disclosure stream uses. The runner appends this stream right
1032
+ # after the per-file stream rather than into the run-level block: these rows are about files, and
1033
+ # the files they name are usually not among the analysed targets (a view template), so there is no
1034
+ # file slot to splice them into.
1035
+ def plugin_run_emission_diagnostics
1036
+ ordered_run_disclosure_records
1037
+ .select { |record| record.key?(:diagnostics) }
1038
+ .flat_map { |record| record[:diagnostics].map { |row| run_emission_diagnostic(row, record[:plugin_id]) } }
1039
+ end
1040
+
1041
+ def ordered_run_disclosure_records
1042
+ registry = plugin_registry
1043
+ return [] if registry.empty?
1044
+
1045
+ positions = {}
1046
+ registry.plugins.each_with_index { |plugin, index| positions[disclosure_plugin_id(plugin)] ||= index }
1047
+ collect_run_disclosure_records(registry)
1048
+ .sort_by { |record| [positions.fetch(record[:plugin_id], positions.size), record[:key].to_s] }
1049
+ end
1050
+
1051
+ def collect_run_disclosure_records(registry)
1052
+ records = {}
1053
+ Array(pooled_run_disclosures).each do |record|
1054
+ records[[record[:plugin_id], record[:key]]] ||= record
1055
+ end
1056
+ registry.plugins.each do |plugin|
1057
+ plugin_id = disclosure_plugin_id(plugin)
1058
+ Array(plugin.run_disclosure_records).each do |record|
1059
+ records[[plugin_id, record[:key]]] ||= record.merge(plugin_id: plugin_id)
1060
+ end
1061
+ rescue StandardError
1062
+ next
1063
+ end
1064
+ records.values
1065
+ end
1066
+
1067
+ def run_disclosure_diagnostic(record)
1068
+ Diagnostic.new(
1069
+ path: ".rigor.yml", line: 1, column: 1,
1070
+ message: record.fetch(:message),
1071
+ severity: record.fetch(:severity),
1072
+ rule: record.fetch(:rule),
1073
+ source_family: "plugin.#{record[:plugin_id]}"
1074
+ )
1075
+ end
1076
+
1077
+ def run_emission_diagnostic(row, plugin_id)
1078
+ Diagnostic.new(
1079
+ path: row.path, line: row.line, column: row.column,
1080
+ message: row.message,
1081
+ severity: row.severity,
1082
+ rule: row.rule,
1083
+ source_family: "plugin.#{plugin_id}"
1084
+ )
1085
+ end
1086
+
1087
+ def disclosure_plugin_id(plugin)
1088
+ plugin.manifest.id
1089
+ rescue StandardError
1090
+ plugin.class.to_s
1091
+ end
1092
+
1093
+ def plugin_trust_refusal_diagnostic(plugin)
1094
+ summary = plugin.io_boundary.refusal_summary
1095
+ return nil if summary.nil?
1096
+
1097
+ plugin_id = plugin.manifest.id
1098
+ Diagnostic.new(
1099
+ path: ".rigor.yml", line: 1, column: 1,
1100
+ message: "plugin #{plugin_id.inspect} had #{summary[:count]} read(s) refused by the trust " \
1101
+ "policy; first refused path: #{summary[:first_path].inspect}, outside read root " \
1102
+ "#{summary[:nearest_root].inspect}. Spell the path the way that read root spells it " \
1103
+ "(a symlink alias such as macOS' /tmp does not match its real path), or add a " \
1104
+ "`plugins_io.allowed_paths:` entry in .rigor.yml covering the path.",
1105
+ severity: :info,
1106
+ rule: "plugin_trust.read-refused",
1107
+ source_family: :builtin
1108
+ )
1109
+ rescue StandardError
1110
+ nil
1111
+ end
1112
+
814
1113
  def build_reporter_diagnostic(source_location, rule:, message:)
815
1114
  path, line, column = location_fields(source_location)
816
1115
  Diagnostic.new(
@@ -877,6 +1176,10 @@ module Rigor
877
1176
  @cached_plugin_prepare_diagnostics_reader.call
878
1177
  end
879
1178
 
1179
+ def pooled_run_disclosures
1180
+ @pooled_run_disclosures_reader.call
1181
+ end
1182
+
880
1183
  def pre_eval_diagnostics_from_scanner
881
1184
  @pre_eval_diagnostics_from_scanner_reader.call
882
1185
  end
@@ -889,6 +1192,10 @@ module Rigor
889
1192
  @quarantined_signatures_snapshot_reader.call
890
1193
  end
891
1194
 
1195
+ def signature_standdowns_snapshot
1196
+ @signature_standdowns_snapshot_reader.call
1197
+ end
1198
+
892
1199
  def env_build_failure_snapshot
893
1200
  @env_build_failure_snapshot_reader.call
894
1201
  end
@@ -58,8 +58,7 @@ module Rigor
58
58
  "every run of the project."
59
59
  private_constant :MESSAGE
60
60
 
61
- # @param configuration [Rigor::Configuration]
62
- # @param virtual_rbs [Array<Array(String, String)>, nil] `[buffer name, RBS source]` pairs the
61
+ # @param virtual_rbs — `[buffer name, RBS source]` pairs the
63
62
  # run ALREADY resolved; never a loader built for this pass. Empty / nil simply drops the
64
63
  # virtual-RBS stratum.
65
64
  def initialize(configuration:, virtual_rbs: nil)
@@ -67,7 +66,7 @@ module Rigor
67
66
  @virtual_rbs = virtual_rbs
68
67
  end
69
68
 
70
- # @return [Array<Diagnostic>] zero or one.
69
+ # @return zero or one.
71
70
  def diagnostics
72
71
  return NO_DIAGNOSTICS if @configuration.effects_enabled?
73
72
  return NO_DIAGNOSTICS if @configuration.disabled_rules.include?(RULE)
@@ -78,22 +78,21 @@ module Rigor
78
78
  LABELS_CONSEQUENCE = "the label is not registered"
79
79
  private_constant :LABELS_CONSEQUENCE
80
80
 
81
- # @param configuration [Rigor::Configuration]
82
- # @param rbs_loader [Rigor::Environment::RbsLoader, nil] the run's loader; nil disables the pass.
83
- # @param effect_table [Rigor::Effects::EffectTable] the propagated graph.
84
- # @param discovery [#call] forces and returns the cross-file discovery tables as
81
+ # @param rbs_loader — the run's loader; nil disables the pass.
82
+ # @param effect_table — the propagated graph.
83
+ # @param discovery — forces and returns the cross-file discovery tables as
85
84
  # `[def_sources, singleton_def_sources, class_sources]`. Called only when a finding needs a
86
85
  # position — a judged-clean envelope never forces it.
87
- # @param sources [Hash{String => String}] in-memory sources, for the buffer-backed run path.
88
- # @param unit_sources [Hash{String => Array<String>}] `Runner#effect_sources` — where each effect
86
+ # @param sources — in-memory sources, for the buffer-backed run path.
87
+ # @param unit_sources — `Runner#effect_sources` — where each effect
89
88
  # unit is defined, which is what an `effects.envelopes[].match:` path glob selects on. Its paths
90
89
  # are relativised against the working directory, which is the project root for every run that
91
90
  # reaches here (the same assumption `Snapshot.build` makes about `reach:`).
92
- # @param ancestry [#call, nil] returns the run's as-written superclass table
91
+ # @param ancestry — returns the run's as-written superclass table
93
92
  # (`FileCollection#superclasses`) — the nominal relation `effect.liskov-widened` reads. A
94
93
  # lambda, and called only once an envelope exists, because merging the run's collections is
95
- # not free.
96
- # @param apply_tolerated [Boolean] false runs the judgment with an empty tolerated set
94
+ # not free. nil leaves that relation empty.
95
+ # @param apply_tolerated — false runs the judgment with an empty tolerated set
97
96
  # (`--no-tolerated-effects`).
98
97
  def initialize(configuration:, rbs_loader:, effect_table:, discovery:, # rubocop:disable Metrics/ParameterLists
99
98
  sources: nil, unit_sources: nil, ancestry: nil, apply_tolerated: true,
@@ -109,7 +108,7 @@ module Rigor
109
108
  @apply_tolerated = apply_tolerated
110
109
  end
111
110
 
112
- # @return [Array<Diagnostic>] one per (method, exceeding label) plus one per unrecognised
111
+ # @return one per (method, exceeding label) plus one per unrecognised
113
112
  # label, suppression already applied.
114
113
  def diagnostics
115
114
  return NO_DIAGNOSTICS unless @configuration.effects_check?
@@ -251,9 +250,7 @@ module Rigor
251
250
  )
252
251
  end
253
252
 
254
- def table_class_names
255
- @effect_table.keys.filter_map { |key| Effects::MethodKey.owner(key) }.uniq
256
- end
253
+ def table_class_names = @effect_table.keys.filter_map { |k| Effects::MethodKey.envelope_owner(k) }.uniq
257
254
 
258
255
  def judge(scan)
259
256
  Effects::EnvelopeCheck.run(
@@ -302,7 +299,7 @@ module Rigor
302
299
 
303
300
  def build_diagnostic(finding)
304
301
  Diagnostic.new(
305
- path: finding.path || ".rigor.yml",
302
+ path: finding.path || unit_source_path(finding.key) || ".rigor.yml",
306
303
  line: finding.line,
307
304
  column: 1,
308
305
  message: message_for(finding),
@@ -313,6 +310,13 @@ module Rigor
313
310
  )
314
311
  end
315
312
 
313
+ # #393 — a template unit has no `def` for {Effects::EnvelopeCheck::Positions} to find, so a view
314
+ # finding would land on `.rigor.yml` — the config file, not the template the reviewer has to open.
315
+ # `Runner#effect_sources` already records the file every unit key came from; the line stays at the
316
+ # finding's own (1 for a whole-file unit), because the bound is exceeded by the unit, not by one
317
+ # line of it, and the explanation chain names the origin site.
318
+ def unit_source_path(key) = Effects::MethodKey.template_unit?(key) ? @unit_sources[key.to_s]&.first : nil
319
+
316
320
  def message_for(finding)
317
321
  EnvelopeMessages.exceeded(finding)
318
322
  end