Run autocorrect pre-pass rubocop capture through ProjectShell #850

Closed
opened 2026-09-30 17:23:54 +00:00 by jared · 2 comments
Owner

Problem

The autocorrect pre-pass runs rubocop twice, and the two runs see different environments. The first run goes through TestRunner, which calls ProjectShell and clears RUBYOPT and BUNDLE_* (#594). The second run calls Open3.capture3 directly, so it inherits the plugin's Bundler environment. The second run can therefore load a different rubocop version or configuration than the first run in the same project.

plugins/os-sdlc/lib/os_sdlc/adapter/autocorrect_prepass/capture_runner.rb
  def run(command)
    @runner.run(command)                              # TestRunner -> ProjectShell (unbundled env)
  end
  def run_capture3(command)
    stdout, stderr, status = Open3.capture3(command)  # plain Open3 (plugin env)
    [stdout, stderr, status.exitstatus]
  end

pass.rb:43 calls run. pass.rb:53 calls run_capture3. The audit in #845 found this defect.

Reproduce

n/a: the defect shows only when the parent process has RUBYOPT or BUNDLE_* set. The acceptance criteria give the observable check.

Approach

CaptureRunner#run_capture3 calls ProjectShell.capture3(command) in place of Open3.capture3(command). The return shape does not change.

Required work: none beyond the acceptance criteria.

Rejected: send run_capture3 through TestRunner — TestRunner returns combined output, and pass.rb:53 needs stdout and stderr apart.

Acceptance criteria

  1. Given the parent process has BUNDLE_GEMFILE and RUBYOPT set, when CaptureRunner#run_capture3 runs a command that prints those variables, then the output shows neither value.
  2. Given a command that writes to stdout and stderr and exits 3, when CaptureRunner#run_capture3 runs it, then it returns the stdout text, the stderr text, and 3.

Owner sketch

class OsSdlc::AutocorrectPrepass::CaptureRunner
  def run_capture3(command)
    stdout, stderr, status = ProjectShell.capture3(command)
    [stdout, stderr, status.exitstatus]
  end
end

Non-goals

  • Other shell calls from the #845 audit. Each has its own ticket.

Origin

  • Trigger: #845 shell-call audit, 2026-09-30.
  • Improvised this session: none
  • Chain: pre-pass rubocop runs see different environments ← run_capture3 calls Open3.capture3 directly (capture_runner.rb) ← IMPROVISATION: the #594 unbundled-env rule reached TestRunner but not this sibling method.
  • Root candidate: this ticket
  • Where: plugins/os-sdlc/lib/os_sdlc/adapter/autocorrect_prepass/capture_runner.rb
  • Session: f090dd39-8ad6-4f4e-8743-7bbedb8f08d4
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/f090dd39-8ad6-4f4e-8743-7bbedb8f08d4.jsonl
## Problem The autocorrect pre-pass runs rubocop twice, and the two runs see different environments. The first run goes through `TestRunner`, which calls `ProjectShell` and clears `RUBYOPT` and `BUNDLE_*` (#594). The second run calls `Open3.capture3` directly, so it inherits the plugin's Bundler environment. The second run can therefore load a different rubocop version or configuration than the first run in the same project. ``` plugins/os-sdlc/lib/os_sdlc/adapter/autocorrect_prepass/capture_runner.rb def run(command) @runner.run(command) # TestRunner -> ProjectShell (unbundled env) end def run_capture3(command) stdout, stderr, status = Open3.capture3(command) # plain Open3 (plugin env) [stdout, stderr, status.exitstatus] end ``` `pass.rb:43` calls `run`. `pass.rb:53` calls `run_capture3`. The audit in #845 found this defect. ## Reproduce n/a: the defect shows only when the parent process has `RUBYOPT` or `BUNDLE_*` set. The acceptance criteria give the observable check. ## Approach `CaptureRunner#run_capture3` calls `ProjectShell.capture3(command)` in place of `Open3.capture3(command)`. The return shape does not change. Required work: none beyond the acceptance criteria. Rejected: send `run_capture3` through `TestRunner` — `TestRunner` returns combined output, and `pass.rb:53` needs stdout and stderr apart. ## Acceptance criteria 1. Given the parent process has `BUNDLE_GEMFILE` and `RUBYOPT` set, when `CaptureRunner#run_capture3` runs a command that prints those variables, then the output shows neither value. 2. Given a command that writes to stdout and stderr and exits 3, when `CaptureRunner#run_capture3` runs it, then it returns the stdout text, the stderr text, and `3`. ## Owner sketch ```ruby class OsSdlc::AutocorrectPrepass::CaptureRunner def run_capture3(command) stdout, stderr, status = ProjectShell.capture3(command) [stdout, stderr, status.exitstatus] end end ``` ## Non-goals - Other shell calls from the #845 audit. Each has its own ticket. ## Origin - Trigger: #845 shell-call audit, 2026-09-30. - Improvised this session: none - Chain: pre-pass rubocop runs see different environments ← `run_capture3` calls `Open3.capture3` directly (capture_runner.rb) ← IMPROVISATION: the #594 unbundled-env rule reached `TestRunner` but not this sibling method. - Root candidate: this ticket - Where: plugins/os-sdlc/lib/os_sdlc/adapter/autocorrect_prepass/capture_runner.rb - Session: f090dd39-8ad6-4f4e-8743-7bbedb8f08d4 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/f090dd39-8ad6-4f4e-8743-7bbedb8f08d4.jsonl
Author
Owner

Work started on branch ticket-850 (os-sdlc implement pipeline).

Work started on branch ticket-850 (os-sdlc implement pipeline).
Author
Owner

Resolution

Done: CaptureRunner#run_capture3 now calls ProjectShell.capture3, so the second pre-pass rubocop run gets the same unbundled environment as the first (#594).

Evidence: Commit 47c37747 on ticket-850 (implementation 14): capture_runner.rb 1 line, contract test +19 lines. All os-sdlc gates passed: behavior-verifier, full suite, contract-auditor.

Follow-ups: none

## Resolution **Done:** CaptureRunner#run_capture3 now calls ProjectShell.capture3, so the second pre-pass rubocop run gets the same unbundled environment as the first (#594). **Evidence:** Commit 47c37747 on ticket-850 (implementation 14): capture_runner.rb 1 line, contract test +19 lines. All os-sdlc gates passed: behavior-verifier, full suite, contract-auditor. **Follow-ups:** none
jared closed this issue 2026-09-30 17:40:25 +00:00
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#850
No description provided.