rigortype 0.2.6 → 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 (432) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -3
  3. data/docs/manual/02-cli-reference.md +2 -1
  4. data/docs/manual/04-diagnostics.md +7 -4
  5. data/docs/manual/06-baseline.md +5 -2
  6. data/docs/manual/08-skills.md +21 -0
  7. data/docs/manual/15-type-protection-coverage.md +6 -4
  8. data/lib/rigor/analysis/baseline.rb +56 -96
  9. data/lib/rigor/analysis/buffer_binding.rb +13 -20
  10. data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
  11. data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
  12. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
  13. data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
  14. data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
  15. data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
  16. data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
  17. data/lib/rigor/analysis/check_rules.rb +174 -274
  18. data/lib/rigor/analysis/dependency_recorder.rb +26 -38
  19. data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
  20. data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
  21. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
  22. data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
  23. data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
  24. data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
  25. data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
  26. data/lib/rigor/analysis/diagnostic.rb +48 -75
  27. data/lib/rigor/analysis/erb_template_detector.rb +13 -18
  28. data/lib/rigor/analysis/fact_store.rb +12 -22
  29. data/lib/rigor/analysis/incremental.rb +47 -63
  30. data/lib/rigor/analysis/incremental_session.rb +74 -100
  31. data/lib/rigor/analysis/project_scan.rb +12 -23
  32. data/lib/rigor/analysis/result.rb +3 -5
  33. data/lib/rigor/analysis/rule_catalog.rb +67 -95
  34. data/lib/rigor/analysis/run_stats.rb +38 -61
  35. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
  36. data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
  37. data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
  38. data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
  39. data/lib/rigor/analysis/runner.rb +220 -333
  40. data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
  41. data/lib/rigor/analysis/worker_session.rb +64 -102
  42. data/lib/rigor/ast/type_node.rb +6 -8
  43. data/lib/rigor/ast.rb +6 -10
  44. data/lib/rigor/bleeding_edge.rb +27 -35
  45. data/lib/rigor/builtins/hkt_builtins.rb +7 -14
  46. data/lib/rigor/builtins/imported_refinements.rb +106 -149
  47. data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
  48. data/lib/rigor/builtins/regex_refinement.rb +32 -43
  49. data/lib/rigor/builtins/static_return_refinements.rb +57 -81
  50. data/lib/rigor/cache/descriptor.rb +58 -94
  51. data/lib/rigor/cache/incremental_snapshot.rb +32 -48
  52. data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
  53. data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
  54. data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
  55. data/lib/rigor/cache/rbs_constant_table.rb +8 -13
  56. data/lib/rigor/cache/rbs_descriptor.rb +8 -13
  57. data/lib/rigor/cache/rbs_environment.rb +11 -19
  58. data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
  59. data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
  60. data/lib/rigor/cache/store.rb +96 -151
  61. data/lib/rigor/cli/annotate_command.rb +51 -88
  62. data/lib/rigor/cli/baseline_command.rb +10 -17
  63. data/lib/rigor/cli/check_command.rb +121 -199
  64. data/lib/rigor/cli/check_runner_factory.rb +8 -14
  65. data/lib/rigor/cli/ci_detector.rb +9 -14
  66. data/lib/rigor/cli/command.rb +8 -14
  67. data/lib/rigor/cli/coverage_command.rb +45 -20
  68. data/lib/rigor/cli/coverage_mutation.rb +14 -19
  69. data/lib/rigor/cli/coverage_report.rb +0 -2
  70. data/lib/rigor/cli/coverage_scan.rb +7 -11
  71. data/lib/rigor/cli/diagnostic_formats.rb +43 -62
  72. data/lib/rigor/cli/diff_command.rb +10 -20
  73. data/lib/rigor/cli/docs_command.rb +26 -40
  74. data/lib/rigor/cli/doctor_command.rb +8 -9
  75. data/lib/rigor/cli/explain_command.rb +4 -7
  76. data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
  77. data/lib/rigor/cli/fused_protection_report.rb +6 -8
  78. data/lib/rigor/cli/lsp_command.rb +11 -19
  79. data/lib/rigor/cli/mcp_command.rb +4 -6
  80. data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
  81. data/lib/rigor/cli/mutation_protection_report.rb +6 -9
  82. data/lib/rigor/cli/options.rb +11 -19
  83. data/lib/rigor/cli/plugin_command.rb +18 -30
  84. data/lib/rigor/cli/plugins_command.rb +29 -51
  85. data/lib/rigor/cli/plugins_renderer.rb +12 -20
  86. data/lib/rigor/cli/prism_colorizer.rb +13 -19
  87. data/lib/rigor/cli/protection_renderer.rb +5 -7
  88. data/lib/rigor/cli/protection_report.rb +27 -17
  89. data/lib/rigor/cli/renderable.rb +4 -6
  90. data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
  91. data/lib/rigor/cli/sig_gen_command.rb +14 -26
  92. data/lib/rigor/cli/skill_command.rb +70 -36
  93. data/lib/rigor/cli/skill_describe.rb +40 -64
  94. data/lib/rigor/cli/trace_command.rb +9 -16
  95. data/lib/rigor/cli/trace_renderer.rb +35 -51
  96. data/lib/rigor/cli/triage_command.rb +6 -10
  97. data/lib/rigor/cli/triage_renderer.rb +1 -2
  98. data/lib/rigor/cli/type_of_command.rb +14 -23
  99. data/lib/rigor/cli/type_of_renderer.rb +3 -5
  100. data/lib/rigor/cli/type_scan_command.rb +6 -10
  101. data/lib/rigor/cli/type_scan_renderer.rb +3 -5
  102. data/lib/rigor/cli/type_scan_report.rb +2 -3
  103. data/lib/rigor/cli/upgrade_command.rb +2 -3
  104. data/lib/rigor/cli.rb +16 -28
  105. data/lib/rigor/config_audit.rb +35 -44
  106. data/lib/rigor/configuration/dependencies.rb +41 -70
  107. data/lib/rigor/configuration/severity_profile.rb +28 -42
  108. data/lib/rigor/configuration.rb +150 -241
  109. data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
  110. data/lib/rigor/environment/class_registry.rb +19 -26
  111. data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
  112. data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
  113. data/lib/rigor/environment/lockfile_resolver.rb +31 -47
  114. data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
  115. data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
  116. data/lib/rigor/environment/rbs_loader.rb +433 -503
  117. data/lib/rigor/environment/reflection.rb +28 -53
  118. data/lib/rigor/environment/reporters.rb +13 -24
  119. data/lib/rigor/environment.rb +176 -291
  120. data/lib/rigor/flow_contribution/conflict.rb +16 -27
  121. data/lib/rigor/flow_contribution/element.rb +7 -12
  122. data/lib/rigor/flow_contribution/fact.rb +33 -57
  123. data/lib/rigor/flow_contribution/merge_result.rb +6 -9
  124. data/lib/rigor/flow_contribution/merger.rb +32 -47
  125. data/lib/rigor/flow_contribution.rb +37 -55
  126. data/lib/rigor/inference/acceptance.rb +133 -219
  127. data/lib/rigor/inference/block_parameter_binder.rb +52 -82
  128. data/lib/rigor/inference/body_fixpoint.rb +22 -30
  129. data/lib/rigor/inference/budget_trace.rb +43 -63
  130. data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
  131. data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
  132. data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
  133. data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
  134. data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
  135. data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
  136. data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
  137. data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
  138. data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
  139. data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
  140. data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
  141. data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
  142. data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
  143. data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
  144. data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
  145. data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
  146. data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
  147. data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
  148. data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
  149. data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
  150. data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
  151. data/lib/rigor/inference/coverage_scanner.rb +13 -18
  152. data/lib/rigor/inference/def_return_typer.rb +8 -14
  153. data/lib/rigor/inference/dynamic_origin.rb +23 -20
  154. data/lib/rigor/inference/expression_typer.rb +774 -1152
  155. data/lib/rigor/inference/fallback.rb +8 -12
  156. data/lib/rigor/inference/fallback_tracer.rb +4 -10
  157. data/lib/rigor/inference/flow_tracer.rb +25 -36
  158. data/lib/rigor/inference/hkt_body.rb +45 -68
  159. data/lib/rigor/inference/hkt_body_parser.rb +33 -52
  160. data/lib/rigor/inference/hkt_reducer.rb +38 -59
  161. data/lib/rigor/inference/hkt_registry.rb +50 -76
  162. data/lib/rigor/inference/indexed_narrowing.rb +55 -81
  163. data/lib/rigor/inference/macro_block_self_type.rb +21 -34
  164. data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
  165. data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
  166. data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
  167. data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
  168. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
  169. data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
  170. data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
  171. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
  172. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
  173. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
  174. data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
  175. data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
  176. data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
  177. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
  178. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
  179. data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
  180. data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
  181. data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
  182. data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
  183. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
  184. data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
  185. data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
  186. data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
  187. data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
  188. data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
  189. data/lib/rigor/inference/method_dispatcher.rb +378 -524
  190. data/lib/rigor/inference/method_parameter_binder.rb +81 -114
  191. data/lib/rigor/inference/multi_target_binder.rb +51 -68
  192. data/lib/rigor/inference/mutation_widening.rb +115 -158
  193. data/lib/rigor/inference/narrowing.rb +509 -727
  194. data/lib/rigor/inference/origin_lookup.rb +38 -0
  195. data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
  196. data/lib/rigor/inference/precision_scanner.rb +26 -35
  197. data/lib/rigor/inference/project_patched_methods.rb +20 -32
  198. data/lib/rigor/inference/project_patched_scanner.rb +23 -37
  199. data/lib/rigor/inference/protection_scanner.rb +16 -18
  200. data/lib/rigor/inference/rbs_type_translator.rb +49 -66
  201. data/lib/rigor/inference/scope_indexer.rb +473 -817
  202. data/lib/rigor/inference/statement_evaluator.rb +706 -1115
  203. data/lib/rigor/inference/struct_fold_safety.rb +34 -48
  204. data/lib/rigor/inference/synthetic_method.rb +9 -16
  205. data/lib/rigor/inference/synthetic_method_index.rb +20 -35
  206. data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
  207. data/lib/rigor/language_server/buffer_resolution.rb +6 -9
  208. data/lib/rigor/language_server/buffer_table.rb +11 -18
  209. data/lib/rigor/language_server/completion_provider.rb +69 -116
  210. data/lib/rigor/language_server/debouncer.rb +16 -25
  211. data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
  212. data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
  213. data/lib/rigor/language_server/folding_range_provider.rb +11 -19
  214. data/lib/rigor/language_server/hover_provider.rb +8 -11
  215. data/lib/rigor/language_server/hover_renderer.rb +56 -93
  216. data/lib/rigor/language_server/loop.rb +14 -19
  217. data/lib/rigor/language_server/project_context.rb +46 -74
  218. data/lib/rigor/language_server/selection_range_provider.rb +9 -12
  219. data/lib/rigor/language_server/server.rb +55 -83
  220. data/lib/rigor/language_server/signature_help_provider.rb +29 -46
  221. data/lib/rigor/language_server/synchronized_writer.rb +4 -7
  222. data/lib/rigor/language_server/uri.rb +8 -13
  223. data/lib/rigor/language_server.rb +4 -6
  224. data/lib/rigor/mcp/loop.rb +2 -3
  225. data/lib/rigor/mcp/server.rb +4 -7
  226. data/lib/rigor/mcp.rb +3 -6
  227. data/lib/rigor/plugin/access_denied_error.rb +5 -8
  228. data/lib/rigor/plugin/additional_initializer.rb +21 -31
  229. data/lib/rigor/plugin/base.rb +335 -518
  230. data/lib/rigor/plugin/blueprint.rb +14 -23
  231. data/lib/rigor/plugin/box.rb +18 -29
  232. data/lib/rigor/plugin/fact_store.rb +16 -26
  233. data/lib/rigor/plugin/inflector.rb +37 -53
  234. data/lib/rigor/plugin/io_boundary.rb +33 -56
  235. data/lib/rigor/plugin/isolation.rb +42 -55
  236. data/lib/rigor/plugin/load_error.rb +10 -15
  237. data/lib/rigor/plugin/loader.rb +30 -49
  238. data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
  239. data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
  240. data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
  241. data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
  242. data/lib/rigor/plugin/macro.rb +10 -15
  243. data/lib/rigor/plugin/manifest.rb +85 -144
  244. data/lib/rigor/plugin/node_context.rb +14 -22
  245. data/lib/rigor/plugin/node_rule_walk.rb +49 -74
  246. data/lib/rigor/plugin/protocol_contract.rb +25 -39
  247. data/lib/rigor/plugin/registry.rb +132 -205
  248. data/lib/rigor/plugin/services.rb +21 -33
  249. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
  250. data/lib/rigor/plugin/trust_policy.rb +24 -38
  251. data/lib/rigor/plugin/type_node_resolver.rb +15 -24
  252. data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
  253. data/lib/rigor/protection/mutation_scanner.rb +27 -35
  254. data/lib/rigor/protection/mutator.rb +50 -70
  255. data/lib/rigor/protection/test_suite_oracle.rb +20 -27
  256. data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
  257. data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
  258. data/lib/rigor/rbs_extended/reporter.rb +24 -40
  259. data/lib/rigor/rbs_extended.rb +107 -197
  260. data/lib/rigor/reflection.rb +68 -86
  261. data/lib/rigor/scope/discovery_index.rb +14 -19
  262. data/lib/rigor/scope.rb +255 -310
  263. data/lib/rigor/sig_gen/classification.rb +6 -10
  264. data/lib/rigor/sig_gen/generator.rb +208 -309
  265. data/lib/rigor/sig_gen/layout_index.rb +12 -20
  266. data/lib/rigor/sig_gen/method_candidate.rb +15 -15
  267. data/lib/rigor/sig_gen/observation_collector.rb +38 -70
  268. data/lib/rigor/sig_gen/observed_call.rb +13 -23
  269. data/lib/rigor/sig_gen/path_mapper.rb +17 -29
  270. data/lib/rigor/sig_gen/renderer.rb +7 -13
  271. data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
  272. data/lib/rigor/sig_gen/write_result.rb +8 -16
  273. data/lib/rigor/sig_gen/writer.rb +141 -173
  274. data/lib/rigor/sig_gen.rb +3 -6
  275. data/lib/rigor/signature_path_audit.rb +24 -30
  276. data/lib/rigor/source/constant_path.rb +10 -14
  277. data/lib/rigor/source/literals.rb +31 -45
  278. data/lib/rigor/source/node_locator.rb +9 -11
  279. data/lib/rigor/source/node_walker.rb +9 -13
  280. data/lib/rigor/source.rb +3 -4
  281. data/lib/rigor/testing.rb +16 -20
  282. data/lib/rigor/triage/catalogue.rb +38 -62
  283. data/lib/rigor/triage.rb +31 -52
  284. data/lib/rigor/trinary.rb +9 -13
  285. data/lib/rigor/type/acceptance_router.rb +4 -6
  286. data/lib/rigor/type/accepts_result.rb +10 -14
  287. data/lib/rigor/type/app.rb +19 -27
  288. data/lib/rigor/type/bot.rb +4 -6
  289. data/lib/rigor/type/bound_method.rb +10 -15
  290. data/lib/rigor/type/combinator.rb +165 -257
  291. data/lib/rigor/type/constant.rb +23 -34
  292. data/lib/rigor/type/data_class.rb +10 -15
  293. data/lib/rigor/type/data_instance.rb +14 -20
  294. data/lib/rigor/type/difference.rb +21 -32
  295. data/lib/rigor/type/dynamic.rb +3 -5
  296. data/lib/rigor/type/hash_shape.rb +32 -18
  297. data/lib/rigor/type/integer_range.rb +11 -16
  298. data/lib/rigor/type/intersection.rb +27 -42
  299. data/lib/rigor/type/nominal.rb +10 -15
  300. data/lib/rigor/type/plain_lattice.rb +9 -13
  301. data/lib/rigor/type/refined.rb +67 -114
  302. data/lib/rigor/type/singleton.rb +4 -6
  303. data/lib/rigor/type/struct_class.rb +11 -16
  304. data/lib/rigor/type/struct_instance.rb +15 -21
  305. data/lib/rigor/type/tuple.rb +14 -19
  306. data/lib/rigor/type/union.rb +30 -42
  307. data/lib/rigor/type_node/generic.rb +14 -26
  308. data/lib/rigor/type_node/identifier.rb +12 -19
  309. data/lib/rigor/type_node.rb +3 -12
  310. data/lib/rigor/value_semantics.rb +16 -21
  311. data/lib/rigor/version.rb +1 -1
  312. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
  313. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
  314. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
  315. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
  316. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
  317. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
  318. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
  319. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
  320. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
  321. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
  322. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
  323. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +107 -83
  324. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
  325. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
  326. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
  327. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
  328. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
  329. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
  330. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
  331. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
  332. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
  333. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -236
  334. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
  335. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
  336. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
  337. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
  338. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
  339. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
  340. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
  341. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
  342. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
  343. data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
  344. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
  345. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
  346. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
  347. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
  348. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
  349. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
  350. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
  351. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
  352. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
  353. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
  354. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
  355. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
  356. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
  357. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
  358. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
  359. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
  360. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
  361. data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
  362. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +20 -36
  363. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +23 -30
  364. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
  365. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
  366. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
  367. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
  368. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
  369. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
  370. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
  371. data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
  372. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
  373. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
  374. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
  375. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
  376. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
  377. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
  378. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
  379. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
  380. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
  381. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
  382. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
  383. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
  384. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
  385. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
  386. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
  387. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
  388. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
  389. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
  390. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
  391. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
  392. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
  393. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
  394. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
  395. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
  396. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
  397. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
  398. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
  399. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
  400. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
  401. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
  402. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
  403. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
  404. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
  405. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
  406. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
  407. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
  408. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
  409. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
  410. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +182 -328
  411. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
  412. data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
  413. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
  414. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
  415. data/sig/rigor/scope.rbs +6 -0
  416. data/skills/rigor-ask/SKILL.md +21 -1
  417. data/skills/rigor-baseline-reduce/SKILL.md +16 -0
  418. data/skills/rigor-ci-setup/SKILL.md +96 -249
  419. data/skills/rigor-doctor/SKILL.md +39 -49
  420. data/skills/rigor-doctor/references/01-checks.md +52 -0
  421. data/skills/rigor-editor-setup/SKILL.md +14 -0
  422. data/skills/rigor-mcp-setup/SKILL.md +14 -0
  423. data/skills/rigor-monkeypatch-resolve/SKILL.md +15 -0
  424. data/skills/rigor-plugin-author/SKILL.md +16 -0
  425. data/skills/rigor-plugin-review/SKILL.md +174 -0
  426. data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +214 -0
  427. data/skills/rigor-plugin-tune/SKILL.md +21 -2
  428. data/skills/rigor-project-init/SKILL.md +16 -0
  429. data/skills/rigor-protection-uplift/SKILL.md +15 -0
  430. data/skills/rigor-rbs-setup/SKILL.md +15 -0
  431. data/skills/rigor-upgrade/SKILL.md +16 -0
  432. metadata +8 -4
@@ -27,6 +27,22 @@ environment.
27
27
  > covers the in-repo layout, `plugin_helpers.rb`, and `make verify`.
28
28
  > This skill is for plugins that live in *your* project.
29
29
 
30
+ ## First: load the version-current copy
31
+
32
+ The plugin contract is pre-1.0 and moving (see the next section), so this
33
+ skill's step detail — in its `references/` files — is exactly the kind that
34
+ drifts between releases. Follow the copy that ships with the **installed**
35
+ Rigor rather than any vendored or frozen copy of this file. Get the complete
36
+ current procedure (body + all references, inline) in one call:
37
+
38
+ ```sh
39
+ rigor skill --full rigor-plugin-author
40
+ ```
41
+
42
+ If you already loaded this skill *via* `rigor skill` you have the current
43
+ copy — just proceed. If the `rigor` command is not available, run
44
+ **`rigor-next-steps`** to install Rigor first, then come back.
45
+
30
46
  ## Important — the plugin contract is a preview (pre-1.0)
31
47
 
32
48
  Rigor's plugin contract (ADR-2) is **not yet frozen**. It stabilises
@@ -0,0 +1,174 @@
1
+ ---
2
+ name: rigor-plugin-review
3
+ description: |
4
+ Review an existing Rigor plugin's source against the current authoring contract and produce a prioritized upgrade path — the modernization counterpart to rigor-plugin-author. Audits config-default declaration (ADR-40), the AST-walk model (node_rule vs a hand-rolled traversal), return-type / narrowing hooks (dynamic_return / narrowing_facts, not the removed flow_contribution_for or deprecated type_specifier), the ADR-60 WD4 authoring helpers (diagnostic / diagnostics_for / suggest / producer_value / read_fact), engine-collaboration vs reimplementation, cache-producer soundness, manifest-field hygiene, and doc freshness. Triggers: "review this Rigor plugin", "does my plugin follow best practices", "upgrade our rigor-prefixed plugin to the latest contract", "modernize this plugin", "is this plugin using the current API". NOT for authoring a new plugin (use rigor-plugin-author), enabling bundled plugins on a project (use rigor-plugin-tune), or tuning plugin config.
5
+ license: MPL-2.0
6
+ metadata:
7
+ version: 0.1.0
8
+ homepage: https://github.com/rigortype/rigor
9
+ ---
10
+
11
+ # Rigor Plugin Review
12
+
13
+ Audit an **existing** Rigor plugin — a bundled one in the rigor
14
+ monorepo (`plugins/` or `examples/`), or your own `rigor-<id>` gem —
15
+ against the current `Rigor::Plugin::Base` authoring contract, and hand
16
+ back a **prioritized upgrade path**. This is the review / upgrade
17
+ counterpart to `rigor-plugin-author` (which creates new plugins).
18
+
19
+ Plugins written before a contract addition keep working — the gate is
20
+ compatibility, not currency — but they drift from the idiom other
21
+ authors copy. The commonest drift, in rough order of how often it
22
+ appears:
23
+
24
+ 1. **Config defaults** declared with a `DEFAULT_*` constant +
25
+ `config.fetch(k, DEFAULT)` instead of `config_schema {kind:,
26
+ default:}` (ADR-40).
27
+ 2. **Hand-rolled boilerplate** the ADR-60 WD4 authoring helpers now
28
+ own — a Levenshtein "did you mean", a `@table`/`@load_error` memo +
29
+ rescue, a `Diagnostic.new` where a node exists.
30
+ 3. **A hand-rolled AST walk** where the engine-owned `node_rule` now
31
+ fits (ADR-37 / ADR-52).
32
+ 4. **Removed / renamed hooks** still named — `flow_contribution_for`
33
+ (deleted, ADR-52 WD3) or `type_specifier` (deprecated alias for
34
+ `narrowing_facts`, ADR-80).
35
+ 5. **Stale docs** — archaeology about deleted hooks, pinned version
36
+ references that no longer mean anything.
37
+
38
+ ## First: load the version-current copy
39
+
40
+ This skill audits against a contract that moves release to release (hook
41
+ renames, new helpers, deprecations), so its checklist — in its
42
+ `references/` files — is only as good as the Rigor it ships with. Follow
43
+ the copy that ships with the **installed** Rigor rather than any vendored
44
+ or frozen copy of this file. Get the complete current procedure (body + all
45
+ references, inline) in one call:
46
+
47
+ ```sh
48
+ rigor skill --full rigor-plugin-review
49
+ ```
50
+
51
+ If you already loaded this skill *via* `rigor skill` you have the current
52
+ copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
53
+ **`rigor-next-steps`** to install Rigor first, then come back.
54
+
55
+ ## When to use / not use
56
+
57
+ **Use it** when someone asks to review a plugin's quality, check it
58
+ against best practices, or upgrade it to the current contract — whether
59
+ it lives in the rigor monorepo or in an external repo.
60
+
61
+ **Do not use it** for:
62
+
63
+ - **Authoring a new plugin** → `rigor-plugin-author`.
64
+ - **Choosing / enabling bundled plugins on a project** →
65
+ `rigor-plugin-tune`.
66
+ - **A behavioural bug in a plugin** — that is ordinary debugging, not a
67
+ contract-conformance pass.
68
+
69
+ ## Read the plugin — and read the contract
70
+
71
+ Rigor is installed on disk, so both the plugin under review and the
72
+ worked-example plugins are readable source:
73
+
74
+ ```sh
75
+ rigor plugin list # every bundled + example plugin, with paths
76
+ rigor plugin print rigor-<id> # a plugin's main source, inline
77
+ rigor plugin path rigor-<id> # its directory, to browse
78
+ ```
79
+
80
+ The **authoritative** authoring surface — the one this review scores
81
+ against — is the internal spec, not this file:
82
+
83
+ - [`docs/internal-spec/plugin.md`](https://github.com/rigortype/rigor/blob/master/docs/internal-spec/plugin.md)
84
+ — manifest, `node_rule` / `node_file_context`, `dynamic_return` /
85
+ `narrowing_facts`, the `#diagnostic` / `#diagnostics_for` / `.suggest`
86
+ / `#read_fact` author helpers, `config_schema` `{kind:, default:}`.
87
+ - [`docs/internal-spec/plugin-cache-producers.md`](https://github.com/rigortype/rigor/blob/master/docs/internal-spec/plugin-cache-producers.md)
88
+ — `producer` / `#cache_for` / `#producer_value` / `#producer_error`,
89
+ ADR-60 WD3 record-and-validate.
90
+ - [`docs/internal-spec/plugin-trust.md`](https://github.com/rigortype/rigor/blob/master/docs/internal-spec/plugin-trust.md)
91
+ — `TrustPolicy` / `IoBoundary`.
92
+
93
+ When the checklist below and the spec disagree, **the spec binds** —
94
+ it tracks the installed `rigor` version; this skill is a snapshot.
95
+
96
+ ## Procedure
97
+
98
+ ### Phase 1 — Inventory
99
+
100
+ Read the plugin's `lib/**/*.rb`, its `README.md`, and its integration /
101
+ unit spec. Note: the `manifest(...)` block, every `config.fetch` /
102
+ `DEFAULT_*` constant, every `Rigor::Analysis::Diagnostic.new`, any
103
+ hand-rolled `levenshtein` / `each_child` walk / cross-plugin `@*_resolved`
104
+ flag, and every mention of `flow_contribution_for` / `type_specifier`.
105
+
106
+ ### Phase 2 — Score against the checklist
107
+
108
+ Walk [`references/01-best-practices-checklist.md`](references/01-best-practices-checklist.md)
109
+ concern by concern. For each finding, record: the smell, the modern
110
+ replacement, the authoritative citation, and — critically — whether the
111
+ change is **mechanical** (byte-identical diagnostics expected) or
112
+ **design-level** (needs judgment / may change behaviour).
113
+
114
+ ### Phase 3 — Establish the oracle BEFORE changing anything
115
+
116
+ The plugin's integration spec is the contract you must preserve. Run it
117
+ green first, so you can prove each later step is a faithful refactor:
118
+
119
+ ```sh
120
+ # external gem:
121
+ bundle exec rspec spec/
122
+ # in the rigor monorepo:
123
+ nix … develop --command bundle exec rspec spec/integration/<plugins|examples>/<id>_plugin_spec.rb
124
+ ```
125
+
126
+ If there is no spec, **write one first** (per `rigor-plugin-author`
127
+ Phase 3) — a modernization with no oracle is a guess.
128
+
129
+ ### Phase 4 — Apply the upgrade path, mechanical first
130
+
131
+ Order the work low-risk → high-risk, and **re-run the spec after every
132
+ step**:
133
+
134
+ 1. **Mechanical (expect byte-identical diagnostics):** ADR-40 config
135
+ defaults · helper swaps (`suggest` / `producer_value` / `diagnostic`
136
+ / `diagnostics_for` / `read_fact`) · manifest-field renames (ADR-60
137
+ WD1/WD2) · doc freshness. A spec that changes here means the swap was
138
+ not faithful — fix it, do not re-baseline.
139
+ 2. **Design-level (may change behaviour — validate empirically):** an
140
+ AST-walk migration onto `node_rule`; an engine-collaboration refactor
141
+ that reads `Scope#type_of` instead of a hand-rolled binding map.
142
+ **Do not delete hand-rolled state before proving the engine gives you
143
+ the same information** — see the `rigor-units` trap in the checklist
144
+ (the diagnostics-side `Scope` is a *seed entry scope* without
145
+ flow-accumulated local bindings, so a cross-statement binding map can
146
+ be necessary, not redundant).
147
+
148
+ ### Phase 5 — Verify
149
+
150
+ ```sh
151
+ rigor check <plugin>/lib # ADR-43 contract self-check — MUST be clean
152
+ rigor plugins --strict # the plugin still loads
153
+ rigor plugins --capabilities # node-rule types / dynamic_return receivers look right
154
+ bundle exec rspec … # the oracle spec, still green
155
+ ```
156
+
157
+ In the **rigor monorepo**, the gate is `make check-plugins` (runs
158
+ `rigor check` over every `plugins/*/lib` + `examples/*/lib`) plus
159
+ `make verify`; land the change as its own commit(s) with the spec as
160
+ the byte-identical gate.
161
+
162
+ ## Output
163
+
164
+ Hand the user a table — smell → replacement → authority → mechanical /
165
+ design — ranked so the mechanical, oracle-gated wins land first, and
166
+ call out any finding (like the units binding-map case) where the
167
+ "obvious" modernization is actually wrong. If nothing is stale, say so
168
+ plainly: a plugin that already tracks the current contract is a pass,
169
+ not an occasion to invent churn.
170
+
171
+ ## Next step
172
+
173
+ Re-run `rigor skill describe` for the next move, or `rigor-plugin-author`
174
+ if the review surfaced a *new* capability the plugin should grow.
@@ -0,0 +1,214 @@
1
+ # Plugin best-practices checklist
2
+
3
+ Score a plugin concern by concern. Each row is **smell → modern
4
+ replacement → authority**. The authority column names the binding
5
+ surface (`docs/internal-spec/*` or the ADR); when it disagrees with
6
+ this file, it wins.
7
+
8
+ `M` = mechanical (byte-identical diagnostics expected; oracle-gated).
9
+ `D` = design-level (may change behaviour; validate empirically).
10
+
11
+ ---
12
+
13
+ ## 1. Manifest & config defaults (ADR-40) — `M`
14
+
15
+ **Smell:**
16
+
17
+ ```ruby
18
+ DEFAULT_ROUTES_FILE = "config/routes.yml"
19
+ config_schema: { "routes_file" => :string }
20
+ # …
21
+ @routes_file = config.fetch("routes_file", DEFAULT_ROUTES_FILE)
22
+ ```
23
+
24
+ **Modern:**
25
+
26
+ ```ruby
27
+ config_schema: { "routes_file" => { kind: :string, default: "config/routes.yml" } }
28
+ # …
29
+ @routes_file = config["routes_file"] # default merged under user config
30
+ ```
31
+
32
+ `Base#config` merges `manifest.config_defaults` beneath the user config,
33
+ so the plugin reads the key directly and the `DEFAULT_*` constant goes
34
+ away. Keep a constant only where a value needs *validation the merged
35
+ default cannot express* (e.g. an allow-listed `severity` that must fall
36
+ back when a user supplies a bad value).
37
+
38
+ **Authority:** `docs/internal-spec/plugin.md` § "Declared config
39
+ defaults — `config_schema` `{ kind:, default: }`".
40
+
41
+ ---
42
+
43
+ ## 2. AST-walk ownership (ADR-37 / ADR-52) — `D`
44
+
45
+ **Smell:** a hand-rolled traversal for per-node checks —
46
+ `root.compact_child_nodes.each { … }`, a bespoke `Walker` that recurses
47
+ the tree, or a `#diagnostics_for_file` that re-walks to find call sites.
48
+
49
+ **Modern:** declare `node_rule(Prism::CallNode) { |node, scope, path| … }`
50
+ and let the engine own the single per-file walk. For a two-pass
51
+ (collect-then-validate) plugin, add `node_file_context { |root, scope| … }`
52
+ — it runs once before the node rules and threads a file-local value in
53
+ as the rule block's fourth argument.
54
+
55
+ **Keep `#diagnostics_for_file`** only for genuinely whole-file
56
+ diagnostics a per-node walk cannot express — see concern 5 for the case
57
+ where a stateful whole-file walk is *required*, not lazy.
58
+
59
+ **"Genuinely whole-file" is a sharp line — apply it, don't defer to
60
+ precedent.** A per-*class* or per-*def* contract check (does this class
61
+ define `#get`? does its body return the contracted type?) IS
62
+ node-expressible — migrate it to `node_rule(Prism::ClassNode)` /
63
+ `node_rule(Prism::DefNode)`, even if a shipped production plugin still
64
+ uses `#diagnostics_for_file` + a hand-rolled `class_nodes` walk for the
65
+ same job (`rigor-hanami`'s ADR-28 check half does — an equivalent,
66
+ older shape, not a reason to keep a new copy hand-rolled). The genuine
67
+ whole-file case is a diagnostic whose *identity or count is not tied to
68
+ any one node* — e.g. `rigor-routes`'s "routes file failed to load"
69
+ warning, which must fire exactly once per file (or run) even on a file
70
+ with zero matching nodes. That cannot be a node rule; a per-class check
71
+ can.
72
+
73
+ **Authority:** `docs/internal-spec/plugin.md` §§ "Node-scoped rules —
74
+ `node_rule`", "`node_file_context`".
75
+
76
+ ---
77
+
78
+ ## 3. Return-type & narrowing hooks (ADR-37 / ADR-52 / ADR-80) — `M`/`D`
79
+
80
+ **Smell:** `flow_contribution_for` (deleted in ADR-52 WD3 — *defining it
81
+ now raises `ArgumentError`*), or `type_specifier` (the pre-ADR-80 name).
82
+
83
+ **Modern:** `dynamic_return(receivers:/methods:/file_methods:)` to
84
+ *supply* a return type, `narrowing_facts(methods:)` to supply
85
+ post-return narrowing facts. `type_specifier` survives as a
86
+ deprecating alias removed in 0.3.0 — rename to `narrowing_facts`.
87
+
88
+ The gate resolves after `#init` when passed a callable
89
+ (`methods: -> { [@method_name] }`), so config-derived method names work.
90
+
91
+ **Authority:** `docs/internal-spec/plugin.md` § "Return-type and
92
+ narrowing contributions". `M` for the pure rename; `D` if you are adding
93
+ a contribution the plugin did not have.
94
+
95
+ ---
96
+
97
+ ## 4. Authoring helpers (ADR-37 / ADR-60 WD4) — `M`
98
+
99
+ The single richest source of drift. Each helper replaces a hand-rolled
100
+ shape and is expected to be diagnostics-preserving.
101
+
102
+ | Smell | Modern | Authority |
103
+ | --- | --- | --- |
104
+ | `Rigor::Analysis::Diagnostic.new(line:, column: loc.start_column + 1, …)` where a node exists | `#diagnostic(node, path:, message:, severity:, rule:)` (or `location: node.message_loc` for a sub-span) | plugin.md § "Positioning a diagnostic — `#diagnostic`" |
105
+ | `violations.map { |v| diagnostic(node, message: v.message, …) }` | `#diagnostics_for(violations, path:, node:)` — duck-types `#message` / `#node` / `#severity` / `#rule` / `#location` | plugin.md § author helpers (ADR-60 WD4) |
106
+ | A hand-rolled `levenshtein` / `closest_*` "did you mean" | `Rigor::Plugin::Base.suggest(name, candidates)` (`DidYouMean::SpellChecker`) — a **class** method, callable from an `Analyzer` too | plugin.md § "`Base.suggest`" |
107
+ | `@table ||= cache_for(id).call` + a multi-`rescue` ladder + an `@load_error` ivar | `#producer_value(id, params:)` (memoised incl. nil) + `#producer_error(id)` (the rescued exception, for a tailored message) | plugin-cache-producers.md § "Invalidation contract" |
108
+ | `@x_resolved` flag guarding `services.fact_store.read` | `#read_fact(plugin_id:, name:)` — nil-inclusive memo, retires the flag | plugin.md § author helpers (ADR-60 WD4) |
109
+ | Hand-parsing a `Prism::SymbolNode#value` / string literal | `Rigor::Source::Literals` | plugin.md § "Extracting argument literals" |
110
+ | A private reimplementation of a target library (own inflector, own pure helper) | Call the library's safe methods directly — `Plugin::Inflector` over real `ActiveSupport::Inflector` (ADR-39) | plugin.md § "Target-library invocation" |
111
+
112
+ **Note on tailored load-error messages:** `#producer_value` rescues
113
+ every `StandardError` into `#producer_error`, so a plugin that wants
114
+ class-specific messages ("not found" vs "failed to parse" vs
115
+ access-denied) switches on `producer_error(id)`'s class when building
116
+ the load-error diagnostic — cleaner than an inline rescue ladder and
117
+ still message-preserving. A *file-level* load-error (line 1, no node)
118
+ legitimately keeps a direct `Diagnostic.new` — `#diagnostic` needs a
119
+ node to position at.
120
+
121
+ ---
122
+
123
+ ## 5. Engine collaboration vs reimplementation — `D` (read the trap)
124
+
125
+ **Principle (rigor-pattern):** do not re-implement a fact the engine
126
+ already computes. If you need "is this a literal string?" / "what type
127
+ did inference give this expression?", read `Scope#type_of(node)` rather
128
+ than tracking it yourself. `rigor-pattern` reads the engine's
129
+ `LiteralStringFolding` result back instead of propagating strings by
130
+ hand.
131
+
132
+ **The trap (rigor-units) — when a hand-rolled binding map is NOT
133
+ redundant:** the `Scope` handed to the **diagnostics** side
134
+ (`#diagnostics_for_file` / a `node_rule`) is the **seed entry scope**.
135
+ `Scope#type_of` re-evaluates a *self-contained* expression on demand
136
+ (`scope.type_of(100.kilometers)` folds through the plugin's own
137
+ `dynamic_return`), but it carries **no flow-accumulated local
138
+ bindings**: for `speed = distance / time`, `scope.type_of(distance)` is
139
+ `untyped`, because the entry scope never bound the earlier assignment.
140
+ Only the **flow scope** handed to a `dynamic_return` block resolves such
141
+ locals. So a diagnostics-side check that must follow a dimension /
142
+ type across statements legitimately keeps its own single-pass binding
143
+ map — deleting it and reaching for `scope.type_of` collapses
144
+ cross-statement propagation.
145
+
146
+ **How to tell which case you are in:** if the value you need lives *at
147
+ the call site you are inspecting* (a literal argument, a self-contained
148
+ sub-expression) → read `Scope#type_of`. If it lives *in an earlier
149
+ statement's local binding* and you are on the diagnostics side → the
150
+ engine will not hand it to you; keep the binding map, and document why.
151
+ **Validate empirically before deleting state:** run the integration
152
+ spec after the change; a wave of failures on multi-statement fixtures is
153
+ this trap.
154
+
155
+ **Authority:** the reasoning is recorded in
156
+ `docs/notes/20260704-examples-plugin-modernization-survey.md` (the
157
+ `rigor-units` section) and the `rigor-units` class comment itself.
158
+
159
+ ---
160
+
161
+ ## 6. Cache producers (ADR-60 WD3) — `M`/`D`
162
+
163
+ **Smell:** a "prime the read before `cache_for` so the digest is
164
+ captured" comment, or a producer that globs a directory but does not
165
+ declare `watch:`.
166
+
167
+ **Modern:** record-and-validate — the `io_boundary.read_file` inside the
168
+ `producer` block is captured into the dependency descriptor *after* the
169
+ block runs, so there is nothing to prime. A producer that reads a *set*
170
+ of files by glob declares `watch:` so file *additions* invalidate too.
171
+
172
+ **Authority:** `docs/internal-spec/plugin-cache-producers.md` §§
173
+ "Invalidation contract", "`producer(... watch:)`".
174
+
175
+ ---
176
+
177
+ ## 7. Manifest-field hygiene (ADR-60 WD1 / WD2) — `M`
178
+
179
+ **Smell:** `external_files:` (never wired — removed in ADR-60 WD1);
180
+ `BlockAsMethod verbs:` (renamed → `method_names:`); `NestedClassTemplate
181
+ name_arg_position:` (renamed → `symbol_arg_position:`).
182
+
183
+ **Modern:** drop `external_files:`; use the renamed keys.
184
+
185
+ **Authority:** `docs/internal-spec/plugin.md` § "`Rigor::Plugin::Manifest`"
186
+ and `macro-substrate.md`.
187
+
188
+ ---
189
+
190
+ ## 8. Documentation freshness — `M`
191
+
192
+ **Smell:** "the former `flow_contribution_for` hook was removed"
193
+ archaeology in a README / class comment (a reader who never knew the
194
+ deleted hook does not need it named); pinned version references that no
195
+ longer carry meaning ("introduced in v0.0.9").
196
+
197
+ **Modern:** describe the *current* mechanism directly. Keep a historical
198
+ note only where a reader migrating an old plugin genuinely needs it —
199
+ and put that in a CHANGELOG / migration note, not the class docstring.
200
+
201
+ ---
202
+
203
+ ## 9. Verification (ADR-43) — always
204
+
205
+ - `rigor check <plugin>/lib` — the ADR-43 contract self-check resolves
206
+ the plugin's inherited `Plugin::Base` calls and warns on contract
207
+ misuse. MUST be clean; fix the cause, never disable the rule.
208
+ - `rigor plugins --strict` — the plugin still activates.
209
+ - `rigor plugins --capabilities` — `node_rule_types` /
210
+ `dynamic_return_receivers` / `type_specifier_methods` reflect the
211
+ declarations.
212
+ - The integration / unit spec — green, and byte-identical across every
213
+ mechanical step.
214
+ - In the monorepo: `make check-plugins` + `make verify`.
@@ -20,6 +20,21 @@ All `rigor-*` plugins ship **bundled inside the `rigortype` gem** — no
20
20
  separate install. Enabling one is just adding its id to `plugins:` in
21
21
  `.rigor.dist.yml`.
22
22
 
23
+ ## First: load the version-current copy
24
+
25
+ This skill's exact commands, flags, and config keys drift between Rigor
26
+ releases, so follow the copy that ships with the **installed** Rigor rather
27
+ than any vendored or frozen copy of this file. Get the complete current
28
+ procedure in one call:
29
+
30
+ ```sh
31
+ rigor skill --full rigor-plugin-tune
32
+ ```
33
+
34
+ If you already loaded this skill *via* `rigor skill` you have the current
35
+ copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
36
+ **`rigor-next-steps`** to install Rigor first, then come back.
37
+
23
38
  ## When to use
24
39
 
25
40
  - The project added (or removed) a gem since it was onboarded.
@@ -43,9 +58,13 @@ dependencies in `Gemfile.lock`.
43
58
  ### Phase 2 — match against the bundled catalogue
44
59
 
45
60
  The authoritative, current list of bundled plugins (the count drifts as
46
- new ones land) is the catalogue:
61
+ new ones land) comes from the **installed** Rigor itself — read it there,
62
+ not from a web page that may describe a different version:
47
63
 
48
- <https://github.com/rigortype/rigor/blob/master/plugins/README.md>
64
+ ```sh
65
+ rigor docs manual/plugins/README # the catalogue of every bundled plugin, offline
66
+ rigor plugins # what is enabled in THIS project right now
67
+ ```
49
68
 
50
69
  For each Gemfile.lock dependency, check whether a bundled `rigor-<gem>`
51
70
  plugin exists and is **not** already in `plugins:`. Common matches:
@@ -23,6 +23,22 @@ chapter for the install channels (`mise` recommended). This skill
23
23
  references only public CLI flags and config keys — the same surface
24
24
  `rigor --help` documents.
25
25
 
26
+ ## First: load the version-current copy
27
+
28
+ This skill's step detail lives in its `references/` files, and its exact
29
+ commands, flags, and config keys drift between Rigor releases — so follow
30
+ the copy that ships with the **installed** Rigor rather than any vendored
31
+ or frozen copy of this file. Get the complete current procedure (body + all
32
+ references, inline) in one call:
33
+
34
+ ```sh
35
+ rigor skill --full rigor-project-init
36
+ ```
37
+
38
+ If you already loaded this skill *via* `rigor skill` you have the current
39
+ copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
40
+ **`rigor-next-steps`** to install Rigor first, then come back.
41
+
26
42
  ## Phase 0 — When to use this skill
27
43
 
28
44
  Trigger when the user says "set up Rigor here", "configure rigor for
@@ -16,6 +16,21 @@ type. This skill *acts* on that surfacing under the discipline that keeps
16
16
  Rigor false-positive-safe: protection goes up, and not one line of
17
17
  working code starts reporting a new diagnostic.
18
18
 
19
+ ## First: load the version-current copy
20
+
21
+ This skill's exact commands, flags, carrier syntax, and rule ids drift
22
+ between Rigor releases, so follow the copy that ships with the **installed**
23
+ Rigor rather than any vendored or frozen copy of this file. Get the
24
+ complete current procedure in one call:
25
+
26
+ ```sh
27
+ rigor skill --full rigor-protection-uplift
28
+ ```
29
+
30
+ If you already loaded this skill *via* `rigor skill` you have the current
31
+ copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
32
+ **`rigor-next-steps`** to install Rigor first, then come back.
33
+
19
34
  ## When to use
20
35
 
21
36
  - A user wants to raise how much of their code Rigor can actually catch
@@ -18,6 +18,21 @@ community RBS collection at
18
18
  [`ruby/gem_rbs_collection`](https://github.com/ruby/gem_rbs_collection);
19
19
  this skill wires it into the project.
20
20
 
21
+ ## First: load the version-current copy
22
+
23
+ This skill's exact commands, flags, and config keys drift between Rigor
24
+ releases, so follow the copy that ships with the **installed** Rigor rather
25
+ than any vendored or frozen copy of this file. Get the complete current
26
+ procedure in one call:
27
+
28
+ ```sh
29
+ rigor skill --full rigor-rbs-setup
30
+ ```
31
+
32
+ If you already loaded this skill *via* `rigor skill` you have the current
33
+ copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
34
+ **`rigor-next-steps`** to install Rigor first, then come back.
35
+
21
36
  ## When to use
22
37
 
23
38
  - `rigor check` ends with `info: N gem(s) in Gemfile.lock have no RBS
@@ -17,6 +17,22 @@ sharpness exposes. This skill adopts the upgrade without either blindly
17
17
  regenerating the baseline (which buries genuine new catches) or treating
18
18
  every new line as a regression.
19
19
 
20
+ ## First: load the version-current copy
21
+
22
+ This skill's exact commands, flags, and config keys drift between Rigor
23
+ releases, so follow the copy that ships with the **installed** Rigor rather
24
+ than any vendored or frozen copy of this file — doubly so here, since you
25
+ just changed the version this skill is meant to track. Get the complete
26
+ current procedure in one call:
27
+
28
+ ```sh
29
+ rigor skill --full rigor-upgrade
30
+ ```
31
+
32
+ If you already loaded this skill *via* `rigor skill` you have the current
33
+ copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
34
+ **`rigor-next-steps`** to install Rigor first, then come back.
35
+
20
36
  ## When to use
21
37
 
22
38
  - You just ran `mise use gem:rigortype` / `gem update rigortype` and want
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rigortype
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rigor contributors
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 1980-01-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: language_server-protocol
@@ -591,6 +591,7 @@ files:
591
591
  - lib/rigor/inference/multi_target_binder.rb
592
592
  - lib/rigor/inference/mutation_widening.rb
593
593
  - lib/rigor/inference/narrowing.rb
594
+ - lib/rigor/inference/origin_lookup.rb
594
595
  - lib/rigor/inference/parameter_inference_collector.rb
595
596
  - lib/rigor/inference/precision_scanner.rb
596
597
  - lib/rigor/inference/project_patched_methods.rb
@@ -886,6 +887,7 @@ files:
886
887
  - skills/rigor-baseline-reduce/references/02-fix-or-suppress.md
887
888
  - skills/rigor-ci-setup/SKILL.md
888
889
  - skills/rigor-doctor/SKILL.md
890
+ - skills/rigor-doctor/references/01-checks.md
889
891
  - skills/rigor-editor-setup/SKILL.md
890
892
  - skills/rigor-mcp-setup/SKILL.md
891
893
  - skills/rigor-monkeypatch-resolve/SKILL.md
@@ -894,6 +896,8 @@ files:
894
896
  - skills/rigor-plugin-author/references/01-plan-and-scaffold.md
895
897
  - skills/rigor-plugin-author/references/02-walker-and-types.md
896
898
  - skills/rigor-plugin-author/references/03-test-and-ship.md
899
+ - skills/rigor-plugin-review/SKILL.md
900
+ - skills/rigor-plugin-review/references/01-best-practices-checklist.md
897
901
  - skills/rigor-plugin-tune/SKILL.md
898
902
  - skills/rigor-project-init/SKILL.md
899
903
  - skills/rigor-project-init/references/01-detect.md
@@ -909,7 +913,7 @@ licenses:
909
913
  metadata:
910
914
  bug_tracker_uri: https://github.com/rigortype/rigor/issues
911
915
  source_code_uri: https://github.com/rigortype/rigor
912
- documentation_uri: https://github.com/rigortype/rigor/tree/main/docs
916
+ documentation_uri: https://github.com/rigortype/rigor/tree/master/docs
913
917
  rubygems_mfa_required: 'true'
914
918
  rdoc_options: []
915
919
  require_paths:
@@ -958,7 +962,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
958
962
  - !ruby/object:Gem::Version
959
963
  version: '0'
960
964
  requirements: []
961
- rubygems_version: 4.0.10
965
+ rubygems_version: 3.7.2
962
966
  specification_version: 4
963
967
  summary: Inference-first static analysis for Ruby.
964
968
  test_files: []