rigortype 0.4.7 → 0.4.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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/data/core_overlay/bigdecimal.rbs +33 -0
  4. data/data/core_overlay/resolv.rbs +30 -0
  5. data/data/gem_overlay/activesupport/core_ext.rbs +29 -0
  6. data/data/gem_overlay/addressable/addressable.rbs +40 -0
  7. data/docs/handbook/01-getting-started.md +1 -1
  8. data/docs/handbook/11-sig-gen.md +35 -0
  9. data/docs/install.md +20 -0
  10. data/docs/manual/01-installation.md +67 -26
  11. data/docs/manual/02-cli-reference.md +307 -91
  12. data/docs/manual/03-configuration.md +41 -3
  13. data/docs/manual/04-diagnostics.md +5 -4
  14. data/docs/manual/08-skills.md +11 -0
  15. data/docs/manual/09-editor-integration.md +8 -0
  16. data/docs/manual/14-rails-quickstart.md +1 -1
  17. data/docs/manual/19-effect-labels.md +3 -4
  18. data/docs/manual/plugins/rigor-actionpack.md +15 -6
  19. data/docs/manual/plugins/rigor-activesupport-core-ext.md +3 -0
  20. data/docs/manual/plugins/rigor-sorbet.md +17 -3
  21. data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
  22. data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
  23. data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +8 -0
  24. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +13 -2
  25. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -0
  26. data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
  27. data/lib/rigor/analysis/check_rules/published_constant_guard.rb +40 -11
  28. data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
  29. data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
  30. data/lib/rigor/analysis/check_rules/rule_ids.rb +8 -1
  31. data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
  32. data/lib/rigor/analysis/check_rules.rb +598 -99
  33. data/lib/rigor/analysis/crash_signature.rb +43 -1
  34. data/lib/rigor/analysis/dependency_recorder.rb +233 -76
  35. data/lib/rigor/analysis/incremental.rb +11 -1
  36. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -1
  37. data/lib/rigor/analysis/incremental_session.rb +279 -37
  38. data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
  39. data/lib/rigor/analysis/rule_catalog.rb +37 -1
  40. data/lib/rigor/analysis/run_cache_key.rb +27 -3
  41. data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
  42. data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +4 -0
  43. data/lib/rigor/analysis/runner/declaration_position.rb +7 -7
  44. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +72 -20
  45. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +3 -8
  46. data/lib/rigor/analysis/runner/pool_coordinator.rb +26 -5
  47. data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
  48. data/lib/rigor/analysis/runner.rb +222 -39
  49. data/lib/rigor/analysis/self_call_resolution_recorder.rb +61 -22
  50. data/lib/rigor/analysis/worker_session.rb +42 -41
  51. data/lib/rigor/bleeding_edge.rb +19 -4
  52. data/lib/rigor/cache/file_digest.rb +39 -31
  53. data/lib/rigor/cache/incremental_snapshot.rb +66 -7
  54. data/lib/rigor/cache/rbs_descriptor.rb +37 -2
  55. data/lib/rigor/cache/rbs_environment.rb +5 -1
  56. data/lib/rigor/cli/baseline_command.rb +15 -2
  57. data/lib/rigor/cli/check_command.rb +4 -2
  58. data/lib/rigor/cli/effects_snapshot_command.rb +8 -1
  59. data/lib/rigor/cli/lens_command.rb +17 -2
  60. data/lib/rigor/cli/sig_gen_command.rb +8 -0
  61. data/lib/rigor/cli/skill_describe.rb +1 -0
  62. data/lib/rigor/cli.rb +5 -2
  63. data/lib/rigor/config_audit.rb +32 -18
  64. data/lib/rigor/configuration/severity_profile.rb +9 -0
  65. data/lib/rigor/configuration.rb +32 -5
  66. data/lib/rigor/effects/collector.rb +53 -21
  67. data/lib/rigor/environment/bundle_sig_discovery.rb +63 -28
  68. data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
  69. data/lib/rigor/environment/default_libraries.rb +17 -0
  70. data/lib/rigor/environment/deferred_signature_plan.rb +104 -6
  71. data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
  72. data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
  73. data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
  74. data/lib/rigor/environment/rbs_hierarchy.rb +6 -0
  75. data/lib/rigor/environment/rbs_loader.rb +312 -63
  76. data/lib/rigor/environment.rb +131 -35
  77. data/lib/rigor/inference/acceptance.rb +50 -15
  78. data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
  79. data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
  80. data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
  81. data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
  82. data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
  83. data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
  84. data/lib/rigor/inference/block_auto_splat.rb +1 -1
  85. data/lib/rigor/inference/block_run_count.rb +9 -2
  86. data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
  87. data/lib/rigor/inference/callee_mutation_chain.rb +1 -1
  88. data/lib/rigor/inference/captured_locals.rb +2 -0
  89. data/lib/rigor/inference/census_foreign_block.rb +354 -0
  90. data/lib/rigor/inference/clause_widening.rb +1 -1
  91. data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
  92. data/lib/rigor/inference/construction_escape.rb +56 -15
  93. data/lib/rigor/inference/declaration_walk.rb +4 -0
  94. data/lib/rigor/inference/def_node_resolver.rb +32 -14
  95. data/lib/rigor/inference/def_return_typer.rb +1 -1
  96. data/lib/rigor/inference/definer_resolution.rb +15 -7
  97. data/lib/rigor/inference/definers_index.rb +72 -16
  98. data/lib/rigor/inference/error_info.rb +16 -0
  99. data/lib/rigor/inference/expression_typer.rb +1459 -287
  100. data/lib/rigor/inference/flow_tracer.rb +31 -14
  101. data/lib/rigor/inference/fork_map.rb +3 -0
  102. data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
  103. data/lib/rigor/inference/global_write_census.rb +8 -6
  104. data/lib/rigor/inference/in_effect_refinements.rb +7 -0
  105. data/lib/rigor/inference/indexed_narrowing.rb +57 -8
  106. data/lib/rigor/inference/ivar_writer_census.rb +106 -11
  107. data/lib/rigor/inference/key_presence_guard.rb +51 -25
  108. data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
  109. data/lib/rigor/inference/last_line.rb +5 -2
  110. data/lib/rigor/inference/loop_value_record.rb +63 -15
  111. data/lib/rigor/inference/match_rebinding/frame.rb +11 -0
  112. data/lib/rigor/inference/match_rebinding/yielding_match.rb +74 -18
  113. data/lib/rigor/inference/match_rebinding.rb +21 -8
  114. data/lib/rigor/inference/method_dispatcher/block_folding.rb +43 -29
  115. data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
  116. data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
  117. data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
  118. data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
  119. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
  120. data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
  121. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +12 -0
  122. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +409 -67
  123. data/lib/rigor/inference/method_dispatcher/self_substitute.rb +1 -1
  124. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +384 -64
  125. data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
  126. data/lib/rigor/inference/method_dispatcher.rb +66 -18
  127. data/lib/rigor/inference/method_parameter_binder.rb +136 -56
  128. data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
  129. data/lib/rigor/inference/module_function_state.rb +13 -2
  130. data/lib/rigor/inference/multi_target_binder.rb +1 -1
  131. data/lib/rigor/inference/mutation_widening.rb +44 -4
  132. data/lib/rigor/inference/narrowing.rb +201 -27
  133. data/lib/rigor/inference/operand_effects.rb +43 -2
  134. data/lib/rigor/inference/optimistic_origin.rb +133 -8
  135. data/lib/rigor/inference/origin_lookup.rb +1 -1
  136. data/lib/rigor/inference/precision_scanner.rb +1 -1
  137. data/lib/rigor/inference/project_method_ownership.rb +28 -10
  138. data/lib/rigor/inference/project_patched_methods.rb +65 -13
  139. data/lib/rigor/inference/project_patched_scanner.rb +120 -25
  140. data/lib/rigor/inference/project_patched_visibility.rb +39 -3
  141. data/lib/rigor/inference/rbs_type_translator.rb +68 -15
  142. data/lib/rigor/inference/receiver_writer_call.rb +67 -0
  143. data/lib/rigor/inference/recursion_rebinding.rb +157 -0
  144. data/lib/rigor/inference/refined_dispatch/constructed_class.rb +56 -18
  145. data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -17
  146. data/lib/rigor/inference/refined_dispatch.rb +13 -0
  147. data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
  148. data/lib/rigor/inference/remover_holder_scan.rb +76 -22
  149. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
  150. data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +12 -11
  151. data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +188 -28
  152. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
  153. data/lib/rigor/inference/scope_indexer.rb +2079 -183
  154. data/lib/rigor/inference/self_call_ivar_writes.rb +26 -7
  155. data/lib/rigor/inference/self_preserving_block.rb +87 -0
  156. data/lib/rigor/inference/singleton_hook_decline.rb +41 -6
  157. data/lib/rigor/inference/singleton_object_constant.rb +32 -2
  158. data/lib/rigor/inference/statement_evaluator.rb +970 -250
  159. data/lib/rigor/inference/string_mutation.rb +14 -0
  160. data/lib/rigor/inference/synthetic_method_scanner.rb +35 -11
  161. data/lib/rigor/inference/toplevel_def_census.rb +14 -6
  162. data/lib/rigor/inference/unknown_store_widening.rb +1 -1
  163. data/lib/rigor/inference/untyped_rejoin.rb +1 -1
  164. data/lib/rigor/inference/void_tail_summary.rb +29 -10
  165. data/lib/rigor/lens/claims/comparison.rb +78 -0
  166. data/lib/rigor/lens/claims/request.rb +131 -0
  167. data/lib/rigor/lens/claims/type_reader.rb +216 -0
  168. data/lib/rigor/lens/claims.rb +208 -0
  169. data/lib/rigor/lens/contract_reader.rb +5 -0
  170. data/lib/rigor/lens/document.rb +1 -1
  171. data/lib/rigor/lens/file_reader.rb +21 -8
  172. data/lib/rigor/lens/run.rb +8 -0
  173. data/lib/rigor/lens.rb +1 -1
  174. data/lib/rigor/library_probe.rb +68 -0
  175. data/lib/rigor/memo_audit.rb +282 -0
  176. data/lib/rigor/plugin/base.rb +15 -2
  177. data/lib/rigor/plugin/io_boundary.rb +25 -3
  178. data/lib/rigor/plugin/manifest.rb +7 -3
  179. data/lib/rigor/plugin/registry.rb +13 -2
  180. data/lib/rigor/plugin/trust_policy.rb +46 -0
  181. data/lib/rigor/protection/mutation_cache.rb +4 -1
  182. data/lib/rigor/reflection/constant_ancestors.rb +79 -22
  183. data/lib/rigor/reflection/declaration_collectors.rb +55 -0
  184. data/lib/rigor/reflection/declarations.rb +58 -0
  185. data/lib/rigor/reflection/project_method.rb +177 -0
  186. data/lib/rigor/reflection.rb +2 -0
  187. data/lib/rigor/rooted_glob.rb +18 -1
  188. data/lib/rigor/run_scoped_slots.rb +20 -20
  189. data/lib/rigor/scope/discovery_index.rb +149 -2
  190. data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
  191. data/lib/rigor/scope/resolution_chain.rb +900 -55
  192. data/lib/rigor/scope.rb +307 -34
  193. data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
  194. data/lib/rigor/sig_gen/classification.rb +14 -1
  195. data/lib/rigor/sig_gen/generator.rb +222 -13
  196. data/lib/rigor/sig_gen/method_candidate.rb +14 -0
  197. data/lib/rigor/sig_gen/observation_collector.rb +11 -2
  198. data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
  199. data/lib/rigor/sig_gen/skip_reason_catalog.rb +47 -0
  200. data/lib/rigor/sig_gen/writer.rb +21 -0
  201. data/lib/rigor/sig_gen.rb +1 -0
  202. data/lib/rigor/source/forwardable_delegators.rb +122 -0
  203. data/lib/rigor/source/node_children.rb +13 -0
  204. data/lib/rigor/type/combinator.rb +164 -8
  205. data/lib/rigor/type/interface.rb +60 -0
  206. data/lib/rigor/type.rb +1 -0
  207. data/lib/rigor/type_of_memo/serving.rb +99 -0
  208. data/lib/rigor/type_of_memo/stats.rb +43 -0
  209. data/lib/rigor/type_of_memo.rb +337 -0
  210. data/lib/rigor/typing_state.rb +1094 -0
  211. data/lib/rigor/version.rb +1 -1
  212. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +2 -2
  213. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +57 -25
  214. data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
  215. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
  216. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
  217. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
  218. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
  219. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +130 -30
  220. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -5
  221. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
  222. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +49 -13
  223. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
  224. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +4 -2
  225. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +13 -5
  226. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +29 -0
  227. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
  228. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
  229. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -2
  230. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
  231. data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +6 -0
  232. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +199 -1
  233. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +3 -2
  234. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +4 -4
  235. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +209 -2
  236. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
  237. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
  238. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +80 -3
  239. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
  240. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +1 -6
  241. data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
  242. data/sig/rigor/analysis/fact_store.rbs +2 -3
  243. data/sig/rigor/cache/descriptor.rbs +14 -0
  244. data/sig/rigor/environment.rbs +4 -1
  245. data/sig/rigor/plugin/base.rbs +53 -19
  246. data/sig/rigor/plugin/interfaces.rbs +51 -0
  247. data/sig/rigor/plugin/io_boundary.rbs +2 -3
  248. data/sig/rigor/plugin/node_context.rbs +21 -0
  249. data/sig/rigor/reflection.rbs +37 -0
  250. data/sig/rigor/scope.rbs +22 -3
  251. data/sig/rigor/trinary.rbs +1 -0
  252. data/sig/rigor/type.rbs +9 -3
  253. data/sig/rigor.rbs +3 -2
  254. data/skills/rigor-ask/SKILL.md +1 -0
  255. data/skills/rigor-project-init/references/01-detect.md +1 -0
  256. data/skills/rigor-project-init/references/03-baseline-and-bugs.md +5 -1
  257. data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
  258. data/skills/rigor-rbs-contract/SKILL.md +147 -0
  259. data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
  260. data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
  261. data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
  262. data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
  263. data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
  264. data/skills/rigor-rbs-setup/SKILL.md +3 -1
  265. data/skills/rigor-type-oracle/SKILL.md +26 -1
  266. data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
  267. metadata +48 -2
  268. data/lib/rigor/inference/memo_audit.rb +0 -55
@@ -148,22 +148,38 @@ when a lockfile is present.
148
148
 
149
149
  ## `rigor annotate`
150
150
 
151
- Reprint a file with every line tagged by the type of the
152
- expression it evaluates to, as a trailing `#=>` comment. See
153
- [Inspecting inferred types](05-inspecting-types.md).
151
+ Reprint a source file with every line tagged by the inferred type of the expression it evaluates to, as a trailing `#=>` comment (following the xmpfilter / seeing_is_believing convention). See [Inspecting inferred types](05-inspecting-types.md).
154
152
 
155
153
  ```sh
156
- rigor annotate [--[no-]color] [--[no-]bat] [--format=text|json] [--config=PATH] FILE
154
+ rigor annotate [options] FILE
157
155
  ```
158
156
 
159
- `FILE` is required. Colour is auto-detected for a tty and
160
- honours `NO_COLOR`; `--color` / `--no-color` override. When
161
- colour is on and [`bat`](https://github.com/sharkdp/bat) is on
162
- `PATH`, highlighting goes through bat (`--no-bat` opts out;
163
- `--bat` warns if bat is missing and falls back to the built-in
164
- colorizer). `--format=json` emits an `{ "schema_version": 1, "annotations": { line => type } }`
165
- document instead of the annotated source. Exit `1` on a parse error or a
166
- missing file.
157
+ | Flag | Purpose |
158
+ | --- | --- |
159
+ | `--config=PATH` | Path to `.rigor.yml`. Overrides auto-discovery. |
160
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
161
+ | `--[no-]color` | Force or disable ANSI color (default: auto-detected for a tty; honors `NO_COLOR`). |
162
+ | `--[no-]bat` | Force or disable syntax highlighting through [`bat`](https://github.com/sharkdp/bat) (default: enabled when color is on and `bat` is found on `PATH`). |
163
+
164
+ The positional `FILE` argument is required. For each line, the command locates the last expression evaluated on that line (e.g. the last statement in `1; 2; 3` reports `3`, and a conditional header `if nil` reports its condition) and annotates it with `#=> <type>`. On method definition headers (`def`), the line is annotated with the method's inferred return type rather than the header expression.
165
+
166
+ Annotations are idempotent: re-running `rigor annotate` matches existing `#=>` comments (as well as legacy `#=> dump_type:` comments) and replaces them in-place instead of stacking comments.
167
+
168
+ When color is enabled and `bat` is installed, output is syntax-highlighted via `bat --language=ruby --style=plain --paging=never`. Passing `--no-bat` disables `bat` integration in favor of Rigor's built-in Prism colorizer. `--bat` warns on stderr and falls back to the built-in colorizer if `bat` is not found on `PATH`.
169
+
170
+ In `--format=json`, the command emits a JSON object mapping line numbers to type strings conforming to schema version 1:
171
+
172
+ ```json
173
+ {
174
+ "schema_version": 1,
175
+ "annotations": {
176
+ "1": "Integer",
177
+ "2": "String | nil"
178
+ }
179
+ }
180
+ ```
181
+
182
+ Exit `0` on success, `1` if the file does not exist or contains a Prism syntax error, and `64` on usage errors (such as a missing `FILE` argument).
167
183
 
168
184
  ## JSON documents
169
185
 
@@ -249,58 +265,144 @@ against `rigor check --no-cache` removes that variable.
249
265
 
250
266
  ## `rigor lens`
251
267
 
252
- Map the declarations of one or more files with their types and
253
- type provenance (experimental). See
254
- [Inspecting inferred types](05-inspecting-types.md).
268
+ Map the declarations of one or more files with their types and type provenance (experimental). See [Inspecting inferred types](05-inspecting-types.md).
255
269
 
256
270
  ```sh
257
- rigor lens [--format=text|json] [--config=PATH] [--workers=N] FILE...
271
+ rigor lens [options] FILE...
258
272
  ```
259
273
 
260
- A lens runs the same incremental analysis as
261
- `rigor check --incremental` and reads and writes its cache.
262
- `--workers` applies to a cold run. `--format=json` writes one
263
- `lens/v0` document ([specification](../internal-spec/lens.md)).
264
- Exit `0` when at least one file has a result, `1` when none
265
- does, `64` on a usage error; diagnostics in the code never
266
- change the exit status.
274
+ | Flag | Purpose |
275
+ | --- | --- |
276
+ | `--config=PATH` | Path to `.rigor.yml`. Overrides auto-discovery. |
277
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
278
+ | `--workers=N` | Worker process count for a cold run. |
279
+ | `--claims=REQUEST` | Check documentation type claims against authored contracts (unreleased). Requires `--format=json`. |
280
+
281
+ `rigor lens` performs the same incremental analysis as `rigor check --incremental` and shares its cache (`.rigor/cache`). The first execution on a file costs a full check, while subsequent runs reuse the recorded cache.
282
+
283
+ `--claims=REQUEST` checks documentation type claims against authored contracts in the same run (unreleased). It requires `--format=json` and a UTF-8 JSON request following the [claims contract](../internal-spec/lens-claims.md). The `claims` section reports each claim's verdict; a conflict does not change the exit status. An invalid or stale request exits `64` without a document. Claims do not alter analysis, diagnostics or cached types.
284
+
285
+ ### Text representation
286
+
287
+ Text output lists each declaration preceded by an anchor identifier:
288
+
289
+ ```text
290
+ lib/shop.rb xxh3-64:845f7426b6acfe12 (2 omitted)
291
+ 4:7c90 class Shop
292
+ 8:e37e Shop#label: (::Integer n) -> ::String [contract inline]
293
+ 14:ce93 Shop#total: () -> Integer [single_site inferred]
294
+ 16:182b Shop#fetch [contested; also lib/legacy_shop.rb:2]
295
+ omitted constant Shop::LIMIT (line 5)
296
+ ```
297
+
298
+ - **`LINE:HASH`**: Stable anchor hash identifying the declaration. It changes when the declaration signature or preceding type comments change, but remains unchanged across prose comment edits.
299
+ - **`provenance`**: The origin of the type in square brackets (e.g. `[contract sig]`, `[contract inline]`, `[single_site inferred]`, `[plugin <name>]`).
300
+ - **`contested`**: Denotes methods where multiple definitions exist in the project (e.g. reopened classes across files); Rigor prints no signature rather than guessing runtime dispatch.
301
+ - **`omitted`**: Constructs not yet modeled by the lens (constants, `alias`, `define_method`) are explicitly recorded as omitted rather than discarded.
302
+
303
+ In `--format=json`, the command outputs a `lens/v0` JSON document (specified in [the lens output contract](../internal-spec/lens.md)).
304
+
305
+ Exit `0` when at least one input file is successfully analysed (`status: "ok"`), `1` when no target achieves `status: "ok"` (the JSON document is still written to stdout), and `64` on usage errors (such as missing `FILE` arguments or an unreadable `--config` path). Diagnostics present in the analysed files do not affect the exit code.
267
306
 
268
307
  ## `rigor trace`
269
308
 
270
- Replay HOW the engine typed a file, step by step, as a
271
- terminal animation. It is a teaching probe over the same
272
- inference `rigor check` runs.
309
+ Replay how the engine typed a file step-by-step as an interactive terminal animation. It is a teaching and debugging probe operating over the inference engine. See [Inspecting inferred types](05-inspecting-types.md).
273
310
 
274
311
  ```sh
275
- rigor trace [--delay=SECONDS] [--line=N] [--verbose] [--format=json] FILE
312
+ rigor trace [options] FILE
276
313
  ```
277
314
 
278
- Each frame highlights the source range being evaluated next to
279
- the scope's locals and describes one inference moment: a local
280
- binding entering the scope (`bind`), branch types merging
281
- (`union`), or a method call resolving — or fail-softening to
282
- `Dynamic[top]` (`dispatch`). On a tty the replay steps on a key
283
- press (`q` quits); `--delay` autoplays. `--verbose` adds every
284
- expression enter/result frame, `--line=N` keeps only events on
285
- one line, and `--format=json` dumps the raw event stream for
286
- tooling or course material. See
287
- [Inspecting inferred types](05-inspecting-types.md).
315
+ | Flag | Purpose |
316
+ | --- | --- |
317
+ | `--config=PATH` | Path to `.rigor.yml`. Overrides auto-discovery. |
318
+ | `--delay=SECONDS` | Autoplay frames with `SECONDS` delay between steps (default: step manually on key press). |
319
+ | `--line=N` | Replay only inference events whose source range starts on line `N`. |
320
+ | `--verbose` | Include every expression `enter` and `result` frame (default: only teachable inference milestones). |
321
+ | `--format=FORMAT` | Output format: `text` (interactive terminal animation) or `json` (raw event stream). Default: `text`. |
322
+
323
+ ### Inference events
324
+
325
+ By default, the replay highlights the AST expression being evaluated alongside the current local variable environment, presenting three core inference event kinds:
326
+ - **`bind`**: A local variable binding enters the active scope.
327
+ - **`union`**: Types from distinct control-flow branches merge into a union type.
328
+ - **`dispatch`**: A method call resolves against its receiver or falls back to `Dynamic[top]`.
329
+
330
+ With `--verbose`, every AST node entry and evaluated type result is captured as a frame.
331
+
332
+ ### Interactive navigation
333
+
334
+ On a terminal TTY, the animation advances one frame per key press. Press `q` to exit playback early. Specifying `--delay=SECONDS` enables hands-free autoplay.
335
+
336
+ In `--format=json`, the raw stream of event objects is emitted as a top-level JSON array for external tooling or courseware integration. Like `rigor explain`, it prints a deprecation notice on stderr noting the upcoming `schema_version: 1` envelope migration (see [JSON documents](#json-documents)).
337
+
338
+ Exit `0` on successful playback or stream emission, `1` if the file does not exist or contains syntax errors (Prism parse errors), and `64` on usage errors (such as missing `FILE`, passing multiple files, or invalid flags).
288
339
 
289
340
  ## `rigor type-scan`
290
341
 
291
- Report `type_of` inference coverage across paths — a
292
- diagnostic-of-the-diagnoser, useful when investigating why a
293
- class infers poorly.
342
+ Report `Scope#type_of` inference coverage across paths — an engine diagnostic measuring how much of the target source is directly typed without falling back to `Dynamic[top]`.
294
343
 
295
344
  ```sh
296
- rigor type-scan PATH...
345
+ rigor type-scan [options] PATH...
346
+ ```
347
+
348
+ | Flag | Purpose |
349
+ | --- | --- |
350
+ | `--config=PATH` | Path to `.rigor.yml`. Overrides auto-discovery. |
351
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
352
+ | `--limit=N` | Maximum unrecognized example events to display in text mode. Default: `10`. |
353
+ | `--show-recognized` | Include AST node classes with 0 unrecognized visits in the summary table. |
354
+ | `--threshold=RATIO` | Exit non-zero (`1`) when the ratio of unrecognized nodes (`unrecognized / visits`) exceeds `RATIO` (float). |
355
+
356
+ At least one `PATH` argument is required. The command traverses every Prism node across the specified paths and queries `Scope#type_of` against a discovery-seeded project environment (matching `rigor coverage`).
357
+
358
+ ### Output format
359
+
360
+ In text mode, `rigor type-scan` prints a summary block, class breakdown, and unrecognized examples:
361
+
362
+ ```text
363
+ Type-of scan: 1 file
364
+ - lib/user.rb
365
+
366
+ Summary:
367
+ files processed: 1
368
+ parse errors: 0
369
+ AST nodes visited: 165
370
+ unrecognized: 14 ( 8.5%)
371
+
372
+ Coverage by node class (unrecognized/visits):
373
+ Prism::CallNode 12/120 ( 10.0%)
374
+ Prism::ConstantReadNode 2/ 45 ( 4.4%)
375
+
376
+ Unrecognized examples (showing 10 of 14):
377
+ Prism::CallNode @ lib/user.rb:14:5
297
378
  ```
298
379
 
299
- `--limit=N` caps the printed examples (default 10),
300
- `--show-recognized` includes fully-covered classes,
301
- `--threshold=RATIO` makes the command exit non-zero when the
302
- unrecognized-node ratio exceeds `RATIO`, and `--format=text|json`
303
- selects the output format. `--config=PATH` overrides config discovery.
380
+ Passing `--show-recognized` includes node classes with 0 unrecognized visits in the class breakdown. `--limit=N` caps the total number of examples shown in the unrecognized events section.
381
+
382
+ In `--format=json`, the command emits a JSON document conforming to schema version 1:
383
+
384
+ ```json
385
+ {
386
+ "schema_version": 1,
387
+ "files": [ "lib/user.rb" ],
388
+ "summary": {
389
+ "files_processed": 1,
390
+ "parse_errors": 0,
391
+ "visited": 165,
392
+ "unrecognized": 14,
393
+ "unrecognized_ratio": 0.0848
394
+ },
395
+ "by_class": {
396
+ "Prism::CallNode": { "visits": 120, "unrecognized": 12 }
397
+ },
398
+ "events": [
399
+ { "file": "lib/user.rb", "node_class": "Prism::CallNode", "family": "call", "line": 14, "column": 5 }
400
+ ],
401
+ "parse_errors": []
402
+ }
403
+ ```
404
+
405
+ Exit `0` if the unrecognized ratio is within `--threshold` (or when no threshold is set) and no parse errors occurred. Exit `1` if the ratio exceeds `--threshold` or if any scanned file contains parse errors. Exit `64` if no paths are provided, a specified path does not exist, or flags are invalid.
304
406
 
305
407
  ## `rigor effects`
306
408
 
@@ -433,6 +535,12 @@ origin/main:.rigor-effects.yml)` in a bot),
433
535
  `explain` additionally accepts `--symbol=KEY` to explain one
434
536
  specific unit instead of the changed ones.
435
537
 
538
+ `update` writes `effects.snapshot.path:` (default
539
+ `.rigor-effects.yml`). A path whose directory does not exist, or
540
+ that cannot be written, is a usage error: one
541
+ `rigor effects: cannot write PATH: REASON` line on stderr and
542
+ exit `64`. The directory is not created.
543
+
436
544
  The file records two tables. `methods:` holds each method's
437
545
  **direct** summary — what its own body does, block literals
438
546
  and catalogued callees included, but not what the project
@@ -550,38 +658,107 @@ reviewer's judgment, which is what makes it a review artefact.
550
658
 
551
659
  ## `rigor explain`
552
660
 
553
- Print the catalogue entry for a diagnostic rule, or list every
554
- rule when called with no argument.
661
+ Print the catalogue entry for a diagnostic rule, or list every rule when called with no argument.
555
662
 
556
663
  ```sh
557
- rigor explain [options] [rule]
664
+ rigor explain [options] [<rule>]
558
665
  ```
559
666
 
560
- `rule` is a rule ID (`call.undefined-method`), a legacy alias,
561
- or a family wildcard (`call`, `flow`, `def`, `assert`, `dump`, `global`,
562
- `suppression`, `static`, `effect`).
563
- `--format=text|json` selects the output format. Exit `64` for an unknown rule.
667
+ | Flag | Purpose |
668
+ | --- | --- |
669
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
670
+
671
+ ### Invocation modes
672
+
673
+ | Form | Purpose |
674
+ | --- | --- |
675
+ | (none) | Print the complete rule catalogue index (rule ID and one-line summary) followed by every `rigor sig-gen` skip reason. |
676
+ | `<rule>` | Print the full catalogue entry for a canonical rule ID (e.g. `call.undefined-method`) or a legacy alias (e.g. `undefined-method`). |
677
+ | `<family>` | Family wildcard (`call`, `flow`, `def`, `assert`, `dump`, `global`, `suppression`, `static`, `effect`). Prints every rule under that prefix. |
678
+ | `<skip-reason>` | Print the explanation and recommended next action for an exact `rigor sig-gen` skip reason token (e.g. `sig.skipped.untyped-return`). Does not accept wildcards. |
679
+
680
+ A rule entry includes its summary, legacy aliases, authored severity, severity mapped across configuration profiles (`lenient`, `balanced`, `strict`), evidence tier, `Fires when:` conditions, `Does not fire when:` exceptions, inline suppression syntax (`# rigor:disable <rule>`), documentation link, and the Rigor release where the rule was introduced.
681
+
682
+ A `<skip-reason>` token explains why `rigor sig-gen` declined to write a signature (such as dynamic returns or unbounded blocks) and suggests what to address instead. Because skip reasons report signatures that were not written rather than code findings, they carry no severity and nothing to suppress.
564
683
 
565
- It also answers the `sig.skipped.*` identifiers `rigor sig-gen`
566
- prints when it declines to write a signature — `rigor explain
567
- sig.skipped.untyped-return` says what the skip means and what to do
568
- instead. Those are not diagnostic rules, so they are rendered without
569
- a severity or a suppression line.
684
+ In `--format=json`, diagnostic rules (whether requested by a single rule ID, legacy alias, family wildcard, or with no arguments) always emit a JSON array of catalogue entry objects. In contrast, querying an individual `<skip-reason>` emits a single JSON object. In the index mode with no arguments, `--format=json` outputs only the diagnostic rule array; individual skip reasons must be queried by token. Top-level JSON shapes currently print a stderr deprecation notice regarding the future envelope migration (see [JSON documents](#json-documents)).
685
+
686
+ Exit `0` on success, `64` on an unknown rule name or invalid options.
570
687
 
571
688
  ## `rigor diff`
572
689
 
573
- Compare the current diagnostics against a saved
574
- `rigor check --format=json` result and report only what is new.
690
+ Compare current diagnostics against a saved `rigor check --format=json` result and report the delta.
575
691
 
576
692
  ```sh
577
693
  rigor diff [options] <rigor-check.json> [paths...]
578
694
  ```
579
695
 
580
- `--current=PATH` compares against a saved diagnostics JSON
581
- instead of running a fresh check. `--format=text|json` selects the
582
- output format (default `text`), and `--config=PATH` passes a custom
583
- configuration to the implicit check. Exit `1` when new
584
- diagnostics appear.
696
+ | Flag | Purpose |
697
+ | --- | --- |
698
+ | `--current=PATH` | Compare against a saved diagnostics JSON file instead of executing a fresh `rigor check` run. |
699
+ | `--config=PATH` | Custom `.rigor.yml` configuration forwarded to the implicit `rigor check` run. Ignored when `--current` is supplied. |
700
+ | `--format=FORMAT` | Output format (`text` or `json`). Default: `text`. |
701
+
702
+ The positional `<rigor-check.json>` file is required. Per ADR-126 § WD1, this file is a saved `rigor check --format=json` result or JSON snapshot, distinct from the rule-bucket `.rigor-baseline.yml` managed by `rigor baseline`.
703
+
704
+ When `[paths...]` are given, only those source paths are evaluated by the implicit check. Note that if `<rigor-check.json>` covers the entire repository, restricting the implicit check to a subset of paths causes all diagnostics in omitted files to be reported as `- FIXED`. When `--current=PATH` is provided, `[paths...]` are ignored.
705
+
706
+ ### Identity matching
707
+
708
+ Diagnostic identity is determined by the 6-element tuple:
709
+
710
+ ```
711
+ (path, line, column, rule, source_family, message)
712
+ ```
713
+
714
+ Diagnostics matching a saved tuple are considered pre-existing. Current diagnostics missing from the saved snapshot are classified as `+ NEW` (regressions). Saved diagnostics missing from the current run are classified as `- FIXED` (progress). Moving code to another line or changing a message produces one fixed and one new entry. Severity is not part of the identity tuple; changing a diagnostic's severity without moving its location does not register as a diff.
715
+
716
+ ### Output format
717
+
718
+ Text output reports the diff header, line-by-line additions and removals, and summary counts:
719
+
720
+ ```text
721
+ # diff against rigor-check.json (10 baseline / 9 current)
722
+ + NEW lib/user.rb:14:5 [call.undefined-method] Method `full_name` does not exist on `User`
723
+ - FIXED lib/auth.rb:22:9 [call.argument-type-mismatch] Expected `String`, got `Integer`
724
+
725
+ 1 new, 1 fixed
726
+ ```
727
+
728
+ With `--format=json`, the output conforms to schema version 1:
729
+
730
+ ```json
731
+ {
732
+ "schema_version": 1,
733
+ "baseline": "rigor-check.json",
734
+ "baseline_count": 10,
735
+ "current_count": 9,
736
+ "new": [ ... ],
737
+ "fixed": [ ... ]
738
+ }
739
+ ```
740
+
741
+ ### CI integration
742
+
743
+ `rigor diff` enables regression gating in repositories adopting Rigor incrementally by catching newly introduced diagnostics without requiring all legacy diagnostics to be fixed first.
744
+
745
+ In standard CI workflows, commit a saved snapshot in-tree (e.g. `rigor-check.json`) generated by `rigor check --format=json > rigor-check.json`. In CI, run:
746
+
747
+ ```sh
748
+ rigor diff rigor-check.json
749
+ ```
750
+
751
+ When comparing against a dynamic base branch in a multi-step job (where `rigor check` on the base branch may exit non-zero due to pre-existing diagnostics), guard the exit code under `set -e`:
752
+
753
+ ```sh
754
+ # On base branch (or via git show / artifact)
755
+ rigor check --format=json > /tmp/base-check.json || true
756
+
757
+ # On PR branch
758
+ rigor diff /tmp/base-check.json
759
+ ```
760
+
761
+ Exit `0` when no new diagnostics appear (`new` is empty, even if `fixed` contains entries). Exit `1` when any new diagnostic of any severity (`error`, `warning`, or `info`) appears. Exit `64` if either JSON file is missing, contains invalid JSON, or command usage is incorrect.
585
762
 
586
763
  ## `rigor sig-gen`
587
764
 
@@ -636,6 +813,11 @@ repair through.
636
813
  routes to a signature path it does not write to, because it is declared there or its class lives there) on its own `skipped Class#method (reason)` line; the exit status is
637
814
  unchanged. See [handbook chapter 11](../handbook/11-sig-gen.md#safety-what---write-will-and-will-not-do).
638
815
 
816
+ A `sig/` target `--write` cannot write — a permission denial, a directory sitting where the `.rbs`
817
+ belongs, or a parent path that is a regular file — is a usage error: one `rigor sig-gen: cannot
818
+ write PATH: REASON` line on stderr and exit `64`. A missing parent directory is created as before.
819
+ The run stops at the first failure; targets already written stay written.
820
+
639
821
  A method declared inline with `# @rbs` / `#:` is written as that
640
822
  declaration, not as what its body infers; a parameter-only
641
823
  annotation keeps its parameters and takes the return from the body,
@@ -738,6 +920,12 @@ rigor baseline <generate|regenerate|dump|drift|prune> [options]
738
920
  `--baseline=PATH` to read a non-default baseline file. `dump`
739
921
  also accepts `--format=text|json` for machine-readable output.
740
922
 
923
+ A write target — `generate` and `regenerate`'s `--output`, or the
924
+ `--baseline` file `prune` rewrites — whose directory does not exist,
925
+ or that cannot be written, is a usage error: one
926
+ `rigor baseline: cannot write PATH: REASON` line on stderr and exit
927
+ `64`. The directory is not created.
928
+
741
929
  No subcommand takes a positional path: `generate` and `regenerate`
742
930
  analyse the configured `paths:`, `drift` and `prune` audit them, and
743
931
  `dump` reads the baseline file and filters with `--rule`/`--file`. A path argument is a usage error
@@ -1116,15 +1304,28 @@ caveat for container / cross-filesystem setups).
1116
1304
 
1117
1305
  ## `rigor playground`
1118
1306
 
1119
- Start the browser playground (a CodeMirror editor with
1120
- real-time diagnostics). Requires the separate `rigor-playground`
1121
- gem; if it is not installed the command prints an install hint
1122
- and exits `64`.
1307
+ Start a local browser playground (a CodeMirror editor with real-time Rigor diagnostics and type inspection).
1123
1308
 
1124
1309
  ```sh
1125
- rigor playground
1310
+ rigor playground [options]
1311
+ ```
1312
+
1313
+ | Flag | Purpose |
1314
+ | --- | --- |
1315
+ | `--port=PORT` | Port to bind the playground HTTP server to (default: `9393`). |
1316
+ | `--no-open` | Do not automatically open the playground URL in the default browser on launch. |
1317
+
1318
+ The playground requires the separate `rigor-playground` companion gem. When the gem is not installed in the active Ruby environment, the command prints an installation hint on stderr and exits `64` (`CLI::EXIT_USAGE`):
1319
+
1320
+ ```text
1321
+ rigor playground requires the rigor-playground gem.
1322
+ Install it with: gem install rigor-playground
1126
1323
  ```
1127
1324
 
1325
+ When installed, the command boots the local web server and opens an interactive browser playground where Ruby code can be written, edited, and analyzed with immediate feedback.
1326
+
1327
+ Exit `0` on clean shutdown, `1` if server startup fails (e.g. port already in use), and `64` if `rigor-playground` is missing or on usage errors.
1328
+
1128
1329
  ## `rigor skill`
1129
1330
 
1130
1331
  List or print the bundled Agent Skills shipped inside the
@@ -1182,24 +1383,27 @@ they now read as an unknown skill. Use the forms above.
1182
1383
 
1183
1384
  ## `rigor describe`
1184
1385
 
1185
- Top-level alias for [`rigor skill describe`](#rigor-skill) — the
1186
- onboarding entry point that recommends the next skill for this project.
1187
- A bare `rigor describe` is the intuitive guess most users reach for
1188
- first, so it is surfaced as its own command ([ADR-73](../adr/73-skill-driven-user-experience.md)
1189
- § WD2).
1386
+ Top-level alias for [`rigor skill describe`](#rigor-skill) — the onboarding entry point that assesses the project's state and recommends the next skill to run ([ADR-73](../adr/73-skill-driven-user-experience.md) § WD2).
1190
1387
 
1191
1388
  ```sh
1192
- rigor describe
1389
+ rigor describe [--deep]
1193
1390
  ```
1194
1391
 
1195
- It reports a presence-only project-state probe (does a `.rigor.yml`,
1196
- `.rigor-baseline.yml`, `sig/` directory, or CI integration exist?) and a
1197
- recommended next skill. It is read-only and side-effect-free — it never
1198
- runs `rigor check`. Identical output to `rigor skill describe`.
1392
+ | Flag | Purpose |
1393
+ | --- | --- |
1394
+ | `--deep` | Run `rigor check` first and route the headline recommendation on the diagnostic results. |
1395
+
1396
+ ### Audit modes
1397
+
1398
+ By default, `rigor describe` performs a presence-only probe checking for the existence of `.rigor.yml`, `.rigor-baseline.yml`, a `sig/` directory, and CI configurations. This default mode is read-only, fast, and side-effect-free — it never executes `rigor check` and never reads or writes `.rigor/cache`.
1199
1399
 
1200
- `rigor describe --deep` forwards to
1201
- [`rigor skill describe --deep`](#rigor-skill), which opts into running the
1202
- check first — slow, and it writes the cache.
1400
+ Passing `--deep` opts into running `rigor check` before generating recommendations, routing based on analysis health:
1401
+ - **`rigor-doctor`**: Selected if the RBS environment builds with 0 classes or any `configuration-error` diagnostic is raised.
1402
+ - **`rigor-monkeypatch-resolve`**: Selected if at least one third of remaining error diagnostics stem from calls resolving to project-side reopened core or gem definitions (addressable via `pre_eval:`).
1403
+ - **`rigor-baseline-reduce`**: Selected when remaining error diagnostics exist.
1404
+ - **Presence-only recommendation**: Retained when the analysis is completely clean or when no configuration exists yet.
1405
+
1406
+ Output is identical to `rigor skill describe`. Exit `0` on completion, or `64` on invalid options.
1203
1407
 
1204
1408
  ## `rigor docs`
1205
1409
 
@@ -1265,9 +1469,10 @@ Queued today:
1265
1469
  | --- | --- | --- |
1266
1470
  | `reject-unparseable-signatures` | severity | A broken RBS set **fails the run** instead of degrading it silently: an unparseable `.rbs` under `signature_paths:` (`rbs.coverage.quarantined-signature` → `error`), a declaration that collides on resolve and collapses the whole environment (`rbs.coverage.environment-build-failed` → `error`), and a duplicate method definition that collapses one class's method surface (`rbs.coverage.definition-build-failed` → `error`). |
1267
1471
  | `use-of-void-value` | severity | Using a value recovered from an author-declared `-> void` return in value context is reported as `static.value-use.void` (`warning`). |
1472
+ | `plugin-api-surface` | severity | In plugin source (a class that inherits `Rigor::Plugin::Base`), a call on a `Rigor::Plugin` interface value (a hook's `scope`, `scope.environment`, ...) to a member the interface does not declare is reported as `plugin.api-surface` (`warning`). |
1268
1473
  | `discovery-seeded-mutation-sites` | behaviour | [`rigor coverage --protection --mutation`](15-type-protection-coverage.md) measures against the same cross-file project discovery Tier 1 already uses — both when picking the sites and when deciding whether a breakage was caught — so a call on a project class declared in a *sibling* file is measured instead of dropped, and a breakage there can actually be caught. **Adds sites to the denominator, so the reported effectiveness ratio moves** — check it against any `--threshold` you pin in CI before adopting. |
1269
1474
  | `dependent-closure-kill-oracle` | behaviour | [`rigor coverage --protection --mutation`](15-type-protection-coverage.md) counts a breakage as caught when the diagnostic appears anywhere in the mutated file **or the files that depend on it**, instead of in the mutated file alone — so changing what a method returns counts as caught when the error lands in its callers. Can only **add** kills, so the ratio moves up or not at all; it costs about a third more wall time per mutant, and a ratio measured under it is not comparable with one measured without it. |
1270
- | `effects-on-by-default` | behaviour | A project whose `.rigor.yml` carries no [`effects:`](03-configuration.md#effect-labels) key at all is treated as if it had written `effects: {}` — [effect collection](03-configuration.md#effect-labels), the `rigor effects` verbs' cache sharing, and `effects.check` all turn on with every sub-key at its default. Writing `effects: false` explicitly still opts out. **Scheduled to graduate at v0.4.0** ([ADR-103](../adr/103-effect-labels.md) § WD15) rather than at the next major — an owner ruling specific to this feature, ahead of the general v1.0.0 majors-only cadence below. |
1475
+ | `effects-on-by-default` | behaviour | A project whose `.rigor.yml` carries no [`effects:`](03-configuration.md#effect-labels) key at all is treated as if it had written `effects: {}` — [effect collection](03-configuration.md#effect-labels), the `rigor effects` verbs' cache sharing, and `effects.check` all turn on with every sub-key at its default. Writing `effects: false` explicitly still opts out. **Scheduled to graduate at v0.5.0** ([ADR-103](../adr/103-effect-labels.md) § WD15, moved from v0.4.0 by § WD18) rather than at the next major — an owner ruling specific to this feature, ahead of the general v1.0.0 majors-only cadence below. |
1271
1476
 
1272
1477
  Once a feature **graduates** — it becomes the default at a major
1273
1478
  ([ADR-50](../adr/50-release-engineering-and-stability-strategy.md) § WD7)
@@ -1352,18 +1557,29 @@ Exits `1` when any check fails, `0` when all pass.
1352
1557
 
1353
1558
  ## `rigor upgrade`
1354
1559
 
1355
- Migration command skeleton ([ADR-50](../adr/50-release-engineering-and-stability-strategy.md)
1356
- WD7). The real body lands when a concrete backwards-compatibility
1357
- break gives it a target (e.g. re-running `baseline regenerate`
1358
- against a strengthened default profile, surfacing renamed
1359
- suppression ids, reporting `bleeding_edge:` graduations).
1560
+ Migration command skeleton for major version transitions and breaking changes ([ADR-50](../adr/50-release-engineering-and-stability-strategy.md) § WD7).
1360
1561
 
1361
1562
  ```sh
1362
1563
  rigor upgrade
1363
1564
  ```
1364
1565
 
1365
- Until then it prints the current version and notes that upgrade is
1366
- queued. Exits `0`.
1566
+ ### Design and future role
1567
+
1568
+ The upgrade command is designed to automate project migrations when major version transitions or breaking changes occur:
1569
+ - **Baseline regeneration**: Re-running `rigor baseline regenerate` against strengthened default rules or profiles.
1570
+ - **Suppression updates**: Surfacing and updating renamed or deprecated inline `# rigor:disable` rules.
1571
+ - **Bleeding-edge consolidation**: Reporting and removing graduated features from `bleeding_edge:` configurations.
1572
+
1573
+ ### Current status
1574
+
1575
+ Until a backward-incompatible release provides an active migration target, the command serves as a placeholder. It prints the current Rigor version and reports that migrations are queued:
1576
+
1577
+ ```text
1578
+ rigor upgrade: No migration target available yet (ADR-50 WD7, queued).
1579
+ Current version: <version>
1580
+ ```
1581
+
1582
+ Exit `0` on completion.
1367
1583
 
1368
1584
  ## `rigor help`
1369
1585
 
@@ -227,12 +227,50 @@ project Ruby's gem home without running your toolchain. If `rigor check`'s
227
227
  `--stats` shows gems whose RBS it could not find, point it at the bundle
228
228
  explicitly with `bundler.bundle_path:`, or supply signatures another way:
229
229
  `rbs collection install` (auto-discovered) or `dependencies.source_inference:`.
230
- | `rbs_collection.auto_detect` | Boolean | `true` | Auto-discover `rbs_collection.lock.yaml`. An entry is not loaded for a gem your `Gemfile.lock` sources from a `path:` directory that is at or under one of your `paths:` entries, or contains one, and is not left out by `exclude:` (the gem is code Rigor analyses, which your `sig/` declares). |
230
+ | `rbs_collection.auto_detect` | Boolean | `true` | Auto-discover `rbs_collection.lock.yaml`. A `source: { type: stdlib }` entry loads that stdlib library, as `libraries:` would. An entry is not loaded for a gem your `Gemfile.lock` sources from a `path:` directory that is at or under one of your `paths:` entries, or contains one, and is not left out by `exclude:` (the gem is code Rigor analyses, which your `sig/` declares). |
231
231
  | `rbs_collection.lockfile` | String | `nil` | Explicit `rbs_collection.lock.yaml` path. |
232
232
  | `dependencies.source_inference` | Array | `[]` | Per-gem source-inference modes (ADR-10). |
233
233
  | `dependencies.budget_per_gem` | Integer | `5000` | Per-gem source-walk cap, counted in **method definitions** (not time): the walker stops harvesting a gem's catalog once it reaches this many `def`s, then emits `dynamic.dependency-source.budget-exceeded` and degrades the rest to `Dynamic[top]`. Range 1250–20000. |
234
234
  | `dependencies.budget_overrun_strategy` | String | `"walker_cap"` | What happens to calls on a gem that hit `budget_per_gem` (ADR-10 § 5b). `walker_cap` (default) lets methods past the cap fall through to the engine's normal user-class resolution. `dependency_silence` instead resolves any call on a class from a budget-exceeded gem to `Dynamic[top]`, silencing `call.undefined-method` on that gem's unrecorded surface at the cost of weaker static checking there. |
235
235
 
236
+ #### Running Rigor in a gem_rbs_collection `_test/` directory
237
+
238
+ To check a signature you are writing for
239
+ [`ruby/gem_rbs_collection`](https://github.com/ruby/gem_rbs_collection), run
240
+ Rigor in the gem's `gems/<name>/<version>/_test/` directory after the
241
+ collection's own test setup:
242
+
243
+ 1. Run `bin/test gems/<name>/<version>` once from the collection root. It
244
+ writes `rbs_collection.yaml` and `rbs_collection.lock.yaml` into `_test/`
245
+ and installs the gem and its dependencies into `_test/.gem_rbs_collection/`,
246
+ with your working tree linked in as a `local` source.
247
+ 2. Put a `.rigor.yml` in `_test/`:
248
+
249
+ ```yaml
250
+ paths: ["."]
251
+ signature_paths: ["."]
252
+ ```
253
+
254
+ This is the shape of the `Steepfile` that `bin/test` writes (`check "."`,
255
+ `signature "."`). Rigor finds the lockfile and loads the gem and its
256
+ dependencies from it, and `signature_paths: ["."]` loads the `.rbs` files in
257
+ `_test/` itself. Without that line, the types those test signatures declare
258
+ are absent and calls into them are not checked.
259
+
260
+ `signature_paths: [".."]` is also safe. It loads the gem from your working
261
+ tree, and Rigor skips the lockfile's copy of the same gem and version, so the
262
+ gem is not loaded twice. Use it when the lockfile's copy is not your working
263
+ tree, for example when `rbs_collection.yaml` names a `git` source instead of
264
+ the collection's `gems/` directory.
265
+ 3. Run `rigor check` in `_test/`.
266
+
267
+ Rigor skips every `_`-prefixed directory inside a collected gem, such as another
268
+ gem's `_test/` fixtures, as `rbs` does. Rigor does not read `manifest.yaml`
269
+ or a gemspec to find dependencies. Without a lockfile it loads none of them and
270
+ reports `rbs.coverage.collection-not-installed` (`:info`), which tells you to run
271
+ the `bin/test` setup, or `rbs collection install` where an `rbs_collection.yaml`
272
+ is present.
273
+
236
274
  ### Execution
237
275
 
238
276
  | Key | Type | Default | Meaning |
@@ -270,8 +308,8 @@ config with no `effects:` key at all behave as `effects: {}` — collection,
270
308
  `effects.check`, and everything else on this page all turn on at their
271
309
  defaults. It only fills an *absence*: write `effects: false` and you stay
272
310
  opted out regardless, and any `effects:` block you do write is left exactly
273
- as written. It previews what becomes the default at **v0.4.0**
274
- ([ADR-103](../adr/103-effect-labels.md) § WD15).
311
+ as written. It previews what becomes the default at **v0.5.0**
312
+ ([ADR-103](../adr/103-effect-labels.md) § WD15, moved by § WD18).
275
313
 
276
314
  #### Entry-point presets
277
315