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,60 @@
1
+ module Rigor
2
+ module Effects
3
+ # The propagated result of the fixpoint walk: every method's direct summary plus the transitive
4
+ # `proven` / `undischarged` / `declared` lanes, the exhaustiveness bit, and the resolved callee keys.
5
+ class EffectTable
6
+ # One row of the closed table.
7
+ class Entry < ::Data
8
+ # sig-gen gap: #1150 — `Data.define` member reads carry no type.
9
+ def key: () -> String
10
+ # sig-gen gap: #1150
11
+ def direct: () -> Summary
12
+ # sig-gen gap: #1150
13
+ def proven: () -> LabelSet
14
+ # sig-gen gap: #1150
15
+ def undischarged: () -> LabelSet
16
+ # sig-gen gap: #1150
17
+ def declared: () -> LabelSet
18
+ # sig-gen gap: #1150
19
+ def exhaustive: () -> bool
20
+ # sig-gen gap: #1150 — `causes` are frozen `[cause, detail]` pairs.
21
+ def causes: () -> Array[[String, String?]]
22
+ # sig-gen gap: #1150 — resolved callee keys (`"Class#m"` strings).
23
+ def edges: () -> Array[String]
24
+ # sig-gen gap: #1150
25
+ def unclaimed: () -> bool
26
+
27
+ # sig-gen gap: #1150 — the custom `initialize` supplies defaults for `undischarged` (=
28
+ # `proven`), `declared` (= empty) and `unclaimed` (= false); it accepts kwargs only, so the
29
+ # member constructor has no positional arm.
30
+ def self.new: (key: String, direct: Summary, proven: LabelSet, exhaustive: bool,
31
+ causes: Array[[String, String?]], edges: Array[String],
32
+ ?undischarged: LabelSet?, ?declared: LabelSet?, ?unclaimed: bool) -> instance
33
+ # sig-gen gap: #1150 — as above.
34
+ def self.[]: (key: String, direct: Summary, proven: LabelSet, exhaustive: bool,
35
+ causes: Array[[String, String?]], edges: Array[String],
36
+ ?undischarged: LabelSet?, ?declared: LabelSet?, ?unclaimed: bool) -> instance
37
+
38
+ def unclaimed?: () -> bool
39
+
40
+ # The declared labels worth printing beside `proven` — those the proven lane does not already
41
+ # admit.
42
+ def rendered_declared: () -> LabelSet
43
+
44
+ def exhaustive?: () -> bool
45
+ def trivial?: () -> bool
46
+ end
47
+
48
+ def self.empty: () -> EffectTable
49
+
50
+ def initialize: (Hash[String, Entry] entries) -> void
51
+
52
+ def []: (String key) -> Entry?
53
+ def keys: () -> Array[String]
54
+ def each: () { (Entry) -> untyped } -> void
55
+ include Enumerable[Entry]
56
+ def size: () -> Integer
57
+ def empty?: () -> bool
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,97 @@
1
+ module Rigor
2
+ module Effects
3
+ # An author-declared upper bound on one method's effect labels (ADR-103 WD1). A `Data` record —
4
+ # see lib/rigor/effects/envelope.rb for the member semantics.
5
+ class Envelope < ::Data
6
+ # {#source} for a bound written in `.rigor.yml` rather than on a declaration.
7
+ CONFIG_SOURCE: :config_envelope
8
+
9
+ # sig-gen gap: #1150 — `Data.define` member reads carry no type; `build` proves each of these.
10
+ def owner_key: () -> String
11
+
12
+ # sig-gen gap: #1150 — as above; `LabelSet::TOP` means "no envelope" (the fail-open reading).
13
+ def bound: () -> LabelSet
14
+
15
+ # sig-gen gap: #1150 — as above; the four documented producers are the closed set.
16
+ def source: () -> (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope)
17
+
18
+ # sig-gen gap: #1150 — as above (`path:line`, the `.rigor.yml` key path, or nil).
19
+ def location: () -> String?
20
+
21
+ # sig-gen gap: #1150 — as above (the author's own annotation text, or nil).
22
+ def spelling: () -> String?
23
+
24
+ # sig-gen gap: #1150 — as above (sorted, de-duplicated by `build`; #384 reads them).
25
+ def unknown_labels: () -> Array[String]
26
+
27
+ # sig-gen gap: #1150 — as above (every token the tag listed, recognised or not).
28
+ def declared_labels: () -> Array[String]
29
+
30
+ # sig-gen gap: #1150 — as above; keyword form mirrors the member declarations.
31
+ def self.new: (
32
+ owner_key: String,
33
+ bound: LabelSet,
34
+ source: (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope),
35
+ location: String?,
36
+ spelling: String?,
37
+ unknown_labels: Array[String],
38
+ declared_labels: Array[String]
39
+ ) -> instance
40
+ | (
41
+ String owner_key,
42
+ LabelSet bound,
43
+ (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope) source,
44
+ String? location,
45
+ String? spelling,
46
+ Array[String] unknown_labels,
47
+ Array[String] declared_labels
48
+ ) -> instance
49
+
50
+ # sig-gen gap: #1150 — as above.
51
+ def self.[]: (
52
+ owner_key: String,
53
+ bound: LabelSet,
54
+ source: (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope),
55
+ location: String?,
56
+ spelling: String?,
57
+ unknown_labels: Array[String],
58
+ declared_labels: Array[String]
59
+ ) -> instance
60
+ | (
61
+ String owner_key,
62
+ LabelSet bound,
63
+ (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope) source,
64
+ String? location,
65
+ String? spelling,
66
+ Array[String] unknown_labels,
67
+ Array[String] declared_labels
68
+ ) -> instance
69
+
70
+ # Normalising constructor — sorts/dedupes `unknown_labels` and freezes `declared_labels`.
71
+ def self.build: (
72
+ owner_key: String,
73
+ bound: LabelSet,
74
+ source: (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope),
75
+ ?location: String?,
76
+ ?spelling: String?,
77
+ ?unknown_labels: Array[String],
78
+ ?declared_labels: Array[String]
79
+ ) -> Envelope
80
+
81
+ # Whether this reads as ⊤ — no bound at all.
82
+ def top?: () -> bool
83
+
84
+ # `label` is `to_s`-compared through {LabelSet#admits?}; `mutate.local` always tolerates.
85
+ def tolerates?: (untyped label) -> bool
86
+
87
+ # The members of `label_set` this envelope does NOT admit, sorted.
88
+ def exceeded_by: (LabelSet label_set) -> Array[String]
89
+
90
+ # Whether the bound was written in `.rigor.yml`.
91
+ def config?: () -> bool
92
+
93
+ # The same bound on another method key — the class-level distribution seam.
94
+ def rebind: (String key) -> Envelope
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,33 @@
1
+ module Rigor
2
+ module Effects
3
+ # The envelopes a call site may import as a `≤` bound (ADR-103 WD6; #386) — per-method
4
+ # annotation, class-level annotation, `effects.envelopes:`, then the accepted signature stratum.
5
+ class EnvelopeIndex
6
+ # The index a run with no declaration of any kind uses — and the fail-soft answer for a build
7
+ # that raised.
8
+ def self.empty: () -> EnvelopeIndex
9
+
10
+ # Reads every stratum this index serves, once per process. `environment` supplies the accepted
11
+ # stratum through its RBS loader (nil drops it, fail-quiet); `plugin_facts` is the plugins'
12
+ # compiled `Effects::PluginFacts` contributions (nil where no plugin set was loaded).
13
+ def self.build: (configuration: Configuration, ?environment: Rigor::Environment?,
14
+ ?plugin_facts: PluginFacts?) -> EnvelopeIndex
15
+
16
+ def initialize: (
17
+ ?method_envelopes: Hash[String, Envelope],
18
+ ?class_envelopes: Hash[String, Envelope],
19
+ ?config_entries: Array[ConfigEnvelopes::Entry],
20
+ ?accepted: Hash[String, Envelope]
21
+ ) -> void
22
+
23
+ # Whether no stratum has anything to say — the scan's fast path.
24
+ def empty?: () -> bool
25
+
26
+ # The envelope bounding `owner`'s `selector`, or nil — never a ⊤ envelope.
27
+ def []: (String? owner, bool singleton, untyped selector) -> Envelope?
28
+
29
+ # Whether ANY stratum carries an annotation for this key — ⊤ envelopes included (#391).
30
+ def annotated?: (String? owner, bool singleton, untyped selector) -> bool
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,81 @@
1
+ module Rigor
2
+ module Effects
3
+ # The per-file unit of the collection window (ADR-103): the direct summaries, the call edges, and
4
+ # the as-written ancestry a file contributes. {merge_all} folds a run's collections in one pass.
5
+ class FileCollection
6
+ # An ancestry entry that names nothing and can never resolve (#1039).
7
+ OPAQUE_ANCESTOR: String
8
+
9
+ # One recorded call edge: who the receiver's class is, which lane (`:instance` / `:singleton`)
10
+ # the selector dispatches on, and the bits that decide what an unresolved edge seeds.
11
+ class Edge < ::Data
12
+ # sig-gen gap: #1150 — `Data.define` member reads carry no type.
13
+ def receiver_class: () -> String?
14
+ # sig-gen gap: #1150
15
+ def kind: () -> (:instance | :singleton)
16
+ # sig-gen gap: #1150
17
+ def selector: () -> String
18
+ # sig-gen gap: #1150
19
+ def self_call: () -> bool
20
+ # sig-gen gap: #1150
21
+ def super_call: () -> bool
22
+ # sig-gen gap: #1150
23
+ def unclaimed: () -> bool
24
+ # sig-gen gap: #1150
25
+ def constant_receiver: () -> bool
26
+ # sig-gen gap: #1150 — the `[cause, detail]` pair an unresolved edge seeds, when the row
27
+ # named a `taint:`.
28
+ def taint_if_unresolved: () -> [String, String?]?
29
+ # sig-gen gap: #1150 — the plugin's lookup-order retry selectors (#1065).
30
+ def fallback_selectors: () -> Array[String]?
31
+
32
+ # sig-gen gap: #1150 — member constructor. The custom `initialize` accepts kwargs only and
33
+ # defaults every member after `self_call`, so there is no positional arm.
34
+ def self.new: (receiver_class: String?, kind: (:instance | :singleton), selector: String,
35
+ self_call: bool, ?super_call: bool, ?unclaimed: bool,
36
+ ?constant_receiver: bool, ?taint_if_unresolved: [String, String?]?,
37
+ ?fallback_selectors: Array[String]?) -> instance
38
+ # sig-gen gap: #1150 — as above.
39
+ def self.[]: (receiver_class: String?, kind: (:instance | :singleton), selector: String,
40
+ self_call: bool, ?super_call: bool, ?unclaimed: bool,
41
+ ?constant_receiver: bool, ?taint_if_unresolved: [String, String?]?,
42
+ ?fallback_selectors: Array[String]?) -> instance
43
+ end
44
+
45
+ # The collection a file with nothing to say contributes.
46
+ def self.empty: (?String? path) -> FileCollection
47
+
48
+ # sig-gen gap: #1154 — `initialize` freezes the kwarg tables rather than assigning them
49
+ # verbatim, so sig-gen proposes the default constant's shape for each reader.
50
+ attr_reader path: String?
51
+ # sig-gen gap: #1154 — `{ "Class#m" => Summary }`.
52
+ attr_reader summaries: Hash[String, Summary]
53
+ # sig-gen gap: #1154 — `{ "Class#m" => [Edge] }`, sorted and de-duplicated.
54
+ attr_reader edges: Hash[String, Array[Edge]]
55
+ # sig-gen gap: #1154 — `{ class_name => superclass_name }`, `OPAQUE_ANCESTOR` included.
56
+ attr_reader superclasses: Hash[String, String]
57
+ # sig-gen gap: #1154 — `{ class_name => [module_name] }`, include/prepend merged.
58
+ attr_reader includes: Hash[String, Array[String]]
59
+
60
+ def initialize: (?path: String?, ?summaries: Hash[String, Summary],
61
+ ?edges: Hash[String, Array[Edge]], ?superclasses: Hash[String, String],
62
+ ?includes: Hash[String, Array[String]], ?failed: bool) -> void
63
+
64
+ # Whether the collector gave up on this file entirely (the fail-soft path).
65
+ def failed?: () -> bool
66
+
67
+ # Whether this collection contributes nothing.
68
+ def empty?: () -> bool
69
+
70
+ # Two-collection fold — {merge_all} for a whole run.
71
+ def merge: (FileCollection other) -> FileCollection
72
+
73
+ # Folds a run's collections in one linear pass; order-independent except `superclasses`.
74
+ def self.merge_all: (Enumerable[FileCollection] collections) -> FileCollection
75
+
76
+ def ==: (untyped other) -> bool
77
+ alias eql? ==
78
+ def hash: () -> Integer
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,26 @@
1
+ module Rigor
2
+ module Effects
3
+ # The effect-label grammar and segment-aware prefix subsumption (ADR-103 WD1). Every predicate is
4
+ # total — a malformed input is answered, never raised on — so the parameters are `untyped` on
5
+ # purpose.
6
+ module Label
7
+ PATTERN: Regexp
8
+
9
+ def self?.valid?: (untyped str) -> bool
10
+
11
+ # `[]` for a malformed label, the segments otherwise.
12
+ def self?.segments: (untyped label) -> Array[String]
13
+
14
+ def self?.subsumes?: (untyped bound, untyped label) -> bool
15
+
16
+ # One segment shallower; nil for a root and for a malformed label.
17
+ def self?.parent: (untyped label) -> String?
18
+
19
+ # Proper ancestors, outermost first; `[]` for a root and for a malformed label.
20
+ def self?.ancestors: (untyped label) -> Array[String]
21
+
22
+ # The outermost segment; nil for a malformed label.
23
+ def self?.root: (untyped label) -> String?
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,46 @@
1
+ module Rigor
2
+ module Effects
3
+ # An immutable, sorted, de-duplicated set of effect labels (ADR-103 WD1) — the carrier of a
4
+ # summary lane and of an envelope's bound. `TOP` is the unbounded sentinel: it admits every
5
+ # label, absorbs every join, and answers `[]` from `to_a`.
6
+ class LabelSet
7
+ # `labels` is any enumerable of label Strings (`#to_a` is called on it); `top:` is internal —
8
+ # it exists to build {TOP} and is not part of the public surface.
9
+ def initialize: (?untyped labels, ?top: bool) -> void
10
+
11
+ TOP: LabelSet
12
+ EMPTY: LabelSet
13
+
14
+ def top?: () -> bool
15
+
16
+ # {TOP} is not empty: it records nothing because it stands for everything.
17
+ def empty?: () -> bool
18
+
19
+ # The members, sorted. {TOP} yields `[]` — consult {top?} first.
20
+ def to_a: () -> Array[String]
21
+
22
+ # Exact membership — `include?` is not subsumption ({admits?} is).
23
+ def include?: (untyped label) -> bool
24
+
25
+ # Whether some member subsumes `label`. {TOP} admits every well-formed label.
26
+ def admits?: (untyped label) -> bool
27
+
28
+ # Union — {TOP} absorbs. May return `self`/`other` without allocating.
29
+ def join: (LabelSet other) -> LabelSet
30
+
31
+ # The members `other` does not already admit — the declared lane's rendering rule.
32
+ def excluding_subsumed_by: (LabelSet other) -> LabelSet
33
+
34
+ # The envelope check, modulo policy discharge. {TOP} is bounded only by {TOP}.
35
+ def subsumed_by?: (LabelSet bound_set) -> bool
36
+
37
+ def ==: (untyped other) -> bool
38
+
39
+ # `alias eql? ==` — no sig-gen shape for alias rows; unmarked residue.
40
+ def eql?: (untyped other) -> bool
41
+
42
+ def hash: () -> Integer
43
+ def inspect: () -> String
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,24 @@
1
+ module Rigor
2
+ module Effects
3
+ # The `Owner#selector` / `Owner.selector` key spelling (ADR-103 WD14). `key` parameters accept any
4
+ # object — every entry point `to_s`s its input — so they are `untyped` on purpose.
5
+ module MethodKey
6
+ TEMPLATE_UNIT_PREFIX: String
7
+
8
+ # `[owner, separator, selector]`, or nil when `key` is not a method key at all.
9
+ def self?.split: (untyped key) -> [String, String, String]?
10
+
11
+ def self?.valid?: (untyped key) -> bool
12
+
13
+ # The owner half of a method key, or nil.
14
+ def self?.owner: (untyped key) -> String?
15
+
16
+ # `view:`-prefixed template-unit keys are NOT method keys (#393).
17
+ def self?.template_unit?: (untyped key) -> bool
18
+
19
+ # The name an `effects.envelopes:` entry selects a key by — the owner class for a method key,
20
+ # the unit key itself for a template unit.
21
+ def self?.envelope_owner: (untyped key) -> String?
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ module Rigor
2
+ module Effects
3
+ # Where one bundle of labels in a summary came from (ADR-103 WD14): a callee/construct `name`
4
+ # plus which `source` coloured it.
5
+ class Origin < ::Data
6
+ CONSTRUCT: :construct
7
+ CATALOGUE: :catalogue
8
+ ATTRIBUTION: :attribution
9
+ ENVELOPE: :envelope
10
+ PLUGIN: :plugin
11
+
12
+ # sig-gen gap: #1150 — `Data.define` member reads carry no type; every factory below proves
13
+ # `name` a `to_s`d String and `source` one of the five constants.
14
+ def name: () -> String
15
+
16
+ # sig-gen gap: #1150 — as above; the five source constants are the closed set.
17
+ def source: () -> (:construct | :catalogue | :attribution | :envelope | :plugin)
18
+
19
+ # sig-gen gap: #1150 — as above.
20
+ def self.new: (
21
+ name: String,
22
+ source: (:construct | :catalogue | :attribution | :envelope | :plugin)
23
+ ) -> instance
24
+ | (
25
+ String name,
26
+ (:construct | :catalogue | :attribution | :envelope | :plugin) source
27
+ ) -> instance
28
+
29
+ # sig-gen gap: #1150 — as above.
30
+ def self.[]: (
31
+ name: String,
32
+ source: (:construct | :catalogue | :attribution | :envelope | :plugin)
33
+ ) -> instance
34
+ | (
35
+ String name,
36
+ (:construct | :catalogue | :attribution | :envelope | :plugin) source
37
+ ) -> instance
38
+
39
+ # `name` is `to_s`d, so any object is acceptable — `untyped` is the honest parameter.
40
+ def self.construct: (untyped name) -> Origin
41
+ def self.catalogue: (untyped name) -> Origin
42
+ def self.attribution: (untyped name) -> Origin
43
+ def self.envelope: (untyped name) -> Origin
44
+ def self.plugin: (untyped name) -> Origin
45
+
46
+ # `"source:name"` — the rendering the report and the JSON payload key on.
47
+ def to_s: () -> String
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,141 @@
1
+ module Rigor
2
+ module Effects
3
+ # The loaded plugins' effect contributions, compiled once per process (ADR-103): attribution rows
4
+ # indexed by receiver spelling, framework-edge strategies, label vocabulary, entry-point presets,
5
+ # and plugin-claimed ancestry.
6
+ class PluginFacts
7
+ # How many ancestors the walk visits before giving up — bounds work, not correctness.
8
+ ANCESTRY_CAP: Integer
9
+
10
+ # One compiled attribution row. `discharge` is the RESOLVED grant — `.build` has already
11
+ # demoted a non-first-party plugin's `true` and recorded a warning.
12
+ class Row < ::Data
13
+ # sig-gen gap: #1150 — `Data.define` member reads carry no type.
14
+ def key: () -> String
15
+ # sig-gen gap: #1150
16
+ def labels: () -> LabelSet
17
+ # sig-gen gap: #1150 — a {Narrowing} handler name, when the call's own arguments settle the
18
+ # row's answer.
19
+ def narrow: () -> String?
20
+ # sig-gen gap: #1150
21
+ def discharge: () -> bool
22
+ # sig-gen gap: #1150 — the class a `self`-path row is scoped to.
23
+ def within: () -> String?
24
+ # sig-gen gap: #1150 — the taint cause the row seeds where its edge resolves to nothing.
25
+ def taint: () -> String?
26
+ # sig-gen gap: #1150
27
+ def plugin_id: () -> String
28
+ # sig-gen gap: #1150 — the receiver a UNIT rule has no call node to read off.
29
+ def receiver: () -> String?
30
+ # sig-gen gap: #1150 — the {CalleeRule} rule name (#1048), nil when the row records no edge.
31
+ def callee: () -> String?
32
+ # sig-gen gap: #1150 — the plugin's lookup-order table for the rule's answer (#1065).
33
+ def callee_fallbacks: () -> Hash[String, Array[String]]?
34
+ # sig-gen gap: #1150
35
+ def responds: () -> bool
36
+
37
+ # sig-gen gap: #1150 — member constructor. The custom `initialize` accepts kwargs only and
38
+ # defaults `receiver` / `callee` / `callee_fallbacks` / `responds`, so there is no
39
+ # positional arm.
40
+ def self.new: (key: String, labels: LabelSet, narrow: String?, discharge: bool,
41
+ within: String?, taint: String?, plugin_id: String, ?receiver: String?,
42
+ ?callee: String?, ?callee_fallbacks: Hash[String, Array[String]]?,
43
+ ?responds: bool) -> instance
44
+ # sig-gen gap: #1150 — as above.
45
+ def self.[]: (key: String, labels: LabelSet, narrow: String?, discharge: bool,
46
+ within: String?, taint: String?, plugin_id: String, ?receiver: String?,
47
+ ?callee: String?, ?callee_fallbacks: Hash[String, Array[String]]?,
48
+ ?responds: bool) -> instance
49
+
50
+ def discharge?: () -> bool
51
+ end
52
+
53
+ # One compiled framework-edge strategy: the base class a plugin pointed the engine at, and
54
+ # which plugin pointed.
55
+ class Edge < ::Data
56
+ # sig-gen gap: #1150 — `Data.define` member reads carry no type. `target` is the closed
57
+ # {Plugin::EffectEdge::TARGETS} set.
58
+ def target: () -> (:activerecord_callbacks | :perform_now | :mailer_body)
59
+ # sig-gen gap: #1150
60
+ def receiver: () -> String
61
+ # sig-gen gap: #1150 — nil for a whole-class edge (`method` omitted).
62
+ def selector: () -> Symbol?
63
+ # sig-gen gap: #1150
64
+ def plugin_id: () -> String
65
+
66
+ # sig-gen gap: #1150 — member constructor.
67
+ def self.new: (target: (:activerecord_callbacks | :perform_now | :mailer_body),
68
+ receiver: String, selector: Symbol?, plugin_id: String) -> instance
69
+ | ((:activerecord_callbacks | :perform_now | :mailer_body) target, String receiver,
70
+ Symbol? selector, String plugin_id) -> instance
71
+ # sig-gen gap: #1150 — as above.
72
+ def self.[]: (target: (:activerecord_callbacks | :perform_now | :mailer_body),
73
+ receiver: String, selector: Symbol?, plugin_id: String) -> instance
74
+ end
75
+
76
+ # The rows whose `callee:` names a {CalleeRule::UNIT_RULES} rule — applied once per unit.
77
+ # Unmarked residue: populated by `absorb_attribution`, not assigned from an `initialize`
78
+ # parameter, so no gap issue covers it (same shape as `Registry#additional_initializers`).
79
+ attr_reader unit_callee_rows: Array[Row]
80
+
81
+ # Human-readable notes about contributions accepted only in part. Surfaced by `rigor effects`;
82
+ # never a diagnostic. Unmarked residue — accumulated by `absorb`/`extend_registry`.
83
+ attr_reader warnings: Array[String]
84
+
85
+ # `{owner => [labels]}` — what {#extend_registry} folds into the run's vocabulary.
86
+ # Unmarked residue — built by `absorb`, not an `initialize` parameter.
87
+ attr_reader labels_by_owner: Hash[String, Array[String]]
88
+
89
+ # Every `effect_entry_points:` preset across the loaded set.
90
+ # Unmarked residue — concatenated by `absorb`, not an `initialize` parameter.
91
+ attr_reader entry_points: Array[Plugin::EffectEntryPoints]
92
+
93
+ # A content digest of every compiled plugin fact; {Identity} folds it in.
94
+ # Unmarked residue — computed by `compute_digest`, not an `initialize` parameter.
95
+ attr_reader digest: String
96
+
97
+ def self.empty: () -> PluginFacts
98
+
99
+ # `superclasses`/`includes` are the project's as-written ancestry tables
100
+ # (`Scope::DiscoveryIndex#discovered_superclasses` / `#discovered_includes`).
101
+ def self.build: (Plugin::Registry? plugin_registry, ?superclasses: Hash[String, String]?,
102
+ ?includes: Hash[String, Array[String]]?) -> PluginFacts
103
+
104
+ # `contributions:` elements are `Plugin::Registry::Contribution` rows.
105
+ def initialize: (contributions: Array[Plugin::Registry::Contribution],
106
+ superclasses: Hash[String, String]?,
107
+ ?includes: Hash[String, Array[String]]?) -> void
108
+
109
+ def empty?: () -> bool
110
+
111
+ # Whether any attribution row exists at all — the scan's fast path.
112
+ def attributions?: () -> bool
113
+ def edges?: () -> bool
114
+
115
+ # The row colouring `owner`'s `selector`, on `owner` itself or a project ancestor.
116
+ def class_row: (String? owner, bool singleton, String selector) -> Row?
117
+
118
+ # The row colouring receiver `path`'s `selector` (`"Rails.cache"`) — exact, no ancestry walk.
119
+ def path_row: (String? path, String selector) -> Row?
120
+
121
+ # The row colouring `path`'s `selector` for a self-rooted receiver (`"self.flash.now"`) inside
122
+ # a unit whose class is `owner_class`.
123
+ def self_path_row: (String? path, String selector, String? owner_class) -> Row?
124
+
125
+ # The row colouring `selector` on the RESULT of a call to `producer` (or an ancestor of it).
126
+ def result_row: (String? producer, String selector) -> Row?
127
+
128
+ # The framework-edge strategies of one kind, e.g. every `:activerecord_callbacks` base class
129
+ # the loaded plugins named.
130
+ def edges_for: (Symbol target) -> Array[Edge]
131
+
132
+ # Whether `class_name` is `ancestor`, or reaches it through the project's own `class … <`
133
+ # lines and included modules.
134
+ def descends_from?: (String? class_name, String? ancestor) -> bool
135
+
136
+ # `registry` extended with every plugin's `effect_labels:` under its own owner. Rejected
137
+ # extensions land in {#warnings} instead of raising.
138
+ def extend_registry: (Registry registry) -> Registry
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,68 @@
1
+ module Rigor
2
+ module Effects
3
+ # The effect-label vocabulary: which spellings are recognised, which have been retired, and who
4
+ # may open a new root (ADR-103 WD2; normative in `docs/type-specification/effect-labels.md`).
5
+ # A frozen value object; {#with} returns a new one rather than mutating.
6
+ class Registry
7
+ class Error < StandardError
8
+ end
9
+
10
+ # An added label whose root neither exists already nor belongs to the extender.
11
+ class OwnershipError < Error
12
+ end
13
+
14
+ # An added label that is not well-formed under {Label::PATTERN}.
15
+ class InvalidLabelError < Error
16
+ end
17
+
18
+ DATA_PATH: String
19
+
20
+ # How far a misspelling may be from a known label before {#suggest} declines to guess.
21
+ SUGGESTION_DISTANCE_CAP: Integer
22
+
23
+ # The shared registry as shipped, memoised per process.
24
+ def self.default: () -> Registry
25
+
26
+ # The vocabulary one run works in: the shipped registry plus `effects.labels:` and every
27
+ # loaded plugin's `effect_labels:` (ADR-103 WD2 / #385 / #387). Fail-soft: a rejected
28
+ # extension degrades to the shipped vocabulary.
29
+ def self.for_configuration: (Configuration configuration, ?plugin_facts: PluginFacts?) -> Registry
30
+
31
+ # Build a registry from a registry-shaped YAML file. Missing or unreadable data degrades to
32
+ # an empty vocabulary (fail-open).
33
+ def self.load_file: (untyped path) -> Registry
34
+
35
+ # sig-gen gap: #1154 — `attr_reader` over an ivar assigned in `initialize` from the
36
+ # parameter of the same name; sig-gen proposes `untyped` for it.
37
+ attr_reader vocabulary_version: Integer
38
+ # sig-gen gap: #1154 — every declared label, sorted; implied ancestors are recognised by
39
+ # {#known?} but not listed here.
40
+ attr_reader labels: Array[String]
41
+ # The roots of the vocabulary — the outermost segments {#with} treats as already owned.
42
+ # Unmarked residue: `@roots` is computed from `@known` in `initialize`, not assigned from a
43
+ # parameter, so #1154 does not cover it.
44
+ attr_reader roots: Array[String]
45
+ # sig-gen gap: #1154 — one line per root, from the data file's `descriptions:`.
46
+ attr_reader descriptions: Hash[String, String]
47
+
48
+ def initialize: (vocabulary_version: Integer, labels: Array[untyped],
49
+ ?retired: Hash[untyped, untyped],
50
+ ?descriptions: Hash[untyped, untyped]) -> void
51
+
52
+ # Whether the vocabulary recognises `label`: an exact row, or an ancestor of one.
53
+ def known?: (untyped label) -> bool
54
+
55
+ # The nearest recognised label to a misspelling within {SUGGESTION_DISTANCE_CAP} edits, or
56
+ # nil when nothing is close enough.
57
+ def suggest: (untyped label) -> String?
58
+
59
+ # The replacement labels for a retired spelling, or nil when it was never retired.
60
+ def retired: (untyped label) -> Array[String]?
61
+
62
+ # A new registry carrying `labels` on top of this one. `owner` is the identity opening a
63
+ # root — a plugin id, or a first-party plugin's framework root; nil is the project, which
64
+ # may open any root.
65
+ def with: (labels: Array[untyped], owner: String?) -> Registry
66
+ end
67
+ end
68
+ end