rigortype 0.2.7 → 0.2.8

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 (414) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docs/manual/04-diagnostics.md +7 -4
  4. data/docs/manual/06-baseline.md +5 -2
  5. data/docs/manual/15-type-protection-coverage.md +6 -4
  6. data/lib/rigor/analysis/baseline.rb +56 -96
  7. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  8. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  9. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  10. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  11. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  12. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  13. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  14. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  15. data/lib/rigor/analysis/check_rules.rb +174 -274
  16. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  17. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  18. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  19. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  20. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  21. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  22. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  23. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  24. data/lib/rigor/analysis/diagnostic.rb +48 -75
  25. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  26. data/lib/rigor/analysis/fact_store.rb +12 -22
  27. data/lib/rigor/analysis/incremental.rb +47 -63
  28. data/lib/rigor/analysis/incremental_session.rb +74 -100
  29. data/lib/rigor/analysis/project_scan.rb +12 -23
  30. data/lib/rigor/analysis/result.rb +3 -5
  31. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  32. data/lib/rigor/analysis/run_stats.rb +38 -61
  33. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  34. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  35. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  36. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  37. data/lib/rigor/analysis/runner.rb +220 -333
  38. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  39. data/lib/rigor/analysis/worker_session.rb +64 -102
  40. data/lib/rigor/ast/type_node.rb +6 -8
  41. data/lib/rigor/ast.rb +6 -10
  42. data/lib/rigor/bleeding_edge.rb +27 -35
  43. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  44. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  45. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  46. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  47. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  48. data/lib/rigor/cache/descriptor.rb +58 -94
  49. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  50. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  51. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  52. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  53. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  54. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  55. data/lib/rigor/cache/rbs_environment.rb +11 -19
  56. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  57. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  58. data/lib/rigor/cache/store.rb +96 -151
  59. data/lib/rigor/cli/annotate_command.rb +51 -88
  60. data/lib/rigor/cli/baseline_command.rb +10 -17
  61. data/lib/rigor/cli/check_command.rb +121 -199
  62. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  63. data/lib/rigor/cli/ci_detector.rb +9 -14
  64. data/lib/rigor/cli/command.rb +8 -14
  65. data/lib/rigor/cli/coverage_command.rb +19 -26
  66. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  67. data/lib/rigor/cli/coverage_report.rb +0 -2
  68. data/lib/rigor/cli/coverage_scan.rb +7 -11
  69. data/lib/rigor/cli/diagnostic_formats.rb +43 -62
  70. data/lib/rigor/cli/diff_command.rb +10 -20
  71. data/lib/rigor/cli/docs_command.rb +26 -40
  72. data/lib/rigor/cli/doctor_command.rb +8 -9
  73. data/lib/rigor/cli/explain_command.rb +4 -7
  74. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  75. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  76. data/lib/rigor/cli/lsp_command.rb +11 -19
  77. data/lib/rigor/cli/mcp_command.rb +4 -6
  78. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  79. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  80. data/lib/rigor/cli/options.rb +11 -19
  81. data/lib/rigor/cli/plugin_command.rb +18 -30
  82. data/lib/rigor/cli/plugins_command.rb +29 -51
  83. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  84. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  85. data/lib/rigor/cli/protection_renderer.rb +5 -7
  86. data/lib/rigor/cli/protection_report.rb +27 -17
  87. data/lib/rigor/cli/renderable.rb +4 -6
  88. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  89. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  90. data/lib/rigor/cli/skill_command.rb +30 -47
  91. data/lib/rigor/cli/skill_describe.rb +40 -64
  92. data/lib/rigor/cli/trace_command.rb +9 -16
  93. data/lib/rigor/cli/trace_renderer.rb +35 -51
  94. data/lib/rigor/cli/triage_command.rb +6 -10
  95. data/lib/rigor/cli/triage_renderer.rb +1 -2
  96. data/lib/rigor/cli/type_of_command.rb +14 -23
  97. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  98. data/lib/rigor/cli/type_scan_command.rb +6 -10
  99. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  100. data/lib/rigor/cli/type_scan_report.rb +2 -3
  101. data/lib/rigor/cli/upgrade_command.rb +2 -3
  102. data/lib/rigor/cli.rb +16 -28
  103. data/lib/rigor/config_audit.rb +35 -44
  104. data/lib/rigor/configuration/dependencies.rb +41 -70
  105. data/lib/rigor/configuration/severity_profile.rb +28 -42
  106. data/lib/rigor/configuration.rb +150 -241
  107. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  108. data/lib/rigor/environment/class_registry.rb +19 -26
  109. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  110. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  111. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  112. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  113. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  114. data/lib/rigor/environment/rbs_loader.rb +420 -518
  115. data/lib/rigor/environment/reflection.rb +28 -53
  116. data/lib/rigor/environment/reporters.rb +13 -24
  117. data/lib/rigor/environment.rb +176 -291
  118. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  119. data/lib/rigor/flow_contribution/element.rb +7 -12
  120. data/lib/rigor/flow_contribution/fact.rb +33 -57
  121. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  122. data/lib/rigor/flow_contribution/merger.rb +32 -47
  123. data/lib/rigor/flow_contribution.rb +37 -55
  124. data/lib/rigor/inference/acceptance.rb +133 -219
  125. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  126. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  127. data/lib/rigor/inference/budget_trace.rb +43 -63
  128. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  129. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  130. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  131. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  132. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  133. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  134. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  135. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  136. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  137. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  138. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  139. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  140. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  141. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  142. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  143. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  144. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  145. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  146. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  147. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  148. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  149. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  150. data/lib/rigor/inference/def_return_typer.rb +8 -14
  151. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  152. data/lib/rigor/inference/expression_typer.rb +774 -1152
  153. data/lib/rigor/inference/fallback.rb +8 -12
  154. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  155. data/lib/rigor/inference/flow_tracer.rb +25 -36
  156. data/lib/rigor/inference/hkt_body.rb +45 -68
  157. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  158. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  159. data/lib/rigor/inference/hkt_registry.rb +50 -76
  160. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  161. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  162. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  163. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  164. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  165. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  166. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  167. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  168. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  169. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  170. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  171. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  172. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  173. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  174. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  175. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  176. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  177. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  178. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  179. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  180. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  181. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  182. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  183. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  184. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  185. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  186. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  187. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  188. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  189. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  190. data/lib/rigor/inference/mutation_widening.rb +115 -158
  191. data/lib/rigor/inference/narrowing.rb +509 -727
  192. data/lib/rigor/inference/origin_lookup.rb +38 -0
  193. data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
  194. data/lib/rigor/inference/precision_scanner.rb +26 -35
  195. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  196. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  197. data/lib/rigor/inference/protection_scanner.rb +16 -18
  198. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  199. data/lib/rigor/inference/scope_indexer.rb +473 -817
  200. data/lib/rigor/inference/statement_evaluator.rb +706 -1111
  201. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  202. data/lib/rigor/inference/synthetic_method.rb +9 -16
  203. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  204. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  205. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  206. data/lib/rigor/language_server/buffer_table.rb +11 -18
  207. data/lib/rigor/language_server/completion_provider.rb +69 -116
  208. data/lib/rigor/language_server/debouncer.rb +16 -25
  209. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  210. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  211. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  212. data/lib/rigor/language_server/hover_provider.rb +8 -11
  213. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  214. data/lib/rigor/language_server/loop.rb +14 -19
  215. data/lib/rigor/language_server/project_context.rb +46 -74
  216. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  217. data/lib/rigor/language_server/server.rb +55 -83
  218. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  219. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  220. data/lib/rigor/language_server/uri.rb +8 -13
  221. data/lib/rigor/language_server.rb +4 -6
  222. data/lib/rigor/mcp/loop.rb +2 -3
  223. data/lib/rigor/mcp/server.rb +4 -7
  224. data/lib/rigor/mcp.rb +3 -6
  225. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  226. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  227. data/lib/rigor/plugin/base.rb +335 -518
  228. data/lib/rigor/plugin/blueprint.rb +14 -23
  229. data/lib/rigor/plugin/box.rb +18 -29
  230. data/lib/rigor/plugin/fact_store.rb +16 -26
  231. data/lib/rigor/plugin/inflector.rb +37 -53
  232. data/lib/rigor/plugin/io_boundary.rb +33 -56
  233. data/lib/rigor/plugin/isolation.rb +42 -55
  234. data/lib/rigor/plugin/load_error.rb +10 -15
  235. data/lib/rigor/plugin/loader.rb +30 -49
  236. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  237. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  238. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  239. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  240. data/lib/rigor/plugin/macro.rb +10 -15
  241. data/lib/rigor/plugin/manifest.rb +85 -144
  242. data/lib/rigor/plugin/node_context.rb +14 -22
  243. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  244. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  245. data/lib/rigor/plugin/registry.rb +132 -205
  246. data/lib/rigor/plugin/services.rb +21 -33
  247. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  248. data/lib/rigor/plugin/trust_policy.rb +24 -38
  249. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  250. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  251. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  252. data/lib/rigor/protection/mutator.rb +50 -70
  253. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  254. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  255. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  256. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  257. data/lib/rigor/rbs_extended.rb +107 -197
  258. data/lib/rigor/reflection.rb +68 -86
  259. data/lib/rigor/scope/discovery_index.rb +14 -19
  260. data/lib/rigor/scope.rb +255 -310
  261. data/lib/rigor/sig_gen/classification.rb +6 -10
  262. data/lib/rigor/sig_gen/generator.rb +197 -323
  263. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  264. data/lib/rigor/sig_gen/method_candidate.rb +12 -17
  265. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  266. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  267. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  268. data/lib/rigor/sig_gen/renderer.rb +7 -13
  269. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  270. data/lib/rigor/sig_gen/write_result.rb +8 -16
  271. data/lib/rigor/sig_gen/writer.rb +95 -174
  272. data/lib/rigor/sig_gen.rb +3 -6
  273. data/lib/rigor/signature_path_audit.rb +24 -30
  274. data/lib/rigor/source/constant_path.rb +10 -14
  275. data/lib/rigor/source/literals.rb +31 -45
  276. data/lib/rigor/source/node_locator.rb +9 -11
  277. data/lib/rigor/source/node_walker.rb +9 -13
  278. data/lib/rigor/source.rb +3 -4
  279. data/lib/rigor/testing.rb +16 -20
  280. data/lib/rigor/triage/catalogue.rb +38 -62
  281. data/lib/rigor/triage.rb +31 -52
  282. data/lib/rigor/trinary.rb +9 -13
  283. data/lib/rigor/type/acceptance_router.rb +4 -6
  284. data/lib/rigor/type/accepts_result.rb +10 -14
  285. data/lib/rigor/type/app.rb +19 -27
  286. data/lib/rigor/type/bot.rb +4 -6
  287. data/lib/rigor/type/bound_method.rb +10 -15
  288. data/lib/rigor/type/combinator.rb +165 -257
  289. data/lib/rigor/type/constant.rb +23 -34
  290. data/lib/rigor/type/data_class.rb +10 -15
  291. data/lib/rigor/type/data_instance.rb +14 -20
  292. data/lib/rigor/type/difference.rb +21 -32
  293. data/lib/rigor/type/dynamic.rb +3 -5
  294. data/lib/rigor/type/hash_shape.rb +32 -18
  295. data/lib/rigor/type/integer_range.rb +11 -16
  296. data/lib/rigor/type/intersection.rb +27 -42
  297. data/lib/rigor/type/nominal.rb +10 -15
  298. data/lib/rigor/type/plain_lattice.rb +9 -13
  299. data/lib/rigor/type/refined.rb +67 -114
  300. data/lib/rigor/type/singleton.rb +4 -6
  301. data/lib/rigor/type/struct_class.rb +11 -16
  302. data/lib/rigor/type/struct_instance.rb +15 -21
  303. data/lib/rigor/type/tuple.rb +14 -19
  304. data/lib/rigor/type/union.rb +30 -42
  305. data/lib/rigor/type_node/generic.rb +14 -26
  306. data/lib/rigor/type_node/identifier.rb +12 -19
  307. data/lib/rigor/type_node.rb +3 -12
  308. data/lib/rigor/value_semantics.rb +16 -21
  309. data/lib/rigor/version.rb +1 -1
  310. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  311. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  312. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  313. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  314. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  315. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  316. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  317. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  318. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
  319. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  320. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  321. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
  322. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  323. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  324. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  325. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  326. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
  327. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
  328. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
  329. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  330. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  331. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
  332. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  333. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  334. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  335. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  336. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  337. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  338. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  339. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  340. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  341. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  342. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  343. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  344. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  345. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  346. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  347. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  348. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  349. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  350. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  351. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  352. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  353. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  354. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  355. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  356. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  357. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  358. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  359. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  360. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
  361. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
  362. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  363. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  364. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  365. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  366. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  367. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  368. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  369. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  370. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  371. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  372. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  373. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  374. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  375. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  376. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  377. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
  381. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  382. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  383. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  384. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  386. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  387. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  388. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  389. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  390. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  391. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  392. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  393. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  394. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  395. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  396. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  397. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  398. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  399. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  400. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  401. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  402. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  403. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  404. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  405. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  406. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  407. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  408. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
  409. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  410. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  411. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  412. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  413. data/sig/rigor/scope.rbs +6 -0
  414. metadata +2 -1
@@ -2,25 +2,19 @@
2
2
 
3
3
  module Rigor
4
4
  module Plugin
5
- # Frozen, `Ractor.shareable?` description of how to materialise
6
- # a single plugin instance inside a worker.
7
- # [ADR-15](../../../docs/adr/15-ractor-concurrency.md) Phase 3
8
- # introduces the carrier so the eventual worker pool can pass
9
- # `Array<Blueprint>` across a Ractor boundary verbatim; each
10
- # worker calls {#materialize} once at startup, then owns its
11
- # plugin instances (and their mutable per-run accumulators)
12
- # for the lifetime of the worker.
5
+ # Frozen, `Ractor.shareable?` description of how to materialise a single plugin instance inside a worker.
6
+ # [ADR-15](../../../docs/adr/15-ractor-concurrency.md) Phase 3 introduces the carrier so the eventual worker
7
+ # pool can pass `Array<Blueprint>` across a Ractor boundary verbatim; each worker calls {#materialize} once
8
+ # at startup, then owns its plugin instances (and their mutable per-run accumulators) for the lifetime of
9
+ # the worker.
13
10
  #
14
- # Holds the constant path (`String`) of the plugin class — NOT
15
- # the class object itself. Plugin gems are required from the
16
- # main Ractor BEFORE any worker spawns, so every Ractor
17
- # resolves the same constant via `Object.const_get`.
11
+ # Holds the constant path (`String`) of the plugin class — NOT the class object itself. Plugin gems are
12
+ # required from the main Ractor BEFORE any worker spawns, so every Ractor resolves the same constant via
13
+ # `Object.const_get`.
18
14
  #
19
- # The `config` Hash is deep-copied + made shareable at
20
- # construction so the Blueprint stays decoupled from whatever
21
- # Hash the project configuration emitted. The original config
22
- # Hash held by the loader is therefore unaffected by Blueprint
23
- # construction.
15
+ # The `config` Hash is deep-copied + made shareable at construction so the Blueprint stays decoupled from
16
+ # whatever Hash the project configuration emitted. The original config Hash held by the loader is therefore
17
+ # unaffected by Blueprint construction.
24
18
  class Blueprint
25
19
  attr_reader :klass_name, :config
26
20
 
@@ -30,12 +24,9 @@ module Rigor
30
24
  freeze
31
25
  end
32
26
 
33
- # Resolves the plugin class via `Object.const_get`, builds a
34
- # fresh instance bound to the supplied services container,
35
- # and calls `#init(services)`. Mirrors
36
- # {Rigor::Plugin::Loader#instantiate} bit-for-bit so the
37
- # blueprint-driven path stays consistent with the
38
- # configuration-driven load path.
27
+ # Resolves the plugin class via `Object.const_get`, builds a fresh instance bound to the supplied services
28
+ # container, and calls `#init(services)`. Mirrors {Rigor::Plugin::Loader#instantiate} bit-for-bit so the
29
+ # blueprint-driven path stays consistent with the configuration-driven load path.
39
30
  def materialize(services:)
40
31
  klass = Object.const_get(@klass_name)
41
32
  plugin = klass.new(services: services, config: @config)
@@ -2,45 +2,36 @@
2
2
 
3
3
  module Rigor
4
4
  module Plugin
5
- # ADR-39 slice 5 — the isolation boundary for target-library
6
- # invocation. When Rigor is launched with `RUBY_BOX=1` (opt-in; the
7
- # launcher re-execs only when a project opts in), a target library is
8
- # loaded and called inside a `Ruby::Box` so its **core-class
9
- # monkey-patches and gem version cannot contaminate Rigor's main
10
- # space**. `Ruby::Box` is not a security sandbox (the gem's code still
11
- # runs in-process) — it is the *contamination* boundary the rule
12
- # needs, which is sufficient because the rule only ever invokes a
5
+ # ADR-39 slice 5 — the isolation boundary for target-library invocation. When Rigor is launched with
6
+ # `RUBY_BOX=1` (opt-in; the launcher re-execs only when a project opts in), a target library is loaded and
7
+ # called inside a `Ruby::Box` so its **core-class monkey-patches and gem version cannot contaminate Rigor's
8
+ # main space**. `Ruby::Box` is not a security sandbox (the gem's code still runs in-process) — it is the
9
+ # *contamination* boundary the rule needs, which is sufficient because the rule only ever invokes a
13
10
  # declared, trusted, pure target library.
14
11
  #
15
- # **Disabled by default.** {enabled?} is false unless the experimental
16
- # `Ruby::Box` feature is active, so every consumer keeps a non-box
17
- # path and the default behaviour (loading the trusted library into the
18
- # main space, per slices 2/4) is unchanged. The box only ever holds
19
- # the trusted, project-agnostic target libraries (inflection rules,
20
- # the Rack status table); per-project / exact-version boxes are a
21
- # later slice.
12
+ # **Disabled by default.** {enabled?} is false unless the experimental `Ruby::Box` feature is active, so
13
+ # every consumer keeps a non-box path and the default behaviour (loading the trusted library into the main
14
+ # space, per slices 2/4) is unchanged. The box only ever holds the trusted, project-agnostic target
15
+ # libraries (inflection rules, the Rack status table); per-project / exact-version boxes are a later slice.
22
16
  module Box
23
17
  module_function
24
18
 
25
- # True only when the experimental `Ruby::Box` feature is present and
26
- # active (the process was started with `RUBY_BOX=1`). Any error
27
- # answers false so a consumer silently keeps its non-box path.
19
+ # True only when the experimental `Ruby::Box` feature is present and active (the process was started with
20
+ # `RUBY_BOX=1`). Any error answers false so a consumer silently keeps its non-box path.
28
21
  def enabled?
29
22
  defined?(::Ruby::Box) && ::Ruby::Box.respond_to?(:enabled?) && ::Ruby::Box.enabled?
30
23
  rescue StandardError
31
24
  false
32
25
  end
33
26
 
34
- # The process-shared box for trusted, project-agnostic target
35
- # libraries. Built lazily on first use.
27
+ # The process-shared box for trusted, project-agnostic target libraries. Built lazily on first use.
36
28
  def shared
37
29
  @shared ||= ::Ruby::Box.new
38
30
  end
39
31
 
40
- # Requires `feature` into the shared box exactly once. Returns true
41
- # when the feature is available in the box, false when it could not
42
- # be loaded (the caller then declines — never falls back to loading
43
- # into the main space, which would defeat the boundary).
32
+ # Requires `feature` into the shared box exactly once. Returns true when the feature is available in the
33
+ # box, false when it could not be loaded (the caller then declines never falls back to loading into the
34
+ # main space, which would defeat the boundary).
44
35
  def require_feature(feature)
45
36
  @required ||= {}
46
37
  return @required[feature] if @required.key?(feature)
@@ -51,11 +42,9 @@ module Rigor
51
42
  @required[feature] = false
52
43
  end
53
44
 
54
- # Evaluates `code` inside the shared box and returns the result
55
- # across the box boundary. The caller MUST build `code` safely
56
- # interpolate value arguments through `String#inspect` (which
57
- # round-trips as a safe Ruby literal) and only ever interpolate
58
- # method names from a fixed allow-list, never free user input.
45
+ # Evaluates `code` inside the shared box and returns the result across the box boundary. The caller MUST
46
+ # build `code` safely interpolate value arguments through `String#inspect` (which round-trips as a safe
47
+ # Ruby literal) and only ever interpolate method names from a fixed allow-list, never free user input.
59
48
  def eval(code)
60
49
  shared.eval(code)
61
50
  end
@@ -4,23 +4,17 @@ module Rigor
4
4
  module Plugin
5
5
  # Per-run cross-plugin fact store. ADR-9 § "Plugin::FactStore".
6
6
  #
7
- # A plugin publishes typed `(plugin_id, name) -> value` tuples
8
- # in its `Plugin::Base#prepare(services)` hook (slice 3); other
9
- # plugins read them in `#diagnostics_for_file` via
10
- # `services.fact_store.read(plugin_id:, name:)`. The store is
11
- # constructed fresh at the start of every `Analysis::Runner.run`
12
- # and discarded at the end — caching the underlying expensive
13
- # computation is the producer's job (`Plugin::Base.producer`);
14
- # the FactStore just publishes a *reference* to that
7
+ # A plugin publishes typed `(plugin_id, name) -> value` tuples in its `Plugin::Base#prepare(services)` hook
8
+ # (slice 3); other plugins read them in `#diagnostics_for_file` via
9
+ # `services.fact_store.read(plugin_id:, name:)`. The store is constructed fresh at the start of every
10
+ # `Analysis::Runner.run` and discarded at the end — caching the underlying expensive computation is the
11
+ # producer's job (`Plugin::Base.producer`); the FactStore just publishes a *reference* to that
15
12
  # already-cached result.
16
13
  #
17
- # `(plugin_id, name)` is a unique key. A second `publish` with
18
- # the same value is a no-op (`==` comparison); a second
19
- # `publish` with a different value raises {Conflict}. Since
20
- # `plugin_id` namespaces the key, a real conflict only happens
21
- # when a single plugin publishes twice with differing values —
22
- # the conflict signals a plugin-author bug, never a load-time
23
- # interaction between unrelated plugins.
14
+ # `(plugin_id, name)` is a unique key. A second `publish` with the same value is a no-op (`==` comparison);
15
+ # a second `publish` with a different value raises {Conflict}. Since `plugin_id` namespaces the key, a real
16
+ # conflict only happens when a single plugin publishes twice with differing values — the conflict signals a
17
+ # plugin-author bug, never a load-time interaction between unrelated plugins.
24
18
  class FactStore
25
19
  Fact = Data.define(:plugin_id, :name, :value)
26
20
 
@@ -45,15 +39,13 @@ module Rigor
45
39
  @mutex = Mutex.new
46
40
  end
47
41
 
48
- # Writes a `(plugin_id, name) -> value` triple. Idempotent if
49
- # the same value is published twice (`==`); raises
50
- # {Conflict} if the values differ.
42
+ # Writes a `(plugin_id, name) -> value` triple. Idempotent if the same value is published twice (`==`);
43
+ # raises {Conflict} if the values differ.
51
44
  #
52
45
  # @param plugin_id [String] producing plugin's manifest id.
53
- # @param name [Symbol, String] fact name (canonicalised to
54
- # Symbol for lookup).
55
- # @param value [Object] frozen-shape value object the
56
- # producer chose to publish. The value is stored as-is.
46
+ # @param name [Symbol, String] fact name (canonicalised to Symbol for lookup).
47
+ # @param value [Object] frozen-shape value object the producer chose to publish. The value is stored
48
+ # as-is.
57
49
  def publish(plugin_id:, name:, value:)
58
50
  plugin_id = plugin_id.to_s
59
51
  name = name.to_sym
@@ -68,10 +60,8 @@ module Rigor
68
60
  nil
69
61
  end
70
62
 
71
- # @return [Object, nil] the published value, or `nil` when no
72
- # fact is registered. Reads do NOT establish a dependency
73
- # `manifest(consumes:)` (slice 4) is the dependency
74
- # declaration mechanism.
63
+ # @return [Object, nil] the published value, or `nil` when no fact is registered. Reads do NOT establish
64
+ # a dependency `manifest(consumes:)` (slice 4) is the dependency declaration mechanism.
75
65
  def read(plugin_id:, name:)
76
66
  fact = @mutex.synchronize { @facts[[plugin_id.to_s, name.to_sym]] }
77
67
  fact&.value
@@ -2,58 +2,46 @@
2
2
 
3
3
  module Rigor
4
4
  module Plugin
5
- # ADR-39 — the shared inflection helper for the Rails-family plugins
6
- # (`rigor-rails-routes`, `rigor-activerecord`, `rigor-actionpack`).
5
+ # ADR-39 — the shared inflection helper for the Rails-family plugins (`rigor-rails-routes`,
6
+ # `rigor-activerecord`, `rigor-actionpack`).
7
7
  #
8
- # Inflection (`posts` ↔ `post`, `BlogPost` → `blog_posts`) drives
9
- # route-helper and model-name resolution, so an inflection that
10
- # diverges from Rails' *actual* rules produces a wrong helper / model
11
- # name and therefore a **false positive on working code** (a bogus
12
- # `unknown-helper` / `unknown-permit-key`). Rigor therefore inflects
13
- # **only** with the real `ActiveSupport::Inflector` — the authority
14
- # Rails itself uses — and carries **no built-in approximation**: an
15
- # approximation would be exactly the source of wrong facts the
8
+ # Inflection (`posts` ↔ `post`, `BlogPost` → `blog_posts`) drives route-helper and model-name resolution, so
9
+ # an inflection that diverges from Rails' *actual* rules produces a wrong helper / model name and therefore
10
+ # a **false positive on working code** (a bogus `unknown-helper` / `unknown-permit-key`). Rigor therefore
11
+ # inflects **only** with the real `ActiveSupport::Inflector` the authority Rails itself uses — and
12
+ # carries **no built-in approximation**: an approximation would be exactly the source of wrong facts the
16
13
  # false-positive discipline forbids.
17
14
  #
18
- # Per ADR-39 this is a permitted *target-library* invocation: the
19
- # methods called are a fixed, pure, allow-listed set
20
- # ({ALLOWED_METHODS}) on a trusted gem the consuming plugins declare
21
- # as a dependency. (`ActiveSupport::Inflector` is loaded, not the
22
- # analyzed application's code; project-specific inflection rules are
23
- # ingested by *statically parsing* `config/initializers/inflections.rb`
24
- # in a later slice, never by executing it.)
15
+ # Per ADR-39 this is a permitted *target-library* invocation: the methods called are a fixed, pure,
16
+ # allow-listed set ({ALLOWED_METHODS}) on a trusted gem the consuming plugins declare as a dependency.
17
+ # (`ActiveSupport::Inflector` is loaded, not the analyzed application's code; project-specific inflection
18
+ # rules are ingested by *statically parsing* `config/initializers/inflections.rb` in a later slice, never
19
+ # by executing it.)
25
20
  #
26
- # **Absence is silence, never a guess.** When `ActiveSupport::Inflector`
27
- # cannot be loaded (a misconfiguration — the consuming plugins declare
28
- # it as a dependency, so it is present in practice), the inflection
29
- # methods raise {Unavailable} rather than approximate. That raise
30
- # propagates to the caller's per-plugin rescue boundary, so the
31
- # inflection-dependent check degrades to **no diagnostics** — reduced
32
- # coverage, never a wrong fact. A consumer that wants to fail cleanly
33
- # up front can gate on {available?} and emit a single load-error.
21
+ # **Absence is silence, never a guess.** When `ActiveSupport::Inflector` cannot be loaded (a
22
+ # misconfiguration — the consuming plugins declare it as a dependency, so it is present in practice), the
23
+ # inflection methods raise {Unavailable} rather than approximate. That raise propagates to the caller's
24
+ # per-plugin rescue boundary, so the inflection-dependent check degrades to **no diagnostics** — reduced
25
+ # coverage, never a wrong fact. A consumer that wants to fail cleanly up front can gate on {available?}
26
+ # and emit a single load-error.
34
27
  module Inflector
35
- # The pure, side-effect-free `ActiveSupport::Inflector` methods this
36
- # helper is permitted to call (ADR-39 § "safety harness"). The set
37
- # is fixed and greppable — never a dynamic `public_send`.
28
+ # The pure, side-effect-free `ActiveSupport::Inflector` methods this helper is permitted to call (ADR-39
29
+ # § "safety harness"). The set is fixed and greppable — never a dynamic `public_send`.
38
30
  #
39
- # `tableize` is deliberately NOT delegated: `ActiveSupport::Inflector.
40
- # tableize("Admin::User")` returns `"admin/users"`, but ActiveRecord's
41
- # *actual* table name flattens the namespace to `"admin_users"` (the
42
- # table-name computation does more than the pure `tableize` string
43
- # method). So {.tableize} composes the AS-backed `underscore` /
44
- # `pluralize` with the `::`→`_` flattening AR really uses.
31
+ # `tableize` is deliberately NOT delegated: `ActiveSupport::Inflector.tableize("Admin::User")` returns
32
+ # `"admin/users"`, but ActiveRecord's *actual* table name flattens the namespace to `"admin_users"` (the
33
+ # table-name computation does more than the pure `tableize` string method). So {.tableize} composes the
34
+ # AS-backed `underscore` / `pluralize` with the `::`→`_` flattening AR really uses.
45
35
  ALLOWED_METHODS = %i[underscore camelize singularize pluralize classify].freeze
46
36
 
47
- # The target library + the constant the allow-listed methods are
48
- # called on. Passed to {Isolation} so the call runs under the
49
- # configured isolation strategy (none / ruby_box / process).
37
+ # The target library + the constant the allow-listed methods are called on. Passed to {Isolation} so the
38
+ # call runs under the configured isolation strategy (none / ruby_box / process).
50
39
  FEATURE = "active_support/inflector"
51
40
  RECEIVER = "ActiveSupport::Inflector"
52
41
 
53
- # Raised when `ActiveSupport::Inflector` is required for an
54
- # inflection but cannot be loaded. Caught by the per-plugin isolation
55
- # boundary, so it surfaces as "this plugin produced no diagnostics"
56
- # rather than a wrong inflection.
42
+ # Raised when `ActiveSupport::Inflector` is required for an inflection but cannot be loaded. Caught by
43
+ # the per-plugin isolation boundary, so it surfaces as "this plugin produced no diagnostics" rather than
44
+ # a wrong inflection.
57
45
  class Unavailable < StandardError
58
46
  end
59
47
 
@@ -84,17 +72,15 @@ module Rigor
84
72
  invoke(:classify, table_name)
85
73
  end
86
74
 
87
- # `BlogPost` → `blog_posts`; `Admin::User` → `admin_users`.
88
- # Composed (not delegated) so the namespace flattens with `_` the
89
- # way ActiveRecord's table naming does — see {ALLOWED_METHODS}.
75
+ # `BlogPost` → `blog_posts`; `Admin::User` → `admin_users`. Composed (not delegated) so the namespace
76
+ # flattens with `_` the way ActiveRecord's table naming does — see {ALLOWED_METHODS}.
90
77
  def tableize(class_name)
91
78
  underscored = underscore(class_name.to_s.gsub("::", "/")).tr("/", "_")
92
79
  pluralize(underscored)
93
80
  end
94
81
 
95
- # Whether inflection is available under the configured isolation
96
- # strategy. A consumer can gate inflection-dependent work on this to
97
- # emit a single clean load-error instead of letting the first
82
+ # Whether inflection is available under the configured isolation strategy. A consumer can gate
83
+ # inflection-dependent work on this to emit a single clean load-error instead of letting the first
98
84
  # inflection raise. Probes with a trivial pluralization.
99
85
  def available?
100
86
  invoke(:pluralize, "rigor_inflector_probe")
@@ -103,12 +89,10 @@ module Rigor
103
89
  false
104
90
  end
105
91
 
106
- # Delegates an allow-listed method to the real
107
- # `ActiveSupport::Inflector` through the configured isolation
108
- # strategy ({Isolation}: none / ruby_box / process). Raises
109
- # {Unavailable} (never approximates) when the library cannot be
110
- # reached in that strategy — the caller's per-plugin rescue turns
111
- # that into silence, never a wrong inflection.
92
+ # Delegates an allow-listed method to the real `ActiveSupport::Inflector` through the configured
93
+ # isolation strategy ({Isolation}: none / ruby_box / process). Raises {Unavailable} (never approximates)
94
+ # when the library cannot be reached in that strategy the caller's per-plugin rescue turns that into
95
+ # silence, never a wrong inflection.
112
96
  def invoke(name, arg)
113
97
  raise ArgumentError, "method not allow-listed: #{name}" unless ALLOWED_METHODS.include?(name)
114
98
 
@@ -6,46 +6,30 @@ require_relative "access_denied_error"
6
6
 
7
7
  module Rigor
8
8
  module Plugin
9
- # Analyzer-side helper plugins go through to read files and
10
- # (eventually) reach the network. The boundary enforces the
11
- # active {TrustPolicy} and accumulates a {Cache::Descriptor}
12
- # of every read so plugin contributions stay invalidatable
13
- # alongside their inputs.
9
+ # Analyzer-side helper plugins go through to read files and (eventually) reach the network. The boundary
10
+ # enforces the active {TrustPolicy} and accumulates a {Cache::Descriptor} of every read so plugin
11
+ # contributions stay invalidatable alongside their inputs.
14
12
  #
15
- # ADR-2 § "Plugin Trust and I/O Policy" is the binding
16
- # contract. The boundary is **not** a sandbox: a plugin that
17
- # uses `File.read` directly bypasses everything here, and the
18
- # ADR explicitly accepts that trade-off. The discipline is:
19
- # when plugin code goes through this surface, reads stay
20
- # within the trust scope and feed the cache descriptor;
21
- # contributions built on top of out-of-scope reads will not
13
+ # ADR-2 § "Plugin Trust and I/O Policy" is the binding contract. The boundary is **not** a sandbox: a
14
+ # plugin that uses `File.read` directly bypasses everything here, and the ADR explicitly accepts that
15
+ # trade-off. The discipline is: when plugin code goes through this surface, reads stay within the trust
16
+ # scope and feed the cache descriptor; contributions built on top of out-of-scope reads will not
22
17
  # invalidate correctly.
23
18
  #
24
19
  # Slice 2 ships a minimal surface:
25
20
  #
26
- # - `#read_file(path)` — validates against the policy, returns
27
- # the file's contents, and adds a digest-keyed
28
- # {Cache::Descriptor::FileEntry} to the boundary's
29
- # accumulated descriptor.
30
- # - `#open_url(url)` fetches the URL when the policy
31
- # permits it (`network_policy: :allowlist` plus an
32
- # `allowed_url_hosts` match) and raises
33
- # {AccessDeniedError} otherwise. v0.1.2 ships the
34
- # allowlist surface; the default project policy still
35
- # has `network_policy: :disabled` so plugins that want
36
- # network access opt in explicitly through
37
- # `.rigor.yml`'s `plugins_io.network: allowlist` plus
38
- # `plugins_io.allowed_url_hosts: [...]`. The HTTP fetch
39
- # is GET-only over HTTPS, capped at {URL_TIMEOUT_SECONDS}
40
- # wall time and {URL_MAX_BYTES} body size; non-2xx
41
- # responses raise {AccessDeniedError} so plugin code
42
- # doesn't have to rescue mid-build.
43
- # - `#cache_descriptor` — flushes the accumulated entries into
44
- # a fresh {Cache::Descriptor} for the contribution that
45
- # built it. URL fetches contribute `ConfigEntry` rows
46
- # keyed `"url:#{url}"` with the response body's SHA-256
47
- # so contributions invalidate when the remote document
48
- # changes.
21
+ # - `#read_file(path)` — validates against the policy, returns the file's contents, and adds a
22
+ # digest-keyed {Cache::Descriptor::FileEntry} to the boundary's accumulated descriptor.
23
+ # - `#open_url(url)` — fetches the URL when the policy permits it (`network_policy: :allowlist` plus an
24
+ # `allowed_url_hosts` match) and raises {AccessDeniedError} otherwise. v0.1.2 ships the allowlist
25
+ # surface; the default project policy still has `network_policy: :disabled` so plugins that want network
26
+ # access opt in explicitly through `.rigor.yml`'s `plugins_io.network: allowlist` plus
27
+ # `plugins_io.allowed_url_hosts: [...]`. The HTTP fetch is GET-only over HTTPS, capped at
28
+ # {URL_TIMEOUT_SECONDS} wall time and {URL_MAX_BYTES} body size; non-2xx responses raise
29
+ # {AccessDeniedError} so plugin code doesn't have to rescue mid-build.
30
+ # - `#cache_descriptor` flushes the accumulated entries into a fresh {Cache::Descriptor} for the
31
+ # contribution that built it. URL fetches contribute `ConfigEntry` rows keyed `"url:#{url}"` with the
32
+ # response body's SHA-256 so contributions invalidate when the remote document changes.
49
33
  class IoBoundary
50
34
  URL_TIMEOUT_SECONDS = 10
51
35
  URL_MAX_BYTES = 10 * 1024 * 1024
@@ -61,10 +45,9 @@ module Rigor
61
45
  @mutex = Mutex.new
62
46
  end
63
47
 
64
- # Reads the file at `path` after validating it against the
65
- # policy. Raises {AccessDeniedError} when the path is outside
66
- # every allowed read root. Records a `:digest` {FileEntry}
67
- # so the resulting cache slice invalidates on content change.
48
+ # Reads the file at `path` after validating it against the policy. Raises {AccessDeniedError} when the
49
+ # path is outside every allowed read root. Records a `:digest` {FileEntry} so the resulting cache slice
50
+ # invalidates on content change.
68
51
  def read_file(path)
69
52
  absolute = File.expand_path(path.to_s)
70
53
  unless @policy.allow_read?(absolute)
@@ -81,15 +64,11 @@ module Rigor
81
64
  contents
82
65
  end
83
66
 
84
- # Fetches the URL when the policy permits it. Returns the
85
- # response body. Raises {AccessDeniedError} when the policy
86
- # is `:disabled`, the URL scheme is not `https`, the host is
87
- # not on the allowlist, the response is non-2xx, the body
88
- # exceeds {URL_MAX_BYTES}, or the request times out
89
- # ({URL_TIMEOUT_SECONDS}). On success, records a
90
- # `ConfigEntry` keyed `"url:#{url}"` with the body's
91
- # SHA-256 so the cache descriptor invalidates if the remote
92
- # document changes.
67
+ # Fetches the URL when the policy permits it. Returns the response body. Raises {AccessDeniedError} when
68
+ # the policy is `:disabled`, the URL scheme is not `https`, the host is not on the allowlist, the
69
+ # response is non-2xx, the body exceeds {URL_MAX_BYTES}, or the request times out
70
+ # ({URL_TIMEOUT_SECONDS}). On success, records a `ConfigEntry` keyed `"url:#{url}"` with the body's
71
+ # SHA-256 so the cache descriptor invalidates if the remote document changes.
93
72
  def open_url(url)
94
73
  url_string = url.to_s
95
74
  unless @policy.allow_url?(url_string)
@@ -107,10 +86,9 @@ module Rigor
107
86
  body
108
87
  end
109
88
 
110
- # @return [Rigor::Cache::Descriptor] frozen snapshot of every
111
- # file / URL the boundary has read so far. Calling this
112
- # multiple times yields equal descriptors; subsequent
113
- # reads expand the underlying record tables.
89
+ # @return [Rigor::Cache::Descriptor] frozen snapshot of every file / URL the boundary has read so far.
90
+ # Calling this multiple times yields equal descriptors; subsequent reads expand the underlying record
91
+ # tables.
114
92
  def cache_descriptor
115
93
  files, configs = @mutex.synchronize { [@file_entries.values.dup, @config_entries.values.dup] }
116
94
  Cache::Descriptor.new(files: files, configs: configs)
@@ -132,10 +110,9 @@ module Rigor
132
110
  end
133
111
  end
134
112
 
135
- # Default HTTP client wrapping `Net::HTTP`. Wraps a single
136
- # `GET` over HTTPS. Specs inject a fake client that conforms
137
- # to the same `#get(url, timeout:, max_bytes:)` shape so the
138
- # tests don't require network access.
113
+ # Default HTTP client wrapping `Net::HTTP`. Wraps a single `GET` over HTTPS. Specs inject a fake client
114
+ # that conforms to the same `#get(url, timeout:, max_bytes:)` shape so the tests don't require network
115
+ # access.
139
116
  class DefaultHttpClient
140
117
  def get(url, timeout:, max_bytes:)
141
118
  require "net/http"