One --target parser: ArgvOption leaves unknown options in place; TargetFlag and LintWorklist.parse_target retire #790

Closed
opened 2026-09-28 14:16:58 +00:00 by jared · 3 comments
Owner

Problem

There are three --target parsers: CLI::TargetFlag (parser/cli/target_flag.rb), LintWorklist.parse_target (controller/lint_worklist.rb:59), and ArgvOption.take! (controller/argv_option.rb, used by controller/implementation/scripts.rb:13). ArgvOption.take! cannot replace the other two yet: it calls OptionParser#parse! on the whole argv, and that raises OptionParser::InvalidOption on any other flag. bin/os-sdlc project-config demo --path lib --target x and bin/lint-worklist --target x --new both carry other flags.

Reproduce

ruby -roptparse -e 'a=%w[project-config demo --path lib --target x]; OptionParser.new{|p| p.on("--target NAME"){}}.parse!(a)'
# => OptionParser::InvalidOption: invalid option: --path

Failing test: ArgvOption.take!(%w[project-config demo --path lib --target os-sdlc], "--target NAME") returns "os-sdlc" and leaves argv %w[project-config demo --path lib]. Today it raises InvalidOption.

Approach

Files (7): controller/argv_option.rb, controller/project/setup_cli.rb, bin/os-sdlc, controller/lint_worklist.rb, parser/cli/target_flag.rb (delete), tests/controller/project/cli_target_flag_env_test.rb (retire), tests/controller/argv_option_test.rb (new).
ADRs cited: ADR-0213 (target only from --target or implementation.target), ADR-0236 (controller/ owns argv), ADR-0123 (Thor). No ADR to supersede. The parser location is a heuristic (ADR-0224).

Required work:

  1. ArgvOption.take! removes only the named option and its value, in the --target x and --target=x forms. It leaves every other token in argv, in order. It still raises on a missing value. — check: ruby plugins/os-sdlc/tests/controller/argv_option_test.rb
  2. Project::SetupCLI#project_config takes the target with ArgvOption.take!(args, "--target NAME"). Delete the SetupCLI.target accessor, SetupCLI.extract_target!, and the SetupCLI.target = line in bin/os-sdlc. — check: ! grep -rn "extract_target!\|SetupCLI.target" plugins/os-sdlc
  3. LintWorklist.run takes the target with ArgvOption.take!(argv, "--target NAME"). Delete LintWorklist.parse_target. — check: ! grep -rn "parse_target" plugins/os-sdlc/lib
  4. Delete parser/cli/target_flag.rb. — check: ! grep -rn "TargetFlag" plugins/os-sdlc
  5. The dispatch-id and sweep-only callers keep their behavior. — check: ruby plugins/os-sdlc/tests/all.rb

Rejected: Thor method_option :target on SetupCLI. LintWorklist and the implementation scripts do not use Thor, so there would still be two parsers.
Rejected: keep TargetFlag and delete ArgvOption. ArgvOption is the general option reader already in controller/; TargetFlag knows only one flag.

Acceptance criteria

  1. Given argv project-config demo --path lib --target os-sdlc, when the target is taken, then the value is os-sdlc and argv is project-config demo --path lib.
  2. Given argv --target=os-sdlc --new, when the target is taken, then the value is os-sdlc and argv is --new.
  3. Given argv without --target, when the target is taken, then the value is nil and argv is unchanged.
  4. Given bin/os-sdlc project-config demo --target os-backlog in a targets-shaped project, when it runs, then it fails with the same TargetSelectionError text as today (the write is refused).
  5. Given bin/lint-worklist --target os-sdlc in cc-os, when it runs, then it lists the os-sdlc target's offenses as before.

Owner sketch

module OsSdlc::ArgvOption
  def self.take!(argv, spec)   # removes only spec's option; unknown tokens stay in place
end
class OsSdlc::Project::SetupCLI < Thor
  def project_config(*args)
    target = ArgvOption.take!(args, "--target NAME")
    CLI::ProjectConfigCommand.new(args, target: target, invocation: @invocation).call
  end
end

Non-goals

Thor option declarations for other subcommands. CLI::OptionParserBuilder (project-config's own flags).

Retired tests

plugins/os-sdlc/tests/controller/project/cli_target_flag_env_test.rb

Origin

  • Trigger: CRC review of the Project concept, user ruling R1 (one --target parser, in controller/)
  • Improvised this session: none
  • Chain: three --target parsers ← ArgvOption raises on unknown flags, so callers with other flags wrote their own ← controller/argv_option.rb uses OptionParser#parse! over the whole argv (DESIGN: controller/argv_option.rb)
  • Where: plugins/os-sdlc/lib/os_sdlc/controller/argv_option.rb, parser/cli/target_flag.rb, controller/lint_worklist.rb
  • Session: bb36e383-017d-486e-809f-7ce25d151e61
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-crc-pass-os-sdlc/bb36e383-017d-486e-809f-7ce25d151e61.jsonl
## Problem There are three `--target` parsers: `CLI::TargetFlag` (parser/cli/target_flag.rb), `LintWorklist.parse_target` (controller/lint_worklist.rb:59), and `ArgvOption.take!` (controller/argv_option.rb, used by controller/implementation/scripts.rb:13). `ArgvOption.take!` cannot replace the other two yet: it calls `OptionParser#parse!` on the whole argv, and that raises `OptionParser::InvalidOption` on any other flag. `bin/os-sdlc project-config demo --path lib --target x` and `bin/lint-worklist --target x --new` both carry other flags. ## Reproduce ``` ruby -roptparse -e 'a=%w[project-config demo --path lib --target x]; OptionParser.new{|p| p.on("--target NAME"){}}.parse!(a)' # => OptionParser::InvalidOption: invalid option: --path ``` Failing test: `ArgvOption.take!(%w[project-config demo --path lib --target os-sdlc], "--target NAME")` returns `"os-sdlc"` and leaves argv `%w[project-config demo --path lib]`. Today it raises InvalidOption. ## Approach Files (7): controller/argv_option.rb, controller/project/setup_cli.rb, bin/os-sdlc, controller/lint_worklist.rb, parser/cli/target_flag.rb (delete), tests/controller/project/cli_target_flag_env_test.rb (retire), tests/controller/argv_option_test.rb (new). ADRs cited: ADR-0213 (target only from `--target` or `implementation.target`), ADR-0236 (controller/ owns argv), ADR-0123 (Thor). No ADR to supersede. The parser location is a heuristic (ADR-0224). Required work: 1. `ArgvOption.take!` removes only the named option and its value, in the `--target x` and `--target=x` forms. It leaves every other token in argv, in order. It still raises on a missing value. — check: `ruby plugins/os-sdlc/tests/controller/argv_option_test.rb` 2. `Project::SetupCLI#project_config` takes the target with `ArgvOption.take!(args, "--target NAME")`. Delete the `SetupCLI.target` accessor, `SetupCLI.extract_target!`, and the `SetupCLI.target =` line in `bin/os-sdlc`. — check: `! grep -rn "extract_target!\|SetupCLI.target" plugins/os-sdlc` 3. `LintWorklist.run` takes the target with `ArgvOption.take!(argv, "--target NAME")`. Delete `LintWorklist.parse_target`. — check: `! grep -rn "parse_target" plugins/os-sdlc/lib` 4. Delete `parser/cli/target_flag.rb`. — check: `! grep -rn "TargetFlag" plugins/os-sdlc` 5. The dispatch-id and sweep-only callers keep their behavior. — check: `ruby plugins/os-sdlc/tests/all.rb` Rejected: Thor `method_option :target` on SetupCLI. LintWorklist and the implementation scripts do not use Thor, so there would still be two parsers. Rejected: keep `TargetFlag` and delete `ArgvOption`. `ArgvOption` is the general option reader already in controller/; `TargetFlag` knows only one flag. ## Acceptance criteria 1. Given argv `project-config demo --path lib --target os-sdlc`, when the target is taken, then the value is `os-sdlc` and argv is `project-config demo --path lib`. 2. Given argv `--target=os-sdlc --new`, when the target is taken, then the value is `os-sdlc` and argv is `--new`. 3. Given argv without `--target`, when the target is taken, then the value is nil and argv is unchanged. 4. Given `bin/os-sdlc project-config demo --target os-backlog` in a targets-shaped project, when it runs, then it fails with the same TargetSelectionError text as today (the write is refused). 5. Given `bin/lint-worklist --target os-sdlc` in cc-os, when it runs, then it lists the os-sdlc target's offenses as before. ## Owner sketch ```ruby module OsSdlc::ArgvOption def self.take!(argv, spec) # removes only spec's option; unknown tokens stay in place end class OsSdlc::Project::SetupCLI < Thor def project_config(*args) target = ArgvOption.take!(args, "--target NAME") CLI::ProjectConfigCommand.new(args, target: target, invocation: @invocation).call end end ``` ## Non-goals Thor option declarations for other subcommands. `CLI::OptionParserBuilder` (project-config's own flags). ## Retired tests plugins/os-sdlc/tests/controller/project/cli_target_flag_env_test.rb ## Origin - Trigger: CRC review of the Project concept, user ruling R1 (one `--target` parser, in controller/) - Improvised this session: none - Chain: three --target parsers ← ArgvOption raises on unknown flags, so callers with other flags wrote their own ← controller/argv_option.rb uses `OptionParser#parse!` over the whole argv (DESIGN: controller/argv_option.rb) - Where: plugins/os-sdlc/lib/os_sdlc/controller/argv_option.rb, parser/cli/target_flag.rb, controller/lint_worklist.rb - Session: bb36e383-017d-486e-809f-7ce25d151e61 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os--claude-worktrees-crc-pass-os-sdlc/bb36e383-017d-486e-809f-7ce25d151e61.jsonl
Author
Owner

Project concept batch, run in order: T1 #790, T2 #791, T3 #792, T4 #793, T5 #794, T6 held for a lint-paths ruling, T7 #795, T8 #796. Write the ADR before T3.

Project concept batch, run in order: T1 #790, T2 #791, T3 #792, T4 #793, T5 #794, T6 held for a lint-paths ruling, T7 #795, T8 #796. Write the ADR before T3.
Author
Owner

Work started on branch ticket-790 via /os-sdlc:implement (session bb36e383).

Work started on branch ticket-790 via /os-sdlc:implement (session bb36e383).
Author
Owner

Resolution

Done: ArgvOption.take! consumes only its own flag and leaves other argv entries in place; SetupCLI#project_config and LintWorklist use it; CLI::TargetFlag, SetupCLI.extract_target!, the class-level target accessor, and LintWorklist.parse_target are deleted.

Evidence: os-sdlc implementation 54 on branch ticket-790: 11 dispatches, 1 suite-check retry repaired; final suite-check pass; contract-auditor pass; commit b9c8862c

Follow-ups: none

## Resolution **Done:** ArgvOption.take! consumes only its own flag and leaves other argv entries in place; SetupCLI#project_config and LintWorklist use it; CLI::TargetFlag, SetupCLI.extract_target!, the class-level target accessor, and LintWorklist.parse_target are deleted. **Evidence:** os-sdlc implementation 54 on branch ticket-790: 11 dispatches, 1 suite-check retry repaired; final suite-check pass; contract-auditor pass; commit b9c8862c **Follow-ups:** none
jared closed this issue 2026-09-28 14:29:21 +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#790
No description provided.