rigortype 0.4.0 → 0.4.2

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 (237) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -3
  3. data/data/builtins/ruby_core/io.yml +1 -0
  4. data/data/builtins/ruby_core/proc.yml +4 -0
  5. data/data/builtins/ruby_core/random.yml +4 -0
  6. data/data/builtins/ruby_core/struct.yml +3 -0
  7. data/data/core_overlay/env.rbs +34 -0
  8. data/data/core_overlay/gc.rbs +50 -0
  9. data/data/core_overlay/hash.rbs +45 -0
  10. data/data/core_overlay/integer.rbs +31 -0
  11. data/data/core_overlay/io_buffer.rbs +27 -0
  12. data/data/core_overlay/kernel.rbs +40 -0
  13. data/data/core_overlay/match_data.rbs +28 -0
  14. data/data/core_overlay/module.rbs +51 -0
  15. data/data/core_overlay/object_space.rbs +29 -0
  16. data/data/core_overlay/proc.rbs +45 -0
  17. data/data/core_overlay/range.rbs +40 -0
  18. data/data/core_overlay/source_range.rbs +78 -0
  19. data/data/core_overlay/string.rbs +86 -0
  20. data/data/vendored_gem_sigs/prime/integer-extension.rbs +41 -0
  21. data/data/vendored_gem_sigs/prime/prime.rbs +372 -0
  22. data/docs/handbook/01-getting-started.md +3 -3
  23. data/docs/handbook/06-classes.md +3 -1
  24. data/docs/handbook/08-understanding-errors.md +3 -2
  25. data/docs/handbook/11-sig-gen.md +6 -0
  26. data/docs/handbook/appendix-liskov.md +5 -0
  27. data/docs/install.md +87 -15
  28. data/docs/manual/02-cli-reference.md +2 -2
  29. data/docs/manual/03-configuration.md +6 -1
  30. data/docs/manual/04-diagnostics.md +2 -1
  31. data/docs/manual/06-baseline.md +2 -0
  32. data/docs/manual/07-plugins.md +1 -1
  33. data/docs/manual/08-skills.md +4 -3
  34. data/docs/manual/12-caching.md +43 -4
  35. data/docs/manual/14-rails-quickstart.md +19 -11
  36. data/docs/manual/18-removing-dead-code.md +32 -2
  37. data/docs/manual/plugins/README.md +6 -1
  38. data/docs/manual/plugins/rigor-activerecord.md +21 -0
  39. data/docs/manual/plugins/rigor-alba.md +60 -0
  40. data/docs/manual/plugins/rigor-minitest.md +22 -4
  41. data/docs/manual/plugins/rigor-typelizer.md +71 -0
  42. data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +27 -3
  43. data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +21 -92
  44. data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +275 -0
  45. data/lib/rigor/analysis/check_rules/rule_ids.rb +4 -0
  46. data/lib/rigor/analysis/check_rules/source_arity.rb +184 -61
  47. data/lib/rigor/analysis/check_rules.rb +407 -144
  48. data/lib/rigor/analysis/dependency_recorder.rb +215 -18
  49. data/lib/rigor/analysis/dependency_source_inference/builder.rb +8 -1
  50. data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +21 -0
  51. data/lib/rigor/analysis/dependency_source_inference/index.rb +36 -11
  52. data/lib/rigor/analysis/dependency_source_inference/walker.rb +196 -27
  53. data/lib/rigor/analysis/erb_template_detector.rb +2 -1
  54. data/lib/rigor/analysis/incremental.rb +29 -9
  55. data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +291 -0
  56. data/lib/rigor/analysis/incremental_run_slot.rb +173 -0
  57. data/lib/rigor/analysis/incremental_session.rb +287 -50
  58. data/lib/rigor/analysis/plugin_fact_fingerprint.rb +24 -13
  59. data/lib/rigor/analysis/reachability/graph.rb +123 -23
  60. data/lib/rigor/analysis/reachability/scan.rb +290 -36
  61. data/lib/rigor/analysis/reachability/scan_cache.rb +2 -1
  62. data/lib/rigor/analysis/rule_catalog.rb +43 -0
  63. data/lib/rigor/analysis/run_cache_key.rb +43 -9
  64. data/lib/rigor/analysis/run_stats.rb +2 -2
  65. data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +88 -6
  66. data/lib/rigor/analysis/runner/pool_coordinator.rb +52 -13
  67. data/lib/rigor/analysis/runner/project_pre_passes.rb +21 -13
  68. data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
  69. data/lib/rigor/analysis/runner.rb +371 -49
  70. data/lib/rigor/analysis/shadow_harness.rb +27 -0
  71. data/lib/rigor/analysis/source_rbs_gate.rb +157 -0
  72. data/lib/rigor/analysis/template_unit_paths.rb +6 -4
  73. data/lib/rigor/analysis/template_units.rb +15 -5
  74. data/lib/rigor/analysis/worker_session.rb +11 -7
  75. data/lib/rigor/cache/descriptor.rb +40 -1
  76. data/lib/rigor/cache/file_digest.rb +28 -0
  77. data/lib/rigor/cache/incremental_snapshot.rb +92 -8
  78. data/lib/rigor/cache/store.rb +63 -3
  79. data/lib/rigor/cache/value_digest.rb +325 -0
  80. data/lib/rigor/cli/annotate_command.rb +5 -0
  81. data/lib/rigor/cli/baseline_command.rb +7 -18
  82. data/lib/rigor/cli/check_command.rb +85 -14
  83. data/lib/rigor/cli/doctor_command.rb +1 -1
  84. data/lib/rigor/cli/effects_report.rb +2 -2
  85. data/lib/rigor/cli/trace_command.rb +5 -0
  86. data/lib/rigor/cli/type_of_command.rb +5 -3
  87. data/lib/rigor/cli/unused_command.rb +16 -10
  88. data/lib/rigor/cli.rb +11 -3
  89. data/lib/rigor/config_audit.rb +3 -2
  90. data/lib/rigor/config_path.rb +29 -0
  91. data/lib/rigor/configuration/severity_profile.rb +11 -0
  92. data/lib/rigor/configuration.rb +52 -7
  93. data/lib/rigor/effects/plugin_facts.rb +6 -6
  94. data/lib/rigor/environment/bundle_sig_discovery.rb +5 -3
  95. data/lib/rigor/environment/gated_signature_guard.rb +43 -0
  96. data/lib/rigor/environment/gated_signature_plan.rb +339 -0
  97. data/lib/rigor/environment/lockfile_resolver.rb +10 -1
  98. data/lib/rigor/environment/rbs_collection_discovery.rb +10 -1
  99. data/lib/rigor/environment/rbs_coverage_report.rb +1 -1
  100. data/lib/rigor/environment/rbs_loader.rb +284 -15
  101. data/lib/rigor/environment/required_features.rb +206 -0
  102. data/lib/rigor/environment/source_rbs_synthesis.rb +147 -0
  103. data/lib/rigor/environment.rb +34 -73
  104. data/lib/rigor/inference/acceptance.rb +28 -3
  105. data/lib/rigor/inference/block_parameter_binder.rb +29 -13
  106. data/lib/rigor/inference/builtins/cruby_definers.rb +61 -0
  107. data/lib/rigor/inference/builtins/method_catalog.rb +3 -0
  108. data/lib/rigor/inference/captured_locals.rb +7 -1
  109. data/lib/rigor/inference/declaration_walk/context.rb +195 -0
  110. data/lib/rigor/inference/declaration_walk/errors.rb +18 -0
  111. data/lib/rigor/inference/declaration_walk/shadow.rb +147 -0
  112. data/lib/rigor/inference/declaration_walk/traversal.rb +441 -0
  113. data/lib/rigor/inference/declaration_walk.rb +7 -0
  114. data/lib/rigor/inference/def_node_resolver.rb +26 -3
  115. data/lib/rigor/inference/definer_resolution.rb +266 -0
  116. data/lib/rigor/inference/expression_typer.rb +351 -61
  117. data/lib/rigor/inference/external_ancestor_resolution.rb +13 -16
  118. data/lib/rigor/inference/global_write_census.rb +7 -4
  119. data/lib/rigor/inference/guard_rebinding.rb +54 -13
  120. data/lib/rigor/inference/in_effect_refinements/proc_literals.rb +83 -0
  121. data/lib/rigor/inference/in_effect_refinements/refine_defs.rb +54 -0
  122. data/lib/rigor/inference/in_effect_refinements/scope_reads.rb +282 -0
  123. data/lib/rigor/inference/in_effect_refinements/walk.rb +276 -0
  124. data/lib/rigor/inference/in_effect_refinements.rb +256 -0
  125. data/lib/rigor/inference/key_presence_guard/scan.rb +281 -0
  126. data/lib/rigor/inference/key_presence_guard.rb +303 -0
  127. data/lib/rigor/inference/macro_block_self_type.rb +73 -14
  128. data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
  129. data/lib/rigor/inference/method_dispatcher/arithmetic_sequence_elements.rb +73 -0
  130. data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +68 -0
  131. data/lib/rigor/inference/method_dispatcher/integer_step_block_params.rb +81 -0
  132. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -0
  133. data/lib/rigor/inference/method_dispatcher/keyword_arguments.rb +267 -0
  134. data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -1
  135. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +122 -105
  136. data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +305 -54
  137. data/lib/rigor/inference/method_dispatcher/splat_arity.rb +71 -0
  138. data/lib/rigor/inference/method_dispatcher.rb +44 -11
  139. data/lib/rigor/inference/module_function_state.rb +214 -0
  140. data/lib/rigor/inference/multi_target_binder.rb +21 -16
  141. data/lib/rigor/inference/narrowing.rb +166 -22
  142. data/lib/rigor/inference/object_mixins.rb +180 -0
  143. data/lib/rigor/inference/parameter_inference_collector.rb +10 -9
  144. data/lib/rigor/inference/project_patched_methods.rb +10 -5
  145. data/lib/rigor/inference/project_patched_scanner.rb +71 -3
  146. data/lib/rigor/inference/refine_census.rb +239 -0
  147. data/lib/rigor/inference/refine_self.rb +317 -0
  148. data/lib/rigor/inference/refined_dispatch/own_method.rb +403 -0
  149. data/lib/rigor/inference/refined_dispatch.rb +272 -0
  150. data/lib/rigor/inference/scope_indexer/certainty.rb +176 -0
  151. data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +35 -0
  152. data/lib/rigor/inference/scope_indexer/def_nestings_collector.rb +47 -0
  153. data/lib/rigor/inference/scope_indexer/layered_def_nestings.rb +36 -0
  154. data/lib/rigor/inference/scope_indexer/member_layouts_collector.rb +61 -0
  155. data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +143 -0
  156. data/lib/rigor/inference/scope_indexer/superclasses_collector.rb +64 -0
  157. data/lib/rigor/inference/scope_indexer.rb +2129 -354
  158. data/lib/rigor/inference/singleton_hook_decline.rb +253 -0
  159. data/lib/rigor/inference/statement_evaluator.rb +149 -32
  160. data/lib/rigor/inference/string_mutation.rb +9 -3
  161. data/lib/rigor/inference/synthetic_method_scanner.rb +2 -1
  162. data/lib/rigor/inference/version_guard.rb +19 -10
  163. data/lib/rigor/language_server/project_context.rb +31 -0
  164. data/lib/rigor/language_server/server.rb +18 -0
  165. data/lib/rigor/plugin/base.rb +28 -8
  166. data/lib/rigor/plugin/inflector.rb +1 -1
  167. data/lib/rigor/plugin/io_boundary.rb +124 -7
  168. data/lib/rigor/plugin/isolation.rb +1 -1
  169. data/lib/rigor/plugin/macro/block_as_method.rb +51 -8
  170. data/lib/rigor/plugin/trust_policy.rb +3 -3
  171. data/lib/rigor/project_environment.rb +29 -6
  172. data/lib/rigor/protection/discovery_seed.rb +18 -9
  173. data/lib/rigor/protection/mutation_cache.rb +4 -4
  174. data/lib/rigor/protection/mutator.rb +1 -1
  175. data/lib/rigor/reflection/constant_ancestors.rb +211 -46
  176. data/lib/rigor/reflection/constant_path.rb +33 -7
  177. data/lib/rigor/reflection.rb +41 -10
  178. data/lib/rigor/scope/discovery_index.rb +258 -3
  179. data/lib/rigor/scope/resolution_chain/relevance.rb +165 -0
  180. data/lib/rigor/scope/resolution_chain.rb +1032 -0
  181. data/lib/rigor/scope.rb +480 -328
  182. data/lib/rigor/sig_gen/alias_index.rb +1 -1
  183. data/lib/rigor/sig_gen/classification.rb +4 -1
  184. data/lib/rigor/sig_gen/effect_annotation.rb +2 -2
  185. data/lib/rigor/sig_gen/generator.rb +77 -21
  186. data/lib/rigor/sig_gen/method_candidate.rb +2 -2
  187. data/lib/rigor/sig_gen/skip_reason_catalog.rb +12 -0
  188. data/lib/rigor/source/alias_names.rb +52 -0
  189. data/lib/rigor/source/literals.rb +3 -2
  190. data/lib/rigor/source/symbol_name.rb +20 -0
  191. data/lib/rigor/type/combinator.rb +59 -3
  192. data/lib/rigor/type/float_range.rb +4 -1
  193. data/lib/rigor/type/nominal.rb +6 -1
  194. data/lib/rigor/version.rb +1 -1
  195. data/plugins/rigor-ac-library-rb/lib/rigor/plugin/ac_library_rb.rb +28 -0
  196. data/plugins/rigor-ac-library-rb/lib/rigor-ac-library-rb.rb +3 -0
  197. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/convolution.rbs +7 -0
  198. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/data_structures.rbs +160 -0
  199. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/graph.rbs +94 -0
  200. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/math.rbs +42 -0
  201. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/modint.rbs +71 -0
  202. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/object.rbs +37 -0
  203. data/plugins/rigor-ac-library-rb/sig/ac_library_rb/segtree.rbs +83 -0
  204. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +2 -1
  205. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +100 -0
  206. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +94 -10
  207. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +43 -2
  208. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +3 -1
  209. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +149 -0
  210. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +74 -0
  211. data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +104 -0
  212. data/plugins/rigor-alba/lib/rigor/plugin/alba.rb +85 -0
  213. data/plugins/rigor-alba/lib/rigor-alba.rb +3 -0
  214. data/plugins/rigor-alba/sig/alba.rbs +4 -0
  215. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +2 -2
  216. data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +29 -12
  217. data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +13 -2
  218. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +130 -21
  219. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +6 -4
  220. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +4 -4
  221. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +1 -1
  222. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +1 -1
  223. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +31 -0
  224. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +102 -0
  225. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +87 -0
  226. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +76 -0
  227. data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +73 -0
  228. data/plugins/rigor-typelizer/lib/rigor-typelizer.rb +3 -0
  229. data/sig/rigor/analysis/dependency_source_inference/index.rbs +1 -1
  230. data/sig/rigor/environment.rbs +2 -0
  231. data/sig/rigor/plugin/io_boundary.rbs +4 -0
  232. data/sig/rigor/scope.rbs +69 -4
  233. data/sig/rigor/source.rbs +4 -0
  234. data/sig/rigor.rbs +3 -1
  235. data/skills/rigor-project-init/SKILL.md +37 -11
  236. data/skills/rigor-project-init/references/02-configure.md +7 -2
  237. metadata +90 -1
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rigor
4
+ module Plugin
5
+ class Typelizer < Rigor::Plugin::Base
6
+ # The project's classes (every file of the project's `paths:` and the typelizer `dirs`), as the {SerializerCollector} found them. A frozen value
7
+ # object the `:serializer_index` producer caches.
8
+ #
9
+ # {#roots} answers the one question, conservatively — a wrong "no" costs a missing root, a wrong "yes"
10
+ # hides a dead class.
11
+ class SerializerIndex
12
+ # A class the project declares. `superclass` is the constant as written (`nil` when absent) and
13
+ # `nesting` the `Module.nesting` chain (innermost first) its header was written under, which is where
14
+ # Ruby resolves that constant. `dsl` is true when its body includes or extends `Typelizer::DSL`; `in_dirs` when a file under the
15
+ # typelizer `dirs` declares it (only such a class is published as a root).
16
+ ClassEntry = Data.define(:name, :superclass, :nesting, :dsl, :in_dirs)
17
+
18
+ def initialize(classes:)
19
+ @classes = classes.to_h { |entry| [entry.name, entry] }.freeze
20
+ freeze
21
+ end
22
+
23
+ def empty?
24
+ @classes.empty?
25
+ end
26
+
27
+ # Every class typelizer generates an interface for: a class that registered itself through the DSL, and
28
+ # each subclass of one (`Typelizer.target_serializers` takes `descendants`).
29
+ def roots
30
+ @classes.keys.select { |name| @classes[name].in_dirs && typelized?(name, {}) }.sort
31
+ end
32
+
33
+ private
34
+
35
+ def typelized?(name, seen)
36
+ entry = @classes[name]
37
+ return false if entry.nil? || seen[name]
38
+
39
+ seen[name] = true
40
+ return true if entry.dsl
41
+
42
+ parent = superclass_of(entry)
43
+ !parent.nil? && typelized?(parent, seen)
44
+ end
45
+
46
+ # Resolves the superclass as written the way Ruby does: against the `Module.nesting` chain of the header,
47
+ # innermost first, then the top level. A compact header (`class Admin::UserSerializer < Base`) does not
48
+ # put `Admin` on the chain, so `Base` is not looked up under it. The first match wins, so a class that
49
+ # shadows the DSL base (`Admin::Base` with no DSL) ends the walk there.
50
+ #
51
+ # Ruby also searches the ancestors of the innermost cref before the top level. That is not modelled, so a
52
+ # name that is not found on the lexical chain is declined (nil: not typelized) when the innermost scope
53
+ # is a class with a superclass, since the constant might live on that superclass.
54
+ def superclass_of(entry)
55
+ written = entry.superclass
56
+ return nil if written.nil?
57
+
58
+ if written.start_with?("::")
59
+ rooted = written.delete_prefix("::")
60
+ return @classes.key?(rooted) ? rooted : nil
61
+ end
62
+
63
+ lexical = entry.nesting.map { |scope| "#{scope}::#{written}" }.find { |name| @classes.key?(name) }
64
+ return lexical if lexical
65
+ return nil if cref_has_ancestors?(entry.nesting.first)
66
+
67
+ @classes.key?(written) ? written : nil
68
+ end
69
+
70
+ def cref_has_ancestors?(scope)
71
+ !scope.nil? && !@classes[scope]&.superclass.nil?
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rigor/plugin"
4
+
5
+ require_relative "typelizer/serializer_index"
6
+ require_relative "typelizer/serializer_collector"
7
+ require_relative "typelizer/serializer_discoverer"
8
+
9
+ module Rigor
10
+ module Plugin
11
+ # rigor-typelizer — roots the serializer classes the typelizer gem
12
+ # (https://github.com/skryukov/typelizer) generates TypeScript interfaces from, so `rigor unused` does not
13
+ # list them as removal candidates.
14
+ #
15
+ # typelizer's `Typelizer.target_serializers` is `base_classes + base_classes.flat_map(&:descendants)`, where
16
+ # `base_classes` holds every named class that ran `include Typelizer::DSL` or `extend Typelizer::DSL`. Such
17
+ # a class may never be referenced from Ruby and still be consumed by the frontend through the generated
18
+ # type, so the plugin publishes it — and every project subclass of it — as a `:reachability_roots` fact.
19
+ #
20
+ # The plugin adds no diagnostic and no return type. It models nothing at runtime: `reject_class` (a lambda
21
+ # in typelizer's configuration that can drop a class from the output) is not evaluated, so a rejected class
22
+ # is still rooted. That costs a hidden candidate, never a false report.
23
+ class Typelizer < Rigor::Plugin::Base
24
+ manifest(
25
+ id: "typelizer",
26
+ target_gems: ["typelizer"],
27
+ version: "0.1.0",
28
+ description: "Roots the serializer classes typelizer generates TypeScript interfaces from, " \
29
+ "for `rigor unused`.",
30
+ config_schema: {
31
+ # Typelizer::Railtie sets exactly these two when `Typelizer.dirs` is empty.
32
+ "dirs" => { kind: :array, default: %w[app/resources app/serializers] }
33
+ },
34
+ produces: [:reachability_roots]
35
+ )
36
+
37
+ producer :serializer_index, watch: -> { watch_globs } do |_params|
38
+ SerializerDiscoverer.new(io_boundary: io_boundary, dirs: @dirs, project_paths: @project_paths).discover
39
+ end
40
+
41
+ def init(services)
42
+ @dirs = Array(config.fetch("dirs")).map(&:to_s)
43
+ @project_paths = Array(services.configuration.paths).map(&:to_s)
44
+ end
45
+
46
+ def prepare(services)
47
+ index = producer_value(:serializer_index)
48
+ return if index.nil?
49
+
50
+ roots = index.roots
51
+ return if roots.empty?
52
+
53
+ services.fact_store.publish(plugin_id: manifest.id, name: :reachability_roots, value: roots)
54
+ end
55
+
56
+ private
57
+
58
+ # Superclass resolution reads every file of the project's `paths:` (a shadowing `Admin::Base` may live
59
+ # outside `dirs`), so all of them are watched, not only `dirs`.
60
+ def watch_globs
61
+ (@dirs + @project_paths).filter_map do |entry|
62
+ if io_boundary.directory?(File.expand_path(entry))
63
+ [entry, "**/*.rb"]
64
+ elsif entry.end_with?(".rb") && io_boundary.file?(File.expand_path(entry))
65
+ [File.dirname(entry), File.basename(entry)]
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ Rigor::Plugin.register(Typelizer)
72
+ end
73
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rigor/plugin/typelizer"
@@ -2,7 +2,7 @@ module Rigor
2
2
  module Analysis
3
3
  module DependencySourceInference
4
4
  class Index
5
- def initialize: (?resolved_gems: untyped, ?unresolvable: untyped, ?method_catalog: untyped, ?budget_exceeded: untyped, ?class_to_gem: untyped, ?budget_overrun_strategy: untyped) -> void
5
+ def initialize: (?resolved_gems: untyped, ?unresolvable: untyped, ?method_catalog: untyped, ?budget_exceeded: untyped, ?class_to_gem: untyped, ?budget_overrun_strategy: untyped, ?gem_modes: untyped, ?refinements: untyped) -> void
6
6
  end
7
7
  end
8
8
  end
@@ -20,6 +20,8 @@ module Rigor
20
20
 
21
21
  def self.default: () -> Environment
22
22
  def self.for_project: (?root: String, ?libraries: Array[String], ?signature_paths: Array[String | _ToPath]?, ?cache_store: untyped?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?, ?rbs_extended_reporter: untyped?, ?boundary_cross_reporter: untyped?, ?source_rbs_synthesis_reporter: untyped?, ?bundler_bundle_path: String?, ?bundler_auto_detect: bool, ?synthetic_method_index: untyped?, ?project_patched_methods: untyped?) -> Environment
23
+ # authored: the return is the `signature_paths:` argument's type (authored intent) or the default [Pathname]; sig-gen types the `||` fallback untyped.
24
+ def self.project_signature_roots: (Array[String | _ToPath]? signature_paths, ?root: String) -> Array[String | _ToPath]
23
25
 
24
26
  def initialize: (?class_registry: ClassRegistry, ?rbs_loader: RbsLoader?, ?plugin_registry: untyped?, ?dependency_source_index: untyped?, ?rbs_extended_reporter: untyped?, ?boundary_cross_reporter: untyped?, ?source_rbs_synthesis_reporter: untyped?, ?synthetic_method_index: untyped?, ?project_patched_methods: untyped?) -> void
25
27
  def rbs_extended_reporter: () -> untyped?
@@ -4,10 +4,14 @@ class Rigor::Plugin::IoBoundary
4
4
  def file?: (untyped) -> bool
5
5
  def directory?: (untyped) -> bool
6
6
  def list_directory: (untyped) -> Array[String]
7
+ # Records a `:names` glob row (membership only); a caller needing a match's content `read_file`s it. A `..`
8
+ # segment, a brace or a backslash in `pattern` raises `AccessDeniedError`.
9
+ def glob: (untyped root, untyped pattern) -> Array[String]
7
10
  # sig-gen gap: #1156 — the generator pins the empty-summary literal (`count: 0, first_path: nil`); the
8
11
  # record's fields are the running count and the first refused path, nil when nothing was refused.
9
12
  def refusal_summary: () -> { count: Integer, first_path: String?, nearest_root: String? }?
10
13
  def open_url: (untyped url) -> String
11
14
  # Returns a frozen `Rigor::Cache::Descriptor` (no RBS yet -> untyped).
12
15
  def cache_descriptor: () -> untyped
16
+ def replay: (untyped descriptor) -> nil
13
17
  end
data/sig/rigor/scope.rbs CHANGED
@@ -29,6 +29,10 @@ module Rigor
29
29
  attr_reader opaque_block_self: bool
30
30
  # sig-gen gap: #1154 — sig-gen skips the reader (sig.skipped.untyped-return): an `attr_reader`
31
31
  # over an ivar assigned in `initialize` from a parameter, so inference reaches `untyped`.
32
+ # Issue #1759 — `:refinement` (REFINEMENT_BLOCK_SELF) directly in a `refine X do … end` block body.
33
+ attr_reader block_self_unknown: bool | :refinement
34
+ # sig-gen gap: #1154 — sig-gen skips the reader (sig.skipped.untyped-return): an `attr_reader`
35
+ # over an ivar assigned in `initialize` from a parameter, so inference reaches `untyped`.
32
36
  attr_reader singleton_class_body: bool
33
37
  attr_reader lexical_nesting: Array[String]?
34
38
  # Issue #1429 — a guard's narrowing of a constant reference, keyed by its spelling.
@@ -37,6 +41,12 @@ module Rigor
37
41
  # Issue #1429 — the pre-guard type of each global or constant a guard narrowed.
38
42
  # sig-gen gap: #1154 — a reader over an ivar `initialize` assigns from a parameter.
39
43
  attr_reader guard_records: Hash[[Symbol, Symbol | String], Type::t]
44
+ # Issue #1446 — the classes a class guard narrowed a `bot` receiver to.
45
+ # sig-gen gap: #1154 — a reader over an ivar `initialize` assigns from a parameter.
46
+ attr_reader bot_guard_classes: Hash[[Symbol, Symbol | String], Array[String]]
47
+ # Issue #1667 — the modules a plugin-declared refined block runs under, in declaration order.
48
+ # sig-gen gap: #1154 — a reader over an ivar `initialize` assigns from a parameter.
49
+ attr_reader declared_refinements: Array[String]
40
50
 
41
51
  # ADR-53 Track A — the seed-time discovery tables live on the
42
52
  # DiscoveryIndex; Scope keeps per-table readers as delegates.
@@ -61,8 +71,11 @@ module Rigor
61
71
  # deferred-range table's row shape is hand-written.
62
72
  def discovered_deferred_ranges: () -> Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]]
63
73
  # sig-gen gap: #1148 — sig-gen skips the endless-def reader (untyped return), so the refinement table's
64
- # shape is hand-written.
65
- def discovered_refinements: () -> Hash[String, Hash[Symbol, Array[String]]]
74
+ # shape is hand-written. A method key is a Symbol or the `"*"` wildcard (ADR-121 WD7).
75
+ def discovered_refinements: () -> Hash[String, Hash[Symbol | String, Array[String]]]
76
+ # authored: the list holds module names and the `InEffectRefinements::UNKNOWN` Symbol; sig-gen reads the delegated
77
+ # return through an unsigned class as untyped.
78
+ def in_effect_refinements: (untyped node, ?Array[String] declared) -> Array[String | Symbol]
66
79
  # sig-gen gap: #1148 — sig-gen skips the endless-def reader (untyped return), so the census shape is
67
80
  # hand-written.
68
81
  def discovered_global_write_census: () -> Set[Array[untyped]]
@@ -141,7 +154,7 @@ module Rigor
141
154
  # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
142
155
  attr_reader discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]]
143
156
  # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
144
- attr_reader discovered_refinements: Hash[String, Hash[Symbol, Array[String]]]
157
+ attr_reader discovered_refinements: Hash[String, Hash[Symbol | String, Array[String]]]
145
158
  # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
146
159
  attr_reader discovered_global_write_census: Set[Array[untyped]]
147
160
  attr_reader run_generation: Object?
@@ -154,10 +167,41 @@ module Rigor
154
167
  # `untyped`: the value is an `Inference::LastLine::SelfEvidence` (or nil), and that class carries no signature,
155
168
  # as `Inference::MatchRebinding::Frame` carries none for `match_frame`.
156
169
  attr_reader implicit_self_evidence: untyped
170
+ # sig-gen gap: #1150 — `Data.define` members carry no type; the value is an `Inference::InEffectRefinements`
171
+ # (or nil), which carries no signature either.
172
+ attr_reader in_effect_refinements: untyped
173
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
174
+ attr_reader possible_discovered_methods: Hash[String, Hash[Symbol, Symbol]]
175
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
176
+ attr_reader possible_discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]]
177
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
178
+ attr_reader contested_discovered_def_nodes: Set[Array[untyped]]
179
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
180
+ attr_reader contested_discovered_singleton_def_nodes: Set[Array[untyped]]
181
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
182
+ attr_reader contested_discovered_method_visibilities: Set[Array[untyped]]
183
+ # sig-gen gap: #1150 — `Data.define` members carry no type, as for every other row in this class.
184
+ attr_reader contested_discovered_parameter_envelopes: Set[Array[untyped]]
157
185
 
158
186
  EMPTY: DiscoveryIndex
187
+ # authored: ADR-119 WD1 — hand-written beside the `Data.define` rows above (#1150).
188
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
189
+ SIBLINGS: Hash[Symbol, Symbol]
190
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
191
+ EMPTY_SIBLINGS: Hash[Symbol, untyped]
192
+
193
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
194
+ def self.empty_value: (Symbol name) -> untyped
195
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
196
+ def self.empty_siblings: () -> Hash[Symbol, untyped]
197
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
198
+ def self.compact_pairs: (Hash[Symbol, untyped] tables) -> Hash[Symbol, untyped]
199
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
200
+ def self.paired?: (Symbol name) -> bool
201
+ # authored: ADR-119 WD1 sibling pairing; no inferred signature exists for it (#1150).
202
+ def sibling_tables: () -> Hash[Symbol, untyped]
159
203
 
160
- def with: (?declared_types: Hash[untyped, Type::t], ?class_ivars: Hash[String, Hash[Symbol, Type::t]], ?class_cvars: Hash[String, Hash[Symbol, Type::t]], ?program_globals: Hash[Symbol, Type::t], ?program_global_seeds: Hash[Symbol, Type::t], ?discovered_classes: Hash[String, Type::Singleton], ?in_source_constants: Hash[String, Type::t], ?discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?discovered_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_nestings: Hash[untyped, Array[String]], ?discovered_singleton_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_singleton_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]], ?discovered_parameter_envelopes: Hash[String, Hash[untyped, untyped]], ?discovered_superclasses: Hash[String, String], ?discovered_includes: Hash[String, Array[String]], ?discovered_prepends: Hash[String, Array[String]], ?discovered_extends: Hash[String, Array[String]], ?discovered_class_sources: Hash[String, Set[String]], ?constant_sources: Hash[String, Set[String]], ?constant_writers: Hash[String, Array[String]], ?constant_shadowers: Hash[String, Array[String]], ?published_constant_names: Set[String], ?local_constant_names: Set[String], ?published_constant_alias_names: Set[String], ?published_constant_ivars: Hash[String, Set[Symbol]], ?data_member_layouts: Hash[String, Array[Symbol]], ?struct_member_layouts: Hash[String, { members: Array[Symbol], keyword_init: bool }], ?param_inferred_types: Hash[[String, Symbol, Symbol], Hash[Symbol, Type::t]], ?discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?discovered_refinements: Hash[String, Hash[Symbol, Array[String]]], ?discovered_global_write_census: Set[Array[untyped]], ?run_generation: Object?, ?patched_line_readers: Set[Symbol], ?clears_last_status: bool, ?defines_case_equality: bool, ?implicit_self_evidence: untyped) -> DiscoveryIndex
204
+ def with: (?declared_types: Hash[untyped, Type::t], ?class_ivars: Hash[String, Hash[Symbol, Type::t]], ?class_cvars: Hash[String, Hash[Symbol, Type::t]], ?program_globals: Hash[Symbol, Type::t], ?program_global_seeds: Hash[Symbol, Type::t], ?discovered_classes: Hash[String, Type::Singleton], ?in_source_constants: Hash[String, Type::t], ?discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?discovered_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_nestings: Hash[untyped, Array[String]], ?discovered_singleton_def_nodes: Hash[String, Hash[Symbol, untyped]], ?discovered_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_singleton_def_sources: Hash[String, Hash[Symbol, String]], ?discovered_method_visibilities: Hash[String, Hash[Symbol, Symbol]], ?discovered_parameter_envelopes: Hash[String, Hash[untyped, untyped]], ?discovered_superclasses: Hash[String, String], ?discovered_includes: Hash[String, Array[String]], ?discovered_prepends: Hash[String, Array[String]], ?discovered_extends: Hash[String, Array[String]], ?discovered_class_sources: Hash[String, Set[String]], ?constant_sources: Hash[String, Set[String]], ?constant_writers: Hash[String, Array[String]], ?constant_shadowers: Hash[String, Array[String]], ?published_constant_names: Set[String], ?local_constant_names: Set[String], ?published_constant_alias_names: Set[String], ?published_constant_ivars: Hash[String, Set[Symbol]], ?data_member_layouts: Hash[String, Array[Symbol]], ?struct_member_layouts: Hash[String, { members: Array[Symbol], keyword_init: bool }], ?param_inferred_types: Hash[[String, Symbol, Symbol], Hash[Symbol, Type::t]], ?discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?discovered_refinements: Hash[String, Hash[Symbol | String, Array[String]]], ?discovered_global_write_census: Set[Array[untyped]], ?run_generation: Object?, ?patched_line_readers: Set[Symbol], ?clears_last_status: bool, ?defines_case_equality: bool, ?implicit_self_evidence: untyped, ?in_effect_refinements: untyped, ?possible_discovered_methods: Hash[String, Hash[Symbol, Symbol]], ?possible_discovered_deferred_ranges: Hash[String, Array[[Integer, Integer, Symbol?, Symbol?, String?]]], ?contested_discovered_def_nodes: Set[Array[untyped]], ?contested_discovered_singleton_def_nodes: Set[Array[untyped]], ?contested_discovered_method_visibilities: Set[Array[untyped]], ?contested_discovered_parameter_envelopes: Set[Array[untyped]]) -> DiscoveryIndex
161
205
  end
162
206
 
163
207
  class IndexedKey
@@ -180,8 +224,15 @@ module Rigor
180
224
  def with_lexical_nesting: (Array[String]? chain) -> Scope
181
225
  def struct_fold_safe?: (String | Symbol name) -> bool
182
226
  def entering_opaque_block: () -> Scope
227
+ # sig-gen gap: #1148 — sig-gen skips the endless-def reader (sig.skipped.untyped-return); the row is hand-written.
228
+ def block_self_unknown?: () -> bool
229
+ REFINEMENT_BLOCK_SELF: :refinement
230
+ # sig-gen gap: #1148 — sig-gen skips the endless-def predicate (sig.skipped.untyped-return); the row is hand-written.
231
+ def refinement_block_self?: () -> bool
232
+ def with_block_self_unknown: (bool flag) -> Scope
183
233
  def opaque_block_self?: () -> bool
184
234
  def with_singleton_class_body: (bool) -> Scope
235
+ def with_declared_refinements: (Array[String] names) -> Scope
185
236
  # sig-gen gap: #1148 — sig-gen skips the endless-def reader (sig.skipped.untyped-return); the row is hand-written.
186
237
  def singleton_class_body?: () -> bool
187
238
  def with_discovery: (DiscoveryIndex index) -> Scope
@@ -206,6 +257,15 @@ module Rigor
206
257
  # Issue #1429 — `rigor sig-gen` output; the parameters are not inferred here, as for `with_mutated_local`.
207
258
  def with_guarded_global: (untyped name, untyped type, untyped pre_guard, ?record: untyped) -> Scope
208
259
  def with_constant_narrowing: (untyped key, untyped type, untyped pre_guard) -> Scope
260
+ # Issue #1446 — `rigor sig-gen` output.
261
+ def with_guarded_ivar: (untyped name, untyped type, untyped pre_guard) -> Scope
262
+ def without_ivar_guard: (untyped name) -> Scope
263
+ # sig-gen gap: #1154 — the table reader over an ivar `initialize` assigns from a parameter reaches `untyped`.
264
+ def guard_narrowed_ivar?: (untyped name) -> bool
265
+ # Issue #1446 — `rigor sig-gen` output.
266
+ def with_bot_guard_classes: (untyped kind, untyped name, untyped class_names) -> Scope
267
+ # sig-gen gap: #1154 — the table reader over an ivar `initialize` assigns from a parameter reaches `untyped`.
268
+ def bot_guard_classes_for: (untyped kind, untyped name) -> Array[String]?
209
269
  def without_constant_narrowing: (untyped key) -> Scope
210
270
  def guard_narrowed?: () -> bool
211
271
  def forget_guard_narrowings: () -> Scope
@@ -259,6 +319,8 @@ module Rigor
259
319
  def class_ivars_for: (String | Symbol? class_name) -> Hash[Symbol, Type::t]
260
320
  def class_cvars_for: (String | Symbol? class_name) -> Hash[Symbol, Type::t]
261
321
  def discovered_method?: (String | Symbol class_name, String | Symbol method_name, Symbol kind) -> bool
322
+ # authored: the slot is a Prism::DefNode or a DefHandle until ADR-119 C1d/C2 decide its type.
323
+ def def_node_slot: (untyped class_name, untyped method_name, ?Symbol kind) -> untyped
262
324
  def user_def_for: (String | Symbol class_name, String | Symbol method_name) -> ::Prism::DefNode?
263
325
  def user_def_through_ancestors: (String | Symbol class_name, String | Symbol method_name, ?name_memo: Hash[untyped, untyped]) -> ([::Prism::DefNode, String] | [nil, nil])
264
326
  def discovered_method_through_ancestors?: (String | Symbol? class_name, String | Symbol method_name, Symbol kind, ?name_memo: Hash[untyped, untyped]) -> bool
@@ -295,6 +357,7 @@ module Rigor
295
357
  def singleton_extends_of: (String | Symbol class_name) -> Array[String]
296
358
  def indexed_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, untyped key) -> Type::t?
297
359
  def with_indexed_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, untyped key, Type::t type) -> Scope
360
+ def with_isolated_side_tables: () -> Scope
298
361
  def without_indexed_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, untyped key) -> Scope
299
362
  def without_indexed_narrowings_for: (Symbol receiver_kind, String | Symbol receiver_name) -> Scope
300
363
  def method_chain_narrowing: (Symbol receiver_kind, String | Symbol receiver_name, String | Symbol method_name) -> Type::t?
@@ -303,6 +366,8 @@ module Rigor
303
366
  def without_method_chain_narrowings_for: (Symbol receiver_kind, String | Symbol receiver_name) -> Scope
304
367
  def with_fact: (Analysis::FactStore::Fact fact) -> Scope
305
368
  def with_self_type: (Type::t? type) -> Scope
369
+ def with_block_self_type: (Type::t? type, ?keeps_unknown: bool | :refinement) -> Scope
370
+ def block_self_narrowing_unknown?: (Prism::CallNode call_node) -> bool
306
371
  def facts_for: (?target: Analysis::FactStore::Target?, ?bucket: Symbol?) -> Array[Analysis::FactStore::Fact]
307
372
  def local_facts: (String | Symbol name, ?bucket: Symbol?) -> Array[Analysis::FactStore::Fact]
308
373
  def type_of: (untyped node, ?tracer: Inference::FallbackTracer?) -> Type::t
data/sig/rigor/source.rbs CHANGED
@@ -21,6 +21,10 @@ module Rigor
21
21
  def self?.symbol_arg: (untyped? call_node, Integer index) -> Symbol?
22
22
  end
23
23
 
24
+ module SymbolName
25
+ def self?.of: (String? text) -> Symbol?
26
+ end
27
+
24
28
  module NodeWalker
25
29
  def self?.each: (untyped root) { (untyped) -> void } -> nil
26
30
  | (untyped root) -> Enumerator[untyped, nil]
data/sig/rigor.rbs CHANGED
@@ -14,7 +14,9 @@ module Rigor
14
14
  def self.load: (?String? path) -> Configuration
15
15
  def self.discover: () -> String?
16
16
  def self.load_with_includes: (String path, ?visited: Set[String]) -> Hash[String, untyped]
17
- def initialize: (?Hash[String, untyped] data, ?bool effects_key_present) -> void
17
+ def initialize: (?Hash[String, untyped] data, ?bool effects_key_present, ?bool target_ruby_key_present) -> void
18
+ def target_ruby_explicit?: () -> bool
19
+ def stated_runtime_ruby: () -> String?
18
20
  def to_h: () -> Hash[String, untyped]
19
21
  # ADR-103 WD13 — the `effects:` block, nil when the key was absent. Presence is the opt-in.
20
22
  attr_reader effects: Hash[String, untyped]?
@@ -88,7 +88,7 @@ diagnostics.
88
88
  | Today's diagnostics | Snapshotted into `.rigor-baseline.yml`; suppressed as long as the count does not grow. | All surfaced; every one is fixed or consciously suppressed. |
89
89
  | Hard-to-fix diagnostics | Left in the baseline. The project **trusts its test / spec suite** to cover runtime correctness for those sites — the static `T | nil` reading is worst-case-sound, the suite proves the worst case is not hit. | Fixed, or annotated `# rigor:disable <rule>` with an author-intent reason at the specific line. No blanket suppression. |
90
90
  | `severity_profile` | `lenient` (or `balanced` for a small project). | `strict`. |
91
- | Best for | Mature codebases; incremental adoption; teams that want the regression guard without a big upfront fix. | New / small projects; libraries; teams that want the maximum guarantee and have the budget to reach zero. |
91
+ | Best for | **Every project by default** — mature or brand-new, large or small. | Users fluent in both type theory and RBS who opt in and will resolve Rigor's inference gaps themselves. |
92
92
  | New diagnostics later | Surface immediately — anything beyond the baseline envelope is a regression. | Surface immediately — there is no envelope; every diagnostic is live. |
93
93
 
94
94
  Both modes give the same core guarantee: **a change that introduces
@@ -97,12 +97,35 @@ diagnostics that exist *today*. Acknowledge mode parenthesises them
97
97
  behind a baseline and leans on the test suite; strict mode refuses to
98
98
  parenthesise anything.
99
99
 
100
- If the project's first `rigor check` reports more than ~100 errors,
101
- recommend acknowledge mode as the default and let the user override.
102
- Below that, either mode is reasonable — ask.
103
-
104
- Note the ordering: the error count that feeds this recommendation (and
105
- the `severity_profile` choice in Phase 4) is only *measured* in
100
+ **Recommend acknowledge mode for every project, including new and
101
+ small ones.** Rigor's inference still has gaps, so even a fresh
102
+ project meets diagnostics on correct code. Under strict mode each one
103
+ needs a `sig/` fix, an RBS workaround, or a reasoned
104
+ `# rigor:disable` — work that takes type theory and RBS. Without that
105
+ knowledge, users rewrite correct code for the tool or scatter
106
+ suppressions. Acknowledge mode keeps the same regression guard with an
107
+ exit: a new diagnostic still fails the check, and once it is confirmed
108
+ as an inference gap, the baseline can absorb it (report the false
109
+ positive upstream). `rigor baseline regenerate` rewrites the baseline
110
+ from every live diagnostic, so run it only when a whole-project
111
+ `rigor check` (no path arguments) shows nothing but confirmed false
112
+ positives; otherwise it silences a real
113
+ bug alongside them.
114
+
115
+ Present strict mode as an opt-in for users who know type theory and
116
+ RBS, want the zero-diagnostic gate, and accept resolving inference
117
+ gaps themselves — never as the recommendation, and never because the
118
+ project is new, small, or a library. If the user picks it, tell them
119
+ how to move to acknowledge mode later: optionally relax
120
+ `severity_profile`, re-run triage, then run Phase 7 (generate a
121
+ baseline and wire `baseline:`). The profile change comes first because
122
+ it changes which rules fire.
123
+
124
+ For a large first run (more than ~100 errors), acknowledge mode also
125
+ suggests `severity_profile: lenient`; see Phase 4.
126
+
127
+ Note the ordering: the error count that feeds the `severity_profile`
128
+ choice in Phase 4 is only *measured* in
106
129
  Phase 6's triage run. Any profile written in Phase 4 is therefore
107
130
  **provisional** — Phase 6 revisits it against the measured count (see
108
131
  [`references/02-configure.md`](references/02-configure.md)
@@ -111,12 +134,15 @@ Phase 6's triage run. Any profile written in Phase 4 is therefore
111
134
  ## Non-interactive / agent-driven runs
112
135
 
113
136
  This skill has several ask-the-user points (the mode choice, the RBS
114
- collection install, the final gitignore-and-commit confirmation). When
115
- the user delegated the onboarding up front — including the adoption
116
- mode — those points resolve without blocking:
137
+ collection install, the final gitignore-and-commit confirmation). A
138
+ *delegation* is the user asking, before the run, for the onboarding to
139
+ proceed without these questions ("set it up, don't ask me"). An
140
+ ordinary request to set Rigor up is not one: ask as usual. Under a
141
+ delegation those points resolve without blocking:
117
142
 
118
143
  - The **mode** the user pre-declared is settled; record it and skip the
119
- Phase 2 presentation.
144
+ Phase 2 presentation. If the delegation names no mode, choose
145
+ acknowledge mode and say so in the report — never strict.
120
146
  - Low-risk, reversible, in-repo confirmations (`rbs collection
121
147
  install`, the `.gitignore` addition) are covered by the delegation —
122
148
  act, and note each self-answer in your report.
@@ -138,14 +138,19 @@ entirely — including a `sig/` that `rigor sig-gen --write` created.
138
138
  List only directories that contain `.rbs` files or subdirectories of
139
139
  them. Rigor walks each path recursively.
140
140
 
141
- A strict-mode plain-Ruby gem is shorter:
141
+ A plain-Ruby gem is shorter. The `balanced` default needs no
142
+ `severity_profile:` key:
142
143
 
143
144
  ```yaml
144
145
  paths:
145
146
  - lib
146
147
  test_paths:
147
148
  - test
148
- severity_profile: strict
149
+
150
+ # Phase 7 (acknowledge mode) appends this line after generating the
151
+ # baseline. Strict mode (opt-in) sets `severity_profile: strict` and
152
+ # leaves it out.
153
+ # baseline: .rigor-baseline.yml
149
154
  ```
150
155
 
151
156
  ### Test roots — write `test_paths:` explicitly