Apply ADR-0205 env discipline to os and os-backlog tests; shared git seed #634

Closed
opened 2026-09-23 12:04:39 +00:00 by jared · 1 comment
Owner

Problem

Global ENV mutation in test helpers blocks parallel test runs for plugins/os and plugins/os-backlog, and each worktree test forks its own git init sequence. The original ticket's five-file list is stale: batches #672/#677/#697 folded the worktree helpers into one shared file.

Observed

  • plugins/os/tests/support/worktree_finish_test_case.rb:27 with_path_prepended saves, sets, and restores ENV['PATH'] globally (5 callers).
  • plugins/os/tests/support/worktree_finish_test_case.rb:35 seed_repo runs five system('git', ...) calls per test (callers include finish_command_missing_worktree_test.rb:13, finish_command_resolves_by_branch_test.rb:14, and the other seed_repo_and_start_worktree users).
  • plugins/os-backlog/tests/support/cli_test_helpers.rb:19 with_isolated_project_index sets ENV["CC_OS_HOME"] globally (5 callers: cli_projects_command_test, cli_config_write_command_test, cli_unknown_command_test, cli_issue_create_scope_gate_test, cli_json_first_rendering_test).
  • plugins/os-backlog/tests/support/labels_cli_helpers.rb:49 run_cli interpolates PATH into a backtick shell string (3 callers).
  • Neither plugins/os/tests/all.rb nor plugins/os-backlog/tests/test_helper.rb declares parallelize_me!. plugins/os-sdlc/tests/test_helper.rb:167 does, plus a SerializedChdir monitor, because Dir.chdir races persisted there even after env discipline (ADR-0205 consequence).
  • Backlog::ProjectIndex.default_path(env: ENV) (plugins/os-backlog/lib/backlog/project_index.rb:9) and Backlog::Wakeup::State (plugins/os-backlog/lib/backlog/wakeup/state.rb:9) already accept an env: keyword, but Backlog::CLI does not thread env down to them — the CC_OS_HOME callers run Backlog::CLI.start in-process, so the fix needs lib plumbing, not just test-side changes.
  • plugins/os/lib never reads ENV['PATH'] itself, but its system(...) calls take no env hash, so child spawns (rubocop server stop, os-sdlc-runner sweep) inherit whatever the test process set globally.

Reproduce

grep -rn "ENV\['PATH'\] =" plugins/os/tests
grep -rn "ENV\[" plugins/os-backlog/tests
grep -rn '`git' plugins/os-backlog/lib

Approach

Backlog::CLI.start takes an env: keyword (Invocation-shaped, following the ADR-0207 pattern) and threads it to ProjectIndex/Wakeup::State; tests then call Backlog::CLI.start(args, env: {"CC_OS_HOME" => home}) instead of mutating ENV. with_path_prepended is replaced by passing an env hash to each system/Open3 call inside Worktree::CLI/FinishCommand, which need an env: parameter added at the same seam. labels_cli_helpers.rb spawns via system(env_hash, "ruby", BIN, *args, chdir: dir) (or Open3) instead of a backtick with a string-interpolated PATH=.

Required work:

  1. Backlog::CLI.start accepts env: and passes it to ProjectIndex.default_path/Wakeup::State — check: grep -n "env:" plugins/os-backlog/lib/backlog/cli.rb shows the new parameter.
  2. cli_test_helpers.rb passes env: instead of writing ENV — check: grep -rn "ENV\[" plugins/os-backlog/tests returns nothing that assigns.
  3. labels_cli_helpers.rb spawns via an env hash, not a shell-interpolated string — check: grep -n "PATH=" plugins/os-backlog/tests/support/labels_cli_helpers.rb returns nothing.
  4. Worktree::FinishCommand/Worktree::CLI accept an env: hash threaded to their system/Open3 calls; with_path_prepended deleted — check: grep -rn "ENV\['PATH'\] =" plugins/os/tests returns nothing.
  5. Add plugins/os/tests/support/repo_seeder.rb, mirroring plugins/os-sdlc/tests/support/repo_seeder.rb: seed a git repo once per suite, then FileUtils.cp_r a copy per test.
  6. Add parallelize_me! to both plugins/os/tests/all.rb and plugins/os-backlog/tests/test_helper.rb, with cwd passed through the CLI or a SerializedChdir monitor copied from os-sdlc if Dir.chdir races persist — check: ruby plugins/os/tests/all.rb and ruby -Iplugins/os-backlog -Iplugins/os-backlog/tests plugins/os-backlog/tests/test_helper.rb both pass green on 3 consecutive runs.
  7. Record an ADR amendment via /os-adr:create amends 0205, extending ADR-0205's affected-components list to plugins/os and plugins/os-backlog.

Rejected: a tests-only change. The in-process CC_OS_HOME reads inside ProjectIndex/Wakeup::State cannot be fixed from the test side alone; the lib must accept env:.

Non-goals: plugins/os-sdlc (already done in #624). No new ADR beyond the amendment in item 7 — ADR-0205's pattern itself does not change, only its scope.

Condition that would make this plan wrong: if some other in-process consumer of Backlog::CLI.start outside tests relies on ENV being read directly rather than an injected env: hash, threading env: through could silently change its behavior; check all Backlog::CLI.start call sites before wiring the parameter.

VERDICT: CREATE

Acceptance criteria

  1. Given Backlog::CLI.start is called with an env: hash, when it resolves the project index or wakeup state, then it uses the passed hash, not global ENV.
  2. Given the os-backlog test suite runs, when any test sets up an isolated CC_OS_HOME, then no test assigns to global ENV["CC_OS_HOME"].
  3. Given labels_cli_helpers.rb spawns the CLI binary, when PATH needs a prepended fake bin directory, then it is passed as an env hash to system/Open3, never interpolated into a shell string.
  4. Given a worktree finish test needs a seeded git repo, when it calls the shared seeder, then no test repeats its own git init/config/add/commit sequence.
  5. Given all ENV mutation is removed, when plugins/os/tests/all.rb and plugins/os-backlog/tests/test_helper.rb run with parallelize_me! enabled, then each suite passes green on 3 consecutive runs.
  6. Given the scope of ADR-0205 changes, when this ticket closes, then an ADR amendment extending ADR-0205 to plugins/os and plugins/os-backlog exists.

Owner sketch

class Backlog::CLI
  def self.start(argv, env: ENV.to_h)
    # ... threads env to ProjectIndex.default_path(env: env) and Wakeup::State.new(env: env)
  end
end

class Worktree::FinishCommand
  def initialize(..., env: ENV.to_h)
    @env = env
  end
  # system/Open3 calls pass @env instead of relying on process-global ENV
end

class RepoSeeder
  def self.seed_once(dir)
    # git init/config/add/commit, run once per suite
  end

  def self.copy_into(tmp_dir)
    FileUtils.cp_r(seed_dir, tmp_dir)
  end
end

Non-goals

  • plugins/os-sdlc (already covered by #624).
  • Any change to ADR-0205's pattern itself, beyond widening its affected-components scope.

Origin

  • Trigger: shell-call/ENV audit, session 16bf37b3-8cff-4121-896c-4a5fc61f2e9e (2026-09-23), user-requested.
  • Improvised this session: none.
  • Chain: slow/serialization-prone os and os-backlog tests <- global ENV mutation and per-test git init forks <- DESIGN, ADR-0205 set the env-hash pattern for os-sdlc only, not yet applied to sibling plugins.
  • Root candidate: this ticket.
  • Where: plugins/os/tests/support/worktree_finish_test_case.rb; plugins/os-backlog/tests/support/cli_test_helpers.rb, labels_cli_helpers.rb; plugins/os-backlog/lib/backlog/project_index.rb; plugins/os-backlog/lib/backlog/wakeup/state.rb
  • Session: c15fe063-c64c-417b-b4e1-53a52ffca319 (#624/ADR-0205 precedent); 16bf37b3-8cff-4121-896c-4a5fc61f2e9e (this audit)
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/c15fe063-c64c-417b-b4e1-53a52ffca319.jsonl ; /home/jared/.claude/projects/-home-jared-dev-cc-os/16bf37b3-8cff-4121-896c-4a5fc61f2e9e.jsonl
  • Rewritten 2026-09-24 in session 082c3e8b after blast-radius review.
## Problem Global ENV mutation in test helpers blocks parallel test runs for `plugins/os` and `plugins/os-backlog`, and each worktree test forks its own git init sequence. The original ticket's five-file list is stale: batches #672/#677/#697 folded the worktree helpers into one shared file. ## Observed - `plugins/os/tests/support/worktree_finish_test_case.rb:27` `with_path_prepended` saves, sets, and restores `ENV['PATH']` globally (5 callers). - `plugins/os/tests/support/worktree_finish_test_case.rb:35` `seed_repo` runs five `system('git', ...)` calls per test (callers include `finish_command_missing_worktree_test.rb:13`, `finish_command_resolves_by_branch_test.rb:14`, and the other `seed_repo_and_start_worktree` users). - `plugins/os-backlog/tests/support/cli_test_helpers.rb:19` `with_isolated_project_index` sets `ENV["CC_OS_HOME"]` globally (5 callers: `cli_projects_command_test`, `cli_config_write_command_test`, `cli_unknown_command_test`, `cli_issue_create_scope_gate_test`, `cli_json_first_rendering_test`). - `plugins/os-backlog/tests/support/labels_cli_helpers.rb:49` `run_cli` interpolates `PATH` into a backtick shell string (3 callers). - Neither `plugins/os/tests/all.rb` nor `plugins/os-backlog/tests/test_helper.rb` declares `parallelize_me!`. `plugins/os-sdlc/tests/test_helper.rb:167` does, plus a `SerializedChdir` monitor, because `Dir.chdir` races persisted there even after env discipline (ADR-0205 consequence). - `Backlog::ProjectIndex.default_path(env: ENV)` (`plugins/os-backlog/lib/backlog/project_index.rb:9`) and `Backlog::Wakeup::State` (`plugins/os-backlog/lib/backlog/wakeup/state.rb:9`) already accept an `env:` keyword, but `Backlog::CLI` does not thread env down to them — the CC_OS_HOME callers run `Backlog::CLI.start` in-process, so the fix needs lib plumbing, not just test-side changes. - `plugins/os/lib` never reads `ENV['PATH']` itself, but its `system(...)` calls take no env hash, so child spawns (rubocop server stop, os-sdlc-runner sweep) inherit whatever the test process set globally. ## Reproduce grep -rn "ENV\['PATH'\] =" plugins/os/tests grep -rn "ENV\[" plugins/os-backlog/tests grep -rn '`git' plugins/os-backlog/lib ## Approach `Backlog::CLI.start` takes an `env:` keyword (Invocation-shaped, following the ADR-0207 pattern) and threads it to `ProjectIndex`/`Wakeup::State`; tests then call `Backlog::CLI.start(args, env: {"CC_OS_HOME" => home})` instead of mutating `ENV`. `with_path_prepended` is replaced by passing an env hash to each `system`/`Open3` call inside `Worktree::CLI`/`FinishCommand`, which need an `env:` parameter added at the same seam. `labels_cli_helpers.rb` spawns via `system(env_hash, "ruby", BIN, *args, chdir: dir)` (or `Open3`) instead of a backtick with a string-interpolated `PATH=`. Required work: 1. `Backlog::CLI.start` accepts `env:` and passes it to `ProjectIndex.default_path`/`Wakeup::State` — check: `grep -n "env:" plugins/os-backlog/lib/backlog/cli.rb` shows the new parameter. 2. `cli_test_helpers.rb` passes `env:` instead of writing `ENV` — check: `grep -rn "ENV\[" plugins/os-backlog/tests` returns nothing that assigns. 3. `labels_cli_helpers.rb` spawns via an env hash, not a shell-interpolated string — check: `grep -n "PATH=" plugins/os-backlog/tests/support/labels_cli_helpers.rb` returns nothing. 4. `Worktree::FinishCommand`/`Worktree::CLI` accept an `env:` hash threaded to their `system`/`Open3` calls; `with_path_prepended` deleted — check: `grep -rn "ENV\['PATH'\] =" plugins/os/tests` returns nothing. 5. Add `plugins/os/tests/support/repo_seeder.rb`, mirroring `plugins/os-sdlc/tests/support/repo_seeder.rb`: seed a git repo once per suite, then `FileUtils.cp_r` a copy per test. 6. Add `parallelize_me!` to both `plugins/os/tests/all.rb` and `plugins/os-backlog/tests/test_helper.rb`, with cwd passed through the CLI or a `SerializedChdir` monitor copied from os-sdlc if `Dir.chdir` races persist — check: `ruby plugins/os/tests/all.rb` and `ruby -Iplugins/os-backlog -Iplugins/os-backlog/tests plugins/os-backlog/tests/test_helper.rb` both pass green on 3 consecutive runs. 7. Record an ADR amendment via `/os-adr:create amends 0205`, extending ADR-0205's affected-components list to `plugins/os` and `plugins/os-backlog`. Rejected: a tests-only change. The in-process `CC_OS_HOME` reads inside `ProjectIndex`/`Wakeup::State` cannot be fixed from the test side alone; the lib must accept `env:`. Non-goals: `plugins/os-sdlc` (already done in #624). No new ADR beyond the amendment in item 7 — ADR-0205's pattern itself does not change, only its scope. Condition that would make this plan wrong: if some other in-process consumer of `Backlog::CLI.start` outside tests relies on `ENV` being read directly rather than an injected `env:` hash, threading `env:` through could silently change its behavior; check all `Backlog::CLI.start` call sites before wiring the parameter. VERDICT: CREATE ## Acceptance criteria 1. Given `Backlog::CLI.start` is called with an `env:` hash, when it resolves the project index or wakeup state, then it uses the passed hash, not global `ENV`. 2. Given the os-backlog test suite runs, when any test sets up an isolated `CC_OS_HOME`, then no test assigns to global `ENV["CC_OS_HOME"]`. 3. Given `labels_cli_helpers.rb` spawns the CLI binary, when `PATH` needs a prepended fake bin directory, then it is passed as an env hash to `system`/`Open3`, never interpolated into a shell string. 4. Given a worktree finish test needs a seeded git repo, when it calls the shared seeder, then no test repeats its own `git init`/`config`/`add`/`commit` sequence. 5. Given all ENV mutation is removed, when `plugins/os/tests/all.rb` and `plugins/os-backlog/tests/test_helper.rb` run with `parallelize_me!` enabled, then each suite passes green on 3 consecutive runs. 6. Given the scope of ADR-0205 changes, when this ticket closes, then an ADR amendment extending ADR-0205 to `plugins/os` and `plugins/os-backlog` exists. ## Owner sketch ```ruby class Backlog::CLI def self.start(argv, env: ENV.to_h) # ... threads env to ProjectIndex.default_path(env: env) and Wakeup::State.new(env: env) end end class Worktree::FinishCommand def initialize(..., env: ENV.to_h) @env = env end # system/Open3 calls pass @env instead of relying on process-global ENV end class RepoSeeder def self.seed_once(dir) # git init/config/add/commit, run once per suite end def self.copy_into(tmp_dir) FileUtils.cp_r(seed_dir, tmp_dir) end end ``` ## Non-goals - `plugins/os-sdlc` (already covered by #624). - Any change to ADR-0205's pattern itself, beyond widening its affected-components scope. ## Origin - Trigger: shell-call/ENV audit, session 16bf37b3-8cff-4121-896c-4a5fc61f2e9e (2026-09-23), user-requested. - Improvised this session: none. - Chain: slow/serialization-prone os and os-backlog tests <- global ENV mutation and per-test git init forks <- DESIGN, ADR-0205 set the env-hash pattern for os-sdlc only, not yet applied to sibling plugins. - Root candidate: this ticket. - Where: plugins/os/tests/support/worktree_finish_test_case.rb; plugins/os-backlog/tests/support/cli_test_helpers.rb, labels_cli_helpers.rb; plugins/os-backlog/lib/backlog/project_index.rb; plugins/os-backlog/lib/backlog/wakeup/state.rb - Session: c15fe063-c64c-417b-b4e1-53a52ffca319 (#624/ADR-0205 precedent); 16bf37b3-8cff-4121-896c-4a5fc61f2e9e (this audit) - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/c15fe063-c64c-417b-b4e1-53a52ffca319.jsonl ; /home/jared/.claude/projects/-home-jared-dev-cc-os/16bf37b3-8cff-4121-896c-4a5fc61f2e9e.jsonl - Rewritten 2026-09-24 in session 082c3e8b after blast-radius review.
Author
Owner

Resolution

Done: Split into per-plugin children; the ADR-0205 amendment extending affected-components to os and os-backlog is owed when both children ship

Evidence: #707, #708

Follow-ups: #707 (VERDICT: CREATE), #708 (VERDICT: CREATE); ADR-0205 amendment via /os-adr:create after both children ship, owner orchestrator session 082c3e8b, VERDICT: CREATE

## Resolution **Done:** Split into per-plugin children; the ADR-0205 amendment extending affected-components to os and os-backlog is owed when both children ship **Evidence:** #707, #708 **Follow-ups:** #707 (VERDICT: CREATE), #708 (VERDICT: CREATE); ADR-0205 amendment via /os-adr:create after both children ship, owner orchestrator session 082c3e8b, VERDICT: CREATE
jared closed this issue 2026-09-24 21:14:49 +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#634
No description provided.