cc-architect: PluginConfig::Cli moves out of bin/plugin-config, takes an Invocation (cwd, env HOME); CLI tests run in-process #641

Closed
opened 2026-09-23 13:05:16 +00:00 by jared · 4 comments
Owner

Map: #631
Blocked by #638

Problem

Child of #631. Scope is narrowed to one entry point, bin/plugin-config, so that the work fits one red round. The user's 631d also held the repo-wide fork guard and the timeout revert. Those are test-only and go to a separate child (631e) because each child routes to exactly one pipeline.

Observed

PluginConfig::Cli is defined inside plugins/cc-architect/scripts/plugin_config/bin/plugin-config (127 lines), so tests can reach it only by forking. It reads Dir.pwd and Dir.home (line 83) and FileLocations::INSTALLED_PLUGINS_PATH (line 95). That constant is computed from Dir.home at load time (file_locations.rb:7). Operations::Base#installed_plugins_path also reads Dir.home (operations/base.rb:75). 19 tests fork the binary with a HOME env hash through CliTestHelpers#run_cli (test/test_cli.rb:25) and test_cli_edge_cases.rb:25. The files are test_cli.rb (8), test_cli_edge_cases.rb (8), test_cli_audit_missing_manifest_not_applicable.rb (1), test_cli_register_marketplace_required_keys_coupling.rb (1), and test_cli_schema_error_exit_contract.rb (1). The whole plugin_config suite takes 2.9 s (2026-09-23).

Reproduce

ruby -e 'Dir["plugins/cc-architect/scripts/plugin_config/test/test_cli*.rb"].sort.each { |f| require File.expand_path(f) }'

Expected

One prose sentence: PluginConfig::Cli.run(argv, invocation) reads cwd and HOME only from the invocation and returns an exit status; bin/plugin-config only builds the Invocation and calls it.

Origin

  • Trigger: decomposition of #631, 2026-09-23 (user-decided split)
  • Improvised this session: none
  • Chain: 19 forks ← Cli module defined inside bin/ (DESIGN) ← HOME read from the process at load time (DESIGN, file_locations.rb:7)
  • Root candidate: this ticket
  • Where: plugins/cc-architect/scripts/plugin_config/bin/plugin-config; file_locations.rb; operations/base.rb
  • Session: 5bb052ff-ddf2-4d47-a283-d00d071e9b79
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/5bb052ff-ddf2-4d47-a283-d00d071e9b79.jsonl

Owner sketch

module PluginConfig
  Invocation = Data.define(:cwd, :stdin, :stdout, :stderr, :env)   # plugin_config/invocation.rb
  module Cli                                                         # plugin_config/cli.rb
    def self.run(argv, invocation)
  end
  module FileLocations
    def self.installed_plugins_path(home)
  end
end

Acceptance criteria

  1. Given an Invocation with cwd set to a temp project and env HOME set to a temp home, when PluginConfig::Cli.run(%w[enable project p], invocation) runs, then the project settings file holds a p@ key, the status line is on invocation.stdout, and the status is 0.
  2. Given scope user, when enable runs, then the settings file under the invocation's HOME holds the key and the project settings file does not.
  3. Given installed_plugins.json only under the invocation's HOME, when audit project runs, then the marketplace is resolved from that file.
  4. Given an unknown task, when run, then the usage is on invocation.stdout and the status is 1.
  5. Given a malformed marketplace, when register runs, then diagnostic JSON is on invocation.stdout and the status is 2.
  6. Given the real bin/plugin-config with HOME in its env hash, when list project runs as a subprocess, then it exits 0.
  7. Given the five test_cli*.rb files, when searched for Open3, then only the smoke test matches.

Approach

  • Move PluginConfig::Cli from bin/ to plugin_config/cli.rb. build_params adds home: from invocation.env.fetch('HOME'). FileLocations::INSTALLED_PLUGINS_PATH becomes installed_plugins_path(home), and Operations::Base#installed_plugins_path uses the home param. Keep the exit codes 0/1/2.
  • Convert the 19 tests in place by changing CliTestHelpers#run_cli (and its copy in test_cli_edge_cases.rb) to call Cli.run with an Invocation built from home: and chdir:.
  • Out of scope (follow-up): the plugin_config.rb entry (test_entry_point.rb 7, test_validate_entry_point.rb 3) and scripts/validate_marketplaces.rb (11 tests through test/support/validate_marketplaces_test_base.rb).
  • ADRs to respect: ADR-0195 (one constant per file), ADR-0123 (thin bin).
    Required work:
  1. Move PluginConfig::Cli out of bin/plugin-config into plugin_config/cli.rb, taking home: from invocation.env.fetch('HOME'). (check: ! grep -n 'module Cli' plugins/cc-architect/scripts/plugin_config/bin/plugin-config)
  2. Turn FileLocations::INSTALLED_PLUGINS_PATH into installed_plugins_path(home) and update Operations::Base#installed_plugins_path to take the home param. (proven by AC 3)
  3. Convert the 19 existing tests (CliTestHelpers#run_cli and its copy in test_cli_edge_cases.rb) to call Cli.run with an Invocation. (proven by AC 7)
  4. Keep bin/plugin-config runnable as a real subprocess with HOME in its env. (proven by AC 6)

Structural follow-through

  • cc-architect has no leaf under docs/implementation-status/. Create it with a History table and one row.
Map: #631 Blocked by #638 ## Problem Child of #631. Scope is narrowed to one entry point, bin/plugin-config, so that the work fits one red round. The user's 631d also held the repo-wide fork guard and the timeout revert. Those are test-only and go to a separate child (631e) because each child routes to exactly one pipeline. ## Observed `PluginConfig::Cli` is defined inside plugins/cc-architect/scripts/plugin_config/bin/plugin-config (127 lines), so tests can reach it only by forking. It reads `Dir.pwd` and `Dir.home` (line 83) and `FileLocations::INSTALLED_PLUGINS_PATH` (line 95). That constant is computed from `Dir.home` at load time (file_locations.rb:7). `Operations::Base#installed_plugins_path` also reads `Dir.home` (operations/base.rb:75). 19 tests fork the binary with a `HOME` env hash through `CliTestHelpers#run_cli` (test/test_cli.rb:25) and test_cli_edge_cases.rb:25. The files are test_cli.rb (8), test_cli_edge_cases.rb (8), test_cli_audit_missing_manifest_not_applicable.rb (1), test_cli_register_marketplace_required_keys_coupling.rb (1), and test_cli_schema_error_exit_contract.rb (1). The whole plugin_config suite takes 2.9 s (2026-09-23). ## Reproduce ``` ruby -e 'Dir["plugins/cc-architect/scripts/plugin_config/test/test_cli*.rb"].sort.each { |f| require File.expand_path(f) }' ``` ## Expected One prose sentence: `PluginConfig::Cli.run(argv, invocation)` reads cwd and HOME only from the invocation and returns an exit status; bin/plugin-config only builds the Invocation and calls it. ## Origin - Trigger: decomposition of #631, 2026-09-23 (user-decided split) - Improvised this session: none - Chain: 19 forks ← Cli module defined inside bin/ (DESIGN) ← HOME read from the process at load time (DESIGN, file_locations.rb:7) - Root candidate: this ticket - Where: plugins/cc-architect/scripts/plugin_config/bin/plugin-config; file_locations.rb; operations/base.rb - Session: 5bb052ff-ddf2-4d47-a283-d00d071e9b79 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/5bb052ff-ddf2-4d47-a283-d00d071e9b79.jsonl ## Owner sketch ```ruby module PluginConfig Invocation = Data.define(:cwd, :stdin, :stdout, :stderr, :env) # plugin_config/invocation.rb module Cli # plugin_config/cli.rb def self.run(argv, invocation) end module FileLocations def self.installed_plugins_path(home) end end ``` ## Acceptance criteria 1. Given an Invocation with cwd set to a temp project and env HOME set to a temp home, when `PluginConfig::Cli.run(%w[enable project p], invocation)` runs, then the project settings file holds a `p@` key, the status line is on `invocation.stdout`, and the status is 0. 2. Given scope `user`, when enable runs, then the settings file under the invocation's HOME holds the key and the project settings file does not. 3. Given installed_plugins.json only under the invocation's HOME, when `audit project` runs, then the marketplace is resolved from that file. 4. Given an unknown task, when run, then the usage is on `invocation.stdout` and the status is 1. 5. Given a malformed marketplace, when `register` runs, then diagnostic JSON is on `invocation.stdout` and the status is 2. 6. Given the real bin/plugin-config with HOME in its env hash, when `list project` runs as a subprocess, then it exits 0. 7. Given the five test_cli*.rb files, when searched for `Open3`, then only the smoke test matches. ## Approach - Move `PluginConfig::Cli` from bin/ to plugin_config/cli.rb. `build_params` adds `home:` from `invocation.env.fetch('HOME')`. `FileLocations::INSTALLED_PLUGINS_PATH` becomes `installed_plugins_path(home)`, and `Operations::Base#installed_plugins_path` uses the `home` param. Keep the exit codes 0/1/2. - Convert the 19 tests in place by changing `CliTestHelpers#run_cli` (and its copy in test_cli_edge_cases.rb) to call `Cli.run` with an Invocation built from `home:` and `chdir:`. - Out of scope (follow-up): the plugin_config.rb entry (test_entry_point.rb 7, test_validate_entry_point.rb 3) and scripts/validate_marketplaces.rb (11 tests through test/support/validate_marketplaces_test_base.rb). - ADRs to respect: ADR-0195 (one constant per file), ADR-0123 (thin bin). Required work: 1. Move `PluginConfig::Cli` out of bin/plugin-config into `plugin_config/cli.rb`, taking `home:` from `invocation.env.fetch('HOME')`. (check: `! grep -n 'module Cli' plugins/cc-architect/scripts/plugin_config/bin/plugin-config`) 2. Turn `FileLocations::INSTALLED_PLUGINS_PATH` into `installed_plugins_path(home)` and update `Operations::Base#installed_plugins_path` to take the `home` param. (proven by AC 3) 3. Convert the 19 existing tests (`CliTestHelpers#run_cli` and its copy in test_cli_edge_cases.rb) to call `Cli.run` with an Invocation. (proven by AC 7) 4. Keep bin/plugin-config runnable as a real subprocess with HOME in its env. (proven by AC 6) ## Structural follow-through - cc-architect has no leaf under docs/implementation-status/. Create it with a History table and one row.
Author
Owner

Body before Required-work migration, 2026-09-23

Map: #631
Blocked by #638

Problem

Child of #631. Scope is narrowed to one entry point, bin/plugin-config, so that the work fits one red round. The user's 631d also held the repo-wide fork guard and the timeout revert. Those are test-only and go to a separate child (631e) because each child routes to exactly one pipeline.

Observed

PluginConfig::Cli is defined inside plugins/cc-architect/scripts/plugin_config/bin/plugin-config (127 lines), so tests can reach it only by forking. It reads Dir.pwd and Dir.home (line 83) and FileLocations::INSTALLED_PLUGINS_PATH (line 95). That constant is computed from Dir.home at load time (file_locations.rb:7). Operations::Base#installed_plugins_path also reads Dir.home (operations/base.rb:75). 19 tests fork the binary with a HOME env hash through CliTestHelpers#run_cli (test/test_cli.rb:25) and test_cli_edge_cases.rb:25. The files are test_cli.rb (8), test_cli_edge_cases.rb (8), test_cli_audit_missing_manifest_not_applicable.rb (1), test_cli_register_marketplace_required_keys_coupling.rb (1), and test_cli_schema_error_exit_contract.rb (1). The whole plugin_config suite takes 2.9 s (2026-09-23).

Reproduce

ruby -e 'Dir["plugins/cc-architect/scripts/plugin_config/test/test_cli*.rb"].sort.each { |f| require File.expand_path(f) }'

Expected

One prose sentence: PluginConfig::Cli.run(argv, invocation) reads cwd and HOME only from the invocation and returns an exit status; bin/plugin-config only builds the Invocation and calls it.

Origin

  • Trigger: decomposition of #631, 2026-09-23 (user-decided split)
  • Improvised this session: none
  • Chain: 19 forks ← Cli module defined inside bin/ (DESIGN) ← HOME read from the process at load time (DESIGN, file_locations.rb:7)
  • Root candidate: this ticket
  • Where: plugins/cc-architect/scripts/plugin_config/bin/plugin-config; file_locations.rb; operations/base.rb
  • Session: 5bb052ff-ddf2-4d47-a283-d00d071e9b79
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/5bb052ff-ddf2-4d47-a283-d00d071e9b79.jsonl

Owner sketch

module PluginConfig
  Invocation = Data.define(:cwd, :stdin, :stdout, :stderr, :env)   # plugin_config/invocation.rb
  module Cli                                                         # plugin_config/cli.rb
    def self.run(argv, invocation)
  end
  module FileLocations
    def self.installed_plugins_path(home)
  end
end

Acceptance criteria

  1. Given an Invocation with cwd set to a temp project and env HOME set to a temp home, when PluginConfig::Cli.run(%w[enable project p], invocation) runs, then the project settings file holds a p@ key, the status line is on invocation.stdout, and the status is 0.
  2. Given scope user, when enable runs, then the settings file under the invocation's HOME holds the key and the project settings file does not.
  3. Given installed_plugins.json only under the invocation's HOME, when audit project runs, then the marketplace is resolved from that file.
  4. Given an unknown task, when run, then the usage is on invocation.stdout and the status is 1.
  5. Given a malformed marketplace, when register runs, then diagnostic JSON is on invocation.stdout and the status is 2.
  6. Given the real bin/plugin-config with HOME in its env hash, when list project runs as a subprocess, then it exits 0.
  7. Given the five test_cli*.rb files, when searched for Open3, then only the smoke test matches.

Approach

  • Move PluginConfig::Cli from bin/ to plugin_config/cli.rb. build_params adds home: from invocation.env.fetch('HOME'). FileLocations::INSTALLED_PLUGINS_PATH becomes installed_plugins_path(home), and Operations::Base#installed_plugins_path uses the home param. Keep the exit codes 0/1/2.
  • Convert the 19 tests in place by changing CliTestHelpers#run_cli (and its copy in test_cli_edge_cases.rb) to call Cli.run with an Invocation built from home: and chdir:.
  • Out of scope (follow-up): the plugin_config.rb entry (test_entry_point.rb 7, test_validate_entry_point.rb 3) and scripts/validate_marketplaces.rb (11 tests through test/support/validate_marketplaces_test_base.rb).
  • ADRs to respect: ADR-0195 (one constant per file), ADR-0123 (thin bin).

Structural follow-through

  • cc-architect has no leaf under docs/implementation-status/. Create it with a History table and one row.
Body before Required-work migration, 2026-09-23 Map: #631 Blocked by #638 ## Problem Child of #631. Scope is narrowed to one entry point, bin/plugin-config, so that the work fits one red round. The user's 631d also held the repo-wide fork guard and the timeout revert. Those are test-only and go to a separate child (631e) because each child routes to exactly one pipeline. ## Observed `PluginConfig::Cli` is defined inside plugins/cc-architect/scripts/plugin_config/bin/plugin-config (127 lines), so tests can reach it only by forking. It reads `Dir.pwd` and `Dir.home` (line 83) and `FileLocations::INSTALLED_PLUGINS_PATH` (line 95). That constant is computed from `Dir.home` at load time (file_locations.rb:7). `Operations::Base#installed_plugins_path` also reads `Dir.home` (operations/base.rb:75). 19 tests fork the binary with a `HOME` env hash through `CliTestHelpers#run_cli` (test/test_cli.rb:25) and test_cli_edge_cases.rb:25. The files are test_cli.rb (8), test_cli_edge_cases.rb (8), test_cli_audit_missing_manifest_not_applicable.rb (1), test_cli_register_marketplace_required_keys_coupling.rb (1), and test_cli_schema_error_exit_contract.rb (1). The whole plugin_config suite takes 2.9 s (2026-09-23). ## Reproduce ``` ruby -e 'Dir["plugins/cc-architect/scripts/plugin_config/test/test_cli*.rb"].sort.each { |f| require File.expand_path(f) }' ``` ## Expected One prose sentence: `PluginConfig::Cli.run(argv, invocation)` reads cwd and HOME only from the invocation and returns an exit status; bin/plugin-config only builds the Invocation and calls it. ## Origin - Trigger: decomposition of #631, 2026-09-23 (user-decided split) - Improvised this session: none - Chain: 19 forks ← Cli module defined inside bin/ (DESIGN) ← HOME read from the process at load time (DESIGN, file_locations.rb:7) - Root candidate: this ticket - Where: plugins/cc-architect/scripts/plugin_config/bin/plugin-config; file_locations.rb; operations/base.rb - Session: 5bb052ff-ddf2-4d47-a283-d00d071e9b79 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/5bb052ff-ddf2-4d47-a283-d00d071e9b79.jsonl ## Owner sketch ```ruby module PluginConfig Invocation = Data.define(:cwd, :stdin, :stdout, :stderr, :env) # plugin_config/invocation.rb module Cli # plugin_config/cli.rb def self.run(argv, invocation) end module FileLocations def self.installed_plugins_path(home) end end ``` ## Acceptance criteria 1. Given an Invocation with cwd set to a temp project and env HOME set to a temp home, when `PluginConfig::Cli.run(%w[enable project p], invocation)` runs, then the project settings file holds a `p@` key, the status line is on `invocation.stdout`, and the status is 0. 2. Given scope `user`, when enable runs, then the settings file under the invocation's HOME holds the key and the project settings file does not. 3. Given installed_plugins.json only under the invocation's HOME, when `audit project` runs, then the marketplace is resolved from that file. 4. Given an unknown task, when run, then the usage is on `invocation.stdout` and the status is 1. 5. Given a malformed marketplace, when `register` runs, then diagnostic JSON is on `invocation.stdout` and the status is 2. 6. Given the real bin/plugin-config with HOME in its env hash, when `list project` runs as a subprocess, then it exits 0. 7. Given the five test_cli*.rb files, when searched for `Open3`, then only the smoke test matches. ## Approach - Move `PluginConfig::Cli` from bin/ to plugin_config/cli.rb. `build_params` adds `home:` from `invocation.env.fetch('HOME')`. `FileLocations::INSTALLED_PLUGINS_PATH` becomes `installed_plugins_path(home)`, and `Operations::Base#installed_plugins_path` uses the `home` param. Keep the exit codes 0/1/2. - Convert the 19 tests in place by changing `CliTestHelpers#run_cli` (and its copy in test_cli_edge_cases.rb) to call `Cli.run` with an Invocation built from `home:` and `chdir:`. - Out of scope (follow-up): the plugin_config.rb entry (test_entry_point.rb 7, test_validate_entry_point.rb 3) and scripts/validate_marketplaces.rb (11 tests through test/support/validate_marketplaces_test_base.rb). - ADRs to respect: ADR-0195 (one constant per file), ADR-0123 (thin bin). ## Structural follow-through - cc-architect has no leaf under docs/implementation-status/. Create it with a History table and one row.
Author
Owner

Work started via /os-sdlc:implement on branch ticket-641 (worktree .claude/worktrees/ticket-641).

Work started via /os-sdlc:implement on branch ticket-641 (worktree .claude/worktrees/ticket-641).
Author
Owner

Blocked in os-sdlc implementation 18 (session 007cbccc), branch ticket-641. Programmer dispatch 209 went green on the seam, but the reviewed test set includes TestCliInvocationSeam#test_only_smoke_test_file_uses_open3_across_test_cli_files (test_cli_invocation_seam.rb:134), which asserts that test_cli.rb and test_cli_edge_cases.rb no longer call Open3. Only a test edit (CliTestHelpers#run_cli in-process, ticket item R3) can satisfy it. The map routes a red diff-test to programmer-repair, which may not edit tests; repair dispatches 210 and 211 made no change. Stopped per the user's two-repair rule. Worktree and uncommitted diff left in place.

Blocked in os-sdlc implementation 18 (session 007cbccc), branch ticket-641. Programmer dispatch 209 went green on the seam, but the reviewed test set includes TestCliInvocationSeam#test_only_smoke_test_file_uses_open3_across_test_cli_files (test_cli_invocation_seam.rb:134), which asserts that test_cli.rb and test_cli_edge_cases.rb no longer call Open3. Only a test edit (CliTestHelpers#run_cli in-process, ticket item R3) can satisfy it. The map routes a red diff-test to programmer-repair, which may not edit tests; repair dispatches 210 and 211 made no change. Stopped per the user's two-repair rule. Worktree and uncommitted diff left in place.
Author
Owner

Resolution

Done: PluginConfig::Cli lives in plugin_config/cli.rb and takes an Invocation (cwd, stdin, stdout, stderr, env). bin/plugin-config builds the Invocation and delegates. The 19 CLI tests call Cli.run in-process through Invocation.for_test; one smoke test still forks the binary.

Evidence: Merge 7414b978 on main (branch commit caa11ed6). plugin_config suite on main: 104 runs, 384 assertions, 0 failures. Seam test test_only_smoke_test_file_uses_open3_across_test_cli_files green. Hand-finished after the pipeline stalled at implementation 18 on the test-helper edit.

Follow-ups: #665 filed (user-approved): os-sdlc map routes a red test-file diff to a role that may edit tests. Pre-existing rubocop Style/Documentation offenses in plugin_config/test: dropped, out of scope.

## Resolution **Done:** PluginConfig::Cli lives in plugin_config/cli.rb and takes an Invocation (cwd, stdin, stdout, stderr, env). bin/plugin-config builds the Invocation and delegates. The 19 CLI tests call Cli.run in-process through Invocation.for_test; one smoke test still forks the binary. **Evidence:** Merge 7414b978 on main (branch commit caa11ed6). plugin_config suite on main: 104 runs, 384 assertions, 0 failures. Seam test test_only_smoke_test_file_uses_open3_across_test_cli_files green. Hand-finished after the pipeline stalled at implementation 18 on the test-helper edit. **Follow-ups:** #665 filed (user-approved): os-sdlc map routes a red test-file diff to a role that may edit tests. Pre-existing rubocop Style/Documentation offenses in plugin_config/test: dropped, out of scope.
jared closed this issue 2026-09-24 12:42:18 +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#641
No description provided.