project-config --show --target NAME ignores --target and prints all targets #861

Closed
opened 2026-09-30 20:59:28 +00:00 by jared · 6 comments
Owner

Problem

Evidence: in cc-plugins/api-wrapper, --target api-wrapper and --target cli-wrapper both print both targets. Reproduced on main b80613ec with a shaped project with targets a and b: --show --target=a and --show --target a both print the a line and the b line.

--target is validated (adapter/project/config.rb:15-19), but the status line is not filtered.

plugins/os-sdlc/lib/os_sdlc/command/project/config_command.rb:83  @invocation.stdout.puts "#{config_path(project)}: #{project.status_line}"
plugins/os-sdlc/lib/os_sdlc/model/project.rb:22-23  def status_line / targets.map { |bound| target_status(bound) }.join("\n")
plugins/os-sdlc/lib/os_sdlc/adapter/project/config.rb:15-19  Config.load calls project.target(target)

Approach

Every reachable caller of report prints exactly one target:

  • show path: Config.load already calls project.target(@options[:target]). A shaped project with no --target raises TargetSelectionError before report. A flat project with no --target selects its sole target.
  • upsert path: Config#write refuses a shaped file (raise_if_targets_shaped!), so upsert only reports a flat, one-target project.

So the "all targets" branch has no production caller. Do not add an optional target_name = nil branch that keeps it. Replace it: Project#status_line(target_name) renders the line of target(target_name). Project#target already owns the nil rule (sole target on a flat project, raise on a shaped one). ConfigCommand#report passes @options[:target]. Output for a flat project is unchanged.

Required work:

  1. Project#status_line takes a required target_name and renders target_status(target(target_name)). Remove the all-targets map. check: absent targets.map { |bound| target_status(bound) } in plugins/os-sdlc/lib/os_sdlc/model/project.rb
  2. ConfigCommand#report passes @options[:target], on both the show and upsert paths. check: present status_line(@options[:target]) in plugins/os-sdlc/lib/os_sdlc/command/project/config_command.rb
  3. Replace test_status_line_on_a_shaped_project_names_every_target with a test that the selected target's line is printed and the other target's line is not. check: absent names_every_target in plugins/os-sdlc/tests/model/project_shaped_listing_test.rb
  4. Add --show --target NAME CLI tests for acceptance criteria 1 and 2. Existing tests cover --show without --target only. Criterion 4 already holds on main (print_status rescues TargetSelectionError; tests/model/project_multi_target_test.rb:48 pins the unknown-name raise), so like criterion 3 it gets no new test.
  5. Update the #status_line signature in the CRC docs. check: present status_line(target_name) in docs/crc/os-sdlc/roles-project.md

Orchestrator work: after merge, add one History row to docs/implementation-status/os-sdlc.md (ADR-0255 decision 5: tracker and record work is not Required work). ADR-0238 line 47 says --show and status_line print every target; the History row names that this line no longer holds, and the user rules on whether ADR-0238 gets a note or an amendment.

Sketch:

def status_line(target_name) = target_status(target(target_name))
# ConfigCommand#report: project.status_line(@options[:target])

Acceptance criteria

  1. Given a shaped project.yaml with targets A and B, when project-config --show --target A runs, then the output has A's line and no line for B.
  2. Given the same file, when project-config --show --target B runs, then the output has B's line and no line for A.
  3. Given a flat project, when project-config --show runs with no --target, then its one line prints unchanged. Given the shaped project, when it runs with no --target, then it keeps the "no target selected" message (Config.load raises TargetSelectionError).
  4. Given the same shaped file, when project-config --show --target C runs, then it prints the unknown-target message and exits 0. print_status (config_command.rb:69-73) rescues the error and prints it.

Owner sketch

class OsSdlc::Project
  def status_line(target_name) end
end

Changes since filing (verified 2026-10-02 on main b80613ec)

  • The bug reproduces. No commit has touched config_command.rb or project.rb since filing (last: 01f5edf9, e6961519, 858349ec).

  • Criterion 3 was wrong. A shaped project without --target does not print both lines. Config.load raises TargetSelectionError.

  • Criterion 4 was wrong about the exit code. The exit stays 0.

  • Space form resolved, not a defect. Under bash, --show --target a selects a (then prints both lines, the #861 defect). The earlier "no target selected" came from the repro shell: zsh does not word-split an unquoted variable, so argv held one element "--target a". ArgvOption.flag_index matches only --target or --target=..., so it found no flag. Reproduced exactly with os-sdlc project-config --show "--target a". No second ticket.

  • Approach changed: the optional target_name = nil "all targets" branch was dropped, because no production path reports more than one target.

  • 2026-10-02: #860 (5d0a1cd6) merged. ConfigCommand now takes name:/options:/invocation:; the target is @options[:target] (used at load_project), not @target. The Approach and check use @options[:target].

Blast radius

  • Production: one caller, config_command.rb:83, reached from the show and upsert paths. Entry is onboarding.rb:37-39 (ArgvOption.take!). If #860 moves --target to a Thor option, @options[:target] still reaches ConfigCommand the same way; this ticket's shape does not change.
  • Tests: tests/model/project_shaped_listing_test.rb:14-21 pins the all-targets output and is replaced. tests/controller/project/cli_project_config_test.rb:60-110 and cli_project_config_target_path_test.rb:10-41 cover --show without --target only.
  • Docs: docs/crc/os-sdlc/inventory-model.md:149, roles-project.md:108 and :129; History row in docs/implementation-status/os-sdlc.md (line 255 records #803).
  • ADRs: ADR-0238 line 47 (Consequences) says --show and status_line print every target. Not reachable through --show today (shaped needs --target). Whether ADR-0238 gets a note is the user's ruling; do not write an ADR in this ticket.

Out of scope

  • Whether upsert with an unknown --target should refuse before it writes the file.
  • Whether --show should refuse leftover argv such as a single "--target a" element.

Blocking edges

  • Run after #860: both edit config_command.rb and add a History row to the same status leaf.
## Problem Evidence: in cc-plugins/api-wrapper, `--target api-wrapper` and `--target cli-wrapper` both print both targets. Reproduced on main b80613ec with a shaped project with targets a and b: `--show --target=a` and `--show --target a` both print the a line and the b line. `--target` is validated (`adapter/project/config.rb:15-19`), but the status line is not filtered. ``` plugins/os-sdlc/lib/os_sdlc/command/project/config_command.rb:83 @invocation.stdout.puts "#{config_path(project)}: #{project.status_line}" plugins/os-sdlc/lib/os_sdlc/model/project.rb:22-23 def status_line / targets.map { |bound| target_status(bound) }.join("\n") plugins/os-sdlc/lib/os_sdlc/adapter/project/config.rb:15-19 Config.load calls project.target(target) ``` ## Approach Every reachable caller of `report` prints exactly one target: - show path: `Config.load` already calls `project.target(@options[:target])`. A shaped project with no `--target` raises `TargetSelectionError` before `report`. A flat project with no `--target` selects its sole target. - upsert path: `Config#write` refuses a shaped file (`raise_if_targets_shaped!`), so upsert only reports a flat, one-target project. So the "all targets" branch has no production caller. Do not add an optional `target_name = nil` branch that keeps it. Replace it: `Project#status_line(target_name)` renders the line of `target(target_name)`. `Project#target` already owns the nil rule (sole target on a flat project, raise on a shaped one). `ConfigCommand#report` passes `@options[:target]`. Output for a flat project is unchanged. Required work: 1. `Project#status_line` takes a required `target_name` and renders `target_status(target(target_name))`. Remove the all-targets map. check: absent `targets.map { |bound| target_status(bound) }` in `plugins/os-sdlc/lib/os_sdlc/model/project.rb` 2. `ConfigCommand#report` passes `@options[:target]`, on both the show and upsert paths. check: present `status_line(@options[:target])` in `plugins/os-sdlc/lib/os_sdlc/command/project/config_command.rb` 3. Replace `test_status_line_on_a_shaped_project_names_every_target` with a test that the selected target's line is printed and the other target's line is not. check: absent `names_every_target` in `plugins/os-sdlc/tests/model/project_shaped_listing_test.rb` 4. Add `--show --target NAME` CLI tests for acceptance criteria 1 and 2. Existing tests cover `--show` without `--target` only. Criterion 4 already holds on main (`print_status` rescues `TargetSelectionError`; `tests/model/project_multi_target_test.rb:48` pins the unknown-name raise), so like criterion 3 it gets no new test. 5. Update the `#status_line` signature in the CRC docs. check: present `status_line(target_name)` in `docs/crc/os-sdlc/roles-project.md` Orchestrator work: after merge, add one History row to `docs/implementation-status/os-sdlc.md` (ADR-0255 decision 5: tracker and record work is not Required work). ADR-0238 line 47 says `--show` and `status_line` print every target; the History row names that this line no longer holds, and the user rules on whether ADR-0238 gets a note or an amendment. Sketch: ```ruby def status_line(target_name) = target_status(target(target_name)) # ConfigCommand#report: project.status_line(@options[:target]) ``` ## Acceptance criteria 1. Given a shaped `project.yaml` with targets A and B, when `project-config --show --target A` runs, then the output has A's line and no line for B. 2. Given the same file, when `project-config --show --target B` runs, then the output has B's line and no line for A. 3. Given a flat project, when `project-config --show` runs with no `--target`, then its one line prints unchanged. Given the shaped project, when it runs with no `--target`, then it keeps the "no target selected" message (`Config.load` raises `TargetSelectionError`). 4. Given the same shaped file, when `project-config --show --target C` runs, then it prints the unknown-target message and exits 0. `print_status` (`config_command.rb:69-73`) rescues the error and prints it. ## Owner sketch ```ruby class OsSdlc::Project def status_line(target_name) end end ``` ## Changes since filing (verified 2026-10-02 on main b80613ec) - The bug reproduces. No commit has touched `config_command.rb` or `project.rb` since filing (last: 01f5edf9, e6961519, 858349ec). - Criterion 3 was wrong. A shaped project without `--target` does not print both lines. `Config.load` raises `TargetSelectionError`. - Criterion 4 was wrong about the exit code. The exit stays 0. - Space form resolved, not a defect. Under bash, `--show --target a` selects `a` (then prints both lines, the #861 defect). The earlier "no target selected" came from the repro shell: zsh does not word-split an unquoted variable, so argv held one element `"--target a"`. `ArgvOption.flag_index` matches only `--target` or `--target=...`, so it found no flag. Reproduced exactly with `os-sdlc project-config --show "--target a"`. No second ticket. - Approach changed: the optional `target_name = nil` "all targets" branch was dropped, because no production path reports more than one target. - 2026-10-02: #860 (5d0a1cd6) merged. `ConfigCommand` now takes `name:`/`options:`/`invocation:`; the target is `@options[:target]` (used at `load_project`), not `@target`. The Approach and check use `@options[:target]`. ## Blast radius - Production: one caller, `config_command.rb:83`, reached from the show and upsert paths. Entry is `onboarding.rb:37-39` (`ArgvOption.take!`). If #860 moves `--target` to a Thor option, `@options[:target]` still reaches `ConfigCommand` the same way; this ticket's shape does not change. - Tests: `tests/model/project_shaped_listing_test.rb:14-21` pins the all-targets output and is replaced. `tests/controller/project/cli_project_config_test.rb:60-110` and `cli_project_config_target_path_test.rb:10-41` cover `--show` without `--target` only. - Docs: `docs/crc/os-sdlc/inventory-model.md:149`, `roles-project.md:108` and `:129`; History row in `docs/implementation-status/os-sdlc.md` (line 255 records #803). - ADRs: ADR-0238 line 47 (Consequences) says `--show` and `status_line` print every target. Not reachable through `--show` today (shaped needs `--target`). Whether ADR-0238 gets a note is the user's ruling; do not write an ADR in this ticket. ## Out of scope - Whether `upsert` with an unknown `--target` should refuse before it writes the file. - Whether `--show` should refuse leftover argv such as a single `"--target a"` element. ## Blocking edges - Run after #860: both edit `config_command.rb` and add a History row to the same status leaf.
Author
Owner

Body revised 2026-10-02 after blast-radius re-check on main b80613ec: criteria 3 and 4 corrected (no-target shaped message, exit 0); space-form --target open item added.

Body revised 2026-10-02 after blast-radius re-check on main b80613ec: criteria 3 and 4 corrected (no-target shaped message, exit 0); space-form --target open item added.
Author
Owner

PREDICTED RULING: REVISE
a. No.
b. feedback-no-speculative-hardening-in-artifacts and feedback-remove-habits-dont-backfill-heuristics: status_line(nil) keeps an "all targets" branch with no production caller. Upsert writes only flat files (raise_if_targets_shaped!). A shaped --show needs --target before report runs. Lens A12 (the owner decides): Project#target already owns the nil rule. feedback-adr-conflict-is-a-symptom: design correctly first, then name ADR-0238 line 47.
c. Lazy: an optional nil flag that keeps dead behavior, and a space-form defect left unexplained. Research done: in bash, --show --target a selects a. The repro used zsh with an unquoted variable, so argv held one element "--target a". ArgvOption.flag_index matches only --target or --target=. --show "--target a" reproduces the output exactly. This is not a defect.
d. def status_line(target_name) = target_status(target(target_name)), and report passes @target. There is one rule owner and no dead branch. Flat output does not change. If #860 moves --target to Thor, @target still arrives the same way.
e. Not read: #860's final body, lens-testing/cases, ADR-0088, ADR-0243 full text, and non-CLI callers outside plugins/.
User ruling needed: should ADR-0238 line 47 get a note? No ADR was written.

PREDICTED RULING: REVISE a. No. b. feedback-no-speculative-hardening-in-artifacts and feedback-remove-habits-dont-backfill-heuristics: `status_line(nil)` keeps an "all targets" branch with no production caller. Upsert writes only flat files (`raise_if_targets_shaped!`). A shaped `--show` needs `--target` before `report` runs. Lens A12 (the owner decides): `Project#target` already owns the nil rule. feedback-adr-conflict-is-a-symptom: design correctly first, then name ADR-0238 line 47. c. Lazy: an optional nil flag that keeps dead behavior, and a space-form defect left unexplained. Research done: in bash, `--show --target a` selects `a`. The repro used zsh with an unquoted variable, so argv held one element `"--target a"`. `ArgvOption.flag_index` matches only `--target` or `--target=`. `--show "--target a"` reproduces the output exactly. This is not a defect. d. `def status_line(target_name) = target_status(target(target_name))`, and `report` passes `@target`. There is one rule owner and no dead branch. Flat output does not change. If #860 moves `--target` to Thor, `@target` still arrives the same way. e. Not read: #860's final body, lens-testing/cases, ADR-0088, ADR-0243 full text, and non-CLI callers outside plugins/. User ruling needed: should ADR-0238 line 47 get a note? No ADR was written.
Author
Owner

Work started 2026-10-02 on branch ticket-861 (os-sdlc pipeline, map poodr-ticket-implementation, target os-sdlc). Body updated first: after #860, the target is @options[:target], so the check now reads status_line(@options[:target]).

Work started 2026-10-02 on branch ticket-861 (os-sdlc pipeline, map poodr-ticket-implementation, target os-sdlc). Body updated first: after #860, the target is @options[:target], so the check now reads status_line(@options[:target]).
Author
Owner

Escalation 8 (implementation 26, change-auditor verdict:fail). Cause: under-decomposed ticket — Required work item 6 (History row + ADR-0238 note) is orchestrator work with no criterion or check (ADR-0255 decision 5). Fix: moved to an 'Orchestrator work:' line. Recovery: fresh implementation-open, which abandons implementation 26.

Escalation 8 (implementation 26, change-auditor verdict:fail). Cause: under-decomposed ticket — Required work item 6 (History row + ADR-0238 note) is orchestrator work with no criterion or check (ADR-0255 decision 5). Fix: moved to an 'Orchestrator work:' line. Recovery: fresh implementation-open, which abandons implementation 26.
Author
Owner

Escalation 9 (implementation 27, bound_exhausted:test-reviewer/fail). Cause: contradictory ticket — Required work 4 asked for a new test of criterion 4, which already holds on main, while every new test in the round must be red; writer and reviewer each followed their rules. Fix: R4 now covers criteria 1 and 2 only (criterion 4, like 3, is covered by existing tests); the AC 4 test method was hand-deleted from cli_project_config_show_target_test.rb. Kept: R3 model test and AC 1/2 CLI tests (red for the intended reason per dispatch 241). Recovery: fresh implementation-open.

Escalation 9 (implementation 27, bound_exhausted:test-reviewer/fail). Cause: contradictory ticket — Required work 4 asked for a new test of criterion 4, which already holds on main, while every new test in the round must be red; writer and reviewer each followed their rules. Fix: R4 now covers criteria 1 and 2 only (criterion 4, like 3, is covered by existing tests); the AC 4 test method was hand-deleted from cli_project_config_show_target_test.rb. Kept: R3 model test and AC 1/2 CLI tests (red for the intended reason per dispatch 241). Recovery: fresh implementation-open.
Author
Owner

Resolution

Done: project-config --show --target NAME prints only that target's line. Project#status_line(target_name) renders target_status(target(target_name)); Project#target stays the only owner of the no-target rule (flat: sole target; shaped: TargetSelectionError). ConfigCommand#report passes @options[:target]. CRC roles-project.md updated. The space-form '--target a' failure in the earlier repro was a zsh quoting error, not a code defect.

Evidence: Commits 7169466a (feat, implementation 28: 8 dispatches, 0 retries, suite green, contract-auditor pass) and 7bf03880 (History row) merged to main. New test cli_project_config_show_target_test.rb (AC 1, AC 2); project_shaped_listing_test.rb now asserts only the selected target. Implementations 26 and 27 escalated on ticket defects, fixed in the body: (1) History row/ADR note listed as Required work -> moved to Orchestrator work; (2) a new test for already-true criterion 4 conflicted with the all-new-tests-red rule -> R4 limited to AC 1-2, AC 4 test deleted by hand.

Follow-ups: ADR-0238 line 47 ruling (note vs amendment) raised to the user as a decision in session 074f69a6, not a ticket. Observed, not filed (no reported pain): on a flat project, 'project-config NAME --target X' now writes the file and then fails loudly (exit 3), where before it ignored --target.

## Resolution **Done:** project-config --show --target NAME prints only that target's line. Project#status_line(target_name) renders target_status(target(target_name)); Project#target stays the only owner of the no-target rule (flat: sole target; shaped: TargetSelectionError). ConfigCommand#report passes @options[:target]. CRC roles-project.md updated. The space-form '--target a' failure in the earlier repro was a zsh quoting error, not a code defect. **Evidence:** Commits 7169466a (feat, implementation 28: 8 dispatches, 0 retries, suite green, contract-auditor pass) and 7bf03880 (History row) merged to main. New test cli_project_config_show_target_test.rb (AC 1, AC 2); project_shaped_listing_test.rb now asserts only the selected target. Implementations 26 and 27 escalated on ticket defects, fixed in the body: (1) History row/ADR note listed as Required work -> moved to Orchestrator work; (2) a new test for already-true criterion 4 conflicted with the all-new-tests-red rule -> R4 limited to AC 1-2, AC 4 test deleted by hand. **Follow-ups:** ADR-0238 line 47 ruling (note vs amendment) raised to the user as a decision in session 074f69a6, not a ticket. Observed, not filed (no reported pain): on a flat project, 'project-config NAME --target X' now writes the file and then fails loudly (exit 3), where before it ignored --target.
jared closed this issue 2026-10-02 20:34:29 +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#861
No description provided.