rigortype 0.3.3 → 0.3.4

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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/data/builtins/ruby_core/array.yml +11 -24
  4. data/data/builtins/ruby_core/enumerable.yml +4 -8
  5. data/data/builtins/ruby_core/exception.yml +6 -9
  6. data/data/builtins/ruby_core/hash.yml +13 -20
  7. data/data/builtins/ruby_core/io.yml +6 -9
  8. data/data/builtins/ruby_core/numeric.yml +2 -3
  9. data/data/builtins/ruby_core/proc.yml +4 -8
  10. data/data/builtins/ruby_core/re.yml +6 -9
  11. data/data/builtins/ruby_core/set.yml +2 -3
  12. data/data/builtins/ruby_core/string.yml +4 -7
  13. data/data/builtins/ruby_core/struct.yml +0 -6
  14. data/data/builtins/ruby_core/time.yml +3 -6
  15. data/data/effects/core.yml +843 -0
  16. data/data/effects/registry.yml +67 -0
  17. data/docs/handbook/07-rbs-and-extended.md +106 -5
  18. data/docs/manual/02-cli-reference.md +311 -1
  19. data/docs/manual/03-configuration.md +84 -1
  20. data/docs/manual/04-diagnostics.md +6 -2
  21. data/docs/manual/16-rbs-extended-annotations.md +77 -0
  22. data/docs/manual/18-removing-dead-code.md +326 -0
  23. data/docs/manual/README.md +5 -0
  24. data/docs/manual/plugins/rigor-actionmailer.md +5 -0
  25. data/docs/manual/plugins/rigor-activejob.md +47 -0
  26. data/docs/manual/plugins/rigor-factorybot.md +16 -0
  27. data/docs/manual/plugins/rigor-pundit.md +26 -0
  28. data/docs/manual/plugins/rigor-rails-routes.md +41 -2
  29. data/docs/manual/plugins/rigor-rspec-rails.md +5 -0
  30. data/docs/manual/plugins/rigor-rspec.md +8 -0
  31. data/docs/manual/plugins/rigor-sidekiq.md +54 -0
  32. data/lib/rigor/analysis/check_rules/rule_ids.rb +28 -2
  33. data/lib/rigor/analysis/incremental_session.rb +140 -6
  34. data/lib/rigor/analysis/reachability/graph.rb +244 -0
  35. data/lib/rigor/analysis/reachability/plugin_roots.rb +164 -0
  36. data/lib/rigor/analysis/reachability/project_files.rb +58 -0
  37. data/lib/rigor/analysis/reachability/scan.rb +241 -0
  38. data/lib/rigor/analysis/reachability/signature_scan.rb +134 -0
  39. data/lib/rigor/analysis/rule_catalog.rb +158 -0
  40. data/lib/rigor/analysis/run_cache_key.rb +16 -0
  41. data/lib/rigor/analysis/runner/declaration_position.rb +54 -0
  42. data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +108 -0
  43. data/lib/rigor/analysis/runner/effect_envelope_pass.rb +358 -0
  44. data/lib/rigor/analysis/runner/envelope_messages.rb +61 -0
  45. data/lib/rigor/analysis/runner/pool_coordinator.rb +68 -10
  46. data/lib/rigor/analysis/runner.rb +420 -10
  47. data/lib/rigor/analysis/worker_session.rb +58 -2
  48. data/lib/rigor/bleeding_edge.rb +22 -0
  49. data/lib/rigor/cache/incremental_snapshot.rb +24 -5
  50. data/lib/rigor/cli/check_command.rb +13 -1
  51. data/lib/rigor/cli/check_runner_factory.rb +4 -1
  52. data/lib/rigor/cli/effects_command.rb +131 -0
  53. data/lib/rigor/cli/effects_diff_renderer.rb +123 -0
  54. data/lib/rigor/cli/effects_explain_renderer.rb +68 -0
  55. data/lib/rigor/cli/effects_renderer.rb +54 -0
  56. data/lib/rigor/cli/effects_report.rb +53 -0
  57. data/lib/rigor/cli/effects_snapshot_command.rb +256 -0
  58. data/lib/rigor/cli/unused_command.rb +288 -0
  59. data/lib/rigor/cli.rb +16 -0
  60. data/lib/rigor/configuration/severity_profile.rb +23 -0
  61. data/lib/rigor/configuration.rb +296 -14
  62. data/lib/rigor/effects/attribution.rb +76 -0
  63. data/lib/rigor/effects/catalog.rb +275 -0
  64. data/lib/rigor/effects/collector.rb +182 -0
  65. data/lib/rigor/effects/config_envelopes.rb +185 -0
  66. data/lib/rigor/effects/discharge.rb +69 -0
  67. data/lib/rigor/effects/effect_table.rb +92 -0
  68. data/lib/rigor/effects/entry_points.rb +93 -0
  69. data/lib/rigor/effects/envelope.rb +86 -0
  70. data/lib/rigor/effects/envelope_check.rb +172 -0
  71. data/lib/rigor/effects/envelope_index.rb +157 -0
  72. data/lib/rigor/effects/file_collection.rb +160 -0
  73. data/lib/rigor/effects/framework_units.rb +167 -0
  74. data/lib/rigor/effects/identity.rb +104 -0
  75. data/lib/rigor/effects/label.rb +77 -0
  76. data/lib/rigor/effects/label_intent.rb +73 -0
  77. data/lib/rigor/effects/label_set.rb +136 -0
  78. data/lib/rigor/effects/liskov_check.rb +167 -0
  79. data/lib/rigor/effects/local_ownership.rb +132 -0
  80. data/lib/rigor/effects/method_key.rb +40 -0
  81. data/lib/rigor/effects/mutation_classifier.rb +92 -0
  82. data/lib/rigor/effects/narrowing.rb +202 -0
  83. data/lib/rigor/effects/origin.rb +66 -0
  84. data/lib/rigor/effects/path_finder.rb +89 -0
  85. data/lib/rigor/effects/plugin_facts.rb +335 -0
  86. data/lib/rigor/effects/propagator.rb +275 -0
  87. data/lib/rigor/effects/registry.rb +193 -0
  88. data/lib/rigor/effects/scanner.rb +290 -0
  89. data/lib/rigor/effects/signature_sources.rb +74 -0
  90. data/lib/rigor/effects/snapshot.rb +380 -0
  91. data/lib/rigor/effects/snapshot_diff.rb +265 -0
  92. data/lib/rigor/effects/summary.rb +154 -0
  93. data/lib/rigor/effects/taint_cause.rb +38 -0
  94. data/lib/rigor/effects/unit_scan.rb +572 -0
  95. data/lib/rigor/effects/unknown_label_check.rb +86 -0
  96. data/lib/rigor/effects/unknown_label_report.rb +59 -0
  97. data/lib/rigor/effects.rb +47 -0
  98. data/lib/rigor/environment/rbs_loader.rb +34 -0
  99. data/lib/rigor/environment.rb +8 -1
  100. data/lib/rigor/flow_contribution/element.rb +1 -0
  101. data/lib/rigor/flow_contribution/merge_result.rb +5 -3
  102. data/lib/rigor/flow_contribution/merger.rb +16 -1
  103. data/lib/rigor/flow_contribution.rb +20 -4
  104. data/lib/rigor/inference/expression_typer.rb +18 -4
  105. data/lib/rigor/inference/pre_eval_constants.rb +189 -0
  106. data/lib/rigor/inference/scope_indexer.rb +15 -3
  107. data/lib/rigor/plugin/base.rb +24 -0
  108. data/lib/rigor/plugin/effect_attribution.rb +208 -0
  109. data/lib/rigor/plugin/effect_edge.rb +101 -0
  110. data/lib/rigor/plugin/effect_entry_points.rb +51 -0
  111. data/lib/rigor/plugin/first_party.rb +57 -0
  112. data/lib/rigor/plugin/isolation.rb +4 -1
  113. data/lib/rigor/plugin/manifest.rb +103 -3
  114. data/lib/rigor/plugin/registry.rb +48 -0
  115. data/lib/rigor/plugin.rb +1 -0
  116. data/lib/rigor/rbs_extended/envelope_scanner.rb +160 -0
  117. data/lib/rigor/rbs_extended.rb +168 -0
  118. data/lib/rigor/reflection.rb +131 -30
  119. data/lib/rigor/version.rb +1 -1
  120. data/lib/rigor.rb +1 -0
  121. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +90 -0
  122. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +7 -1
  123. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/effects.rb +85 -0
  124. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +8 -1
  125. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +152 -0
  126. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +9 -1
  127. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +156 -0
  128. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +113 -0
  129. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +68 -3
  130. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +186 -0
  131. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +12 -1
  132. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +145 -0
  133. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/effects.rb +77 -0
  134. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +6 -1
  135. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +143 -0
  136. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -1
  137. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +385 -28
  138. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -2
  139. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +151 -0
  140. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +46 -3
  141. data/plugins/rigor-rails/lib/rigor-rails.rb +1 -0
  142. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/effects.rb +47 -0
  143. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +7 -1
  144. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +81 -0
  145. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +13 -1
  146. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +202 -15
  147. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +43 -3
  148. data/plugins/rigor-railties/lib/rigor/plugin/railties/effects.rb +171 -0
  149. data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +62 -0
  150. data/plugins/rigor-railties/lib/rigor-railties.rb +3 -0
  151. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +122 -0
  152. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +56 -3
  153. data/sig/rigor/analysis/fact_store.rbs +1 -0
  154. data/sig/rigor/inference.rbs +2 -0
  155. data/sig/rigor/rbs_extended.rbs +6 -0
  156. data/sig/rigor.rbs +17 -1
  157. data/skills/rigor-unused-adjudicate/SKILL.md +90 -0
  158. metadata +79 -1
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Rigor
6
+ module Plugin
7
+ class Sidekiq < Rigor::Plugin::Base
8
+ # ADR-102 WD3 / #367 — which workers does this project enqueue by NAME, from schedule configuration?
9
+ #
10
+ # This is the one genuinely Sidekiq-specific reachability root, and the reason it is worth reading YAML
11
+ # for: a cron-scheduled worker is enqueued from `config/schedule.yml` as the string `"HardWorker"`, so
12
+ # the repository may contain no `HardWorker.perform_async` anywhere. The constant scan sees nothing, and
13
+ # `rigor unused` reports a worker that runs every night as dead code.
14
+ #
15
+ # Two layouts, one key. `sidekiq-cron` writes the schedule as the whole document
16
+ # (`{ name => { "cron" =>, "class" => } }`); `sidekiq-scheduler` nests the same entries under
17
+ # `config/sidekiq.yml`'s `:scheduler: :schedule:` block. Both name the worker under `class:`, and
18
+ # `class:` is the ONLY key read here.
19
+ #
20
+ # **What this refuses to read is the queue list.** `:queues:` in `sidekiq.yml` holds queue names, and a
21
+ # queue name is not a class name — inflecting `report_worker` into `ReportWorker` would manufacture a
22
+ # root out of a naming coincidence, which is precisely the over-supply this plugin declined in #350. An
23
+ # over-claiming root source silently hides real dead code (ADR-102 § Consequences), and nothing
24
+ # downstream can tell you it happened. So a queue contributes nothing, deliberately.
25
+ #
26
+ # Names are not trusted either: the caller intersects them with the workers {WorkerDiscoverer} actually
27
+ # found, so a typo or an out-of-tree class costs coverage rather than manufacturing a root, and the
28
+ # report's `matched no declaration` counter stays meaningful.
29
+ #
30
+ # Fail-soft throughout, because this reads user-authored config that Rigor does not own: an absent file,
31
+ # an unreadable one, a YAML syntax error, or a document that is not a Hash contributes nothing rather
32
+ # than raising. `rigor unused` is a report a human reads, and refusing to print it because one
33
+ # `sidekiq.yml` has a stray tab is a bad trade.
34
+ class ScheduleScan
35
+ # The key naming the worker, in both layouts. Symbol keys are accepted because `sidekiq.yml` is
36
+ # conventionally written with them (`:scheduler:`, `:schedule:`), and Psych parses `:class:` as the
37
+ # Symbol `:class`.
38
+ CLASS_KEY = "class"
39
+
40
+ # Where a schedule block hides inside a document. `[]` is the document itself — `sidekiq-cron`'s
41
+ # `schedule.yml` IS the schedule map — and the nested paths are `sidekiq-scheduler`'s, whose entries
42
+ # live under `:scheduler: :schedule:` (with the bare `:schedule:` form kept for pre-3.0 layouts).
43
+ SCHEDULE_BLOCK_KEY_PATHS = [[], %w[scheduler schedule], %w[schedule]].freeze
44
+
45
+ # Errno classes that mean "this path is not readable as a schedule" — swallowed so one bad path does
46
+ # not cost the roots the other paths supply.
47
+ IO_ERRORS = [Errno::ENOENT, Errno::EACCES, Errno::EISDIR].freeze
48
+
49
+ def initialize(io_boundary:, schedule_paths:)
50
+ @io_boundary = io_boundary
51
+ @schedule_paths = schedule_paths
52
+ end
53
+
54
+ # @return [Array<String>] the class names named by a `class:` key in a schedule entry, sorted and
55
+ # unique. NOT yet intersected with the discovered workers — the caller does that.
56
+ def worker_names
57
+ names = Set.new
58
+ @schedule_paths.each do |path|
59
+ document = load_document(path)
60
+ next unless document.is_a?(Hash)
61
+
62
+ SCHEDULE_BLOCK_KEY_PATHS.each { |keys| collect_entries(dig_block(document, keys), names) }
63
+ end
64
+ names.to_a.sort
65
+ end
66
+
67
+ private
68
+
69
+ def load_document(path)
70
+ absolute = File.expand_path(path.to_s)
71
+ return nil unless File.file?(absolute)
72
+
73
+ contents = read_safely(absolute)
74
+ contents && parse_safely(contents)
75
+ end
76
+
77
+ def read_safely(path)
78
+ @io_boundary.read_file(path)
79
+ rescue Plugin::AccessDeniedError, *IO_ERRORS
80
+ nil
81
+ end
82
+
83
+ # `safe_load` with no permitted classes beyond Symbol: a schedule file is data, and Rigor never loads
84
+ # the Rails environment or the sidekiq runtime to read it. `aliases: true` because YAML anchors are
85
+ # ordinary style in a hand-maintained schedule.
86
+ def parse_safely(contents)
87
+ YAML.safe_load(contents, aliases: true, permitted_classes: [Symbol])
88
+ rescue Psych::Exception
89
+ nil
90
+ end
91
+
92
+ # Walks one of {SCHEDULE_BLOCK_KEY_PATHS} into the document. String and Symbol keys are both tried because
93
+ # `sidekiq.yml` is written with Symbol keys and `schedule.yml` with String ones.
94
+ def dig_block(document, keys)
95
+ keys.reduce(document) do |node, key|
96
+ return nil unless node.is_a?(Hash)
97
+
98
+ fetch_either(node, key)
99
+ end
100
+ end
101
+
102
+ # A schedule block maps an arbitrary job NAME to an entry Hash. Only the entry's own `class:` is read
103
+ # — no recursion into the value, so a nested option that happens to be called `class` cannot enter,
104
+ # and neither can anything under `:queues:`, whose values are Strings and Arrays rather than entries.
105
+ def collect_entries(block, names)
106
+ return unless block.is_a?(Hash)
107
+
108
+ block.each_value do |entry|
109
+ next unless entry.is_a?(Hash)
110
+
111
+ value = fetch_either(entry, CLASS_KEY)
112
+ names << value if value.is_a?(String) && !value.empty?
113
+ end
114
+ end
115
+
116
+ def fetch_either(hash, key)
117
+ hash.fetch(key) { hash[key.to_sym] }
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -4,6 +4,7 @@ require "rigor/plugin"
4
4
 
5
5
  require_relative "sidekiq/worker_index"
6
6
  require_relative "sidekiq/worker_discoverer"
7
+ require_relative "sidekiq/schedule_scan"
7
8
  require_relative "sidekiq/analyzer"
8
9
 
9
10
  module Rigor
@@ -22,6 +23,7 @@ module Rigor
22
23
  # config:
23
24
  # worker_search_paths: ["app/workers", "app/sidekiq"] # default; optional
24
25
  # worker_marker_modules: ["Sidekiq::Job", "Sidekiq::Worker"] # default; optional
26
+ # schedule_paths: ["config/schedule.yml", "config/sidekiq.yml"] # default; optional
25
27
  #
26
28
  # ## What it checks
27
29
  #
@@ -30,6 +32,11 @@ module Rigor
30
32
  # schedule and forward the rest. Mismatches emit `wrong-arity`.
31
33
  # 2. **Missing schedule** — `perform_in()` / `perform_at()` with zero arguments emit `missing-schedule`.
32
34
  #
35
+ # ## What it contributes to `rigor unused`
36
+ #
37
+ # The workers a schedule file names under `class:` and nothing else (ADR-102 WD3 / #367) — see
38
+ # {#prepare} and {ScheduleScan}.
39
+ #
33
40
  # ## Limitations (v0.1.0)
34
41
  #
35
42
  # - Direct `include` matches only against the configured marker modules. Indirect includes via a
@@ -41,12 +48,21 @@ module Rigor
41
48
  class Sidekiq < Rigor::Plugin::Base
42
49
  manifest(
43
50
  id: "sidekiq",
44
- version: "0.1.0",
51
+ # Bumped 2026-08-16 — publishes `:reachability_roots` for `rigor unused` (ADR-102 WD3): the workers
52
+ # a schedule file enqueues by name, which no `perform_async` call site writes down.
53
+ version: "0.2.0",
45
54
  description: "Validates Sidekiq `Worker.perform_async` argument arity.",
46
55
  config_schema: {
47
56
  "worker_search_paths" => { kind: :array, default: ["app/workers", "app/sidekiq"] },
48
- "worker_marker_modules" => { kind: :array, default: %w[Sidekiq::Job Sidekiq::Worker] }
49
- }
57
+ "worker_marker_modules" => { kind: :array, default: %w[Sidekiq::Job Sidekiq::Worker] },
58
+ # `schedule_paths` — the schedule configuration {ScheduleScan} reads for the reachability roots
59
+ # below. The two defaults are the conventional locations of the two schedule layouts in the wild:
60
+ # `sidekiq-cron`'s `config/schedule.yml` and `sidekiq-scheduler`'s `:scheduler: :schedule:` block
61
+ # inside `config/sidekiq.yml`. A project that keeps its schedule elsewhere lists the file itself;
62
+ # these are file paths, not directories, because a schedule is a named document rather than a tree.
63
+ "schedule_paths" => { kind: :array, default: ["config/schedule.yml", "config/sidekiq.yml"] }
64
+ },
65
+ produces: [:reachability_roots]
50
66
  )
51
67
 
52
68
  producer :worker_index, watch: -> { [[@worker_search_paths, "**/*.rb"]] } do |_params|
@@ -57,9 +73,46 @@ module Rigor
57
73
  ).discover
58
74
  end
59
75
 
76
+ # Cached separately from `:worker_index` because the two invalidate on different files: editing a
77
+ # schedule changes which workers are reached without touching `app/workers` at all. The `watch:` roots
78
+ # the glob at the working directory so that CREATING a schedule file — not just editing one — is seen.
79
+ producer :scheduled_workers, watch: -> { [[".", *@schedule_paths]] } do |_params|
80
+ ScheduleScan.new(
81
+ io_boundary: io_boundary,
82
+ schedule_paths: @schedule_paths
83
+ ).worker_names
84
+ end
85
+
60
86
  def init(_services)
61
87
  @worker_search_paths = Array(config.fetch("worker_search_paths")).map(&:to_s)
62
88
  @worker_marker_modules = Array(config.fetch("worker_marker_modules")).map(&:to_s)
89
+ @schedule_paths = Array(config.fetch("schedule_paths")).map(&:to_s)
90
+ end
91
+
92
+ # ADR-102 WD3 — publishes the workers a schedule file enqueues BY NAME, for `rigor unused`.
93
+ #
94
+ # `MyWorker.perform_async(...)` writes the worker's name as an ordinary constant, so the report's scan
95
+ # already records that edge and a root would add nothing. A worker named only as the string
96
+ # `class: "MyWorker"` in `config/schedule.yml` is the opposite case: it runs every night and the
97
+ # constant appears nowhere, so it reads as dead.
98
+ #
99
+ # The intersection is what keeps the contribution honest, exactly as in `rigor-pundit`. {ScheduleScan}
100
+ # says which names the schedule WRITES; {WorkerDiscoverer} says which workers EXIST. A `class:` value
101
+ # matching no discovered worker — a typo, a renamed class, a job living outside `worker_search_paths` —
102
+ # is dropped rather than published, so the failure mode is a missing root (a candidate row a human can
103
+ # judge) instead of a spurious one (silence where dead code used to be).
104
+ #
105
+ # Publishing the discovered worker set instead would have been one line and is what this refuses to do:
106
+ # "a file exists under `app/workers`" is not evidence that anything enqueues it.
107
+ def prepare(services)
108
+ index = producer_value(:worker_index)
109
+ scheduled = producer_value(:scheduled_workers)
110
+ return if index.nil? || scheduled.nil?
111
+
112
+ roots = scheduled.select { |name| index.known?(name) }
113
+ return if roots.empty?
114
+
115
+ services.fact_store.publish(plugin_id: manifest.id, name: :reachability_roots, value: roots)
63
116
  end
64
117
 
65
118
  # File-level only: the load-error emission. The per-call arity validation runs over the engine-owned
@@ -5,6 +5,7 @@ module Rigor
5
5
  def self?.build_node_collectors: (String path, untyped scope_index) -> Hash[Symbol, untyped]
6
6
  def self?.node_collector_driver: (Hash[Symbol, untyped] collectors) -> untyped
7
7
  def self?.shadow_verify_converged_collectors: (String path, untyped root, untyped scope_index, Hash[Symbol, untyped]? collectors) -> void
8
+ def self?.filter_suppressed: (Array[Diagnostic] diagnostics, comments: untyped, disabled_rules: Array[String]) -> Array[Diagnostic]
8
9
  end
9
10
 
10
11
  class FactStore
@@ -163,6 +163,8 @@ module Rigor
163
163
  module ScopeIndexer
164
164
  def self?.index: (untyped root, default_scope: Scope) -> Hash[untyped, Scope]
165
165
  def self?.build_declaration_overrides: (untyped root) -> Hash[untyped, Type::t]
166
+ def self?.build_declaration_artifacts: (untyped root) -> [Hash[untyped, Type::t], Hash[String, Type::t]]
167
+ def self?.build_in_source_constants: (untyped root, Scope default_scope) -> Hash[String, Type::t]
166
168
  def self?.record_declarations: (untyped node, Array[String] qualified_prefix, Hash[untyped, Type::t] identity_table, Hash[String, Type::t] discovered) -> void
167
169
  def self?.discovered_classes_for_paths: (Array[String] paths, ?buffer: untyped) -> Hash[String, Type::t]
168
170
  def self?.discovered_def_index_for_paths: (Array[String] paths, ?buffer: untyped) -> Hash[Symbol, untyped]
@@ -47,6 +47,12 @@ module Rigor
47
47
 
48
48
  def self?.parse_conforms_to_annotation: (String? string) -> String?
49
49
 
50
+ # ADR-103 #383 — the effect-envelope reader. `annotations` is any list of RBS annotation nodes
51
+ # (`RBS::Definition::Method#annotations`, or a declaration / member's own `#annotations`); the result
52
+ # is a `Rigor::Effects::Envelope`, or nil when the list carries neither purity spelling.
53
+ def self?.pure_annotation?: (String? string) -> bool
54
+ def self?.read_effect_envelope: (untyped annotations, owner_key: String, ?source: Symbol, ?registry: untyped, ?reporter: untyped) -> untyped?
55
+
50
56
  class ParamOverride
51
57
  attr_reader param_name: Symbol
52
58
  attr_reader type: Type::t
data/sig/rigor.rbs CHANGED
@@ -14,8 +14,12 @@ 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) -> void
17
+ def initialize: (?Hash[String, untyped] data, ?bool effects_key_present) -> void
18
18
  def to_h: () -> Hash[String, untyped]
19
+ # ADR-103 WD13 — the `effects:` block, nil when the key was absent. Presence is the opt-in.
20
+ attr_reader effects: Hash[String, untyped]?
21
+ def effects_enabled?: () -> bool
22
+ def with_effects_enabled: () -> Rigor::Configuration
19
23
  end
20
24
 
21
25
  class CLI
@@ -125,6 +129,18 @@ module Rigor
125
129
  def analysis_file_set: (?Array[String] paths) -> Array[String]
126
130
  def file_dependents: () -> Hash[String, untyped]
127
131
  def prepare_project_scan: (?paths: Array[String]) -> untyped
132
+ # ADR-103 — the propagated effect graph and the merged per-file collections behind it. A report
133
+ # surface: never a diagnostic, empty when the configuration carries no `effects:` block. Typed
134
+ # `untyped` for the same reason `cache_store` is — `Rigor::Effects` is not sig-covered yet
135
+ # (`spec/rigor/public_api_drift_spec.rb`), and a named reference would raise `RBS::UnknownTypeName`.
136
+ def effect_table: () -> untyped
137
+ def effect_collection: () -> untyped
138
+ # #381 — `{ "Class#m" => [path] }`, which the snapshot's `reach:` globs match against. Same
139
+ # `untyped` reasoning as its two siblings above.
140
+ def effect_sources: () -> untyped
141
+ # #387 — the loaded plugins' compiled effect contributions, read by `rigor effects` so the snapshot's
142
+ # vocabulary is the one the collection window scanned under. Same `untyped` reasoning again.
143
+ def effect_plugin_facts: () -> untyped
128
144
  end
129
145
  end
130
146
  end
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: rigor-unused-adjudicate
3
+ description: |
4
+ Find dead code in a Ruby project with `rigor unused` — establish what the report can see on THIS project first, then adjudicate every row before proposing any deletion. Use this whenever someone asks to find or remove dead code, unused classes, unused constants, or "code nobody calls", whenever they ask what a `rigor unused` report means or which rows are safe to delete, and whenever a dead-code cleanup, codebase inventory, or legacy audit comes up — even if they never say "rigor". The report is a review queue and not a defect list; on an adjudicated corpus target only 4 of 57 rows were genuinely dead, so acting on it directly produces mostly wrong deletions. NOT for deleting a specific class you already know is dead, and NOT for `rigor check` diagnostics (those are ordinary type errors).
5
+ license: MPL-2.0
6
+ metadata:
7
+ version: 0.2.0
8
+ homepage: https://github.com/rigortype/rigor
9
+ ---
10
+
11
+ # Adjudicating `rigor unused`
12
+
13
+ `rigor unused` answers *which project classes and modules does no reachable
14
+ code name?* — not *what can I delete*. Closing that gap is the work, and the
15
+ manual chapter has the method:
16
+
17
+ ```sh
18
+ rigor docs 18-removing-dead-code
19
+ ```
20
+
21
+ Read it before adjudicating anything. It carries the false-positive shapes with
22
+ their measured frequencies, the pre-deletion checklist, and how to report the
23
+ result. This skill exists for the part the chapter cannot do for you: **work out
24
+ what the report can and cannot see on this particular project, before you trust
25
+ a single row.**
26
+
27
+ Do that first, because every check below changes how the output should be read —
28
+ and skipping one is how a live class ends up on a deletion list.
29
+
30
+ ## Establish the ground first
31
+
32
+ Run the report and answer these against the project in front of you.
33
+
34
+ **Are framework roots actually supplied?** The summary prints
35
+ `roots: N (M from plugins, …)`. On a framework application `0 from plugins`
36
+ means nothing is naming your controllers, jobs or policies, so most of the
37
+ report is noise. Stop and fix the plugin configuration — `rigor docs
38
+ 07-plugins` — rather than adjudicating hundreds of rows.
39
+
40
+ **Does the project ship signatures?** If `signature_paths:` is configured, also
41
+ run with it emptied and compare:
42
+
43
+ ```sh
44
+ rigor unused # normal
45
+ rigor unused --config /tmp/no-sig.yml # a copy of your config with signature_paths: []
46
+ ```
47
+
48
+ Adjudicate the union. On one application the signature-free run surfaced three
49
+ genuinely dead classes that the default run never showed. Rows that appear only
50
+ without signatures are still worth checking — signatures reference classes, and
51
+ a reference the report counts is not always one a human would.
52
+
53
+ **What do `paths:` actually cover?** Only Ruby under those paths is analysed.
54
+ Views are the common gap: a helper called from `app/views/**/*.erb` has no Ruby
55
+ caller at all, and will land in the report looking unused. Before believing any
56
+ helper or presenter row, grep the view tree for it.
57
+
58
+ **Is the git history usable?** The chapter's checklist uses "when did this file
59
+ last change" as evidence. In a shallow clone, or one whose history is all
60
+ dependency bumps, that signal is empty — notice it and say so rather than
61
+ reporting a bot commit as the file's age.
62
+
63
+ ## Then hand off to the chapter
64
+
65
+ With that established, follow the chapter. Two things worth holding onto as you
66
+ go, because they are what the report's own design is built around:
67
+
68
+ **Framework conventions and configuration reach code without naming it.** The
69
+ chapter lists the shapes; the ones that recur hardest are a class named as a
70
+ *string* in `config/*.yml` (a recurring-job schedule, a queue definition), a
71
+ convention that derives one name from another (`FooHelper` from
72
+ `FooController`, a decorator from a model, a join model from
73
+ `has_many :speakers_talks`), and a registration DSL called inside the class
74
+ body. None of these appear as a constant anywhere.
75
+
76
+ **Wrong rows cascade.** A class used only by a wrongly-classified class follows
77
+ it into the report. When a row turns out to be live, re-check what it
78
+ references before treating those rows as independent findings.
79
+
80
+ ## Two habits that keep the answer honest
81
+
82
+ **A search that finds nothing proves nothing until it has found something.**
83
+ Before concluding a name appears nowhere, run the same search against a name you
84
+ know is used. Silence from a broken command and silence from dead code look
85
+ identical.
86
+
87
+ **Over-claiming hides dead code; under-claiming does not.** If you cannot settle
88
+ a row, leave it in the report and say you could not settle it. A row left on the
89
+ list is visible to a human; a row you dismissed on a hunch is invisible to
90
+ everyone.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rigortype
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rigor contributors
@@ -292,6 +292,8 @@ files:
292
292
  - data/core_overlay/psych.rbs
293
293
  - data/core_overlay/resolv.rbs
294
294
  - data/core_overlay/string_scanner.rbs
295
+ - data/effects/core.yml
296
+ - data/effects/registry.yml
295
297
  - data/gem_overlay/activesupport/core_ext.rbs
296
298
  - data/vendored_gem_sigs/ast/ast.rbs
297
299
  - data/vendored_gem_sigs/bcrypt/bcrypt.rbs
@@ -357,6 +359,7 @@ files:
357
359
  - docs/manual/15-type-protection-coverage.md
358
360
  - docs/manual/16-rbs-extended-annotations.md
359
361
  - docs/manual/17-driving-improvement.md
362
+ - docs/manual/18-removing-dead-code.md
360
363
  - docs/manual/README.md
361
364
  - docs/manual/ci-templates/README.md
362
365
  - docs/manual/plugins/README.md
@@ -425,6 +428,11 @@ files:
425
428
  - lib/rigor/analysis/path_expansion.rb
426
429
  - lib/rigor/analysis/plugin_fact_fingerprint.rb
427
430
  - lib/rigor/analysis/project_scan.rb
431
+ - lib/rigor/analysis/reachability/graph.rb
432
+ - lib/rigor/analysis/reachability/plugin_roots.rb
433
+ - lib/rigor/analysis/reachability/project_files.rb
434
+ - lib/rigor/analysis/reachability/scan.rb
435
+ - lib/rigor/analysis/reachability/signature_scan.rb
428
436
  - lib/rigor/analysis/result.rb
429
437
  - lib/rigor/analysis/rule_catalog.rb
430
438
  - lib/rigor/analysis/run_cache_key.rb
@@ -432,7 +440,11 @@ files:
432
440
  - lib/rigor/analysis/run_stats.rb
433
441
  - lib/rigor/analysis/runner.rb
434
442
  - lib/rigor/analysis/runner/buffer_pool_dispatcher.rb
443
+ - lib/rigor/analysis/runner/declaration_position.rb
435
444
  - lib/rigor/analysis/runner/diagnostic_aggregator.rb
445
+ - lib/rigor/analysis/runner/effect_annotation_residual_pass.rb
446
+ - lib/rigor/analysis/runner/effect_envelope_pass.rb
447
+ - lib/rigor/analysis/runner/envelope_messages.rb
436
448
  - lib/rigor/analysis/runner/pool_coordinator.rb
437
449
  - lib/rigor/analysis/runner/project_pre_passes.rb
438
450
  - lib/rigor/analysis/runner/run_snapshots.rb
@@ -477,6 +489,12 @@ files:
477
489
  - lib/rigor/cli/diff_command.rb
478
490
  - lib/rigor/cli/docs_command.rb
479
491
  - lib/rigor/cli/doctor_command.rb
492
+ - lib/rigor/cli/effects_command.rb
493
+ - lib/rigor/cli/effects_diff_renderer.rb
494
+ - lib/rigor/cli/effects_explain_renderer.rb
495
+ - lib/rigor/cli/effects_renderer.rb
496
+ - lib/rigor/cli/effects_report.rb
497
+ - lib/rigor/cli/effects_snapshot_command.rb
480
498
  - lib/rigor/cli/explain_command.rb
481
499
  - lib/rigor/cli/fused_protection_renderer.rb
482
500
  - lib/rigor/cli/fused_protection_report.rb
@@ -509,11 +527,48 @@ files:
509
527
  - lib/rigor/cli/type_scan_command.rb
510
528
  - lib/rigor/cli/type_scan_renderer.rb
511
529
  - lib/rigor/cli/type_scan_report.rb
530
+ - lib/rigor/cli/unused_command.rb
512
531
  - lib/rigor/cli/upgrade_command.rb
513
532
  - lib/rigor/config_audit.rb
514
533
  - lib/rigor/configuration.rb
515
534
  - lib/rigor/configuration/dependencies.rb
516
535
  - lib/rigor/configuration/severity_profile.rb
536
+ - lib/rigor/effects.rb
537
+ - lib/rigor/effects/attribution.rb
538
+ - lib/rigor/effects/catalog.rb
539
+ - lib/rigor/effects/collector.rb
540
+ - lib/rigor/effects/config_envelopes.rb
541
+ - lib/rigor/effects/discharge.rb
542
+ - lib/rigor/effects/effect_table.rb
543
+ - lib/rigor/effects/entry_points.rb
544
+ - lib/rigor/effects/envelope.rb
545
+ - lib/rigor/effects/envelope_check.rb
546
+ - lib/rigor/effects/envelope_index.rb
547
+ - lib/rigor/effects/file_collection.rb
548
+ - lib/rigor/effects/framework_units.rb
549
+ - lib/rigor/effects/identity.rb
550
+ - lib/rigor/effects/label.rb
551
+ - lib/rigor/effects/label_intent.rb
552
+ - lib/rigor/effects/label_set.rb
553
+ - lib/rigor/effects/liskov_check.rb
554
+ - lib/rigor/effects/local_ownership.rb
555
+ - lib/rigor/effects/method_key.rb
556
+ - lib/rigor/effects/mutation_classifier.rb
557
+ - lib/rigor/effects/narrowing.rb
558
+ - lib/rigor/effects/origin.rb
559
+ - lib/rigor/effects/path_finder.rb
560
+ - lib/rigor/effects/plugin_facts.rb
561
+ - lib/rigor/effects/propagator.rb
562
+ - lib/rigor/effects/registry.rb
563
+ - lib/rigor/effects/scanner.rb
564
+ - lib/rigor/effects/signature_sources.rb
565
+ - lib/rigor/effects/snapshot.rb
566
+ - lib/rigor/effects/snapshot_diff.rb
567
+ - lib/rigor/effects/summary.rb
568
+ - lib/rigor/effects/taint_cause.rb
569
+ - lib/rigor/effects/unit_scan.rb
570
+ - lib/rigor/effects/unknown_label_check.rb
571
+ - lib/rigor/effects/unknown_label_report.rb
517
572
  - lib/rigor/environment.rb
518
573
  - lib/rigor/environment/bundle_sig_discovery.rb
519
574
  - lib/rigor/environment/class_registry.rb
@@ -609,6 +664,7 @@ files:
609
664
  - lib/rigor/inference/optimistic_origin.rb
610
665
  - lib/rigor/inference/origin_lookup.rb
611
666
  - lib/rigor/inference/parameter_inference_collector.rb
667
+ - lib/rigor/inference/pre_eval_constants.rb
612
668
  - lib/rigor/inference/precision_scanner.rb
613
669
  - lib/rigor/inference/project_patched_methods.rb
614
670
  - lib/rigor/inference/project_patched_scanner.rb
@@ -652,7 +708,11 @@ files:
652
708
  - lib/rigor/plugin/base.rb
653
709
  - lib/rigor/plugin/blueprint.rb
654
710
  - lib/rigor/plugin/box.rb
711
+ - lib/rigor/plugin/effect_attribution.rb
712
+ - lib/rigor/plugin/effect_edge.rb
713
+ - lib/rigor/plugin/effect_entry_points.rb
655
714
  - lib/rigor/plugin/fact_store.rb
715
+ - lib/rigor/plugin/first_party.rb
656
716
  - lib/rigor/plugin/inflector.rb
657
717
  - lib/rigor/plugin/io_boundary.rb
658
718
  - lib/rigor/plugin/isolation.rb
@@ -683,6 +743,7 @@ files:
683
743
  - lib/rigor/protection/test_suite_oracle.rb
684
744
  - lib/rigor/rbs_extended.rb
685
745
  - lib/rigor/rbs_extended/conformance_checker.rb
746
+ - lib/rigor/rbs_extended/envelope_scanner.rb
686
747
  - lib/rigor/rbs_extended/hkt_directives.rb
687
748
  - lib/rigor/rbs_extended/reporter.rb
688
749
  - lib/rigor/reflection.rb
@@ -758,9 +819,11 @@ files:
758
819
  - plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb
759
820
  - plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb
760
821
  - plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb
822
+ - plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb
761
823
  - plugins/rigor-actionmailer/lib/rigor-actionmailer.rb
762
824
  - plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb
763
825
  - plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb
826
+ - plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/effects.rb
764
827
  - plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb
765
828
  - plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb
766
829
  - plugins/rigor-actionpack/lib/rigor-actionpack.rb
@@ -768,14 +831,18 @@ files:
768
831
  - plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb
769
832
  - plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb
770
833
  - plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb
834
+ - plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb
771
835
  - plugins/rigor-activejob/lib/rigor-activejob.rb
772
836
  - plugins/rigor-activejob/lib/rigor/plugin/activejob.rb
773
837
  - plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb
838
+ - plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb
774
839
  - plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb
775
840
  - plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb
841
+ - plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb
776
842
  - plugins/rigor-activerecord/lib/rigor-activerecord.rb
777
843
  - plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb
778
844
  - plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb
845
+ - plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb
779
846
  - plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb
780
847
  - plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb
781
848
  - plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb
@@ -787,8 +854,10 @@ files:
787
854
  - plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb
788
855
  - plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb
789
856
  - plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb
857
+ - plugins/rigor-activestorage/lib/rigor/plugin/activestorage/effects.rb
790
858
  - plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb
791
859
  - plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb
860
+ - plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb
792
861
  - plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs
793
862
  - plugins/rigor-devise/lib/rigor-devise.rb
794
863
  - plugins/rigor-devise/lib/rigor/plugin/devise.rb
@@ -826,15 +895,18 @@ files:
826
895
  - plugins/rigor-pundit/lib/rigor-pundit.rb
827
896
  - plugins/rigor-pundit/lib/rigor/plugin/pundit.rb
828
897
  - plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb
898
+ - plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb
829
899
  - plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb
830
900
  - plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb
831
901
  - plugins/rigor-rails-i18n/lib/rigor-rails-i18n.rb
832
902
  - plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb
833
903
  - plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb
904
+ - plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/effects.rb
834
905
  - plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb
835
906
  - plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb
836
907
  - plugins/rigor-rails-routes/lib/rigor-rails-routes.rb
837
908
  - plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb
909
+ - plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb
838
910
  - plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb
839
911
  - plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb
840
912
  - plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb
@@ -843,6 +915,9 @@ files:
843
915
  - plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb
844
916
  - plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb
845
917
  - plugins/rigor-rails/lib/rigor-rails.rb
918
+ - plugins/rigor-railties/lib/rigor-railties.rb
919
+ - plugins/rigor-railties/lib/rigor/plugin/railties.rb
920
+ - plugins/rigor-railties/lib/rigor/plugin/railties/effects.rb
846
921
  - plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb
847
922
  - plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb
848
923
  - plugins/rigor-rspec-rails/lib/rigor-rspec-rails.rb
@@ -862,6 +937,7 @@ files:
862
937
  - plugins/rigor-sidekiq/lib/rigor-sidekiq.rb
863
938
  - plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb
864
939
  - plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb
940
+ - plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb
865
941
  - plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb
866
942
  - plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb
867
943
  - plugins/rigor-sinatra/lib/rigor-sinatra.rb
@@ -946,6 +1022,7 @@ files:
946
1022
  - skills/rigor-project-init/references/05-jit-performance.md
947
1023
  - skills/rigor-protection-uplift/SKILL.md
948
1024
  - skills/rigor-rbs-setup/SKILL.md
1025
+ - skills/rigor-unused-adjudicate/SKILL.md
949
1026
  - skills/rigor-upgrade/SKILL.md
950
1027
  homepage: https://github.com/rigortype/rigor
951
1028
  licenses:
@@ -988,6 +1065,7 @@ require_paths:
988
1065
  - plugins/rigor-rails/lib
989
1066
  - plugins/rigor-rails-i18n/lib
990
1067
  - plugins/rigor-rails-routes/lib
1068
+ - plugins/rigor-railties/lib
991
1069
  - plugins/rigor-rbs-inline/lib
992
1070
  - plugins/rigor-rspec/lib
993
1071
  - plugins/rigor-rspec-rails/lib