rigortype 0.3.7 → 0.3.9

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 (450) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/capability_roles/capability_roles.rbs +36 -0
  4. data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
  5. data/docs/handbook/02-everyday-types.md +17 -14
  6. data/docs/handbook/03-narrowing.md +37 -4
  7. data/docs/handbook/05-methods-and-blocks.md +1 -1
  8. data/docs/handbook/06-classes.md +2 -2
  9. data/docs/handbook/07-rbs-and-extended.md +14 -1
  10. data/docs/handbook/11-sig-gen.md +96 -15
  11. data/docs/handbook/README.md +1 -1
  12. data/docs/handbook/appendix-elixir.md +2 -2
  13. data/docs/handbook/appendix-go.md +2 -2
  14. data/docs/handbook/appendix-java-csharp.md +2 -2
  15. data/docs/handbook/appendix-mypy.md +2 -2
  16. data/docs/handbook/appendix-phpstan.md +1 -1
  17. data/docs/handbook/appendix-rust.md +1 -1
  18. data/docs/handbook/appendix-type-theory.md +4 -4
  19. data/docs/handbook/appendix-typescript.md +1 -1
  20. data/docs/llms.txt +2 -0
  21. data/docs/manual/02-cli-reference.md +39 -4
  22. data/docs/manual/03-configuration.md +11 -0
  23. data/docs/manual/04-diagnostics.md +12 -1
  24. data/docs/manual/07-plugins.md +15 -5
  25. data/docs/manual/08-skills.md +23 -2
  26. data/docs/manual/11-ci.md +9 -0
  27. data/docs/manual/15-type-protection-coverage.md +8 -0
  28. data/docs/manual/16-rbs-extended-annotations.md +6 -1
  29. data/docs/manual/18-removing-dead-code.md +10 -8
  30. data/docs/manual/plugins/rigor-actionpack.md +18 -0
  31. data/docs/manual/plugins/rigor-activerecord.md +28 -0
  32. data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
  33. data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
  34. data/lib/rigor/analysis/baseline.rb +2 -2
  35. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
  36. data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
  37. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
  38. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
  39. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
  40. data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
  41. data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
  42. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
  43. data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
  44. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
  45. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
  46. data/lib/rigor/analysis/check_rules.rb +200 -32
  47. data/lib/rigor/analysis/crash_signature.rb +69 -13
  48. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
  49. data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
  50. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
  51. data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
  52. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
  53. data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
  54. data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
  55. data/lib/rigor/analysis/erb_template_detector.rb +1 -2
  56. data/lib/rigor/analysis/fact_store.rb +6 -1
  57. data/lib/rigor/analysis/incremental.rb +16 -0
  58. data/lib/rigor/analysis/incremental_session.rb +100 -29
  59. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
  60. data/lib/rigor/analysis/reachability/graph.rb +3 -5
  61. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
  62. data/lib/rigor/analysis/reachability/project_files.rb +2 -2
  63. data/lib/rigor/analysis/reachability/scan.rb +5 -5
  64. data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
  65. data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
  66. data/lib/rigor/analysis/result.rb +1 -3
  67. data/lib/rigor/analysis/rule_catalog.rb +4 -1
  68. data/lib/rigor/analysis/run_cache_key.rb +1 -1
  69. data/lib/rigor/analysis/run_cache_probe.rb +3 -5
  70. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
  71. data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
  72. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +316 -40
  73. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
  74. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
  75. data/lib/rigor/analysis/runner/pool_coordinator.rb +430 -77
  76. data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
  77. data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
  78. data/lib/rigor/analysis/runner.rb +200 -43
  79. data/lib/rigor/analysis/worker_session.rb +36 -12
  80. data/lib/rigor/bleeding_edge.rb +12 -24
  81. data/lib/rigor/builtins/hkt_builtins.rb +2 -2
  82. data/lib/rigor/builtins/imported_refinements.rb +166 -30
  83. data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
  84. data/lib/rigor/builtins/regex_refinement.rb +4 -4
  85. data/lib/rigor/builtins/static_return_refinements.rb +5 -6
  86. data/lib/rigor/cache/annotation_location.rb +70 -0
  87. data/lib/rigor/cache/descriptor.rb +58 -20
  88. data/lib/rigor/cache/engine_source.rb +4 -4
  89. data/lib/rigor/cache/file_digest.rb +9 -1
  90. data/lib/rigor/cache/incremental_snapshot.rb +40 -6
  91. data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
  92. data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
  93. data/lib/rigor/cache/rbs_constant_table.rb +0 -3
  94. data/lib/rigor/cache/rbs_descriptor.rb +79 -11
  95. data/lib/rigor/cache/rbs_environment.rb +8 -4
  96. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +39 -0
  97. data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
  98. data/lib/rigor/cache/store.rb +85 -33
  99. data/lib/rigor/cli/annotate_command.rb +5 -6
  100. data/lib/rigor/cli/check_command.rb +57 -10
  101. data/lib/rigor/cli/check_invocation.rb +6 -11
  102. data/lib/rigor/cli/check_runner_factory.rb +1 -5
  103. data/lib/rigor/cli/coverage_command.rb +1 -1
  104. data/lib/rigor/cli/coverage_mutation.rb +12 -3
  105. data/lib/rigor/cli/coverage_scan.rb +1 -5
  106. data/lib/rigor/cli/diff_command.rb +1 -1
  107. data/lib/rigor/cli/doc_links.rb +3 -3
  108. data/lib/rigor/cli/docs_command.rb +2 -2
  109. data/lib/rigor/cli/doctor_command.rb +85 -38
  110. data/lib/rigor/cli/effects_command.rb +2 -2
  111. data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
  112. data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
  113. data/lib/rigor/cli/explain_command.rb +34 -1
  114. data/lib/rigor/cli/fused_protection_report.rb +7 -1
  115. data/lib/rigor/cli/lsp_command.rb +1 -1
  116. data/lib/rigor/cli/mcp_command.rb +1 -1
  117. data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
  118. data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
  119. data/lib/rigor/cli/mutation_protection_report.rb +7 -1
  120. data/lib/rigor/cli/plugin_command.rb +1 -1
  121. data/lib/rigor/cli/plugins_command.rb +2 -1
  122. data/lib/rigor/cli/prism_colorizer.rb +2 -2
  123. data/lib/rigor/cli/protection_fork_scan.rb +6 -6
  124. data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
  125. data/lib/rigor/cli/sig_gen_command.rb +31 -2
  126. data/lib/rigor/cli/skill_command.rb +1 -1
  127. data/lib/rigor/cli/skill_deep_probe.rb +4 -4
  128. data/lib/rigor/cli/skill_describe.rb +36 -35
  129. data/lib/rigor/cli/trace_command.rb +4 -4
  130. data/lib/rigor/cli/trace_renderer.rb +5 -6
  131. data/lib/rigor/cli/triage_command.rb +1 -1
  132. data/lib/rigor/cli/type_of_command.rb +10 -6
  133. data/lib/rigor/cli/type_scan_command.rb +4 -4
  134. data/lib/rigor/cli/unused_command.rb +11 -3
  135. data/lib/rigor/cli/upgrade_command.rb +1 -1
  136. data/lib/rigor/cli.rb +68 -6
  137. data/lib/rigor/config_audit.rb +30 -5
  138. data/lib/rigor/configuration/severity_profile.rb +6 -6
  139. data/lib/rigor/configuration.rb +43 -4
  140. data/lib/rigor/effects/attribution.rb +1 -3
  141. data/lib/rigor/effects/config_envelopes.rb +6 -8
  142. data/lib/rigor/effects/definition_lines.rb +25 -6
  143. data/lib/rigor/effects/effect_table.rb +0 -1
  144. data/lib/rigor/effects/entry_points.rb +0 -2
  145. data/lib/rigor/effects/envelope_check.rb +8 -8
  146. data/lib/rigor/effects/envelope_index.rb +5 -8
  147. data/lib/rigor/effects/framework_units.rb +4 -6
  148. data/lib/rigor/effects/identity.rb +4 -6
  149. data/lib/rigor/effects/inline_anchor.rb +7 -7
  150. data/lib/rigor/effects/label_intent.rb +3 -4
  151. data/lib/rigor/effects/liskov_check.rb +8 -8
  152. data/lib/rigor/effects/method_key.rb +1 -1
  153. data/lib/rigor/effects/plugin_facts.rb +4 -6
  154. data/lib/rigor/effects/propagator.rb +2 -3
  155. data/lib/rigor/effects/scanner.rb +1 -1
  156. data/lib/rigor/effects/signature_sources.rb +3 -5
  157. data/lib/rigor/effects/snapshot.rb +10 -10
  158. data/lib/rigor/effects/snapshot_diff.rb +1 -1
  159. data/lib/rigor/effects/unit_scan.rb +10 -10
  160. data/lib/rigor/effects/unknown_label_check.rb +3 -8
  161. data/lib/rigor/effects/unknown_label_report.rb +3 -4
  162. data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
  163. data/lib/rigor/environment/class_registry.rb +3 -1
  164. data/lib/rigor/environment/failure_slot.rb +28 -0
  165. data/lib/rigor/environment/installed_gem_set.rb +85 -0
  166. data/lib/rigor/environment/lockfile_resolver.rb +51 -4
  167. data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
  168. data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
  169. data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
  170. data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
  171. data/lib/rigor/environment/rbs_loader.rb +613 -113
  172. data/lib/rigor/environment.rb +189 -27
  173. data/lib/rigor/flow_contribution/merger.rb +0 -2
  174. data/lib/rigor/flow_contribution.rb +11 -13
  175. data/lib/rigor/inference/acceptance.rb +153 -8
  176. data/lib/rigor/inference/block_parameter_binder.rb +2 -3
  177. data/lib/rigor/inference/body_fixpoint.rb +5 -5
  178. data/lib/rigor/inference/budget_trace.rb +1 -5
  179. data/lib/rigor/inference/captured_locals.rb +2 -3
  180. data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
  181. data/lib/rigor/inference/coverage_scanner.rb +4 -6
  182. data/lib/rigor/inference/dynamic_origin.rb +1 -1
  183. data/lib/rigor/inference/element_read_widening.rb +168 -0
  184. data/lib/rigor/inference/expression_typer.rb +763 -143
  185. data/lib/rigor/inference/fork_map.rb +5 -7
  186. data/lib/rigor/inference/hkt_reducer.rb +2 -3
  187. data/lib/rigor/inference/hkt_registry.rb +19 -10
  188. data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
  189. data/lib/rigor/inference/index_write_widening.rb +1 -5
  190. data/lib/rigor/inference/macro_block_self_type.rb +1 -4
  191. data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
  192. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
  193. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
  194. data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
  195. data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
  196. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
  197. data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
  198. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
  199. data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
  200. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
  201. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
  202. data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
  203. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
  204. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
  205. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
  206. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
  207. data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
  208. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
  209. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
  210. data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
  211. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
  212. data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
  213. data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
  214. data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
  215. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
  216. data/lib/rigor/inference/method_dispatcher.rb +12 -13
  217. data/lib/rigor/inference/method_parameter_binder.rb +4 -6
  218. data/lib/rigor/inference/multi_target_binder.rb +1 -3
  219. data/lib/rigor/inference/mutation_rejoin.rb +168 -0
  220. data/lib/rigor/inference/mutation_widening.rb +100 -51
  221. data/lib/rigor/inference/narrowing.rb +299 -46
  222. data/lib/rigor/inference/optimistic_origin.rb +1 -7
  223. data/lib/rigor/inference/origin_lookup.rb +2 -3
  224. data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
  225. data/lib/rigor/inference/pre_eval_constants.rb +6 -5
  226. data/lib/rigor/inference/precision_scanner.rb +3 -4
  227. data/lib/rigor/inference/project_patched_methods.rb +2 -2
  228. data/lib/rigor/inference/project_patched_scanner.rb +3 -3
  229. data/lib/rigor/inference/protection_scanner.rb +1 -2
  230. data/lib/rigor/inference/range_constant.rb +57 -0
  231. data/lib/rigor/inference/rbs_type_translator.rb +49 -11
  232. data/lib/rigor/inference/receiver_alias.rb +3 -3
  233. data/lib/rigor/inference/refinement_mutation.rb +72 -0
  234. data/lib/rigor/inference/scope_indexer.rb +688 -151
  235. data/lib/rigor/inference/statement_evaluator.rb +221 -50
  236. data/lib/rigor/inference/string_mutation.rb +60 -0
  237. data/lib/rigor/inference/struct_fold_safety.rb +6 -7
  238. data/lib/rigor/inference/synthetic_method_index.rb +1 -2
  239. data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
  240. data/lib/rigor/inference/version_guard.rb +28 -21
  241. data/lib/rigor/inference/void_origin.rb +3 -3
  242. data/lib/rigor/inference/void_tail_summary.rb +2 -4
  243. data/lib/rigor/language_server/buffer_table.rb +4 -4
  244. data/lib/rigor/language_server/completion_provider.rb +1 -1
  245. data/lib/rigor/language_server/debouncer.rb +1 -1
  246. data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
  247. data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
  248. data/lib/rigor/language_server/folding_range_provider.rb +1 -1
  249. data/lib/rigor/language_server/hover_provider.rb +1 -1
  250. data/lib/rigor/language_server/hover_renderer.rb +2 -2
  251. data/lib/rigor/language_server/incremental_sync.rb +6 -6
  252. data/lib/rigor/language_server/project_context.rb +2 -5
  253. data/lib/rigor/language_server/publish_batcher.rb +2 -2
  254. data/lib/rigor/language_server/selection_range_provider.rb +2 -2
  255. data/lib/rigor/language_server/server.rb +9 -9
  256. data/lib/rigor/language_server/signature_help_provider.rb +1 -1
  257. data/lib/rigor/language_server/uri.rb +1 -1
  258. data/lib/rigor/plugin/base.rb +90 -21
  259. data/lib/rigor/plugin/bundled_catalog.rb +167 -0
  260. data/lib/rigor/plugin/effect_attribution.rb +7 -7
  261. data/lib/rigor/plugin/effect_entry_points.rb +3 -3
  262. data/lib/rigor/plugin/fact_store.rb +6 -6
  263. data/lib/rigor/plugin/io_boundary.rb +93 -8
  264. data/lib/rigor/plugin/isolation.rb +29 -6
  265. data/lib/rigor/plugin/loader.rb +31 -12
  266. data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
  267. data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
  268. data/lib/rigor/plugin/manifest.rb +23 -1
  269. data/lib/rigor/plugin/registry.rb +67 -14
  270. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
  271. data/lib/rigor/plugin/trust_policy.rb +2 -4
  272. data/lib/rigor/plugin/type_node_resolver.rb +3 -3
  273. data/lib/rigor/plugin_gap_advisory.rb +96 -0
  274. data/lib/rigor/project_environment.rb +138 -0
  275. data/lib/rigor/protection/analysis_guard.rb +93 -14
  276. data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
  277. data/lib/rigor/protection/dependency_closure.rb +5 -8
  278. data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
  279. data/lib/rigor/protection/discovery_seed.rb +11 -11
  280. data/lib/rigor/protection/kill_signature.rb +2 -4
  281. data/lib/rigor/protection/measurement_integrity.rb +1 -3
  282. data/lib/rigor/protection/mutation_cache.rb +6 -9
  283. data/lib/rigor/protection/mutation_scanner.rb +12 -15
  284. data/lib/rigor/protection/mutator.rb +2 -1
  285. data/lib/rigor/protection/test_suite_oracle.rb +5 -5
  286. data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
  287. data/lib/rigor/rbs_extended/envelope_scanner.rb +12 -13
  288. data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
  289. data/lib/rigor/rbs_extended/reporter.rb +122 -16
  290. data/lib/rigor/rbs_extended.rb +67 -22
  291. data/lib/rigor/reflection/constant_path.rb +126 -0
  292. data/lib/rigor/reflection.rb +26 -10
  293. data/lib/rigor/runtime/jit.rb +6 -8
  294. data/lib/rigor/scope/discovery_index.rb +30 -0
  295. data/lib/rigor/scope.rb +203 -4
  296. data/lib/rigor/sig_gen/generator.rb +181 -102
  297. data/lib/rigor/sig_gen/layout_index.rb +3 -4
  298. data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
  299. data/lib/rigor/sig_gen/observation_collector.rb +17 -15
  300. data/lib/rigor/sig_gen/path_mapper.rb +4 -6
  301. data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
  302. data/lib/rigor/sig_gen/renderer.rb +17 -11
  303. data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
  304. data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
  305. data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
  306. data/lib/rigor/sig_gen/writer.rb +23 -6
  307. data/lib/rigor/signature_path_audit.rb +153 -6
  308. data/lib/rigor/source/literals.rb +0 -23
  309. data/lib/rigor/source/node_children.rb +0 -2
  310. data/lib/rigor/source/node_locator.rb +5 -11
  311. data/lib/rigor/source/node_walker.rb +2 -5
  312. data/lib/rigor/triage/catalogue.rb +1 -3
  313. data/lib/rigor/triage.rb +3 -5
  314. data/lib/rigor/type/accepts_result.rb +24 -6
  315. data/lib/rigor/type/combinator.rb +68 -5
  316. data/lib/rigor/type/data_class.rb +2 -2
  317. data/lib/rigor/type/data_instance.rb +4 -4
  318. data/lib/rigor/type/float_range.rb +128 -0
  319. data/lib/rigor/type/hash_shape.rb +5 -5
  320. data/lib/rigor/type/integer_range.rb +13 -8
  321. data/lib/rigor/type/nominal.rb +8 -2
  322. data/lib/rigor/type/refined.rb +3 -3
  323. data/lib/rigor/type/struct_class.rb +3 -3
  324. data/lib/rigor/type/struct_instance.rb +4 -4
  325. data/lib/rigor/type.rb +1 -0
  326. data/lib/rigor/type_node/generic.rb +1 -1
  327. data/lib/rigor/type_node/range_literal.rb +25 -0
  328. data/lib/rigor/type_node/resolver_chain.rb +1 -1
  329. data/lib/rigor/type_node.rb +1 -0
  330. data/lib/rigor/version.rb +1 -1
  331. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
  332. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
  333. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
  334. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
  335. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
  336. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
  337. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
  338. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
  339. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
  340. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
  341. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
  342. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
  343. data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
  344. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
  345. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
  346. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
  347. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
  348. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
  349. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
  350. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
  351. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
  352. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
  353. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
  354. data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
  355. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
  356. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
  357. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
  358. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
  359. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
  360. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
  361. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
  362. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
  363. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
  364. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
  365. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
  366. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
  367. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  368. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
  369. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
  370. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
  371. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
  372. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
  373. data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
  374. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
  375. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
  376. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
  377. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
  378. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
  379. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
  380. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
  381. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
  382. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
  383. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
  384. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
  385. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
  386. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
  387. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
  388. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
  389. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
  390. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
  391. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
  392. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
  393. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
  394. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
  395. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
  396. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
  397. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
  398. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
  399. data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
  400. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
  401. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
  402. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
  403. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
  404. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
  405. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
  406. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
  407. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
  408. data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
  409. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
  410. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
  411. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
  412. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
  413. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
  414. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
  415. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
  416. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
  417. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
  418. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
  419. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
  420. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
  421. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
  422. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
  423. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
  424. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
  425. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
  426. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
  427. data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
  428. data/sig/rigor/cache.rbs +2 -3
  429. data/sig/rigor/environment.rbs +11 -0
  430. data/sig/rigor/inference.rbs +3 -10
  431. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  432. data/sig/rigor/plugin/manifest.rbs +2 -1
  433. data/sig/rigor/reflection.rbs +3 -1
  434. data/sig/rigor/scope.rbs +24 -3
  435. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
  436. data/sig/rigor/type.rbs +27 -1
  437. data/sig/rigor.rbs +1 -0
  438. data/skills/rigor-ask/SKILL.md +8 -6
  439. data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
  440. data/skills/rigor-project-init/SKILL.md +6 -3
  441. data/skills/rigor-project-init/references/02-configure.md +10 -6
  442. data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
  443. data/skills/rigor-type-oracle/SKILL.md +228 -0
  444. data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
  445. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
  446. data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
  447. data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
  448. metadata +28 -3
  449. data/lib/rigor/cli/probe_environment.rb +0 -85
  450. data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
@@ -35,11 +35,9 @@ module Rigor
35
35
  :pre_eval_diagnostics_from_scanner
36
36
  )
37
37
 
38
- # @param configuration [Rigor::Configuration]
39
- # @param cache_store [Rigor::Cache::Store, nil]
40
- # @param buffer [BufferBinding, nil]
41
- # @param plugin_requirer [#call, nil]
42
- # @param pool_mode [#call] reader returning the pool-mode flag.
38
+ # @param plugin_requirer — how a plugin gem is brought into the process, called with the gem name; nil
39
+ # lets `Plugin::Loader` apply its own default.
40
+ # @param pool_mode — reader returning the pool-mode flag.
43
41
  def initialize(configuration:, cache_store:, buffer:, plugin_requirer:, pool_mode:)
44
42
  @configuration = configuration
45
43
  @cache_store = cache_store
@@ -56,7 +54,7 @@ module Rigor
56
54
  :discovered_classes, :discovered_def_nodes, :discovered_def_nestings,
57
55
  :discovered_singleton_def_nodes, :discovered_def_sources, :discovered_singleton_def_sources,
58
56
  :discovered_superclasses, :discovered_header_nestings, :discovered_includes,
59
- :discovered_class_sources, :constant_values, :constant_sources, :constant_writes,
57
+ :discovered_extends, :discovered_class_sources, :constant_values, :constant_sources, :constant_writes,
60
58
  :discovered_method_visibilities, :discovered_methods, :data_member_layouts,
61
59
  :struct_member_layouts
62
60
  )
@@ -77,6 +75,11 @@ module Rigor
77
75
  bundle_path: @configuration.bundler_bundle_path,
78
76
  auto_detect: @configuration.bundler_auto_detect
79
77
  )&.to_s
78
+ # ADR-39 slice 5 / #911 — hand the project's `plugins_isolation:` to the invocation layer before
79
+ # any plugin `#prepare` runs, for the same reason the bundle root is resolved above: `#prepare`
80
+ # already calls through {Plugin::Isolation}, so a later assignment would leave prepare-time
81
+ # calls on a different strategy than the rest of the run.
82
+ Plugin::Isolation.configured_strategy = @configuration.plugins_isolation
80
83
  plugin_registry = load_plugins
81
84
  dependency_source_index = DependencySourceInference::Builder.build(@configuration.dependencies)
82
85
  # ADR-18 slice 3 — plugin prepare MUST run before the synthetic-method scanner so cross-plugin
@@ -161,6 +164,9 @@ module Rigor
161
164
  # the cref Ruby resolves it in rather than by peeling the subclass's own qualified name.
162
165
  discovered_header_nestings: def_index.fetch(:header_nestings),
163
166
  discovered_includes: def_index.fetch(:includes),
167
+ # Issue #898 — the singleton-side mixin table, kept beside the instance-side one so a class
168
+ # object's `extend`s reach `Narrowing` from a sibling file the way its `include`s already do.
169
+ discovered_extends: def_index.fetch(:extends),
164
170
  discovered_class_sources: def_index.fetch(:class_sources),
165
171
  # Issue #644 — the cross-file value-constant publication table and its write attribution.
166
172
  constant_values: def_index.fetch(:constant_values),
@@ -16,8 +16,9 @@ module Rigor
16
16
  # back-reference cycle.
17
17
  class RunSnapshots
18
18
  attr_accessor :class_decl_paths, :signature_paths,
19
- :synthesized_namespaces, :quarantined_signatures, :conformance_results,
20
- :env_build_failure, :definition_build_failures, :effect_annotation_carrier
19
+ :synthesized_namespaces, :quarantined_signatures, :signature_standdowns, :conformance_results,
20
+ :env_build_failure, :definition_build_failures, :hkt_scan_failure,
21
+ :effect_annotation_carrier
21
22
 
22
23
  # Constructor defaults match the {Runner} constructor: the pre-seed values `build_run_stats` /
23
24
  # `pre_file_diagnostics` read before the first analysis path runs are frozen empties. The
@@ -25,15 +26,22 @@ module Rigor
25
26
  # `[error_class, first_line, buffer_names]` tuple or nothing. Its per-class sibling
26
27
  # `definition_build_failures` (#696) holds a LIST, because a collapsed universe fails many classes,
27
28
  # and is accumulated ACROSS pool workers rather than assigned once — see
28
- # {PoolCoordinator#merge_worker_reporters}.
29
+ # {PoolCoordinator#merge_worker_reporters}. `hkt_scan_failure` (#784) is nil-or-tuple like
30
+ # `env_build_failure`, not a list like `definition_build_failures`: the scan is ONE build over the
31
+ # whole `signature_paths:` overlay, not a per-class one, so it has exactly one outcome per run.
32
+ # `signature_standdowns` (#610) is the quarantine slot's twin for the plugin-contributed files that
33
+ # stood down against a colliding generic arity: a list, assigned once from the loader as
34
+ # `quarantined_signatures` is, and derived from the final env so a cache HIT carries it too.
29
35
  def initialize
30
36
  @class_decl_paths = {}.freeze
31
37
  @signature_paths = [].freeze
32
38
  @synthesized_namespaces = [].freeze
33
39
  @quarantined_signatures = [].freeze
40
+ @signature_standdowns = [].freeze
34
41
  @conformance_results = [].freeze
35
42
  @env_build_failure = nil
36
43
  @definition_build_failures = [].freeze
44
+ @hkt_scan_failure = nil
37
45
  @effect_annotation_carrier = [].freeze
38
46
  end
39
47
 
@@ -44,9 +52,11 @@ module Rigor
44
52
  @signature_paths = []
45
53
  @synthesized_namespaces = []
46
54
  @quarantined_signatures = []
55
+ @signature_standdowns = []
47
56
  @conformance_results = []
48
57
  @env_build_failure = nil
49
58
  @definition_build_failures = []
59
+ @hkt_scan_failure = nil
50
60
  @effect_annotation_carrier = [].freeze
51
61
  end
52
62
  end
@@ -9,6 +9,7 @@ require_relative "../scope"
9
9
  require_relative "../cache/store"
10
10
  require_relative "../cache/rbs_descriptor"
11
11
  require_relative "../cache/file_digest"
12
+ require_relative "../cache/incremental_snapshot"
12
13
  require_relative "run_cache_key"
13
14
  require_relative "path_expansion"
14
15
  require_relative "../plugin"
@@ -29,6 +30,7 @@ require_relative "../inference/scope_indexer"
29
30
  require_relative "../inference/synthetic_method_scanner"
30
31
  require_relative "../inference/project_patched_scanner"
31
32
  require_relative "../inference/method_dispatcher/file_folding"
33
+ require_relative "crash_signature"
32
34
  require_relative "buffer_binding"
33
35
  require_relative "check_rules"
34
36
  require_relative "dependency_recorder"
@@ -64,7 +66,26 @@ module Rigor
64
66
 
65
67
  attr_reader :cache_store, :plugin_registry, :dependency_source_index,
66
68
  :rbs_extended_reporter, :boundary_cross_reporter,
67
- :analyzed_files, :unresolved_self_calls, :seed_bundles
69
+ :analyzed_files, :unresolved_self_calls, :seed_bundles,
70
+ # #788 rounds 6–7 — the rows per-file analysis produced this run, severity-resolved exactly
71
+ # as the run's stream is and sliced to this run's targets (a pool backend folds `.rigor.yml`
72
+ # prepare / degraded rows into `analyze_files`'s return). `IncrementalSession` caches ONLY
73
+ # this and serves reused files from it without re-stamping: a run-level row is regenerated
74
+ # every run wherever it is positioned, and slicing the run's full stream by path cached the
75
+ # file-positioned ones (`effect.annotations-unchecked`, `source-rbs-*`).
76
+ :per_file_diagnostics
77
+
78
+ # Issues #796 / #794 — the two run-level rows a NARROWED run cannot re-derive from the files it
79
+ # analyses: the `definition-build-failed` set (whose producer is the analysis's own demand, and #696
80
+ # forbids Rigor demanding in its place) and the HKT-scan outcome (whose producer is an environment a
81
+ # nothing-changed recheck otherwise has no reason to build). {IncrementalSession} persists what a full
82
+ # run answers here and hands it back to the next narrowed run.
83
+ def run_level_rows
84
+ Cache::IncrementalSnapshot::RunLevelRows.new(
85
+ definition_build_failures: @snapshots.definition_build_failures,
86
+ hkt_scan_failure: @snapshots.hkt_scan_failure
87
+ )
88
+ end
68
89
 
69
90
  # ADR-46 — the per-file cross-file read records this run captured (empty unless
70
91
  # `record_dependencies: true`). Sequential analysis records into `@file_dependencies` via
@@ -206,41 +227,40 @@ module Rigor
206
227
  end
207
228
  end
208
229
 
209
- # @param configuration [Rigor::Configuration]
210
- # @param explain [Boolean] surface fail-soft fallback events as `:info` diagnostics.
211
- # @param cache_store [Rigor::Cache::Store, nil] the persistent cache the runner exposes to producers
230
+ # @param explain — surface fail-soft fallback events as `:info` diagnostics.
231
+ # @param cache_store — the persistent cache the runner exposes to producers
212
232
  # (`RbsConstantTable` and successors). Pass `nil` to disable caching for this run; the CLI's
213
233
  # `--no-cache` flag wires `nil` through. v0.0.9 group A slice 1 introduces the surface; later
214
234
  # slices route real producers through it.
215
- # @param workers [Integer] ADR-15 Phase 4b — when greater than zero, per-file analysis dispatches
235
+ # @param workers — ADR-15 Phase 4b — when greater than zero, per-file analysis dispatches
216
236
  # across a pool of N workers. Default `0` keeps the sequential code path bit-for-bit unchanged.
217
237
  # Controlled via the `RIGOR_RACTOR_WORKERS` env var or `.rigor.yml` `parallel.workers:` (Phase 4c,
218
238
  # fully wired).
219
- # @param collect_stats [Boolean] when true (default), `#run` builds a {RunStats} summary exposed via
239
+ # @param collect_stats — when true (default), `#run` builds a {RunStats} summary exposed via
220
240
  # `result.stats` — this forces the RBS env build at end-of-run so the `class_decl_paths` snapshot
221
241
  # has real source attribution. Set to false to skip the stats summary entirely; the CLI's
222
242
  # `--no-stats` threads `false` through to keep trivial-fixture runs from warming `.rigor/cache`.
223
- # @param prebuilt [Rigor::Analysis::ProjectScan, nil] when supplied, the runner adopts the pre-built
243
+ # @param prebuilt — when supplied, the runner adopts the pre-built
224
244
  # plugin registry / dependency-source index / scanner outputs from the snapshot and skips the
225
245
  # per-call pre-passes that produce them. Used by long-lived integrations
226
246
  # (`Rigor::LanguageServer::ProjectContext`) to keep per-buffer requests fast — scanners walk the
227
247
  # project once per generation rather than once per request, and plugin `#prepare` runs once per
228
248
  # generation rather than once per request. Watched-file invalidation is the owner's responsibility;
229
249
  # the runner trusts the snapshot it was given.
230
- # @param environment [Rigor::Environment, nil] opt-in Environment override. When supplied, sequential
250
+ # @param environment — opt-in Environment override. When supplied, sequential
231
251
  # mode uses the provided env instance in `#analyze_files` instead of building a fresh one via
232
252
  # `Environment.for_project`, and attaches the runner's per-run reporter pair onto the env's mutable
233
253
  # `Reporters` slot via `Environment#attach_reporters!`. Long-lived consumers (LSP `ProjectContext`)
234
254
  # pass a shared env so per-publish work doesn't repeat the `Environment.for_project` build (bundler
235
255
  # / lockfile / collection discovery, RbsLoader construction). Pool mode ignores the override — each
236
256
  # worker continues to build its own Environment.
237
- # @param discovery_seed [Hash, nil] issue #260 — opt-in cross-file discovery tables, keyed by
257
+ # @param discovery_seed — issue #260 — opt-in cross-file discovery tables, keyed by
238
258
  # {Scope::DiscoveryIndex} slot name, seeded onto every per-file scope through
239
259
  # `project_scope_seed_tables`. The ONE deliberate exception to "a `prebuilt:` runner carries no
240
260
  # discovery tables": {Protection::DiagnosticOracle} threads the table set Tier 2's site filter already
241
261
  # judges anchors against, so a site admitted because a sibling-file class resolved is also a site the
242
262
  # oracle can kill at. nil (the default) leaves the prebuilt/LSP contract byte-identical.
243
- # @param no_tolerated_effects [Boolean] ADR-103 WD1 / #385 — `rigor check --no-tolerated-effects`.
263
+ # @param no_tolerated_effects — ADR-103 WD1 / #385 — `rigor check --no-tolerated-effects`.
244
264
  # Judges effect envelopes as if `effects.tolerated:` were empty. A judgment-time switch only: the
245
265
  # run, its collection and its cache identity are unchanged.
246
266
  def initialize(configuration:, explain: false, # rubocop:disable Metrics/ParameterLists,Metrics/AbcSize,Metrics/MethodLength
@@ -249,8 +269,12 @@ module Rigor
249
269
  buffer: nil, prebuilt: nil, environment: nil,
250
270
  record_dependencies: false, record_self_calls: false, analyze_only: nil,
251
271
  seed_bundles: nil, collect_seed_bundles: false, param_inferred_types: nil,
252
- discovery_seed: nil, no_tolerated_effects: false)
272
+ discovery_seed: nil, no_tolerated_effects: false,
273
+ restored_run_level_rows: nil)
253
274
  @configuration = configuration
275
+ # Issues #796 / #794 — the previous full run's run-level rows, supplied only by
276
+ # {IncrementalSession} on a narrowed run; see {PoolCoordinator#replay_restored_run_level_rows}.
277
+ @restored_run_level_rows = restored_run_level_rows
254
278
  @explain = explain
255
279
  @cache_store = enforce_read_only_cache(cache_store, buffer)
256
280
  @plugin_requirer = plugin_requirer
@@ -302,6 +326,7 @@ module Rigor
302
326
  # See `self_undefined_rule_active?`.
303
327
  @self_undefined_rule_active = nil
304
328
  @analyzed_files = [].freeze
329
+ @per_file_diagnostics = [].freeze
305
330
  # In-memory source map for `#run_source` — `{ logical_path => source String }`. When set,
306
331
  # `parse_source` reads bytes from here instead of disk and `expand_paths` accepts the (possibly
307
332
  # non-existent) logical path. nil on a normal disk-backed run.
@@ -353,6 +378,8 @@ module Rigor
353
378
  # Issue #682 — the `Module.nesting` each class / module declaration HEADER is written in.
354
379
  @project_discovered_header_nestings = {}.freeze
355
380
  @project_discovered_includes = {}.freeze
381
+ # Issue #898 — the singleton-side twin of the include table (`extend M` / `extend self`).
382
+ @project_discovered_extends = {}.freeze
356
383
  @project_discovered_class_sources = {}.freeze
357
384
  # Issue #644 — the cross-file VALUE-constant publication table (`{qualified name => Type::Constant}`,
358
385
  # literal writes only) and its per-name write attribution. The first seeds `in_source_constants` on
@@ -379,6 +406,9 @@ module Rigor
379
406
  # (sequential + fork-worker) through `project_scope_seed_tables`. Empty unless the project lists
380
407
  # `pre_eval:` files that declare publishable constants, so a project without one is byte-identical.
381
408
  @project_pre_eval_constants = {}.freeze
409
+ # Issue #663 — the listed set {#publishable_pre_eval_constants} asks "is this writer one of ours?"
410
+ # against; a function of the configuration alone, so it outlives every discovery rebuild.
411
+ @pre_eval_path_set = nil
382
412
  # ADR-84 WD2 — per-run identity token for the user-method return memo's bucket (see
383
413
  # Scope::DiscoveryIndex#run_generation). Minted fresh in `run_analysis` so the memo never serves an
384
414
  # entry across a run boundary (LSP re-check, ADR-62 warm loop); nil until the first run so
@@ -426,6 +456,9 @@ module Rigor
426
456
  # Per-run reset of the environment the cacheable path resolves, reused by the envelope pass so a
427
457
  # run never builds two.
428
458
  @run_environment = nil
459
+ # #788 — the per-file reader is assigned only on the analysis (miss) path; reset it here so a run the
460
+ # ADR-45 result cache serves does not answer with the previous run's rows.
461
+ @per_file_diagnostics = [].freeze
429
462
  # ADR-84 WD2 — roll the return-memo bucket: a fresh frozen token per run makes every per-file scope
430
463
  # of THIS run share one memo bucket while entries from any earlier run in this process (stale after
431
464
  # an edit) become unreachable.
@@ -467,9 +500,8 @@ module Rigor
467
500
  # so the result matches a one-file disk run; only the cross-file project pre-pass is empty (there is
468
501
  # one file, and the per-file indexer self-discovers its own classes / defs).
469
502
  #
470
- # @param source [String] Ruby source to analyze.
471
- # @param path [String] logical path for diagnostic locations.
472
- # @return [Result]
503
+ # @param source — Ruby source to analyze.
504
+ # @param path — logical path for diagnostic locations.
473
505
  def run_source(source:, path: "(source).rb")
474
506
  @in_memory_sources = { path => source }
475
507
  run([path])
@@ -595,7 +627,7 @@ module Rigor
595
627
  # arg-flooring surface). Only meaningful right after a run populated the memo (baseline / recheck),
596
628
  # before the bucket rolls. Keys are held live here; the session Marshals them for the snapshot.
597
629
  #
598
- # @return [Hash] `{ [path, "Class#method" | "Class.method"] => { keys:, returns:, effects: } }`.
630
+ # @return `{ [path, "Class#method" | "Class.method"] => { keys:, returns:, effects: } }`.
599
631
  def return_summaries
600
632
  memo = Inference::ExpressionTyper.harvest_return_memo
601
633
  return {} if memo.empty?
@@ -653,9 +685,9 @@ module Rigor
653
685
  # store) exactly as a real run's setup does, then re-drives each spec's def through the ADR-84 return
654
686
  # memo. Off any hot path — invoked only when declaration-stable changed pairs carry a persisted summary.
655
687
  #
656
- # @param paths [Array<String>, nil] analysis roots (nil → the configuration's `paths:`).
657
- # @param specs [Array<Hash>] each `{ class_name:, method_name:, singleton:, keys: [[receiver, args], …] }`.
658
- # @return [Hash] `{ [class_name, method_name, singleton] => [return_descriptor_or_nil, …] }`.
688
+ # @param paths — analysis roots (nil → the configuration's `paths:`).
689
+ # @param specs — each `{ class_name:, method_name:, singleton:, keys: [[receiver, args], …] }`.
690
+ # @return `{ [class_name, method_name, singleton] => [return_descriptor_or_nil, …] }`.
659
691
  def evaluate_return_types(paths, specs)
660
692
  return {} if specs.empty?
661
693
 
@@ -674,7 +706,14 @@ module Rigor
674
706
  @run_generation = Object.new.freeze
675
707
  run_project_pre_passes(expansion: expansion)
676
708
  ensure_project_discovery(expansion)
677
- environment = @pool_coordinator.resolve_sequential_environment(source_files: target_files(expansion))
709
+ # Issue #795 — `source_files:` is the WHOLE project's file list, never `target_files(expansion)`.
710
+ # `IncrementalSession#build_runner` threads its own `@buffer` into this Runner, so in editor-buffer
711
+ # mode `target_files` narrows to the buffer's single logical path; an environment built over just
712
+ # that path drops every OTHER file's plugin-synthesized virtual RBS, which can flip a re-evaluated
713
+ # return descriptor and wrongly declare a caller's return type unstable (or stable) relative to the
714
+ # full-project answer. This probe never analyzes a file, so it carries none of #788's per-file-cache
715
+ # duplication concern either.
716
+ environment = @pool_coordinator.resolve_sequential_environment(source_files: expansion.fetch(:files))
678
717
  specs.to_h do |spec|
679
718
  [[spec[:class_name], spec[:method_name], spec[:singleton]], evaluate_spec_returns(spec, environment)]
680
719
  end
@@ -776,7 +815,7 @@ module Rigor
776
815
  # fixpoint over them, which is the pre-split behaviour and the retry the fail-soft posture wants.
777
816
  # Only when neither entry answers does the run re-analyse.
778
817
  #
779
- # @return [Boolean] whether this run was served.
818
+ # @return whether this run was served.
780
819
  def served_from_effect_entries?(descriptor)
781
820
  summary = peek_effect_summary(descriptor)
782
821
  if summary
@@ -927,9 +966,19 @@ module Rigor
927
966
  ).diagnostics
928
967
  end
929
968
 
969
+ # Issue #795 — `source_files:` is the WHOLE project's file list (`expansion.fetch(:files)`), never
970
+ # `target_files(expansion)`. A subset or editor-buffer run's `target_files` narrows to the
971
+ # `analyze_only` closure or the buffer's single logical path; building the envelope walk's
972
+ # environment over that subset drops every plugin-synthesized virtual RBS from an excluded file, so
973
+ # an envelope declared only in that file's inline annotation is invisible to the walk — the same #793
974
+ # gap #788 closed for per-file analysis, left open here because #788's binding sentences scoped
975
+ # themselves to "the environments the per-file analysis builds". This path is exempt from #788's
976
+ # per-file-cache duplication concern: `effect.unknown-label` (and its envelope-pass siblings) are
977
+ # produced by this pass, never by `Runner#per_file_diagnostics`, so `IncrementalSession` never caches
978
+ # them and widening this source list cannot duplicate a row on recheck.
930
979
  def envelope_rbs_loader(expansion)
931
980
  environment = @run_environment ||
932
- @pool_coordinator.resolve_sequential_environment(source_files: target_files(expansion))
981
+ @pool_coordinator.resolve_sequential_environment(source_files: expansion.fetch(:files))
933
982
  environment&.rbs_loader
934
983
  rescue StandardError
935
984
  nil
@@ -985,24 +1034,71 @@ module Rigor
985
1034
  # per-file cache, so it needs the full analyzed set to subtract the affected closure from.
986
1035
  targets = target_files(expansion)
987
1036
  @analyzed_files = targets
988
- diagnostics += @pool_coordinator.analyze_files(targets, environment: environment)
1037
+ # Issue #784 — the whole project's file list rides along so an EMPTY `targets` (a narrowed run whose
1038
+ # closure is empty) can still resolve the environment a full run would — same files, same
1039
+ # synthesized RBS — to demand the HKT registry once, while a project with no files resolves nothing.
1040
+ # #788 rounds 6–7 — the per-file stream is kept apart from the run-level streams appended below,
1041
+ # because `IncrementalSession` must cache ONLY what per-file analysis produced (#per_file_diagnostics),
1042
+ # and the reader is exposed SEVERITY-RESOLVED and sliced to this run's targets. The cache serves a
1043
+ # reused file without re-stamping, so a raw row would resurrect a rule the profile resolves to `:off`
1044
+ # (`static.value-use.void` ships off on the default profile) and serve the authored severity where an
1045
+ # override re-stamps it; and a pool backend folds `.rigor.yml`-positioned prepare / pool-degraded rows
1046
+ # into the same return, which the slice drops. The run's own stream is stamped once, at the end.
1047
+ diagnostics += analyze_targets(targets, environment: environment, project_files: expansion.fetch(:files))
989
1048
  # ADR-103 WD12 — the effect fixpoint, in the post-pool aggregation slot beside the conformance
990
1049
  # results. Graph-only over a finite lattice, so it is a plain worklist to a true fixpoint; it
991
1050
  # contributes NO diagnostics and its result leaves through `#effect_table`, never through the
992
1051
  # stream. Skipped entirely when collection did not run.
993
1052
  close_effect_graph
994
- diagnostics += @diagnostic_aggregator.rbs_quarantined_signature_diagnostics
1053
+ diagnostics + post_analysis_diagnostic_streams
1054
+ end
1055
+
1056
+ # The rbs-coverage build-failure ladder plus every remaining post-analysis stream
1057
+ # `#assemble_run_diagnostics` appends, in their fixed contract order (see the comments at each call
1058
+ # below and at the streams' own definitions). Split out of `#assemble_run_diagnostics` so that
1059
+ # method's `Metrics/AbcSize` stays under budget — every stream here is a plain `DiagnosticAggregator`
1060
+ # reader with no branching of its own, so the split changes nothing about WHEN or in WHAT order they
1061
+ # run.
1062
+ def post_analysis_diagnostic_streams
1063
+ diagnostics = @diagnostic_aggregator.rbs_quarantined_signature_diagnostics
995
1064
  diagnostics += @diagnostic_aggregator.rbs_environment_build_failed_diagnostics
996
- # Issue #696 — after its env-wide twin and before the synthesized-namespace notice: the three
1065
+ # Issue #696 — after its env-wide twin and before the synthesized-namespace notice: the four
997
1066
  # `rbs.coverage.*` build conditions surface widest-consequence first, and their relative order is
998
1067
  # the diagnostic output contract.
999
1068
  diagnostics += @diagnostic_aggregator.rbs_definition_build_failed_diagnostics
1000
- diagnostics += @diagnostic_aggregator.rbs_synthesized_namespace_diagnostics
1069
+ # Issue #784 — last of the four, deliberately: it loses only the IMPLICIT HKT registrations a
1070
+ # `type` alias would have contributed, never a class's own declared method surface, so it is the
1071
+ # narrowest-consequence rung on the same ladder.
1072
+ diagnostics += @diagnostic_aggregator.rbs_hkt_scan_failed_diagnostics
1073
+ # The synthesized-namespace notice, then (#610) the plugin-signature stand-down: the outcome that
1074
+ # AVOIDED a definition-build failure, so the quietest row on that ladder.
1075
+ diagnostics += @diagnostic_aggregator.rbs_coverage_notice_diagnostics
1001
1076
  diagnostics += @diagnostic_aggregator.conforms_to_diagnostics
1002
1077
  diagnostics += @diagnostic_aggregator.rbs_extended_reporter_diagnostics
1003
1078
  diagnostics += @diagnostic_aggregator.boundary_cross_diagnostics
1004
- diagnostics + @diagnostic_aggregator.source_rbs_synthesis_diagnostics
1005
- end
1079
+ diagnostics += @diagnostic_aggregator.source_rbs_synthesis_diagnostics
1080
+ # Issue #959 — read LAST (after `analyze_targets`, which already ran before this method is called),
1081
+ # so every per-file plugin call has had its chance to record a refusal on its (per-plugin-instance
1082
+ # memoised) `IoBoundary`, alongside the prepare-time refusals the same boundary instance may
1083
+ # already carry.
1084
+ diagnostics + @diagnostic_aggregator.plugin_trust_refusal_diagnostics
1085
+ end
1086
+ private :post_analysis_diagnostic_streams
1087
+
1088
+ # #788 round 7 — runs per-file analysis over `targets` and exposes what it produced as
1089
+ # `#per_file_diagnostics`: the `analyze_files` return, stamped with the same severity profile the run's
1090
+ # own stream gets (`SeverityStamp` drops `:off` rows and re-stamps overrides, and the per-file cache never
1091
+ # re-stamps), sliced to the rows positioned at the targets. Returns the RAW return for the run's stream,
1092
+ # which is stamped once, at the end of `#run_analysis`.
1093
+ def analyze_targets(targets, environment:, project_files:)
1094
+ raw = @pool_coordinator.analyze_files(targets, environment: environment, project_files: project_files)
1095
+ analysed = targets.to_set
1096
+ @per_file_diagnostics = @diagnostic_aggregator.apply_severity_profile(raw)
1097
+ .select { |diagnostic| analysed.include?(diagnostic.path) }
1098
+ .freeze
1099
+ raw
1100
+ end
1101
+ private :analyze_targets
1006
1102
 
1007
1103
  # ADR-67 WD6a — the check-walk parameter-inference pre-pass. Populates `@project_param_inferred_types`
1008
1104
  # (read by `project_scope_seed_tables`) with the call-site union of every undeclared parameter, running
@@ -1121,13 +1217,22 @@ module Rigor
1121
1217
  def build_run_dependency_descriptor(expansion, rbs_descriptor)
1122
1218
  entries = analyzed_file_entries(expansion) + discovery_file_entries(expansion) +
1123
1219
  pre_eval_file_entries + rbs_descriptor.files
1124
- globs = []
1220
+ # #979 — the signature ROOTS' listings, one glob row each. `rbs_descriptor.files` covers only the
1221
+ # `.rbs` files that existed while the run read them, so without these a signature file written after
1222
+ # the run left the slot validating fresh and the warm run answered without it.
1223
+ globs = rbs_descriptor.globs.dup
1125
1224
  @plugin_registry.plugins.each do |plugin|
1126
1225
  # Read the boundary WITHOUT triggering its lazy `@io_boundary ||=` initializer: plugin instances
1127
1226
  # are frozen after the run, and a plugin that never built a boundary read no files through it,
1128
1227
  # so it contributes no dependencies.
1129
1228
  boundary = plugin.instance_variable_get(:@io_boundary)
1130
- entries.concat(boundary.cache_descriptor.files) if boundary
1229
+ if boundary
1230
+ # #629 — a boundary carries GlobEntry listing rows too (`IoBoundary#list_directory`), and they
1231
+ # are the only edge back to a file the run decided was ABSENT from a directory it consulted.
1232
+ boundary_descriptor = boundary.cache_descriptor
1233
+ entries.concat(boundary_descriptor.files)
1234
+ globs.concat(boundary_descriptor.globs)
1235
+ end
1131
1236
  plugin.class.producers.each_value do |prod|
1132
1237
  globs.concat(plugin.send(:watch_glob_entries, prod[:watch])) if prod[:watch]
1133
1238
  end
@@ -1248,6 +1353,7 @@ module Rigor
1248
1353
  @project_discovered_superclasses = discovery.discovered_superclasses
1249
1354
  @project_discovered_header_nestings = discovery.discovered_header_nestings
1250
1355
  @project_discovered_includes = discovery.discovered_includes
1356
+ @project_discovered_extends = discovery.discovered_extends
1251
1357
  @project_discovered_class_sources = discovery.discovered_class_sources
1252
1358
  @project_constant_values = discovery.constant_values
1253
1359
  @published_constant_name_set = nil
@@ -1341,7 +1447,7 @@ module Rigor
1341
1447
  3.0.0 3.1.0 3.2.0 3.3.0 3.4.0 3.5.0 4.0.0 4.1.0 4.2.0
1342
1448
  ].freeze
1343
1449
 
1344
- # @return [String, nil] the lowest `target_ruby` this Prism build accepts, or nil if none of the
1450
+ # @return the lowest `target_ruby` this Prism build accepts, or nil if none of the
1345
1451
  # ladder parses. Memoised per process (the value is constant for a given Prism).
1346
1452
  def self.prism_supported_floor
1347
1453
  @prism_supported_floor ||= PRISM_VERSION_LADDER.find do |candidate|
@@ -1427,7 +1533,7 @@ module Rigor
1427
1533
  # indexes, prepare-diagnostic snapshot, and the four end-of-pass snapshots) is reached through reader
1428
1534
  # procs so each collaborator observes the live ivar value at call time without a back-reference
1429
1535
  # cycle. The reporter accumulators and the {RunSnapshots} sink are shared mutable instances.
1430
- def build_collaborators # rubocop:disable Metrics/MethodLength
1536
+ def build_collaborators # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
1431
1537
  @pre_passes = ProjectPrePasses.new(
1432
1538
  configuration: @configuration, cache_store: @cache_store, buffer: @buffer,
1433
1539
  plugin_requirer: @plugin_requirer, pool_mode: -> { pool_mode? }
@@ -1446,7 +1552,8 @@ module Rigor
1446
1552
  synthetic_method_index: -> { @synthetic_method_index },
1447
1553
  project_patched_methods: -> { @project_patched_methods },
1448
1554
  project_scope_seed: -> { project_scope_seed_tables },
1449
- analyze_file: ->(path, environment) { analyze_file(path, environment) }
1555
+ analyze_file: ->(path, environment) { analyze_file(path, environment) },
1556
+ restored_run_level_rows: @restored_run_level_rows
1450
1557
  )
1451
1558
  @diagnostic_aggregator = DiagnosticAggregator.new(
1452
1559
  configuration: @configuration,
@@ -1460,8 +1567,10 @@ module Rigor
1460
1567
  pre_eval_diagnostics_from_scanner: -> { @pre_eval_diagnostics_from_scanner },
1461
1568
  synthesized_namespaces_snapshot: -> { @snapshots.synthesized_namespaces },
1462
1569
  quarantined_signatures_snapshot: -> { @snapshots.quarantined_signatures },
1570
+ signature_standdowns_snapshot: -> { @snapshots.signature_standdowns },
1463
1571
  env_build_failure_snapshot: -> { @snapshots.env_build_failure },
1464
1572
  definition_build_failures_snapshot: -> { @snapshots.definition_build_failures },
1573
+ hkt_scan_failure_snapshot: -> { @snapshots.hkt_scan_failure },
1465
1574
  conformance_results_snapshot: -> { @snapshots.conformance_results }
1466
1575
  )
1467
1576
  end
@@ -1693,7 +1802,7 @@ module Rigor
1693
1802
  unless @project_discovered_header_nestings.empty?
1694
1803
  tables[:discovered_header_nestings] = @project_discovered_header_nestings
1695
1804
  end
1696
- tables[:discovered_includes] = @project_discovered_includes unless @project_discovered_includes.empty?
1805
+ seed_mixin_tables(tables)
1697
1806
  unless @project_discovered_method_visibilities.empty?
1698
1807
  tables[:discovered_method_visibilities] = @project_discovered_method_visibilities
1699
1808
  end
@@ -1704,6 +1813,14 @@ module Rigor
1704
1813
  tables
1705
1814
  end
1706
1815
 
1816
+ # The two mixin tables: the ADR-24 instance-side `include` / `prepend` map and its #898 singleton-side
1817
+ # `extend` twin. Paired here because they are read as one picture of a class's ancestry, and split out
1818
+ # of {#project_scope_seed_tables} to keep it under the complexity budget.
1819
+ def seed_mixin_tables(tables)
1820
+ tables[:discovered_includes] = @project_discovered_includes unless @project_discovered_includes.empty?
1821
+ tables[:discovered_extends] = @project_discovered_extends unless @project_discovered_extends.empty?
1822
+ end
1823
+
1707
1824
  # ADR-46 slice 1 / issue #644 — the two SOURCE-ATTRIBUTION tables, read only by the recording accessors
1708
1825
  # (`Scope#record_class_dependency` for the class-declaration map, `Scope#record_constant_dependency`
1709
1826
  # for the constant-write map). A normal run carries neither. Extracted to keep
@@ -1743,12 +1860,6 @@ module Rigor
1743
1860
  tables[:published_constant_names] = names unless names.empty?
1744
1861
  end
1745
1862
 
1746
- # Issue #644 — the seeded `in_source_constants` table, joining the two cross-file constant producers.
1747
- # The whole-project literal table wins over a `pre_eval:` publication of the same name: both describe
1748
- # the same assignment, the literal table pins the value the write actually carries while ADR-17's
1749
- # widener erases it to the class, and listing a file under `pre_eval:` must never LOSE precision. The
1750
- # per-file table still wins over both (`ScopeIndexer.index`'s merge) — same-file stays the most
1751
- # specific authority.
1752
1863
  # Issue #644 — the LAST SEGMENTS of the published table, the run-wide half of
1753
1864
  # `Scope#published_constant?`. Seeded on EVERY run (unlike `constant_sources`, which only a recording
1754
1865
  # run needs) because {CheckRules::PublishedConstantGuard} asks the question on every `if` / `unless`.
@@ -1757,11 +1868,57 @@ module Rigor
1757
1868
  @project_constant_values.keys.to_set { |name| name.split("::").last }.freeze
1758
1869
  end
1759
1870
 
1871
+ # Issue #644 — the seeded `in_source_constants` table, joining the two cross-file constant producers.
1872
+ # The whole-project literal table wins over a `pre_eval:` publication of the same name: both describe
1873
+ # the same assignment, the literal table pins the value the write actually carries while ADR-17's
1874
+ # widener erases it to the class, and listing a file under `pre_eval:` must never LOSE precision. The
1875
+ # per-file table still wins over both (`ScopeIndexer.index`'s merge) — same-file stays the most
1876
+ # specific authority.
1760
1877
  def merged_seed_constants
1761
- return @project_constant_values if @project_pre_eval_constants.empty?
1762
- return @project_pre_eval_constants if @project_constant_values.empty?
1878
+ pre_eval = publishable_pre_eval_constants
1879
+ return @project_constant_values if pre_eval.empty?
1880
+ return pre_eval if @project_constant_values.empty?
1881
+
1882
+ pre_eval.merge(@project_constant_values).freeze
1883
+ end
1884
+
1885
+ # Issue #663 — the `pre_eval:` publication with every name an UNLISTED file also assigns retracted.
1886
+ # Without this a listed `X = :a` answered `Symbol` at every reader while a sibling `X = "str"` decided
1887
+ # the runtime value half the time: a confident wrong type, not lost precision, and the one asymmetry
1888
+ # #644's precedence work left standing. The retraction costs the opt-in nothing where it earns its
1889
+ # keep — a name only listed files write is untouched, and where the literal table also publishes it
1890
+ # already won (above).
1891
+ #
1892
+ # Conflict is read off #644's write attribution, which censuses every assigning form, so a name the
1893
+ # listed file's own typed walk widened is retracted by an unlisted `X += 1` or `A, X = 1, 2` as
1894
+ # readily as by a plain reassignment. Among LISTED files the rule is unchanged: `PreEvalConstants`
1895
+ # widens first and drops only on disagreement, so two listed files writing `1` and `2` still publish
1896
+ # `Integer` rather than being retracted for being two.
1897
+ def publishable_pre_eval_constants
1898
+ return @project_pre_eval_constants if @project_pre_eval_constants.empty?
1899
+
1900
+ listed = pre_eval_path_set
1901
+ kept = @project_pre_eval_constants.reject { |name, _| unlisted_constant_writer?(name, listed) }
1902
+ kept.size == @project_pre_eval_constants.size ? @project_pre_eval_constants : kept.freeze
1903
+ end
1904
+
1905
+ # True when some file outside `listed` assigns `name`. The wildcard key is consulted alongside the
1906
+ # name's own attribution ([#668](https://github.com/rigortype/rigor/issues/668)): a `k::X = 7` names
1907
+ # no target, and a listed file kept out of `paths:` (ADR-17 WD5 permits it) contributes no censused
1908
+ # name of its own for the wildcard's paths to have been folded into.
1909
+ def unlisted_constant_writer?(name, listed)
1910
+ segment = name.split("::").last
1911
+ writers = @project_constant_sources[name]
1912
+ wildcard = @project_constant_sources["#{Inference::ScopeIndexer::DYNAMIC_TARGET_PREFIX}#{segment}"]
1913
+ [writers, wildcard].any? do |paths|
1914
+ paths&.any? { |path| !listed.include?(File.expand_path(path)) }
1915
+ end
1916
+ end
1763
1917
 
1764
- @project_pre_eval_constants.merge(@project_constant_values).freeze
1918
+ # The listed `pre_eval:` files, expanded so a census path and a configured one compare as the same
1919
+ # file whichever spelling each arrived in.
1920
+ def pre_eval_path_set
1921
+ @pre_eval_path_set ||= @configuration.pre_eval.to_set { |path| File.expand_path(path) }
1765
1922
  end
1766
1923
 
1767
1924
  # ADR-46 — seed the instance + singleton `"path:line"` def-source tables (each only when non-empty).
@@ -1876,7 +2033,7 @@ module Rigor
1876
2033
  path: path,
1877
2034
  line: 1,
1878
2035
  column: 1,
1879
- message: "internal analyzer error: #{e.class}: #{e.message}",
2036
+ message: CrashSignature.check_rule_message(e),
1880
2037
  severity: :error
1881
2038
  )
1882
2039
  ]