CopSource resolves the target Ruby version once for a run, not once for each file #1670

Open
opened 2026-11-10 14:58:13 +00:00 by jared · 0 comments
Owner

Problem

CopSource.target_version makes a new RuboCop::ConfigStore for each file. Each new store finds and loads .rubocop.yml again. RuboCop itself makes one store for a run, and that store keeps the loaded configuration for later files. The per-file store makes every os-sdlc detector that reads files through CopSource slow: the lookup takes about 14 ms for each file, and it is about 92% of the time for each file.

Observed

A profile of 300 lib/ files through CopSource.from_file gave these times (session 56a999f3, 2026-11-10, load 4.4 to 6.0).

Step Time
target_version (RuboCop configuration lookup) 4,213 ms
Prism parse 338 ms
Cop check 45 ms

The collaborator-pass inward source over lib/ takes about 10.4 s, through CopSites, for the same reason.

# plugins/os-sdlc/lib/os_sdlc/cops/structural/cop_source.rb
def self.target_version(path) = RuboCop::ConfigStore.new.for_file(path || Dir.pwd).target_ruby_version

Reproduce

n/a: a speed defect; the profile above is the evidence.

Approach

Resolve the target Ruby version once for a run. Two candidate designs: the caller that walks many files holds one ConfigStore and passes the version to CopSource.from_file(path, version), which already takes a version argument; or the caller passes a fixed version, as reach_through.rb does with RUNNING_RUBY. The implementing agent selects one after it reads the callers.

Required work: none beyond the acceptance criteria.

Acceptance criteria

  1. Given a detector run that reads 300 files through CopSource, when the run completes, then it made one RuboCop configuration lookup for each configuration directory, not one for each file.
  2. Given the same files, when the detector runs before and after the change, then it reports the same findings.

Non-goals

The two cop tests and the collaborator-pass tests that were slow because of this call. #1665 changed their input and does not depend on this ticket.

Origin

  • Trigger: the #1665 survey of the four slow os-sdlc tests (session 56a999f3), which profiled 300 files.
  • Improvised this session: none. #1665 removed the real-lib/ input from the slow tests; it did not change CopSource.
  • Chain: four test files take 9 to 12 s each ← each reads about 1,000 real files through CopSource ← CopSource.target_version makes a new ConfigStore for each file (cop_source.rb) ← DESIGN (plugins/os-sdlc/lib/os_sdlc/cops/structural/cop_source.rb).
  • Root candidate: none found; searched ConfigStore, CopSource, target_version, target_ruby_version, TargetRuby, ruby version
  • Where: RuboCop::Cop::Sdlc::Structural::CopSource, plugins/os-sdlc/lib/os_sdlc/cops/structural/cop_source.rb
  • Session: 720a7477-bb8e-4373-8d74-6bbdb56f0200
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/720a7477-bb8e-4373-8d74-6bbdb56f0200.jsonl

VERDICT: CREATE (ticket-skeptic, backlog search only; the user approved the ticket in the #1665 plan)

## Problem `CopSource.target_version` makes a new `RuboCop::ConfigStore` for each file. Each new store finds and loads `.rubocop.yml` again. RuboCop itself makes one store for a run, and that store keeps the loaded configuration for later files. The per-file store makes every os-sdlc detector that reads files through `CopSource` slow: the lookup takes about 14 ms for each file, and it is about 92% of the time for each file. ## Observed A profile of 300 `lib/` files through `CopSource.from_file` gave these times (session 56a999f3, 2026-11-10, load 4.4 to 6.0). | Step | Time | |---|---| | `target_version` (RuboCop configuration lookup) | 4,213 ms | | Prism parse | 338 ms | | Cop check | 45 ms | The collaborator-pass `inward` source over `lib/` takes about 10.4 s, through `CopSites`, for the same reason. ```ruby # plugins/os-sdlc/lib/os_sdlc/cops/structural/cop_source.rb def self.target_version(path) = RuboCop::ConfigStore.new.for_file(path || Dir.pwd).target_ruby_version ``` ## Reproduce n/a: a speed defect; the profile above is the evidence. ## Approach Resolve the target Ruby version once for a run. Two candidate designs: the caller that walks many files holds one `ConfigStore` and passes the version to `CopSource.from_file(path, version)`, which already takes a version argument; or the caller passes a fixed version, as `reach_through.rb` does with `RUNNING_RUBY`. The implementing agent selects one after it reads the callers. Required work: none beyond the acceptance criteria. ## Acceptance criteria 1. Given a detector run that reads 300 files through `CopSource`, when the run completes, then it made one RuboCop configuration lookup for each configuration directory, not one for each file. 2. Given the same files, when the detector runs before and after the change, then it reports the same findings. ## Non-goals The two cop tests and the collaborator-pass tests that were slow because of this call. #1665 changed their input and does not depend on this ticket. ## Origin - Trigger: the #1665 survey of the four slow os-sdlc tests (session 56a999f3), which profiled 300 files. - Improvised this session: none. #1665 removed the real-`lib/` input from the slow tests; it did not change `CopSource`. - Chain: four test files take 9 to 12 s each ← each reads about 1,000 real files through `CopSource` ← `CopSource.target_version` makes a new `ConfigStore` for each file (`cop_source.rb`) ← DESIGN (`plugins/os-sdlc/lib/os_sdlc/cops/structural/cop_source.rb`). - Root candidate: none found; searched ConfigStore, CopSource, target_version, target_ruby_version, TargetRuby, ruby version - Where: `RuboCop::Cop::Sdlc::Structural::CopSource`, `plugins/os-sdlc/lib/os_sdlc/cops/structural/cop_source.rb` - Session: 720a7477-bb8e-4373-8d74-6bbdb56f0200 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/720a7477-bb8e-4373-8d74-6bbdb56f0200.jsonl VERDICT: CREATE (ticket-skeptic, backlog search only; the user approved the ticket in the #1665 plan)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#1670
No description provided.