rigortype 0.3.9 → 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 (363) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/data/core_overlay/enumerable.rbs +51 -0
  4. data/data/core_overlay/enumerator.rbs +84 -0
  5. data/data/core_overlay/hash_rbs3.rbs +41 -0
  6. data/data/core_overlay/process.rbs +40 -0
  7. data/data/core_overlay/string_io.rbs +33 -0
  8. data/data/effects/core.yml +3 -3
  9. data/data/gem_overlay/activesupport/core_ext.rbs +127 -4
  10. data/docs/handbook/03-narrowing.md +95 -10
  11. data/docs/handbook/04-tuples-and-shapes.md +8 -6
  12. data/docs/handbook/07-rbs-and-extended.md +16 -11
  13. data/docs/handbook/10-sorbet.md +9 -10
  14. data/docs/handbook/11-sig-gen.md +454 -13
  15. data/docs/manual/02-cli-reference.md +63 -2
  16. data/docs/manual/03-configuration.md +7 -0
  17. data/docs/manual/04-diagnostics.md +4 -0
  18. data/docs/manual/07-plugins.md +1 -1
  19. data/docs/manual/10-mcp-server.md +3 -2
  20. data/docs/manual/16-rbs-extended-annotations.md +40 -7
  21. data/docs/manual/19-effect-labels.md +10 -0
  22. data/docs/manual/plugins/README.md +7 -0
  23. data/docs/manual/plugins/rigor-actioncable.md +8 -1
  24. data/docs/manual/plugins/rigor-actionmailer.md +7 -0
  25. data/docs/manual/plugins/rigor-actionpack.md +243 -0
  26. data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
  27. data/docs/manual/plugins/rigor-activejob.md +7 -0
  28. data/docs/manual/plugins/rigor-activerecord.md +110 -4
  29. data/docs/manual/plugins/rigor-activestorage.md +8 -1
  30. data/docs/manual/plugins/rigor-activesupport-core-ext.md +11 -5
  31. data/docs/manual/plugins/rigor-grape.md +106 -0
  32. data/docs/manual/plugins/rigor-graphql.md +23 -2
  33. data/docs/manual/plugins/rigor-pundit.md +8 -1
  34. data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
  35. data/docs/manual/plugins/rigor-rails-routes.md +7 -0
  36. data/docs/manual/plugins/rigor-rbs-inline.md +212 -25
  37. data/docs/manual/plugins/rigor-sidekiq.md +8 -1
  38. data/docs/manual/plugins/rigor-sorbet.md +21 -5
  39. data/exe/rigor +19 -4
  40. data/lib/rigor/analysis/baseline.rb +1 -1
  41. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
  42. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
  43. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -3
  44. data/lib/rigor/analysis/check_rules/rule_ids.rb +20 -5
  45. data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
  46. data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
  47. data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
  48. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +36 -2
  49. data/lib/rigor/analysis/check_rules.rb +520 -49
  50. data/lib/rigor/analysis/dependency_recorder.rb +23 -0
  51. data/lib/rigor/analysis/fact_store.rb +9 -0
  52. data/lib/rigor/analysis/incremental.rb +26 -0
  53. data/lib/rigor/analysis/incremental_session.rb +36 -4
  54. data/lib/rigor/analysis/project_scan.rb +11 -1
  55. data/lib/rigor/analysis/reachability/plugin_roots.rb +0 -1
  56. data/lib/rigor/analysis/reachability/scan_cache.rb +1 -0
  57. data/lib/rigor/analysis/rule_catalog.rb +127 -0
  58. data/lib/rigor/analysis/run_cache_key.rb +20 -11
  59. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +167 -11
  60. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
  61. data/lib/rigor/analysis/runner/pool_coordinator.rb +107 -15
  62. data/lib/rigor/analysis/runner/project_pre_passes.rb +26 -7
  63. data/lib/rigor/analysis/runner.rb +278 -20
  64. data/lib/rigor/analysis/template_unit_collector.rb +303 -0
  65. data/lib/rigor/analysis/template_unit_paths.rb +91 -0
  66. data/lib/rigor/analysis/template_unit_positions.rb +293 -0
  67. data/lib/rigor/analysis/template_units.rb +399 -0
  68. data/lib/rigor/analysis/worker_session.rb +53 -10
  69. data/lib/rigor/bleeding_edge.rb +0 -2
  70. data/lib/rigor/builtins/hkt_builtins.rb +1 -0
  71. data/lib/rigor/builtins/imported_refinements.rb +4 -0
  72. data/lib/rigor/builtins/regex_refinement.rb +17 -9
  73. data/lib/rigor/builtins/static_return_refinements.rb +2 -0
  74. data/lib/rigor/cache/descriptor.rb +6 -1
  75. data/lib/rigor/cache/engine_source.rb +29 -1
  76. data/lib/rigor/cache/incremental_snapshot.rb +33 -1
  77. data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
  78. data/lib/rigor/cache/rbs_descriptor.rb +37 -7
  79. data/lib/rigor/cache/store.rb +0 -1
  80. data/lib/rigor/ci_detector.rb +1 -0
  81. data/lib/rigor/cli/doc_links.rb +1 -1
  82. data/lib/rigor/cli/docs_command.rb +4 -4
  83. data/lib/rigor/cli/plugin_command.rb +3 -3
  84. data/lib/rigor/cli/prism_colorizer.rb +0 -1
  85. data/lib/rigor/cli/sig_gen_command.rb +210 -29
  86. data/lib/rigor/cli/skill_command.rb +1 -1
  87. data/lib/rigor/cli/skill_describe.rb +0 -1
  88. data/lib/rigor/cli/type_of_command.rb +19 -5
  89. data/lib/rigor/cli/type_of_renderer.rb +20 -9
  90. data/lib/rigor/cli/type_of_template_probe.rb +189 -0
  91. data/lib/rigor/cli.rb +21 -3
  92. data/lib/rigor/configuration/severity_profile.rb +19 -3
  93. data/lib/rigor/configuration.rb +66 -3
  94. data/lib/rigor/effects/ancestry_recorder.rb +191 -0
  95. data/lib/rigor/effects/attribution.rb +11 -2
  96. data/lib/rigor/effects/callee_rule.rb +368 -0
  97. data/lib/rigor/effects/catalog.rb +7 -4
  98. data/lib/rigor/effects/collector.rb +11 -5
  99. data/lib/rigor/effects/config_envelopes.rb +9 -2
  100. data/lib/rigor/effects/definition_context.rb +179 -0
  101. data/lib/rigor/effects/effect_table.rb +11 -3
  102. data/lib/rigor/effects/envelope_check.rb +1 -1
  103. data/lib/rigor/effects/envelope_index.rb +15 -0
  104. data/lib/rigor/effects/file_collection.rb +59 -5
  105. data/lib/rigor/effects/framework_units.rb +1 -1
  106. data/lib/rigor/effects/identity.rb +16 -0
  107. data/lib/rigor/effects/local_ownership.rb +38 -12
  108. data/lib/rigor/effects/method_key.rb +21 -0
  109. data/lib/rigor/effects/mutation_classifier.rb +23 -12
  110. data/lib/rigor/effects/plugin_facts.rb +43 -31
  111. data/lib/rigor/effects/propagator.rb +295 -16
  112. data/lib/rigor/effects/registry.rb +1 -1
  113. data/lib/rigor/effects/scanner.rb +121 -72
  114. data/lib/rigor/effects/signature_sources.rb +1 -1
  115. data/lib/rigor/effects/snapshot.rb +2 -1
  116. data/lib/rigor/effects/summary.rb +27 -4
  117. data/lib/rigor/effects/unit_scan.rb +385 -30
  118. data/lib/rigor/effects/visibility.rb +101 -0
  119. data/lib/rigor/environment/lockfile_resolver.rb +17 -0
  120. data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
  121. data/lib/rigor/environment/member_consistency.rb +298 -0
  122. data/lib/rigor/environment/rbs_loader.rb +399 -133
  123. data/lib/rigor/environment.rb +103 -38
  124. data/lib/rigor/hashing/xxh3.rb +264 -0
  125. data/lib/rigor/inference/acceptance.rb +139 -9
  126. data/lib/rigor/inference/block_auto_splat.rb +216 -0
  127. data/lib/rigor/inference/block_call_timing.rb +338 -0
  128. data/lib/rigor/inference/block_parameter_binder.rb +73 -27
  129. data/lib/rigor/inference/block_repetition.rb +71 -0
  130. data/lib/rigor/inference/body_fixpoint.rb +2 -1
  131. data/lib/rigor/inference/budget_trace.rb +2 -1
  132. data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
  133. data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
  134. data/lib/rigor/inference/captured_locals.rb +387 -15
  135. data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
  136. data/lib/rigor/inference/content_join.rb +200 -27
  137. data/lib/rigor/inference/def_return_typer.rb +11 -7
  138. data/lib/rigor/inference/define_method_block_self.rb +64 -0
  139. data/lib/rigor/inference/element_read_widening.rb +22 -10
  140. data/lib/rigor/inference/error_info.rb +196 -0
  141. data/lib/rigor/inference/expression_typer.rb +1532 -496
  142. data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
  143. data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
  144. data/lib/rigor/inference/global_write_census.rb +239 -0
  145. data/lib/rigor/inference/guard_rebinding.rb +447 -0
  146. data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
  147. data/lib/rigor/inference/index_write_widening.rb +16 -3
  148. data/lib/rigor/inference/indexed_narrowing.rb +61 -9
  149. data/lib/rigor/inference/jump_targets.rb +82 -0
  150. data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
  151. data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
  152. data/lib/rigor/inference/last_line.rb +340 -0
  153. data/lib/rigor/inference/last_status.rb +144 -0
  154. data/lib/rigor/inference/macro_block_self_type.rb +167 -17
  155. data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
  156. data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
  157. data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
  158. data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
  159. data/lib/rigor/inference/match_rebinding.rb +392 -0
  160. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  161. data/lib/rigor/inference/method_dispatcher/block_folding.rb +85 -24
  162. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
  163. data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
  164. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +4 -0
  165. data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
  166. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +113 -73
  167. data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
  168. data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
  169. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +881 -38
  170. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
  171. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +128 -53
  172. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +1 -1
  173. data/lib/rigor/inference/method_dispatcher.rb +214 -13
  174. data/lib/rigor/inference/method_parameter_binder.rb +8 -3
  175. data/lib/rigor/inference/multi_target_binder.rb +340 -52
  176. data/lib/rigor/inference/mutation_rejoin.rb +4 -1
  177. data/lib/rigor/inference/mutation_widening.rb +70 -48
  178. data/lib/rigor/inference/narrowing.rb +540 -120
  179. data/lib/rigor/inference/operand_effects.rb +167 -0
  180. data/lib/rigor/inference/operand_walk.rb +88 -0
  181. data/lib/rigor/inference/optimistic_origin.rb +152 -9
  182. data/lib/rigor/inference/parameter_inference_collector.rb +3 -2
  183. data/lib/rigor/inference/project_method_ownership.rb +136 -0
  184. data/lib/rigor/inference/project_patched_methods.rb +7 -2
  185. data/lib/rigor/inference/project_patched_scanner.rb +7 -3
  186. data/lib/rigor/inference/receiver_alias.rb +90 -1
  187. data/lib/rigor/inference/receiver_blind_block.rb +219 -0
  188. data/lib/rigor/inference/refinement_mutation.rb +15 -11
  189. data/lib/rigor/inference/repeated_or_writes.rb +463 -0
  190. data/lib/rigor/inference/return_barrier.rb +54 -0
  191. data/lib/rigor/inference/rewrite_mutation.rb +120 -0
  192. data/lib/rigor/inference/scope_indexer.rb +4615 -551
  193. data/lib/rigor/inference/statement_evaluator.rb +3176 -490
  194. data/lib/rigor/inference/stored_block_call.rb +54 -0
  195. data/lib/rigor/inference/string_mutation.rb +44 -7
  196. data/lib/rigor/inference/unknown_store_widening.rb +200 -0
  197. data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
  198. data/lib/rigor/language_server/debouncer.rb +0 -1
  199. data/lib/rigor/language_server/diagnostic_publisher.rb +3 -2
  200. data/lib/rigor/language_server/hover_renderer.rb +3 -3
  201. data/lib/rigor/language_server/project_context.rb +5 -3
  202. data/lib/rigor/mcp/server.rb +2 -1
  203. data/lib/rigor/plugin/base.rb +168 -5
  204. data/lib/rigor/plugin/box_probe.rb +91 -0
  205. data/lib/rigor/plugin/bundled_catalog.rb +1 -1
  206. data/lib/rigor/plugin/effect_attribution.rb +58 -4
  207. data/lib/rigor/plugin/loader.rb +2 -1
  208. data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
  209. data/lib/rigor/plugin/manifest.rb +71 -10
  210. data/lib/rigor/plugin/registry.rb +35 -1
  211. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
  212. data/lib/rigor/plugin/template_unit.rb +196 -0
  213. data/lib/rigor/plugin.rb +1 -0
  214. data/lib/rigor/protection/discovery_seed.rb +3 -1
  215. data/lib/rigor/protection/kill_signature.rb +0 -1
  216. data/lib/rigor/protection/mutation_cache.rb +1 -2
  217. data/lib/rigor/rbs_extended.rb +27 -0
  218. data/lib/rigor/reflection/constant_ancestors.rb +97 -0
  219. data/lib/rigor/reflection/constant_path.rb +19 -6
  220. data/lib/rigor/reflection.rb +72 -105
  221. data/lib/rigor/scope/discovery_index.rb +135 -2
  222. data/lib/rigor/scope.rb +859 -49
  223. data/lib/rigor/sig_gen/alias_index.rb +289 -0
  224. data/lib/rigor/sig_gen/classification.rb +22 -5
  225. data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
  226. data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
  227. data/lib/rigor/sig_gen/generator.rb +558 -31
  228. data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
  229. data/lib/rigor/sig_gen/method_candidate.rb +47 -3
  230. data/lib/rigor/sig_gen/observation_collector.rb +1 -1
  231. data/lib/rigor/sig_gen/renderer.rb +124 -9
  232. data/lib/rigor/sig_gen/skip_reason_catalog.rb +44 -1
  233. data/lib/rigor/sig_gen/write_result.rb +19 -3
  234. data/lib/rigor/sig_gen/writer.rb +166 -35
  235. data/lib/rigor/sig_gen.rb +3 -0
  236. data/lib/rigor/signature_path_audit.rb +1 -1
  237. data/lib/rigor/source/node_walker.rb +0 -3
  238. data/lib/rigor/source/parameter_envelope.rb +72 -0
  239. data/lib/rigor/source.rb +1 -0
  240. data/lib/rigor/type/combinator.rb +88 -12
  241. data/lib/rigor/type/difference.rb +1 -0
  242. data/lib/rigor/type/hash_shape.rb +1 -1
  243. data/lib/rigor/type/refined.rb +1 -0
  244. data/lib/rigor/version.rb +1 -1
  245. data/lib/rigor.rb +1 -0
  246. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
  247. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +9 -5
  248. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -4
  249. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +0 -1
  250. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
  251. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
  252. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
  253. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
  254. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
  255. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
  256. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +239 -4
  257. data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
  258. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
  259. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
  260. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
  261. data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
  262. data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
  263. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -0
  264. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +9 -5
  265. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
  266. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
  267. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +380 -3
  268. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
  269. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +281 -35
  270. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
  271. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +25 -14
  272. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
  273. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +171 -5
  274. data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +2 -0
  275. data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
  276. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
  277. data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
  278. data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
  279. data/plugins/rigor-grape/sig/grape.rbs +263 -0
  280. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +72 -2
  281. data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
  282. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -0
  283. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +11 -7
  284. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +35 -37
  285. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -1
  286. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -0
  287. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +1 -0
  288. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +9 -17
  289. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
  290. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +217 -36
  291. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -0
  292. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +9 -5
  293. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +0 -1
  294. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +46 -1
  295. data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
  296. data/sig/rigor/analysis/baseline.rbs +69 -7
  297. data/sig/rigor/analysis/fact_store.rbs +1 -1
  298. data/sig/rigor/analysis/project_scan.rbs +74 -0
  299. data/sig/rigor/effects/config_envelopes.rbs +100 -0
  300. data/sig/rigor/effects/effect_table.rbs +60 -0
  301. data/sig/rigor/effects/envelope.rbs +97 -0
  302. data/sig/rigor/effects/envelope_index.rbs +33 -0
  303. data/sig/rigor/effects/file_collection.rbs +81 -0
  304. data/sig/rigor/effects/label.rbs +26 -0
  305. data/sig/rigor/effects/label_set.rbs +46 -0
  306. data/sig/rigor/effects/method_key.rbs +24 -0
  307. data/sig/rigor/effects/origin.rbs +50 -0
  308. data/sig/rigor/effects/plugin_facts.rbs +141 -0
  309. data/sig/rigor/effects/registry.rbs +68 -0
  310. data/sig/rigor/effects/summary.rbs +50 -0
  311. data/sig/rigor/effects/taint_cause.rbs +12 -0
  312. data/sig/rigor/environment.rbs +12 -10
  313. data/sig/rigor/inference/optimistic_origin.rbs +10 -0
  314. data/sig/rigor/inference.rbs +6 -4
  315. data/sig/rigor/plugin/additional_initializer.rbs +36 -0
  316. data/sig/rigor/plugin/base.rbs +30 -7
  317. data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
  318. data/sig/rigor/plugin/effect_attribution.rbs +66 -0
  319. data/sig/rigor/plugin/effect_edge.rbs +33 -0
  320. data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
  321. data/sig/rigor/plugin/io_boundary.rbs +1 -1
  322. data/sig/rigor/plugin/loader.rbs +3 -3
  323. data/sig/rigor/plugin/manifest.rbs +50 -11
  324. data/sig/rigor/plugin/protocol_contract.rbs +68 -0
  325. data/sig/rigor/plugin/registry.rbs +62 -1
  326. data/sig/rigor/plugin.rbs +1 -1
  327. data/sig/rigor/rbs_extended.rbs +1 -1
  328. data/sig/rigor/reflection.rbs +7 -6
  329. data/sig/rigor/scope.rbs +135 -20
  330. data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -7
  331. data/sig/rigor/source.rbs +4 -4
  332. data/sig/rigor/testing.rbs +10 -4
  333. data/sig/rigor/type.rbs +6 -0
  334. data/sig/rigor.rbs +38 -20
  335. data/skills/rigor-ask/SKILL.md +8 -5
  336. data/skills/rigor-baseline-reduce/SKILL.md +4 -6
  337. data/skills/rigor-ci-setup/SKILL.md +17 -21
  338. data/skills/rigor-doctor/SKILL.md +24 -22
  339. data/skills/rigor-doctor/references/01-checks.md +97 -33
  340. data/skills/rigor-editor-setup/SKILL.md +6 -4
  341. data/skills/rigor-mcp-setup/SKILL.md +5 -4
  342. data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
  343. data/skills/rigor-next-steps/SKILL.md +4 -2
  344. data/skills/rigor-plugin-author/SKILL.md +19 -23
  345. data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
  346. data/skills/rigor-plugin-author/references/02-walker-and-types.md +12 -25
  347. data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
  348. data/skills/rigor-plugin-review/SKILL.md +6 -4
  349. data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
  350. data/skills/rigor-plugin-tune/SKILL.md +4 -2
  351. data/skills/rigor-project-init/SKILL.md +9 -7
  352. data/skills/rigor-project-init/references/01-detect.md +13 -9
  353. data/skills/rigor-project-init/references/02-configure.md +33 -8
  354. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
  355. data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
  356. data/skills/rigor-protection-uplift/SKILL.md +4 -6
  357. data/skills/rigor-rbs-setup/SKILL.md +4 -2
  358. data/skills/rigor-type-oracle/SKILL.md +4 -6
  359. data/skills/rigor-type-oracle/references/01-oracle-commands.md +12 -8
  360. data/skills/rigor-type-oracle/references/03-gap-protocol.md +0 -3
  361. data/skills/rigor-unused-adjudicate/SKILL.md +4 -2
  362. data/skills/rigor-upgrade/SKILL.md +13 -8
  363. metadata +108 -1
@@ -0,0 +1,485 @@
1
+ # graphql-ruby class-level DSL surface — bundled by rigor-graphql via
2
+ # the manifest's `signature_paths:` (ADR-25).
3
+ #
4
+ # graphql-ruby ships no RBS of its own, so without this file every
5
+ # `field`/`argument`/`value`/`implements`/`description` call inside a
6
+ # `GraphQL::Schema::*` subclass reads Dynamic[top] — ~5k implicit-self
7
+ # sites on the GitLab checkout alone
8
+ # (docs/notes/20260919-survey-typing-holes.md).
9
+ #
10
+ # Return types follow graphql-ruby's real implementations: `field`
11
+ # returns the created `Schema::Field`, `argument` the created
12
+ # `Schema::Argument`, `Enum.value` the `Schema::EnumValue`, the
13
+ # registration macros on `Schema` return the collection or object they
14
+ # registered, and the setter/getter pairs (`description`,
15
+ # `graphql_name`, `type`, `null`, …) return their configured value.
16
+ # `void` is deliberately avoided — these expressions are ordinary API
17
+ # (a stored `field` result is a real `Field`), and the engine recovers
18
+ # a `void` return as `top`, which would keep every DSL call in the
19
+ # Dynamic census this signature exists to drain.
20
+ #
21
+ # The ancestry follows graphql-ruby's real hierarchy:
22
+ # `Mutation`/`Subscription`/`RelayClassicMutation < Resolver`,
23
+ # `Object`/`InputObject`/`Enum`/`Union`/`Scalar`/`EnumValue <
24
+ # Member`, and `Field`/`Argument` standalone (each `include`s
25
+ # `Member::HasArguments` for their own `argument` DSL). The shared
26
+ # class-level DSL lives on the `Member::*` mixin modules exactly as
27
+ # graphql-ruby arranges it, so user subclasses pick it up by RBS
28
+ # ancestry rather than per-class duplication.
29
+ #
30
+ # Deferred, deliberately:
31
+ #
32
+ # - `include GraphQL::Schema::Interface` — graphql-ruby wires the DSL
33
+ # onto the includer through `Interface.included` →
34
+ # `base.extend(DefinitionMethods)`, an include-time effect RBS
35
+ # cannot express, so `field` inside interface modules stays opaque.
36
+ # `Interface::DefinitionMethods` is still declared so an `extend`
37
+ # of it resolves.
38
+ # - `field ... do ... end` / `argument ... do ... end` block bodies —
39
+ # graphql-ruby `instance_eval`s a zero-arity definition block on the
40
+ # new Field/Argument, and no engine mechanism currently rebinds a
41
+ # block's `self` to a declared block `self:` type. The one-parameter
42
+ # form (`field(:x) { |f| f.argument }`) types through the declared
43
+ # block parameter.
44
+ # - `Schema.execute` / `Schema.multiplex` result typing and
45
+ # resolver-method return checking — tracked by issue #136; they are
46
+ # declared `-> untyped` here so dispatch resolves, but the honestly
47
+ # opaque return is unchanged.
48
+
49
+ module GraphQL
50
+ class Error < ::StandardError
51
+ end
52
+
53
+ class ExecutionError < GraphQL::Error
54
+ def initialize: (untyped message, **untyped opts) -> void
55
+ def extensions: () -> ::Hash[untyped, untyped]
56
+ def to_h: () -> ::Hash[untyped, untyped]
57
+ end
58
+
59
+ class AnalysisError < GraphQL::Error
60
+ end
61
+
62
+ class RequiredImplementationMissingError < GraphQL::Error
63
+ end
64
+
65
+ class UnauthorizedError < GraphQL::Error
66
+ end
67
+
68
+ class Dataloader
69
+ def with: (untyped source_class, *untyped batch_args) -> untyped
70
+ def load: (untyped source_class, untyped id) -> untyped
71
+ def load_all: (untyped source_class, untyped ids) -> ::Array[untyped]
72
+ def append: (untyped job) -> untyped
73
+ def run: () -> untyped
74
+ def run_all: () -> untyped
75
+ def merge!: (untyped) -> untyped
76
+
77
+ class Source
78
+ def load: (untyped input) -> untyped
79
+ def fetch: (untyped inputs) -> ::Array[untyped]
80
+ def result_key_for: (untyped input) -> untyped
81
+ end
82
+ end
83
+
84
+ class Query
85
+ def context: () -> GraphQL::Query::Context
86
+
87
+ class Context
88
+ def []: (untyped key) -> untyped
89
+ def []=: (untyped key, untyped value) -> untyped
90
+ def fetch: (untyped key, ?untyped default) -> untyped
91
+ def key?: (untyped key) -> bool
92
+ def dig: (untyped key, *untyped rest) -> untyped
93
+ def namespace: (untyped ns) -> untyped
94
+ def scoped_set!: (untyped key, untyped value) -> untyped
95
+ def to_h: () -> ::Hash[untyped, untyped]
96
+ def schema: () -> singleton(GraphQL::Schema)
97
+ def query: () -> GraphQL::Query
98
+ def object: () -> untyped
99
+ def dataloader: () -> GraphQL::Dataloader
100
+ def errors: () -> ::Array[untyped]
101
+ def add_error: (untyped error) -> untyped
102
+ end
103
+ end
104
+
105
+ class Schema
106
+ # Schema-class registration DSL (`class AppSchema < GraphQL::Schema`).
107
+ # Setter/getter pairs return the configured value; the `<<`-style
108
+ # registrars return the collection they pushed onto.
109
+ def self.use: (untyped plugin, **untyped kwargs) -> ::Array[untyped]
110
+ def self.plugins: () -> ::Array[untyped]
111
+ def self.query: (?Class? new_query_object) ?{ () -> untyped } -> Class?
112
+ def self.mutation: (?Class? new_mutation_object) ?{ () -> untyped } -> Class?
113
+ def self.subscription: (?Class? new_subscription_object) ?{ () -> untyped } -> Class?
114
+ # Runtime returns the stored Class under a visibility profile, otherwise the
115
+ # `add_type_and_traverse` result — the union is untyped (schema.rb `directive`).
116
+ def self.directive: (untyped new_directive) -> untyped
117
+ def self.directives: (*untyped) -> ::Hash[untyped, untyped]
118
+ def self.orphan_types: (*untyped new_orphan_types) -> ::Array[untyped]
119
+ # `mode:` as an Array re-enters once per element and returns that Array; the
120
+ # default path ends in `add_trace_options_for`, which returns nil.
121
+ def self.trace_with: (untyped trace_mod, **untyped options) -> ::Array[untyped]?
122
+ def self.instrument: (untyped instrument_step, untyped instrumenter, ?untyped options) -> ::Array[untyped]
123
+ # Returns `err_classes.each`'s receiver — the exception-class Array, not the
124
+ # handlers Hash (schema.rb `rescue_from`).
125
+ def self.rescue_from: (*untyped err_classes) ?{ (untyped err) -> void } -> ::Array[untyped]
126
+ def self.error_handlers: () -> ::Hash[untyped, untyped]
127
+ def self.max_depth: (?Integer? new_max_depth, ?count_introspection_fields: bool) -> Integer?
128
+ def self.max_complexity: (?untyped new_max_complexity, ?count_introspection_fields: bool) -> untyped
129
+ def self.max_query_string_tokens: (?Integer? new_max_tokens) -> Integer?
130
+ def self.validate_max_errors: (?Integer? new_validate_max_errors) -> Integer?
131
+
132
+ def self.error_bubbling: (?bool? new_error_bubbling) -> bool
133
+ def self.default_max_page_size: (?Integer? new_default_max_page_size) -> Integer?
134
+ def self.default_page_size: (?Integer? new_default_page_size) -> Integer?
135
+
136
+ def self.cursor_encoder: (?Class? new_encoder) -> Class?
137
+ def self.context_class: (?Class? new_context_class) -> Class?
138
+ def self.connections: (?untyped new_connections) -> untyped
139
+ def self.introspection: (?Module? new_introspection_namespace) -> Module?
140
+ def self.query_analyzer: (untyped new_analyzer) -> ::Array[untyped]
141
+ def self.multiplex_analyzer: (untyped new_analyzer) -> ::Array[untyped]
142
+ def self.query_analyzers: () -> ::Array[untyped]
143
+ def self.multiplex_analyzers: () -> ::Array[untyped]
144
+ def self.visible?: (untyped member, untyped ctx) -> bool
145
+ def self.id_from_object: (untyped application_object, untyped graphql_type, untyped context) -> String?
146
+ def self.object_from_id: (untyped object_id, untyped context) -> untyped
147
+ def self.resolve_type: (untyped abstract_type, untyped obj, untyped ctx) -> untyped
148
+ def self.type_error: (untyped type_error, untyped ctx) -> untyped
149
+ def self.types: (?untyped context) -> ::Hash[untyped, untyped]
150
+ def self.execute: (*untyped) -> untyped
151
+ def self.multiplex: (*untyped) -> untyped
152
+
153
+ # Shared class-level DSL inherited by every schema member class.
154
+ class Member
155
+ module BaseDSLMethods
156
+ def description: (?String? new_description) -> String?
157
+ def graphql_name: (?String? new_name) -> String
158
+ def comment: (?String? new_comment) -> String?
159
+ def introspection: (?untyped new_introspection) -> bool
160
+ def introspection?: () -> bool
161
+ def visible?: (untyped context) -> bool
162
+ def authorized?: (untyped object, untyped context) -> bool
163
+ def mutation: (?Class? mutation_class) -> Class?
164
+ def to_graphql: () -> untyped
165
+ def kind: () -> untyped
166
+ def path: () -> String
167
+ end
168
+ extend BaseDSLMethods
169
+
170
+ module HasArguments
171
+ def argument: ( ?untyped arg_name
172
+ , ?untyped type_expr
173
+ , ?untyped desc
174
+ , **untyped kwargs
175
+ ) ?{ (GraphQL::Schema::Argument) -> void } -> GraphQL::Schema::Argument
176
+ def add_argument: (GraphQL::Schema::Argument arg_defn) -> GraphQL::Schema::Argument
177
+ def arguments: (?untyped context) -> ::Hash[untyped, untyped]
178
+ def get_argument: (untyped argument_name, ?untyped context) -> GraphQL::Schema::Argument?
179
+ def any_arguments?: () -> bool
180
+ def all_argument_definitions: () -> ::Array[untyped]
181
+ def argument_class: (?Class? new_arg_class) -> Class
182
+ end
183
+
184
+ module HasFields
185
+ def field: ( ?untyped name_positional
186
+ , ?untyped type_positional
187
+ , ?untyped desc_positional
188
+ , **untyped kwargs
189
+ ) ?{ (GraphQL::Schema::Field) -> void } -> GraphQL::Schema::Field
190
+ # Runtime ends in a bare `nil` after mutating `own_fields` (has_fields.rb).
191
+ def add_field: (GraphQL::Schema::Field field_defn, **untyped kwargs) -> nil
192
+ def field_class: (?Class? new_field_class) -> Class
193
+ # Ends in `define_singleton_method(field_name)`, which returns the Symbol.
194
+ def global_id_field: (untyped field_name, **untyped kwargs) -> ::Symbol
195
+ def fields: (?untyped context) -> ::Hash[untyped, untyped]
196
+ def get_field: (untyped field_name, ?untyped context) -> GraphQL::Schema::Field?
197
+ end
198
+
199
+ module HasInterfaces
200
+ def implements: (*untyped new_interfaces, **untyped options) -> ::Array[untyped]
201
+ def interfaces: (?untyped context) -> ::Array[untyped]
202
+ def interface_type_memberships: () -> ::Array[untyped]
203
+ end
204
+
205
+ module RelayShortcuts
206
+ def connection_type: () -> Class
207
+ def edge_type: () -> Class
208
+ def connection_type_class: (?Class? new_connection_type_class) -> Class
209
+ def edge_type_class: (?Class? new_edge_type_class) -> Class
210
+ def node_field: (**untyped kwargs) -> GraphQL::Schema::Field
211
+ def nodes_field: (**untyped kwargs) -> GraphQL::Schema::Field
212
+ end
213
+ extend RelayShortcuts
214
+
215
+ module TypeSystemHelpers
216
+ def to_graphql: () -> untyped
217
+ end
218
+ end
219
+
220
+ class Object < Member
221
+ extend Member::HasFields
222
+ extend Member::HasInterfaces
223
+ attr_reader object: untyped
224
+ attr_reader context: GraphQL::Query::Context
225
+ end
226
+
227
+ class Resolver
228
+ module HasPayloadType
229
+ def payload_type: (?Class? new_payload_type) -> Class
230
+ def type: ( ?untyped new_type
231
+ , ?null: bool? null
232
+ ) -> untyped
233
+ def type_expr: () -> untyped
234
+ def null: (?bool? allow_null) -> bool
235
+ def field_class: (?Class? new_class) -> Class
236
+ def object_class: (?Class? new_class) -> Class
237
+ end
238
+ extend Member::BaseDSLMethods
239
+ extend Member::HasArguments
240
+ extend HasPayloadType
241
+
242
+ def self.resolve_method: (?Symbol? new_method) -> Symbol
243
+ def self.resolver_method: (?Symbol? new_method_name) -> Symbol
244
+ def self.extras: (?::Array[untyped]? new_extras) -> ::Array[untyped]
245
+ def self.complexity: (?untyped new_complexity) -> untyped
246
+ def self.broadcastable: (untyped new_broadcastable) -> untyped
247
+ def self.broadcastable?: () -> bool?
248
+ def self.max_page_size: (?Integer? new_max_page_size) -> Integer?
249
+ def self.default_page_size: (?Integer? new_default_page_size) -> Integer?
250
+ def self.has_max_page_size?: () -> bool
251
+ def self.has_default_page_size?: () -> bool
252
+ def self.has_scope: (?untyped new_has_scope) -> bool
253
+ def self.field_arguments: (?untyped context) -> ::Hash[untyped, untyped]
254
+ def self.extension: (untyped extension, **untyped options) -> untyped
255
+ def self.extensions: () -> ::Array[untyped]
256
+ def self.authorizes?: (untyped context) -> bool
257
+
258
+ attr_reader object: untyped
259
+ attr_reader context: GraphQL::Query::Context
260
+ attr_reader field: GraphQL::Schema::Field
261
+ def arguments: () -> ::Hash[untyped, untyped]
262
+ def dataloader: () -> GraphQL::Dataloader
263
+ def resolve: (**untyped args) -> untyped
264
+ def ready?: (**untyped args) -> bool
265
+ def authorized?: (**untyped inputs) -> bool
266
+ def load_application_object: (untyped type, untyped id) -> untyped
267
+ def load_application_object_failed!: (untyped err) -> void
268
+ end
269
+
270
+ class Mutation < Resolver
271
+ extend Member::HasFields
272
+ def self.ready?: (**untyped inputs) -> bool
273
+ def self.authorized?: (**untyped inputs) -> bool
274
+ def self.load_application_object: (untyped type, untyped id) -> untyped
275
+ def object: () -> untyped
276
+ def ready?: (**untyped inputs) -> bool
277
+ def authorized?: (**untyped inputs) -> bool
278
+ end
279
+
280
+ class RelayClassicMutation < Mutation
281
+ def self.input_object_class: (?Class? new_class) -> Class
282
+ def self.return_field: () -> untyped
283
+ end
284
+
285
+ class Subscription < Resolver
286
+ extend Member::HasFields
287
+ def self.topic: (?untyped new_topic) -> untyped
288
+ def self.subscription_scope: (?untyped new_scope) -> untyped
289
+ def subscribe: (**untyped args) -> untyped
290
+ def update: (**untyped args) -> untyped
291
+ def unsubscribe: (**untyped args) -> untyped
292
+ end
293
+
294
+ class InputObject < Member
295
+ extend Member::HasArguments
296
+ def self.prepare: (*untyped) -> untyped
297
+ def self.one_of: () -> bool
298
+ def prepare: () -> untyped
299
+ def arguments: () -> untyped
300
+ end
301
+
302
+ class Union < Member
303
+ def self.possible_types: (*untyped new_types, ?context: untyped, **untyped) -> ::Array[untyped]
304
+ def self.get_possible_type: (untyped type_expr, untyped obj, untyped ctx) -> untyped
305
+ def self.resolve_type: (untyped type_expr, untyped obj, untyped ctx) -> untyped
306
+ def self.type_error: (untyped type_error, untyped ctx) -> untyped
307
+ def self.orphan_types: (*untyped new_orphan_types) -> ::Array[untyped]
308
+ end
309
+
310
+ class Enum < Member
311
+ def self.value: (*untyped args, **untyped kwargs) ?{ (EnumValue) -> void } -> EnumValue
312
+ def self.values: (?untyped context) -> ::Hash[untyped, untyped]
313
+ def self.enum_values: (?untyped context) -> ::Array[untyped]
314
+ def self.enum_value_for: (untyped value_name, ?untyped context) -> EnumValue?
315
+ def self.value_from_ruby: (untyped val, ?untyped ctx) -> untyped
316
+ end
317
+
318
+ class EnumValue < Member
319
+ def value: (?untyped new_val) -> untyped
320
+ def deprecation_reason: (?String? new_reason) -> String?
321
+ attr_reader owner: untyped
322
+ attr_reader graphql_name: String
323
+ attr_reader description: String?
324
+ end
325
+
326
+ class Scalar < Member
327
+ def self.coerce_input: (untyped val, untyped ctx) -> untyped
328
+ def self.coerce_result: (untyped val, untyped ctx) -> untyped
329
+ end
330
+
331
+ class Directive < Member
332
+ extend Member::HasArguments
333
+ def self.locations: (*untyped new_locations) -> ::Array[untyped]
334
+ def self.repeatable: (?bool? new_repeatable) -> bool
335
+ def self.on: (*untyped new_locations) -> ::Array[untyped]
336
+ # Runtime takes the runtime object, argument values, and query context
337
+ # (directive.rb `include?` delegates to `static_include?`).
338
+ def self.include?: (untyped object, untyped arguments, untyped context) -> bool
339
+ end
340
+
341
+ # `Field`/`Argument` are definition objects, not schema members —
342
+ # they `include HasArguments` so `argument` inside a definition
343
+ # block's one-parameter form (`field(:x) { |f| f.argument }`)
344
+ # resolves on the yielded object.
345
+ class Field
346
+ include Member::HasArguments
347
+ def name: () -> String
348
+ def graphql_name: () -> String
349
+ def type: () -> untyped
350
+ def description: (?String? new_description) -> String?
351
+ def deprecation_reason: (?String? new_reason) -> String?
352
+ def extension: (untyped extension, **untyped options) -> untyped
353
+ def extensions: () -> untyped
354
+ def complexity: (?untyped new_complexity) -> untyped
355
+ def scope: (?untyped new_scope) -> untyped
356
+ def null: (?bool? new_null) -> bool
357
+ def null?: () -> bool
358
+ def method_sym: () -> Symbol
359
+ def method_str: () -> String
360
+ def resolver_method: () -> Symbol
361
+ def owner: () -> untyped
362
+ def connection?: () -> bool
363
+ def max_page_size: () -> Integer?
364
+ def default_page_size: () -> Integer?
365
+ def subscription_scope: (?untyped) -> untyped
366
+ end
367
+
368
+ class Argument
369
+ def keyword: () -> Symbol
370
+ def name: () -> String
371
+ def graphql_name: () -> String
372
+ def type: () -> untyped
373
+ def description: (?String? new_description) -> String?
374
+ def deprecation_reason: (?String? new_reason) -> String?
375
+ def default_value: (?untyped new_default_value) -> untyped
376
+ def default_value?: () -> bool
377
+ def required?: () -> bool
378
+ def loads: () -> untyped
379
+ def as: () -> Symbol
380
+ def prepare: () -> untyped
381
+ def owner: () -> untyped
382
+ def validates: () -> untyped
383
+ def replace_null_with_default?: () -> bool
384
+ end
385
+
386
+ class LateBoundType
387
+ def initialize: (untyped name, ?untyped type_defn) -> void
388
+ def graphql_name: () -> String
389
+ end
390
+
391
+ # The include-time `extend DefinitionMethods` is not modelled —
392
+ # see the file header. `DefinitionMethods` is declared so code that
393
+ # extends it directly resolves the same surface class bodies get.
394
+ module Interface
395
+ module DefinitionMethods
396
+ include Member::BaseDSLMethods
397
+ include Member::HasFields
398
+ include Member::HasInterfaces
399
+ include Member::RelayShortcuts
400
+ def orphan_types: (*untyped new_orphan_types) -> ::Array[untyped]
401
+ def resolve_type: (untyped type_expr, untyped obj, untyped ctx) -> untyped
402
+ def definition_methods: (?Module? other_module) -> untyped
403
+ end
404
+ extend DefinitionMethods
405
+ end
406
+ end
407
+
408
+ # graphql-ruby's own scalar / Relay wrapper types, referenced as
409
+ # `field`/`argument` arguments (`field :x, GraphQL::Types::ISO8601DateTime`).
410
+ module Types
411
+ class Boolean < Schema::Scalar
412
+ end
413
+
414
+ class ID < Schema::Scalar
415
+ end
416
+
417
+ class Int < Schema::Scalar
418
+ end
419
+
420
+ class Float < Schema::Scalar
421
+ end
422
+
423
+ class String < Schema::Scalar
424
+ end
425
+
426
+ class BigInt < Schema::Scalar
427
+ end
428
+
429
+ class Decimal < Schema::Scalar
430
+ end
431
+
432
+ class ISO8601Date < Schema::Scalar
433
+ end
434
+
435
+ class ISO8601DateTime < Schema::Scalar
436
+ end
437
+
438
+ class ISO8601DateTimePrecise < Schema::Scalar
439
+ end
440
+
441
+ class JSON < Schema::Scalar
442
+ end
443
+
444
+ class JSONString < Schema::Scalar
445
+ end
446
+
447
+ class Base64Binary < Schema::Scalar
448
+ end
449
+
450
+ module Relay
451
+ class BaseConnection < Schema::Object
452
+ def self.edge_type: (untyped edge_class, **untyped kwargs) -> Class?
453
+ def self.node_type: () -> Class?
454
+ def self.edges_field: (**untyped) -> GraphQL::Schema::Field
455
+ def self.nodes_field: (**untyped) -> GraphQL::Schema::Field
456
+ end
457
+
458
+ class BaseEdge < Schema::Object
459
+ def self.node_type: () -> Class?
460
+ end
461
+
462
+ class PageInfo < Schema::Object
463
+ end
464
+
465
+ class BaseField < Schema::Field
466
+ def self.connection_extension: () -> untyped
467
+ end
468
+
469
+ module Node
470
+ include GraphQL::Schema::Interface
471
+ end
472
+
473
+ module NodeBehaviors
474
+ def object_from_id: (untyped object_id, untyped ctx) -> untyped
475
+ def type_from_object: (untyped obj) -> untyped
476
+ end
477
+
478
+ module HasNodeField
479
+ end
480
+
481
+ module HasNodesField
482
+ end
483
+ end
484
+ end
485
+ end
@@ -93,6 +93,7 @@ module Rigor
93
93
  assert_not_equal: [:literal_then_local, true],
94
94
  assert_match: [:regex_then_local, false]
95
95
  }.freeze
96
+ Ractor.make_shareable(ASSERT_FORM)
96
97
  private_constant :ASSERT_FORM
97
98
 
98
99
  def assert_form_fact(call_node, environment:)
@@ -126,6 +127,7 @@ module Rigor
126
127
  wont_equal: [:literal_arg, true],
127
128
  must_match: [:regex_arg, false]
128
129
  }.freeze
130
+ Ractor.make_shareable(SPEC_MATCHER_FORM)
129
131
  private_constant :SPEC_MATCHER_FORM
130
132
 
131
133
  # ADR-37 slice 2 — the method names this analyzer narrows on, for the plugin's
@@ -109,12 +109,12 @@ module Rigor
109
109
  services.fact_store.publish(plugin_id: manifest.id, name: :reachability_roots, value: roots)
110
110
  end
111
111
 
112
- # File-level only: the load-error emission. The per-call policy validation runs over the engine-owned
113
- # walk via the node_rule below (ADR-37). The index is lazily loaded + memoised by `producer_value`,
114
- # so both surfaces share one load.
112
+ # File-level only: the load-error disclosure. The per-call policy validation runs over the
113
+ # engine-owned walk via the node_rule below (ADR-37). The index is lazily loaded + memoised by
114
+ # `producer_value`, so both surfaces share one load.
115
115
  def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
116
116
  index = producer_value(:policy_index)
117
- return [load_error_diagnostic(path)] if index.nil? && producer_error(:policy_index)
117
+ disclose_load_error if index.nil? && producer_error(:policy_index)
118
118
 
119
119
  []
120
120
  end
@@ -130,10 +130,14 @@ module Rigor
130
130
 
131
131
  private
132
132
 
133
- def load_error_diagnostic(path)
133
+ # Issue #1056 — "the policy index did not load" is a fact about the run's INPUTS, not about the file being
134
+ # analysed, so it is registered rather than returned: see {Plugin::Base#disclose_once} for the
135
+ # channel, the `(plugin id, key)` de-duplication and why the engine positions it at `.rigor.yml:1:1`.
136
+ # Returned from the per-file hook it carried no once-guard at all and repeated on every file.
137
+ def disclose_load_error
134
138
  error = producer_error(:policy_index)
135
- Rigor::Analysis::Diagnostic.new(
136
- path: path, line: 1, column: 1,
139
+ disclose_once(
140
+ :policy_index_load_failed,
137
141
  message: "rigor-pundit: failed to discover policies: #{error.class}: #{error.message}",
138
142
  severity: :warning,
139
143
  rule: "load-error"
@@ -46,10 +46,7 @@ module Rigor
46
46
  # skipped — the controller/action scope cannot be statically determined there.
47
47
  # - View template lazy keys (`t('.key')` inside ERB / Haml / Slim) are validated for key existence and
48
48
  # per-locale coverage. Interpolation variable validation is skipped for view templates (the hash may
49
- # come from controller instance variables not visible in the template). The view scan is a
50
- # project-wide pass surfaced through the per-file diagnostic hook, so under `--workers` each fork-pool
51
- # worker re-emits the full set (the same once-per-run limitation the `load-error` path carries);
52
- # sequential `rigor check` is unaffected.
49
+ # come from controller instance variables not visible in the template).
53
50
  # - Pluralization (`t('errors.messages.too_short', count: n)`) is recognised at the call site but the
54
51
  # `count` key is not used to validate the locale's pluralization branches.
55
52
  # - YAML aliases / merges are accepted (Psych's standard `aliases: true`) but custom Ruby classes
@@ -106,29 +103,26 @@ module Rigor
106
103
  @view_search_paths = Array(config.fetch("view_search_paths")).map(&:to_s)
107
104
  @configured_locales = Array(config.fetch("configured_locales")).map(&:to_s)
108
105
  @load_errors = []
109
- @load_errors_emitted = false
110
- @view_diagnostics_emitted = false
111
106
  end
112
107
 
113
- # File-level only: the once-per-run YAML load errors + the runtime (cache-load) error + the
114
- # view-template scan. Per-call `t('key')` validation runs over the engine-owned walk via the
115
- # node_rule below (ADR-37). The locale index and view diagnostics are lazily loaded + memoised by
116
- # `producer_value`.
108
+ # Returns no rows of its own: every file-level outcome here is run-scoped. The YAML load errors and the
109
+ # runtime (cache-load) errors are disclosures (#1051); the view-template scan is a positioned batch
110
+ # registered through `#emit_once` (#1060) — its rows name view files that are usually not analysed
111
+ # targets, so no per-file return could own them, and returning them from whichever file an instance
112
+ # saw first repeated the whole batch once per fork-pool worker. Per-call `t('key')` validation runs
113
+ # over the engine-owned walk via the node_rule below (ADR-37). The locale index and view diagnostics
114
+ # are lazily loaded + memoised by `producer_value`, so the repeat registrations are no-ops.
117
115
  def diagnostics_for_file(path:, scope:, root:) # rubocop:disable Lint/UnusedMethodArgument
118
116
  index = producer_value(:locale_index)
119
- diagnostics = []
120
- diagnostics.concat(consume_load_error_diagnostics(path)) unless @load_errors.empty?
121
- diagnostics << runtime_error_diagnostic(path) if index.nil? && producer_error(:locale_index)
122
- unless @view_diagnostics_emitted
123
- view_diags = producer_value(:view_diagnostics) || []
124
- if (view_err = producer_error(:view_diagnostics))
125
- diagnostics << view_runtime_error_diagnostic(path, view_err)
126
- else
127
- diagnostics.concat(view_diags)
128
- end
129
- @view_diagnostics_emitted = true
117
+ disclose_load_errors unless @load_errors.empty?
118
+ disclose_locale_runtime_error if index.nil? && producer_error(:locale_index)
119
+ view_diags = producer_value(:view_diagnostics) || []
120
+ if (view_err = producer_error(:view_diagnostics))
121
+ disclose_view_runtime_error(view_err)
122
+ else
123
+ emit_once(:view_diagnostics, view_diags)
130
124
  end
131
- diagnostics
125
+ []
132
126
  end
133
127
 
134
128
  # The lazy-key (`t('.key')`) expansion needs the enclosing method (the controller action), supplied
@@ -211,35 +205,39 @@ module Rigor
211
205
  end
212
206
 
213
207
  # The runner only invokes `diagnostics_for_file` for Ruby files (`paths:` is filtered to `.rb`). YAML
214
- # parse errors therefore can't be anchored on the offending locale file directly; instead, we emit
215
- # them once per run on the first analyzed Ruby file, naming the offending YAML path in the message.
216
- def consume_load_error_diagnostics(path)
217
- return [] if @load_errors_emitted
218
-
219
- @load_errors_emitted = true
220
- @load_errors.map do |err|
221
- Rigor::Analysis::Diagnostic.new(
222
- path: path, line: 1, column: 1,
208
+ # parse errors therefore can't be anchored on the offending locale file directly, and the row used to
209
+ # be pinned to the first analysed Ruby file — once per plugin INSTANCE, so once per fork-pool worker
210
+ # (#1051). They go through the run-scoped channel instead: keyed by the locale path that failed, so a
211
+ # project with two broken YAML files still discloses both, each exactly once, at `.rigor.yml:1:1`.
212
+ def disclose_load_errors
213
+ @load_errors.each do |err|
214
+ disclose_once(
215
+ "locale-parse:#{err.path}",
223
216
  message: "rigor-rails-i18n: failed to parse `#{err.path}`: #{err.message}",
224
217
  severity: :warning,
225
218
  rule: "load-error"
226
219
  )
227
220
  end
221
+ nil
228
222
  end
229
223
 
230
- def runtime_error_diagnostic(path)
224
+ # #1051 — both of these say the plugin could not READ something the project configured, which is a
225
+ # run-level fact with no source position; they were repeated on every analysed file (the locale one)
226
+ # or on each instance's first (the view one). A key distinct from the `:view_diagnostics` batch's:
227
+ # the two channels share one registration table per plugin.
228
+ def disclose_locale_runtime_error
231
229
  error = producer_error(:locale_index)
232
- Rigor::Analysis::Diagnostic.new(
233
- path: path, line: 1, column: 1,
230
+ disclose_once(
231
+ :locale_index_failed,
234
232
  message: "rigor-rails-i18n: failed to load locales: #{error.class}: #{error.message}",
235
233
  severity: :warning,
236
234
  rule: "load-error"
237
235
  )
238
236
  end
239
237
 
240
- def view_runtime_error_diagnostic(path, error)
241
- Rigor::Analysis::Diagnostic.new(
242
- path: path, line: 1, column: 1,
238
+ def disclose_view_runtime_error(error)
239
+ disclose_once(
240
+ :view_scan_failed,
243
241
  message: "rigor-rails-i18n: failed to scan view templates: #{error.class}: #{error.message}",
244
242
  severity: :warning,
245
243
  rule: "load-error"
@@ -61,7 +61,6 @@ module Rigor
61
61
  %r{(?:\A|/)config/}
62
62
  ].freeze
63
63
 
64
- # @param path — file being analysed
65
64
  # The route-helper violations for a single call node (0..2), or `[]` when the node is not an
66
65
  # implicit `*_path` / `*_url` helper call, is shadowed by a same-file binding, or is in a
67
66
  # suppressed directory. ADR-37: the engine owns the walk; the same-file `shadowing` set is built
@@ -58,6 +58,7 @@ module Rigor
58
58
  [nil, "registration", :get, :show]
59
59
  ]
60
60
  }.freeze
61
+ Ractor.make_shareable(CONTROLLER_HELPERS)
61
62
 
62
63
  # Dynamic-provider helper patterns. `_omniauth_authorize_path` and `_omniauth_callback_path` are
63
64
  # generated per configured provider (Facebook, GitHub, …) by an initializer this parser does not
@@ -135,6 +136,7 @@ module Rigor
135
136
  unlocks: %w[new_unlock_path unlock_path],
136
137
  registrations: %w[cancel_registration_path new_registration_path edit_registration_path registration_path]
137
138
  }.freeze
139
+ Ractor.make_shareable(SCOPED_HELPERS)
138
140
  private_constant :SCOPED_HELPERS
139
141
 
140
142
  def scoped_helpers(skip_set)