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
@@ -36,9 +36,14 @@ module Rigor
36
36
  # - `method_names` — Array of Symbol method names. A call shape `<receiver_subclass>.get('/path') { ... }`
37
37
  # matches when `:get` is in this list. (Named `verbs:` before ADR-60 WD2 normalised the macro
38
38
  # value-object vocabulary.)
39
- # - `self_type` — Symbol selecting the kind of `self`-binding the substrate applies inside the block.
40
- # Slice 1a accepts only `:receiver_instance` (the block runs as an instance method of the receiver
41
- # class). Other kinds (`:receiver_singleton`, `:dsl_recorder`) are reserved for later slices.
39
+ # - `self_type` — the `self`-binding the substrate applies inside the block. `:receiver_instance`
40
+ # (the default) types `self` as an instance of the receiver class — the Sinatra contract. A String
41
+ # binds `self` to a *named* class instead of the receiver: `"Grape::Endpoint"` binds
42
+ # `Nominal[Grape::Endpoint]` (the block is `instance_eval`'d on an instance of that class, e.g. a
43
+ # route body on `Grape::Endpoint`), and `"singleton(Grape::API::Instance)"` binds
44
+ # `Singleton[Grape::API::Instance]` (the block is `instance_eval`'d on that *class object*, e.g.
45
+ # a `namespace` body on `Grape::API::Instance`). Reserved Symbol names (`:receiver_singleton`,
46
+ # `:dsl_recorder`) remain unaccepted.
42
47
  #
43
48
  # ## Ractor-shareability
44
49
  #
@@ -48,6 +53,13 @@ module Rigor
48
53
  SELF_TYPE_RECEIVER_INSTANCE = :receiver_instance
49
54
  VALID_SELF_TYPES = [SELF_TYPE_RECEIVER_INSTANCE].freeze
50
55
 
56
+ CLASS_NAME_PATTERN = /[A-Z]\w*(?:::[A-Z]\w*)*/
57
+ # `self_type: "Foo::Bar"` — the block is `instance_eval`'d on an instance of `Foo::Bar`.
58
+ NAMED_SELF_TYPE_PATTERN = /\A#{CLASS_NAME_PATTERN}\z/
59
+ # `self_type: "singleton(Foo::Bar)"` — the block is `instance_eval`'d on the `Foo::Bar` class
60
+ # object itself (Grape's `namespace`/`route_param` bodies run on `Grape::API::Instance`).
61
+ SINGLETON_SELF_TYPE_PATTERN = /\Asingleton\((#{CLASS_NAME_PATTERN})\)\z/
62
+
51
63
  attr_reader :receiver_constraint, :method_names, :self_type
52
64
 
53
65
  def initialize(receiver_constraint:, method_names:, self_type: SELF_TYPE_RECEIVER_INSTANCE)
@@ -57,7 +69,7 @@ module Rigor
57
69
 
58
70
  @receiver_constraint = receiver_constraint.dup.freeze
59
71
  @method_names = method_names.map(&:to_sym).freeze
60
- @self_type = self_type
72
+ @self_type = self_type.is_a?(String) ? self_type.dup.freeze : self_type
61
73
  freeze
62
74
  end
63
75
 
@@ -81,6 +93,31 @@ module Rigor
81
93
  [receiver_constraint, method_names, self_type].hash
82
94
  end
83
95
 
96
+ # The class name a String `self_type` binds `self` to — `"Foo::Bar"` for both the plain and the
97
+ # `singleton(Foo::Bar)` form — or `nil` for `:receiver_instance`.
98
+ def self_type_name
99
+ return nil unless self_type.is_a?(String)
100
+
101
+ self_type[SINGLETON_SELF_TYPE_PATTERN, 1] || self_type
102
+ end
103
+
104
+ # Whether a String `self_type` binds the *class object* (`"singleton(Foo::Bar)"`) rather than an
105
+ # instance (`"Foo::Bar"`). Nominal-receiver call sites only match instance-binding entries —
106
+ # a `singleton(...)` binding exists solely for `instance_eval`-on-class contracts, which always
107
+ # surface as `Singleton[X]` receivers.
108
+ def singleton_binding?
109
+ self_type.is_a?(String) && self_type.match?(SINGLETON_SELF_TYPE_PATTERN)
110
+ end
111
+
112
+ # Whether `self_type` names a class whose *instance* the block is `instance_eval`'d on
113
+ # (`"Foo::Bar"`). These entries are the only ones that also match `Nominal[Y]` receivers: inside
114
+ # an already-narrowed `instance_eval` body (`self : Nominal[ParamsScope]`), a nested call on
115
+ # `self` re-enters the same context. `:receiver_instance` keeps its original Singleton-only
116
+ # contract — a nominal receiver calling a class-level verb is a different (unmodelled) shape.
117
+ def named_instance_binding?
118
+ self_type.is_a?(String) && !singleton_binding?
119
+ end
120
+
84
121
  private
85
122
 
86
123
  def validate_receiver_constraint!(value)
@@ -108,10 +145,12 @@ module Rigor
108
145
 
109
146
  def validate_self_type!(self_type)
110
147
  return if VALID_SELF_TYPES.include?(self_type)
148
+ return if self_type.is_a?(String) &&
149
+ (self_type.match?(NAMED_SELF_TYPE_PATTERN) || self_type.match?(SINGLETON_SELF_TYPE_PATTERN))
111
150
 
112
151
  raise ArgumentError,
113
- "Plugin::Macro::BlockAsMethod#self_type must be one of #{VALID_SELF_TYPES.inspect}, " \
114
- "got #{self_type.inspect}"
152
+ "Plugin::Macro::BlockAsMethod#self_type must be one of #{VALID_SELF_TYPES.inspect} " \
153
+ "or a class-name String ('Foo::Bar' / 'singleton(Foo::Bar)'), got #{self_type.inspect}"
115
154
  end
116
155
  end
117
156
  end
@@ -152,7 +152,7 @@ module Rigor
152
152
  class ReturnsFromArg
153
153
  attr_reader :position, :plugin_id, :fact
154
154
 
155
- # @return [ReturnsFromArg, nil] coerced value class for a Hash / nil / ReturnsFromArg input. Raises
155
+ # @return coerced value class for a Hash / nil / ReturnsFromArg input. Raises
156
156
  # on any other shape so manifest authoring failures surface at construction time.
157
157
  def self.coerce(value)
158
158
  return nil if value.nil?
@@ -105,7 +105,7 @@ module Rigor
105
105
  to_h.hash
106
106
  end
107
107
 
108
- # @return [String, nil] fully-qualified module name for the given trait symbol, or nil when the
108
+ # @return fully-qualified module name for the given trait symbol, or nil when the
109
109
  # registry doesn't know the symbol (caller emits a tier_b.unknown-trait provenance marker and falls
110
110
  # through).
111
111
  def module_for(symbol)
@@ -42,31 +42,49 @@ module Rigor
42
42
  # {Rigor::Effects::Label} accepts as a root.
43
43
  VALID_EFFECT_ROOT = /\A[a-z][a-z0-9_]*\z/
44
44
 
45
+ # ADR-96 WD1 — the gems a plugin models, spelled exactly as `Gemfile.lock` names them. Not derivable
46
+ # from the plugin id (`rigor-factorybot` models `factory_bot`, `rigor-rspec` models `rspec-core`), which
47
+ # is what makes it a declared field; an empty list is the meaningful answer for a plugin that models no
48
+ # gem at all.
49
+ attr_reader :target_gems
50
+
45
51
  attr_reader :id, :version, :description, :config_schema, :config_defaults, :produces, :consumes,
46
- :owns_receivers, :open_receivers, :type_node_resolvers, :block_as_methods,
52
+ :owns_receivers, :open_receivers, :rbs_complete_ancestors, :rbs_complete_extends,
53
+ :type_node_resolvers,
54
+ :block_as_methods,
47
55
  :heredoc_templates, :nested_class_templates, :trait_registries,
48
56
  :hkt_registrations, :hkt_definitions, :signature_paths, :protocol_contracts,
49
57
  :source_rbs_synthesizer, :additional_initializers,
50
58
  :effect_root, :effect_labels, :effect_attributions, :effect_edges, :effect_entry_points,
51
59
  :effect_ancestry
52
60
 
53
- def initialize( # rubocop:disable Metrics/ParameterLists
61
+ # #392 — the project-relative globs whose files this plugin compiles into {TemplateUnit}s. Purely a
62
+ # CLAIM: the engine globs these, reads each match's bytes and offers them to
63
+ # {Base#template_units_for_file}, which is where the transform itself lives. A plugin declaring none
64
+ # is never asked, and a run whose plugins declare none does no extra globbing at all.
65
+ attr_reader :template_globs
66
+
67
+ def initialize( # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
54
68
  id:, version:,
55
- description: nil, config_schema: {},
56
- produces: [], consumes: [], owns_receivers: [], open_receivers: [], type_node_resolvers: [],
69
+ description: nil, config_schema: {}, target_gems: [],
70
+ produces: [], consumes: [], owns_receivers: [], open_receivers: [], rbs_complete_ancestors: [],
71
+ rbs_complete_extends: [], type_node_resolvers: [],
57
72
  block_as_methods: [], heredoc_templates: [], nested_class_templates: [],
58
73
  trait_registries: [],
59
74
  hkt_registrations: [], hkt_definitions: [], signature_paths: [], protocol_contracts: [],
60
75
  source_rbs_synthesizer: nil, additional_initializers: [],
61
76
  effect_root: nil, effect_labels: [], effect_attributions: [], effect_edges: [],
62
- effect_entry_points: [], effect_ancestry: []
77
+ effect_entry_points: [], effect_ancestry: [], template_globs: []
63
78
  )
64
79
  validate_id!(id)
65
80
  validate_version!(version)
66
81
  validate_config_schema!(config_schema)
67
82
  validate_produces!(produces)
83
+ validate_target_gems!(target_gems)
68
84
  validate_owns_receivers!(owns_receivers)
69
85
  validate_open_receivers!(open_receivers)
86
+ validate_rbs_complete_ancestors!(rbs_complete_ancestors)
87
+ validate_rbs_complete_extends!(rbs_complete_extends)
70
88
  validate_type_node_resolvers!(type_node_resolvers)
71
89
  validate_block_as_methods!(block_as_methods)
72
90
  validate_heredoc_templates!(heredoc_templates)
@@ -85,10 +103,12 @@ module Rigor
85
103
  open_receivers, type_node_resolvers, block_as_methods, heredoc_templates, trait_registries,
86
104
  hkt_registrations, hkt_definitions, signature_paths, protocol_contracts,
87
105
  source_rbs_synthesizer)
106
+ assign_target_gems(target_gems)
107
+ assign_rbs_complete_ancestors(rbs_complete_ancestors, rbs_complete_extends)
88
108
  assign_nested_class_templates(nested_class_templates)
89
109
  assign_additional_initializers(additional_initializers)
90
110
  assign_effect_fields(effect_root, effect_labels, effect_attributions, effect_edges,
91
- effect_entry_points, effect_ancestry)
111
+ effect_entry_points, effect_ancestry, template_globs)
92
112
  freeze
93
113
  end
94
114
 
@@ -119,6 +139,19 @@ module Rigor
119
139
  @source_rbs_synthesizer = source_rbs_synthesizer
120
140
  end
121
141
 
142
+ # ADR-96 WD1 — assigned outside assign_fields, which already carries the maximum positional arity.
143
+ def assign_target_gems(target_gems)
144
+ @target_gems = target_gems.map { |g| g.to_s.dup.freeze }.uniq.freeze
145
+ end
146
+ private :assign_target_gems
147
+
148
+ # ADR-43 WD4 — assigned outside assign_fields (which already carries the maximum positional arity).
149
+ def assign_rbs_complete_ancestors(rbs_complete_ancestors, rbs_complete_extends)
150
+ @rbs_complete_ancestors = rbs_complete_ancestors.map { |c| c.to_s.dup.freeze }.freeze
151
+ @rbs_complete_extends = rbs_complete_extends.map { |c| c.to_s.dup.freeze }.freeze
152
+ end
153
+ private :assign_rbs_complete_ancestors
154
+
122
155
  # Assigned outside assign_fields (which already carries the maximum positional arity) — set in
123
156
  # `initialize` before the final freeze. ADR-36 nested-class emission tier.
124
157
  def assign_nested_class_templates(nested_class_templates)
@@ -148,15 +181,27 @@ module Rigor
148
181
  private :validate_effect_fields!
149
182
 
150
183
  def assign_effect_fields(effect_root, effect_labels, effect_attributions, effect_edges,
151
- effect_entry_points, effect_ancestry)
184
+ effect_entry_points, effect_ancestry, template_globs)
152
185
  @effect_root = effect_root.nil? ? nil : effect_root.to_s.dup.freeze
153
186
  @effect_labels = effect_labels.map { |label| label.to_s.dup.freeze }.uniq.sort.freeze
154
187
  @effect_attributions = effect_attributions.dup.freeze
155
188
  @effect_edges = effect_edges.dup.freeze
156
189
  @effect_entry_points = effect_entry_points.dup.freeze
157
190
  @effect_ancestry = effect_ancestry.dup.freeze
191
+ assign_template_globs(template_globs)
158
192
  end
159
193
  private :assign_effect_fields
194
+
195
+ # #392 — a glob is a String, and an absolute one is refused: a template unit is a project file, and
196
+ # a plugin reaching outside the project root is the trust boundary ADR-2 § "Plugin Trust and I/O
197
+ # Policy" draws. Validated and assigned in one call, because `#initialize` is at its line budget.
198
+ def assign_template_globs(globs)
199
+ validate_array_of!("template_globs", globs, "non-empty relative glob Strings") do |glob|
200
+ glob.is_a?(String) && !glob.empty? && !glob.start_with?("/") && !glob.include?("..")
201
+ end
202
+ @template_globs = globs.map { |glob| glob.to_s.dup.freeze }.uniq.freeze
203
+ end
204
+ private :assign_template_globs
160
205
  # rubocop:enable Metrics/ParameterLists, Metrics/AbcSize
161
206
 
162
207
  public
@@ -182,17 +227,20 @@ module Rigor
182
227
  errors
183
228
  end
184
229
 
185
- def to_h # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
230
+ def to_h # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
186
231
  {
187
232
  "id" => id,
188
233
  "version" => version,
189
234
  "description" => description,
190
235
  "config_schema" => config_schema.to_h { |k, v| [k, v.to_s] },
191
236
  "config_defaults" => config_defaults,
237
+ "target_gems" => target_gems,
192
238
  "produces" => produces.map(&:to_s),
193
239
  "consumes" => consumes.map { |c| consumption_hash(c) },
194
240
  "owns_receivers" => owns_receivers,
195
241
  "open_receivers" => open_receivers,
242
+ "rbs_complete_ancestors" => rbs_complete_ancestors,
243
+ "rbs_complete_extends" => rbs_complete_extends,
196
244
  "type_node_resolvers" => type_node_resolvers.map { |r| r.class.name },
197
245
  "block_as_methods" => block_as_methods.map(&:to_h),
198
246
  "heredoc_templates" => heredoc_templates.map(&:to_h),
@@ -209,7 +257,8 @@ module Rigor
209
257
  "effect_attributions" => effect_attributions.map(&:to_h),
210
258
  "effect_edges" => effect_edges.map(&:to_h),
211
259
  "effect_entry_points" => effect_entry_points.map(&:to_h),
212
- "effect_ancestry" => effect_ancestry.map(&:to_h)
260
+ "effect_ancestry" => effect_ancestry.map(&:to_h),
261
+ "template_globs" => template_globs
213
262
  }
214
263
  end
215
264
 
@@ -218,10 +267,11 @@ module Rigor
218
267
  # otherwise. A third-party plugin's `effect_root:` is ignored here (and warned about by the registry),
219
268
  # so it opens only the root named after itself.
220
269
  def effect_owner
221
- return id if effect_root.nil?
270
+ root = effect_root
271
+ return id if root.nil?
222
272
  return id unless FirstParty.bundled?(id)
223
273
 
224
- effect_root
274
+ root
225
275
  end
226
276
 
227
277
  # Whether this plugin's `effect_attributions:` may carry `discharge: true` (ADR-103 WD6).
@@ -344,6 +394,13 @@ module Rigor
344
394
  raise ArgumentError, "plugin manifest #{field} must be an Array of #{label}, got #{value.inspect}"
345
395
  end
346
396
 
397
+ # ADR-96 WD1 — every entry is a gem name as `Gemfile.lock` spells it. The uniform Array-of-String check
398
+ # is all the manifest can do: whether the named gem exists is a fact about RubyGems, not about the
399
+ # manifest, and a typo surfaces as an advisory that never fires rather than as a load failure.
400
+ def validate_target_gems!(target_gems)
401
+ validate_array_of!("target_gems", target_gems, "non-empty String") { |g| g.is_a?(String) && !g.empty? }
402
+ end
403
+
347
404
  def validate_produces!(produces)
348
405
  validate_array_of!("produces", produces, "Symbol/String") { |p| p.is_a?(Symbol) || p.is_a?(String) }
349
406
  end
@@ -366,6 +423,32 @@ module Rigor
366
423
  validate_array_of!("open_receivers", open_receivers, "non-empty String") { |c| c.is_a?(String) && !c.empty? }
367
424
  end
368
425
 
426
+ # ADR-43 WD4 — `rbs_complete_ancestors:` declares the class names this plugin's bundled RBS covers
427
+ # COMPLETELY: a method call the signature does not declare is genuinely absent at runtime. A
428
+ # Ruby-source subclass of a listed class may then bridge inherited calls to the ancestor's RBS
429
+ # (the ADR-43 allow-list's manifest-declared half). The dual of `open_receivers:` — that field
430
+ # marks a class open to suppress diagnostics on an unbounded surface; this one marks a class
431
+ # closed to ENABLE them on subclasses. Listing a class whose signature is partial would turn
432
+ # every omitted inherited method into a `call.undefined-method` false positive on subclass
433
+ # receivers, so only classes the plugin's `signature_paths:` declares exhaustively belong here.
434
+ def validate_rbs_complete_ancestors!(rbs_complete_ancestors)
435
+ validate_array_of!("rbs_complete_ancestors", rbs_complete_ancestors, "non-empty String") do |c|
436
+ c.is_a?(String) && !c.empty?
437
+ end
438
+ end
439
+
440
+ # `rbs_complete_extends:` is the extend-edge twin of `rbs_complete_ancestors:` (ADR-43 WD4): it names
441
+ # the MODULES whose bundled RBS instance surface is complete, so a project `extend M` in a class or
442
+ # module body lets singleton-side calls on that class resolve M's declared instance methods
443
+ # (`class F; extend T::Sig; sig { ... }; end` reaching `T::Sig#sig`). Same completeness rule: a
444
+ # module whose declared surface is partial does not belong here — the extend edge would misreport
445
+ # real calls as `call.undefined-method`.
446
+ def validate_rbs_complete_extends!(rbs_complete_extends)
447
+ validate_array_of!("rbs_complete_extends", rbs_complete_extends, "non-empty String") do |c|
448
+ c.is_a?(String) && !c.empty?
449
+ end
450
+ end
451
+
369
452
  # ADR-13 slice 2 — `type_node_resolvers:` declares the plugin-supplied `TypeNodeResolver` instances the
370
453
  # parser consults (in slice 3) when an RBS::Extended payload's named- or generic-type head misses the
371
454
  # built-in registry. Slice 2 carries the declarations on the manifest and the registry exposes them in
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "blueprint"
4
+ # Issue #806 — {Registry#guarded_manifest} constructs one. `Rigor::Plugin::LoadError` MUST be defined by
5
+ # then: an unresolved bare `LoadError` in this lexical scope silently falls back to Ruby's own ::LoadError.
6
+ require_relative "load_error"
4
7
 
5
8
  module Rigor
6
9
  module Plugin
@@ -217,23 +220,26 @@ module Rigor
217
220
  class Registry
218
221
  attr_reader :plugins, :load_errors, :blueprints, :contribution_index, :resolved_gem_paths
219
222
 
220
- # @param plugins [Array<Rigor::Plugin::Base>] instantiated plugin instances in deterministic order.
221
- # @param load_errors [Array<Rigor::Plugin::LoadError>] failures surfaced during loading. Each error is
223
+ # @param plugins — instantiated plugin instances in deterministic order.
224
+ # @param load_errors — failures surfaced during loading. Each error is
222
225
  # also turned into a diagnostic by the runner.
223
- # @param blueprints [Array<Rigor::Plugin::Blueprint>] frozen, Ractor-shareable replay descriptors
226
+ # @param blueprints — frozen, Ractor-shareable replay descriptors
224
227
  # aligned 1:1 with `plugins`. The loader fills this in; callers that construct Registry manually MAY
225
228
  # pass `[]` and accept that {.materialize} cannot replay the set.
226
- # @param resolved_gem_paths [Hash{String=>String,nil}] #194 slice 1 — `gem name => resolved file path`
229
+ # @param resolved_gem_paths — #194 slice 1 — `gem name => resolved file path`
227
230
  # for each successfully required plugin gem, so `rigor plugins` can print where a loaded (and
228
231
  # possibly frozen) plugin actually loaded from. Defaults to empty; a worker registry built by
229
232
  # {.materialize} carries none (the provenance surface runs only on the coordinator).
230
233
  def initialize(plugins: [], load_errors: [], blueprints: [], resolved_gem_paths: {})
231
234
  @plugins = plugins.dup.freeze
232
- @load_errors = load_errors.dup.freeze
233
235
  @blueprints = blueprints.dup.freeze
234
236
  @resolved_gem_paths = resolved_gem_paths.dup.freeze
235
237
  @contribution_index = ContributionIndex.new(@plugins)
236
- compile_aggregates
238
+ # Issue #806 — every manifest the construction-time aggregates need is read ONCE here, behind a
239
+ # per-plugin rescue, and a plugin that raises joins `load_errors` instead of taking the run down.
240
+ manifest_errors = []
241
+ compile_aggregates(@plugins.map { |plugin| guarded_manifest(plugin, manifest_errors) })
242
+ @load_errors = (load_errors + manifest_errors).freeze
237
243
  # ADR-52 WD4 — the single engine-owned node-rule walk, compiled once per run from the node-rule
238
244
  # plugin subset (registry order). The runner reuses it for every file; it builds fresh per-file
239
245
  # state internally, so it is safe to freeze and share.
@@ -306,9 +312,14 @@ module Rigor
306
312
  # in plugin registration order. `Environment#build_name_scope` builds a `TypeNode::ResolverChain` from
307
313
  # this list (environment.rb). The first non-nil `#resolve(node, scope)` return wins per ADR-13 WD3 /
308
314
  # WD5 — registration order is the user's lever.
309
- def type_node_resolvers
310
- plugins.flat_map { |plugin| plugin.manifest.type_node_resolvers }
311
- end
315
+ #
316
+ # Issue #806 — compiled at construction alongside its `compile_aggregates` siblings, and for a second
317
+ # reason beyond theirs: `Environment#build_name_scope` demands it during Environment CONSTRUCTION, so
318
+ # the previous per-call `plugin.manifest` read had no rescue above it anywhere — a plugin raising here
319
+ # aborted every `Environment.for_project`, i.e. the whole run, before any seam could record it. The
320
+ # guard has to sit at registry construction because that is the last point at which a failure can still
321
+ # join `load_errors` (the registry freezes immediately after).
322
+ attr_reader :type_node_resolvers
312
323
 
313
324
  # ADR-20 slice 6 — aggregate every loaded plugin's manifest-declared HKT registrations + definitions
314
325
  # into a single `Inference::HktRegistry` overlay that `Environment#hkt_registry` merges on top of the
@@ -362,6 +373,30 @@ module Rigor
362
373
  @open_receivers_set.include?(class_name.to_s)
363
374
  end
364
375
 
376
+ # ADR-43 WD4 — the aggregate set of "RBS-complete ancestor" class names declared across loaded
377
+ # plugins (manifest `rbs_complete_ancestors:`). `RbsDispatch`'s inherited-call bridge consults it
378
+ # alongside the engine's own allow-list: a Ruby-source subclass of a listed class resolves
379
+ # inherited calls against the ancestor's RBS, and the signature-reading rules apply to them.
380
+ attr_reader :rbs_complete_ancestors
381
+
382
+ def rbs_complete_ancestor?(class_name)
383
+ return false if class_name.nil?
384
+
385
+ @rbs_complete_ancestors_set.include?(class_name.to_s)
386
+ end
387
+
388
+ # The extend-edge twin of `rbs_complete_ancestors` — the aggregate set of module names declared via
389
+ # manifest `rbs_complete_extends:`. `RbsDispatch`'s singleton bridge consults it for `extend M`
390
+ # edges in project source: `class F; extend T::Sig; sig { ... }; end` resolves `sig` through
391
+ # `T::Sig`'s bundled RBS instance surface.
392
+ attr_reader :rbs_complete_extends
393
+
394
+ def rbs_complete_extends?(class_name)
395
+ return false if class_name.nil?
396
+
397
+ @rbs_complete_extends_set.include?(class_name.to_s)
398
+ end
399
+
365
400
  # ADR-28 — flat, ordered list of every loaded plugin's path-scoped method-protocol contracts, in
366
401
  # plugin registration order. Read from each plugin's `#protocol_contracts` (which the manifest backs
367
402
  # by default but a plugin MAY override to fold in per-project config). Consumed by
@@ -419,15 +454,29 @@ module Rigor
419
454
  # compiled once at construction (the registry is frozen, so the flat_map-on-every-call versions
420
455
  # re-derived an invariant). `@contracts_by_path` is a mutable per-path memo inside the frozen registry
421
456
  # — safe because the contract set and the glob semantics are fixed for the lifetime of the run.
422
- def compile_aggregates
423
- @additional_initializers = @plugins.flat_map { |p| safe_manifest(p)&.additional_initializers || [] }.freeze
424
- @open_receivers = @plugins.flat_map { |p| (safe_manifest(p)&.open_receivers || []).map(&:to_s) }.uniq.freeze
457
+ #
458
+ # @param manifests — one entry per plugin, positionally aligned with `@plugins`;
459
+ # nil where {#guarded_manifest} caught a raising manifest read.
460
+ def compile_aggregates(manifests)
461
+ @additional_initializers = manifests.flat_map { |m| m&.additional_initializers || [] }.freeze
462
+ @open_receivers = aggregate_class_names(manifests, :open_receivers)
425
463
  @open_receivers_set = @open_receivers.to_set.freeze
464
+ @rbs_complete_ancestors = aggregate_class_names(manifests, :rbs_complete_ancestors)
465
+ @rbs_complete_ancestors_set = @rbs_complete_ancestors.to_set.freeze
466
+ @rbs_complete_extends = aggregate_class_names(manifests, :rbs_complete_extends)
467
+ @rbs_complete_extends_set = @rbs_complete_extends.to_set.freeze
468
+ @type_node_resolvers = manifests.flat_map { |m| m&.type_node_resolvers || [] }.freeze
426
469
  @protocol_contracts = @plugins.flat_map { |p| safe_protocol_contracts(p) }.freeze
427
470
  @contracts_by_path = {}
428
471
  @effect_memo = {}
429
472
  end
430
473
 
474
+ # The shared shape of `open_receivers:` / `rbs_complete_ancestors:` — a flat uniq'd class-name list
475
+ # aggregated across manifests, each entry normalised to String.
476
+ def aggregate_class_names(manifests, field)
477
+ manifests.flat_map { |m| (m&.public_send(field) || []).map(&:to_s) }.uniq.freeze
478
+ end
479
+
431
480
  # Fail-soft per plugin: a plugin whose `effect_attributions:` override raises (a missing
432
481
  # `config/application.rb`, an unreadable path) contributes nothing rather than taking the run down.
433
482
  # An effect contribution is an enrichment; ADR-2 isolates plugin failures at the analyzer boundary
@@ -466,6 +515,21 @@ module Rigor
466
515
  nil
467
516
  end
468
517
 
518
+ # Issue #806 — {#safe_manifest} with a receipt. The construction-time aggregates degraded silently
519
+ # when a manifest read raised, so a plugin that contributed nothing looked identical to a plugin that
520
+ # was never asked; the load-error channel is where "a plugin could not be loaded" already lives, and
521
+ # `Analysis::Runner` turns each entry into one `plugin_loader.load-error` row naming the plugin. The
522
+ # plugin ref is the CLASS, not `manifest.id`: the manifest is the surface that just failed.
523
+ def guarded_manifest(plugin, errors)
524
+ plugin.manifest
525
+ rescue StandardError => e
526
+ errors << LoadError.new(
527
+ "plugin #{plugin.class} raised while reading its manifest: #{e.class}: #{e.message}",
528
+ plugin_ref: plugin.class.to_s, cause: e
529
+ )
530
+ nil
531
+ end
532
+
469
533
  def safe_protocol_contracts(plugin)
470
534
  plugin.protocol_contracts || []
471
535
  rescue StandardError
@@ -21,19 +21,36 @@ module Rigor
21
21
  # dropping an annotation it parsed. They are not the same news — the first says the file contributed
22
22
  # nothing, the second that it contributed all but one thing — so they carry distinct diagnostic ids.
23
23
  # Defaults to `:failed`, the pre-WD12 meaning, so an older caller records what it always did.
24
- Entry = Data.define(:plugin_id, :path, :message, :kind)
24
+ #
25
+ # `:contradiction` (ADR-112 WD5, issue #1075) is the consistency rule's error: a member two sources
26
+ # declare in ways that contradict, or a refinement outside its own declared type. It is the one kind
27
+ # positioned at a real line (`line`, nil for every other kind), because it usually sits in a `.rbs`
28
+ # file whose positions are exact; the others are file-level rows at line 1.
29
+ Entry = Data.define(:plugin_id, :path, :message, :kind, :line)
25
30
 
26
31
  def initialize
27
32
  @entries = []
33
+ @seen = {}
28
34
  end
29
35
 
30
- def record(plugin_id:, path:, message:, kind: :failed)
31
- @entries << Entry.new(
36
+ # Identical entries are collapsed. Each becomes one `:info` row, and two rows saying the same thing
37
+ # about the same file carry nothing the first does not: the synthesizer's own per-file notices are
38
+ # already `uniq`, but a worker pool re-runs the whole env build in EVERY worker over the same project
39
+ # file list and then drains each worker's stream into this one reporter, so without the dedup a
40
+ # `--workers N` run printed N copies of a row a `--workers=0` run printed once. The same collapse the
41
+ # {RbsExtended::Reporter} drain documents, for the same reason.
42
+ def record(plugin_id:, path:, message:, kind: :failed, line: nil)
43
+ entry = Entry.new(
32
44
  plugin_id: plugin_id.to_s.dup.freeze,
33
45
  path: path.to_s.dup.freeze,
34
46
  message: message.to_s.dup.freeze,
35
- kind: kind
47
+ kind: kind,
48
+ line: line
36
49
  )
50
+ return nil if @seen.key?(entry)
51
+
52
+ @seen[entry] = true
53
+ @entries << entry
37
54
  nil
38
55
  end
39
56