subagent-start brief composition raises on an unmapped code file; ADR-0202 widened only the gate side #614

Closed
opened 2026-09-18 13:43:01 +00:00 by jared · 3 comments
Owner

Context

Codebase#tests_for raises HarnessError when a changed code file has no mapped test. ADR-0202 added a rescue on the gate side only (GateCommands#diff_test_command widens to the suite). The brief side has no rescue: BriefFacts#test_changes calls Model::Project::Changes#test_paths, which calls tests_for for every changed code file. subagent-start builds the brief before it inserts the dispatch row, so the raise leaves no dispatch and nothing to settle.

Observed on #607 implementation 3 (session 4974deff-0140-4813-8cd1-e96c6356a214): the first refactorer round removed a require_relative line from the manifest plugins/os/lib/worktree.rb. The os tests live in plugins/os/tests/worktree/, so the manifest maps to nothing. The second refactorer's subagent-start raised:

os-sdlc: no test file found for plugins/os/lib/worktree.rb; tried: plugins/os/tests/worktree_test.rb; and scenario globs: plugins/os/tests/worktree_*_test.rb

gate 26 answered gate no dispatch 26; subagent-stop answered no pending dispatch; the run never reached implementation_complete and was hand-finished.

Change

Changes becomes the one place that knows which code files have no mapped test. Both the brief and the gate read that fact. tests_for still raises; Changes catches it once.

# plugins/os-sdlc/lib/os_sdlc/model/project/changes.rb
def test_paths
  (changed_paths.select { |path| @project.test.include?(path) } + mapped_test_paths).uniq
end

def unmapped_code_paths
  code_paths.select { |path| mapped_tests(path).nil? }
end

private

def mapped_test_paths
  code_paths.flat_map { |path| mapped_tests(path) || [] }
end

def mapped_tests(path)
  @project.tests_for(path)
rescue HarnessError
  nil
end
# plugins/os-sdlc/lib/os_sdlc/pipeline/gate_commands.rb
def diff_test_command
  return suite_command if changes.unmapped_code_paths.any?

  test_paths = changes.test_paths
  return nil if test_paths.empty?

  "ruby -I#{project.test.path} -e #{diff_test_require_expr(test_paths).shellescape}"
end

The rescue HarnessError in diff_test_command goes away. BriefFacts#test_changes appends one line when unmapped_code_paths is not empty:

unmapped code files (diff-test widens to the suite): plugins/os/lib/worktree.rb

Tasks

  • Add Changes#unmapped_code_paths; test_paths no longer raises for an unmapped code file.
  • GateCommands#diff_test_command widens to the suite when unmapped_code_paths is not empty; remove its rescue HarnessError.
  • BriefFacts#test_changes appends the unmapped-code-files line when the list is not empty.
  • Run ruby plugins/os-sdlc/tests/all.rb; green.

Acceptance criteria

  • subagent-start composes a brief for a diff that contains an unmapped code file.
  • The diff-test gate for that diff runs the suite command.
  • The brief names the unmapped file.

Retired tests

plugins/os-sdlc/tests/model/project/changes_test.rb#test_code_file_with_no_candidate_test_raises_harness_error
plugins/os-sdlc/tests/model/project/changes_test.rb#test_code_file_with_single_candidate_and_no_test_raises_harness_error

Owner sketch

module OsSdlc
  class Model
    class Project
      class Changes
        def test_paths
        def unmapped_code_paths
      end
    end
  end
  module Pipeline
    class GateCommands
      def diff_test_command
    end
    class BriefFacts
      def test_changes
    end
  end
end

Cases

  1. Given a diff whose only code change is a file with no mapped test, when Changes#test_paths is called, then it returns the changed test paths without raising, and unmapped_code_paths names that file.
  2. Given the same diff, when GateCommands#diff_test_command is called, then it returns the suite command.
  3. Given a diff whose code files all map to tests, when unmapped_code_paths is called, then it is empty and diff_test_command returns the require-list command.
  4. Given the same unmapped diff, when BriefFacts#test_changes renders, then the fact contains unmapped code files (diff-test widens to the suite): <path>.

Origin

  • Trigger: /os-sdlc:implement #607, implementation 3, second refactorer dispatch
  • Session: 4974deff-0140-4813-8cd1-e96c6356a214
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/4974deff-0140-4813-8cd1-e96c6356a214.jsonl
  • Where: plugins/os-sdlc/lib/os_sdlc/model/project/changes.rb, plugins/os-sdlc/lib/os_sdlc/pipeline/gate_commands.rb
  • Design approved by user 2026-09-18; body rewritten with code sketch.
## Context `Codebase#tests_for` raises `HarnessError` when a changed code file has no mapped test. ADR-0202 added a rescue on the gate side only (`GateCommands#diff_test_command` widens to the suite). The brief side has no rescue: `BriefFacts#test_changes` calls `Model::Project::Changes#test_paths`, which calls `tests_for` for every changed code file. `subagent-start` builds the brief before it inserts the dispatch row, so the raise leaves no dispatch and nothing to settle. Observed on #607 implementation 3 (session 4974deff-0140-4813-8cd1-e96c6356a214): the first refactorer round removed a `require_relative` line from the manifest `plugins/os/lib/worktree.rb`. The os tests live in `plugins/os/tests/worktree/`, so the manifest maps to nothing. The second refactorer's `subagent-start` raised: ``` os-sdlc: no test file found for plugins/os/lib/worktree.rb; tried: plugins/os/tests/worktree_test.rb; and scenario globs: plugins/os/tests/worktree_*_test.rb ``` `gate 26` answered `gate no dispatch 26`; `subagent-stop` answered `no pending dispatch`; the run never reached `implementation_complete` and was hand-finished. ## Change `Changes` becomes the one place that knows which code files have no mapped test. Both the brief and the gate read that fact. `tests_for` still raises; `Changes` catches it once. ```ruby # plugins/os-sdlc/lib/os_sdlc/model/project/changes.rb def test_paths (changed_paths.select { |path| @project.test.include?(path) } + mapped_test_paths).uniq end def unmapped_code_paths code_paths.select { |path| mapped_tests(path).nil? } end private def mapped_test_paths code_paths.flat_map { |path| mapped_tests(path) || [] } end def mapped_tests(path) @project.tests_for(path) rescue HarnessError nil end ``` ```ruby # plugins/os-sdlc/lib/os_sdlc/pipeline/gate_commands.rb def diff_test_command return suite_command if changes.unmapped_code_paths.any? test_paths = changes.test_paths return nil if test_paths.empty? "ruby -I#{project.test.path} -e #{diff_test_require_expr(test_paths).shellescape}" end ``` The `rescue HarnessError` in `diff_test_command` goes away. `BriefFacts#test_changes` appends one line when `unmapped_code_paths` is not empty: ``` unmapped code files (diff-test widens to the suite): plugins/os/lib/worktree.rb ``` ## Tasks - [ ] Add `Changes#unmapped_code_paths`; `test_paths` no longer raises for an unmapped code file. - [ ] `GateCommands#diff_test_command` widens to the suite when `unmapped_code_paths` is not empty; remove its `rescue HarnessError`. - [ ] `BriefFacts#test_changes` appends the unmapped-code-files line when the list is not empty. - [ ] Run `ruby plugins/os-sdlc/tests/all.rb`; green. ## Acceptance criteria - [ ] `subagent-start` composes a brief for a diff that contains an unmapped code file. - [ ] The diff-test gate for that diff runs the suite command. - [ ] The brief names the unmapped file. ## Retired tests plugins/os-sdlc/tests/model/project/changes_test.rb#test_code_file_with_no_candidate_test_raises_harness_error plugins/os-sdlc/tests/model/project/changes_test.rb#test_code_file_with_single_candidate_and_no_test_raises_harness_error ## Owner sketch ```ruby module OsSdlc class Model class Project class Changes def test_paths def unmapped_code_paths end end end module Pipeline class GateCommands def diff_test_command end class BriefFacts def test_changes end end end ``` ## Cases 1. Given a diff whose only code change is a file with no mapped test, when `Changes#test_paths` is called, then it returns the changed test paths without raising, and `unmapped_code_paths` names that file. 2. Given the same diff, when `GateCommands#diff_test_command` is called, then it returns the suite command. 3. Given a diff whose code files all map to tests, when `unmapped_code_paths` is called, then it is empty and `diff_test_command` returns the require-list command. 4. Given the same unmapped diff, when `BriefFacts#test_changes` renders, then the fact contains `unmapped code files (diff-test widens to the suite): <path>`. ## Origin - Trigger: /os-sdlc:implement #607, implementation 3, second refactorer dispatch - Session: 4974deff-0140-4813-8cd1-e96c6356a214 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/4974deff-0140-4813-8cd1-e96c6356a214.jsonl - Where: plugins/os-sdlc/lib/os_sdlc/model/project/changes.rb, plugins/os-sdlc/lib/os_sdlc/pipeline/gate_commands.rb - Design approved by user 2026-09-18; body rewritten with code sketch.
Author
Owner

Work started via os-sdlc pipeline on branch ticket-614 (session 4974deff).

Work started via os-sdlc pipeline on branch ticket-614 (session 4974deff).
Author
Owner

Deviation (impl 4): two pre-existing tests in changes_test.rb asserted the raising behavior this ticket retires and were not listed under Retired tests, so programmer-repair looped (dispatches 30-32). Main loop deleted both methods by hand and added the Retired tests section.

Deviation (impl 4): two pre-existing tests in changes_test.rb asserted the raising behavior this ticket retires and were not listed under Retired tests, so programmer-repair looped (dispatches 30-32). Main loop deleted both methods by hand and added the Retired tests section.
Author
Owner

Resolution

Done: Changes#unmapped_code_paths added; test_paths rescues tests_for once; diff_test_command widens to the suite via that fact and lost its rescue; BriefFacts#test_changes appends the unmapped-code-files line.

Evidence: Branch ticket-614, pipeline implementation 4 reached implementation_complete (14 dispatches, 3 retries); suite-check dispatch 38 green; two new tests changes_unmapped_code_paths_test.rb and brief_facts_unmapped_code_files_test.rb; two retired raise-asserting tests removed from changes_test.rb.

Follow-ups: none: the flaky lsp-probe socket suite red (dispatch 35) is #611's territory, already open.

## Resolution **Done:** Changes#unmapped_code_paths added; test_paths rescues tests_for once; diff_test_command widens to the suite via that fact and lost its rescue; BriefFacts#test_changes appends the unmapped-code-files line. **Evidence:** Branch ticket-614, pipeline implementation 4 reached implementation_complete (14 dispatches, 3 retries); suite-check dispatch 38 green; two new tests changes_unmapped_code_paths_test.rb and brief_facts_unmapped_code_files_test.rb; two retired raise-asserting tests removed from changes_test.rb. **Follow-ups:** none: the flaky lsp-probe socket suite red (dispatch 35) is #611's territory, already open.
jared closed this issue 2026-09-18 14:57:55 +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#614
No description provided.