os-adr: adr-new and adr-init run through Adr::CLI command classes with an Invocation; cli_test runs in-process #640

Closed
opened 2026-09-23 13:04:55 +00:00 by jared · 3 comments
Owner

Map: #631
Blocked by #638

Problem

Child of #631. Uses the seam shape that #638 records in its ADR. Prerequisite: .sdlc/project.yaml has no os-adr target, so implementation-open --target os-adr cannot open this ticket until a target is added (code_path plugins/os-adr/lib, test_path plugins/os-adr/tests, test_command ruby plugins/os-adr/tests/all.rb).

Observed

plugins/os-adr/tests/cli_test.rb forks ruby bin/adr-new (4 tests, stdin JSON) and ruby bin/adr-init (2 tests). Both bins hold their logic inline: they read Dir.pwd, ARGV, and $stdin, and fail with abort. The whole os-adr suite takes 0.6 s (2026-09-23).

Reproduce

cd plugins/os-adr && ruby tests/cli_test.rb

Expected

One prose sentence: Adr::CLI::New and Adr::CLI::Init take an Invocation and argv, return an exit status, and write only to the invocation streams; the bins only build the Invocation.

Origin

  • Trigger: decomposition of #631, 2026-09-23 (user-decided split)
  • Improvised this session: none
  • Chain: 6 forks ← CLI logic inline in bin/adr-new and bin/adr-init (DESIGN)
  • Root candidate: this ticket
  • Where: plugins/os-adr/bin/adr-new; plugins/os-adr/bin/adr-init; plugins/os-adr/tests/cli_test.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 Adr
  Invocation = Data.define(:cwd, :stdin, :stdout, :stderr, :env)   # lib/adr/invocation.rb
  module CLI
    class New                                                       # lib/adr/cli/new.rb
      def initialize(invocation)
      def run(argv)
    end
    class Init                                                      # lib/adr/cli/init.rb
      def initialize(invocation)
      def run(argv)
    end
  end
end

Acceptance criteria

  1. Given an initialized root and JSON input on invocation.stdin, when Adr::CLI::New#run(["--root", root]) runs, then the new numbered ADR path is on invocation.stdout and the index lists it.
  2. Given input without a title, when New runs, then adr-new: 'title' is required is on invocation.stderr and the status is nonzero.
  3. Given a root with no docs/adr/, when New runs, then invocation.stderr names adr-init and the status is nonzero.
  4. Given no --root argument, when New runs, then it writes under invocation.cwd.
  5. Given a root with .os-adr/suppress, when Adr::CLI::Init#run(["--root", root]) runs, then docs/adr/ and its index exist, the suppress file is gone, and the ADR dir path is on stdout.
  6. Given the real bin/adr-new, when it runs as a subprocess on an initialized temp root, then it exits 0 and prints the new ADR path.
  7. Given the real bin/adr-init, when it runs as a subprocess on a temp root, then it exits 0.

Approach

  • Replace abort with a stderr line plus a returned status of 1. Convert the six existing tests in place, including the supersession test.
  • Out of scope: adr-find, adr-detect, adr-migrate (no tests fork them).
  • ADRs to respect: ADR-0195 (one constant per file), ADR-0025 (lib/adr.rb require_relative manifest), ADR-0123 (thin bin). Adopting Thor is out of scope.
    Required work:
  1. Move bin/adr-new and bin/adr-init's logic into Adr::CLI::New/Adr::CLI::Init behind an Invocation, replacing abort with a stderr line and a returned status. (check: ! grep -n abort plugins/os-adr/bin/adr-new plugins/os-adr/bin/adr-init)
  2. Convert the 6 existing tests in cli_test.rb in place to call the new CLI classes in-process. (proven by AC 1, AC 2, AC 3, AC 5)
  3. Keep bin/adr-new and bin/adr-init runnable as real subprocesses that exit 0. (proven by AC 6, AC 7)
  4. Add the os-adr target to .sdlc/project.yaml before ticket open. (check: grep -n os-adr .sdlc/project.yaml)

Structural follow-through

  • Add the os-adr target to .sdlc/project.yaml before open (prerequisite, see Context).
  • One History row in docs/implementation-status/os-adr.md.
Map: #631 Blocked by #638 ## Problem Child of #631. Uses the seam shape that #638 records in its ADR. Prerequisite: .sdlc/project.yaml has no `os-adr` target, so `implementation-open --target os-adr` cannot open this ticket until a target is added (code_path plugins/os-adr/lib, test_path plugins/os-adr/tests, test_command `ruby plugins/os-adr/tests/all.rb`). ## Observed plugins/os-adr/tests/cli_test.rb forks `ruby bin/adr-new` (4 tests, stdin JSON) and `ruby bin/adr-init` (2 tests). Both bins hold their logic inline: they read `Dir.pwd`, `ARGV`, and `$stdin`, and fail with `abort`. The whole os-adr suite takes 0.6 s (2026-09-23). ## Reproduce ``` cd plugins/os-adr && ruby tests/cli_test.rb ``` ## Expected One prose sentence: `Adr::CLI::New` and `Adr::CLI::Init` take an Invocation and argv, return an exit status, and write only to the invocation streams; the bins only build the Invocation. ## Origin - Trigger: decomposition of #631, 2026-09-23 (user-decided split) - Improvised this session: none - Chain: 6 forks ← CLI logic inline in bin/adr-new and bin/adr-init (DESIGN) - Root candidate: this ticket - Where: plugins/os-adr/bin/adr-new; plugins/os-adr/bin/adr-init; plugins/os-adr/tests/cli_test.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 Adr Invocation = Data.define(:cwd, :stdin, :stdout, :stderr, :env) # lib/adr/invocation.rb module CLI class New # lib/adr/cli/new.rb def initialize(invocation) def run(argv) end class Init # lib/adr/cli/init.rb def initialize(invocation) def run(argv) end end end ``` ## Acceptance criteria 1. Given an initialized root and JSON input on `invocation.stdin`, when `Adr::CLI::New#run(["--root", root])` runs, then the new numbered ADR path is on `invocation.stdout` and the index lists it. 2. Given input without a title, when New runs, then `adr-new: 'title' is required` is on `invocation.stderr` and the status is nonzero. 3. Given a root with no docs/adr/, when New runs, then `invocation.stderr` names adr-init and the status is nonzero. 4. Given no `--root` argument, when New runs, then it writes under `invocation.cwd`. 5. Given a root with .os-adr/suppress, when `Adr::CLI::Init#run(["--root", root])` runs, then docs/adr/ and its index exist, the suppress file is gone, and the ADR dir path is on stdout. 6. Given the real bin/adr-new, when it runs as a subprocess on an initialized temp root, then it exits 0 and prints the new ADR path. 7. Given the real bin/adr-init, when it runs as a subprocess on a temp root, then it exits 0. ## Approach - Replace `abort` with a stderr line plus a returned status of 1. Convert the six existing tests in place, including the supersession test. - Out of scope: adr-find, adr-detect, adr-migrate (no tests fork them). - ADRs to respect: ADR-0195 (one constant per file), ADR-0025 (lib/adr.rb require_relative manifest), ADR-0123 (thin bin). Adopting Thor is out of scope. Required work: 1. Move `bin/adr-new` and `bin/adr-init`'s logic into `Adr::CLI::New`/`Adr::CLI::Init` behind an `Invocation`, replacing `abort` with a stderr line and a returned status. (check: `! grep -n abort plugins/os-adr/bin/adr-new plugins/os-adr/bin/adr-init`) 2. Convert the 6 existing tests in cli_test.rb in place to call the new CLI classes in-process. (proven by AC 1, AC 2, AC 3, AC 5) 3. Keep bin/adr-new and bin/adr-init runnable as real subprocesses that exit 0. (proven by AC 6, AC 7) 4. Add the os-adr target to .sdlc/project.yaml before ticket open. (check: `grep -n os-adr .sdlc/project.yaml`) ## Structural follow-through - Add the os-adr target to .sdlc/project.yaml before open (prerequisite, see Context). - One History row in docs/implementation-status/os-adr.md.
Author
Owner

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

Map: #631
Blocked by #638

Problem

Child of #631. Uses the seam shape that #638 records in its ADR. Prerequisite: .sdlc/project.yaml has no os-adr target, so implementation-open --target os-adr cannot open this ticket until a target is added (code_path plugins/os-adr/lib, test_path plugins/os-adr/tests, test_command ruby plugins/os-adr/tests/all.rb).

Observed

plugins/os-adr/tests/cli_test.rb forks ruby bin/adr-new (4 tests, stdin JSON) and ruby bin/adr-init (2 tests). Both bins hold their logic inline: they read Dir.pwd, ARGV, and $stdin, and fail with abort. The whole os-adr suite takes 0.6 s (2026-09-23).

Reproduce

cd plugins/os-adr && ruby tests/cli_test.rb

Expected

One prose sentence: Adr::CLI::New and Adr::CLI::Init take an Invocation and argv, return an exit status, and write only to the invocation streams; the bins only build the Invocation.

Origin

  • Trigger: decomposition of #631, 2026-09-23 (user-decided split)
  • Improvised this session: none
  • Chain: 6 forks ← CLI logic inline in bin/adr-new and bin/adr-init (DESIGN)
  • Root candidate: this ticket
  • Where: plugins/os-adr/bin/adr-new; plugins/os-adr/bin/adr-init; plugins/os-adr/tests/cli_test.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 Adr
  Invocation = Data.define(:cwd, :stdin, :stdout, :stderr, :env)   # lib/adr/invocation.rb
  module CLI
    class New                                                       # lib/adr/cli/new.rb
      def initialize(invocation)
      def run(argv)
    end
    class Init                                                      # lib/adr/cli/init.rb
      def initialize(invocation)
      def run(argv)
    end
  end
end

Acceptance criteria

  1. Given an initialized root and JSON input on invocation.stdin, when Adr::CLI::New#run(["--root", root]) runs, then the new numbered ADR path is on invocation.stdout and the index lists it.
  2. Given input without a title, when New runs, then adr-new: 'title' is required is on invocation.stderr and the status is nonzero.
  3. Given a root with no docs/adr/, when New runs, then invocation.stderr names adr-init and the status is nonzero.
  4. Given no --root argument, when New runs, then it writes under invocation.cwd.
  5. Given a root with .os-adr/suppress, when Adr::CLI::Init#run(["--root", root]) runs, then docs/adr/ and its index exist, the suppress file is gone, and the ADR dir path is on stdout.
  6. Given the real bin/adr-new, when it runs as a subprocess on an initialized temp root, then it exits 0 and prints the new ADR path.
  7. Given the real bin/adr-init, when it runs as a subprocess on a temp root, then it exits 0.

Approach

  • Replace abort with a stderr line plus a returned status of 1. Convert the six existing tests in place, including the supersession test.
  • Out of scope: adr-find, adr-detect, adr-migrate (no tests fork them).
  • ADRs to respect: ADR-0195 (one constant per file), ADR-0025 (lib/adr.rb require_relative manifest), ADR-0123 (thin bin). Adopting Thor is out of scope.

Structural follow-through

  • Add the os-adr target to .sdlc/project.yaml before open (prerequisite, see Context).
  • One History row in docs/implementation-status/os-adr.md.
Body before Required-work migration, 2026-09-23 Map: #631 Blocked by #638 ## Problem Child of #631. Uses the seam shape that #638 records in its ADR. Prerequisite: .sdlc/project.yaml has no `os-adr` target, so `implementation-open --target os-adr` cannot open this ticket until a target is added (code_path plugins/os-adr/lib, test_path plugins/os-adr/tests, test_command `ruby plugins/os-adr/tests/all.rb`). ## Observed plugins/os-adr/tests/cli_test.rb forks `ruby bin/adr-new` (4 tests, stdin JSON) and `ruby bin/adr-init` (2 tests). Both bins hold their logic inline: they read `Dir.pwd`, `ARGV`, and `$stdin`, and fail with `abort`. The whole os-adr suite takes 0.6 s (2026-09-23). ## Reproduce ``` cd plugins/os-adr && ruby tests/cli_test.rb ``` ## Expected One prose sentence: `Adr::CLI::New` and `Adr::CLI::Init` take an Invocation and argv, return an exit status, and write only to the invocation streams; the bins only build the Invocation. ## Origin - Trigger: decomposition of #631, 2026-09-23 (user-decided split) - Improvised this session: none - Chain: 6 forks ← CLI logic inline in bin/adr-new and bin/adr-init (DESIGN) - Root candidate: this ticket - Where: plugins/os-adr/bin/adr-new; plugins/os-adr/bin/adr-init; plugins/os-adr/tests/cli_test.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 Adr Invocation = Data.define(:cwd, :stdin, :stdout, :stderr, :env) # lib/adr/invocation.rb module CLI class New # lib/adr/cli/new.rb def initialize(invocation) def run(argv) end class Init # lib/adr/cli/init.rb def initialize(invocation) def run(argv) end end end ``` ## Acceptance criteria 1. Given an initialized root and JSON input on `invocation.stdin`, when `Adr::CLI::New#run(["--root", root])` runs, then the new numbered ADR path is on `invocation.stdout` and the index lists it. 2. Given input without a title, when New runs, then `adr-new: 'title' is required` is on `invocation.stderr` and the status is nonzero. 3. Given a root with no docs/adr/, when New runs, then `invocation.stderr` names adr-init and the status is nonzero. 4. Given no `--root` argument, when New runs, then it writes under `invocation.cwd`. 5. Given a root with .os-adr/suppress, when `Adr::CLI::Init#run(["--root", root])` runs, then docs/adr/ and its index exist, the suppress file is gone, and the ADR dir path is on stdout. 6. Given the real bin/adr-new, when it runs as a subprocess on an initialized temp root, then it exits 0 and prints the new ADR path. 7. Given the real bin/adr-init, when it runs as a subprocess on a temp root, then it exits 0. ## Approach - Replace `abort` with a stderr line plus a returned status of 1. Convert the six existing tests in place, including the supersession test. - Out of scope: adr-find, adr-detect, adr-migrate (no tests fork them). - ADRs to respect: ADR-0195 (one constant per file), ADR-0025 (lib/adr.rb require_relative manifest), ADR-0123 (thin bin). Adopting Thor is out of scope. ## Structural follow-through - Add the os-adr target to .sdlc/project.yaml before open (prerequisite, see Context). - One History row in docs/implementation-status/os-adr.md.
Author
Owner

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

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

Resolution

Done: Adr::CLI::New and Adr::CLI::Init take an Invocation and argv, return an exit status, and write only to the invocation streams; bin/adr-new and bin/adr-init only build the Invocation; cli_test.rb runs in-process with no forks.

Evidence: Branch ticket-640 via os-sdlc implementation 17 (dispatches 189-200, three test-writer/reviewer rounds, all gates pass). os-adr suite: 50 runs, 168 assertions, 0 failures.

Follow-ups: none

## Resolution **Done:** Adr::CLI::New and Adr::CLI::Init take an Invocation and argv, return an exit status, and write only to the invocation streams; bin/adr-new and bin/adr-init only build the Invocation; cli_test.rb runs in-process with no forks. **Evidence:** Branch ticket-640 via os-sdlc implementation 17 (dispatches 189-200, three test-writer/reviewer rounds, all gates pass). os-adr suite: 50 runs, 168 assertions, 0 failures. **Follow-ups:** none
jared closed this issue 2026-09-24 02:11:16 +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#640
No description provided.