Fold Project::CandidateTestPaths into Project#tests_for with an anchored prefix strip (completes ADR-0174) #565

Closed
opened 2026-09-13 23:18:07 +00:00 by jared · 2 comments
Owner

Map: #568
Blocked by #564

Context

Responsibility audit of project.rb on 2026-09-13. Batch position 2 of 4. ADR-0174 already decided that CandidateTestPaths becomes Project#tests_for. The class still exists. This ticket completes that ADR.

Observed

Project#tests_for strips the code path with an unanchored sub, then hands the result to a class that already holds every other fact. The only sender guarantees the prefix sits at position 0, so the invariant lives in the caller.

plugins/os-sdlc/lib/os_sdlc/project.rb:132   code_relative = code_file.sub("#{code_path}/", "")
plugins/os-sdlc/lib/os_sdlc/project.rb:133   Project::CandidateTestPaths.new(project_root: root, test_path:, code_relative:).resolve(code_file)
plugins/os-sdlc/lib/os_sdlc/project/candidate_test_paths.rb:12   mirrored = "#{test_path}/#{code_relative}"
plugins/os-sdlc/lib/os_sdlc/project/changes.rb:37   code_paths.flat_map { |path| @project.tests_for(path) }
plugins/os-sdlc/lib/os_sdlc/project/changes.rb:16   changed_paths.select { |path| path.start_with?(code_prefix) }

Reproduce

ruby -e 'p "app/lib/x.rb".sub("lib/", "")'   # => "app/x.rb"

Expected

def test_tests_for_does_not_strip_an_interior_code_path_segment
  project = Project.new(root: dir, name: "x", code_path: "lib", test_path: "test")
  # a file under lib/ whose relative path contains "lib/" again keeps that interior segment
end

Tasks

  • Failing test: interior occurrence of the code_path segment is not stripped
  • Move the resolution logic from Project::CandidateTestPaths into Project#tests_for
  • Strip with delete_prefix("#{code_path}/")
  • Keep the HarnessError for an unknown file
  • Delete project/candidate_test_paths.rb; move its tests into project_test.rb
  • Project::Changes#mapped_test_paths unchanged

Acceptance criteria

  • Full suite green; no file named candidate_test_paths remains
  • ADR case: completes ADR-0174. No ADR work.

Origin

  • Trigger: /os-sdlc:responsibility-audit plugins/os-sdlc/lib/os_sdlc/project.rb on 2026-09-13; analysis at .sdlc/tmp/responsibility-audit/2026-09-13-project/analysis.yaml
  • Improvised this session: none
  • Chain: mixed responsibilities in OsSdlc::Project ← DESIGN (plugins/os-sdlc/lib/os_sdlc/project.rb, ADR-0055/0074/0088/0169/0174)
  • Root candidate: this ticket
  • Where: OsSdlc::Project, plugins/os-sdlc/lib/os_sdlc/project.rb
  • Session: c72ac6a8-cb89-4b97-904d-155babd2a7a2
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/c72ac6a8-cb89-4b97-904d-155babd2a7a2.jsonl
Map: #568 Blocked by #564 ## Context Responsibility audit of `project.rb` on 2026-09-13. Batch position 2 of 4. ADR-0174 already decided that `CandidateTestPaths` becomes `Project#tests_for`. The class still exists. This ticket completes that ADR. ## Observed `Project#tests_for` strips the code path with an unanchored `sub`, then hands the result to a class that already holds every other fact. The only sender guarantees the prefix sits at position 0, so the invariant lives in the caller. ``` plugins/os-sdlc/lib/os_sdlc/project.rb:132 code_relative = code_file.sub("#{code_path}/", "") plugins/os-sdlc/lib/os_sdlc/project.rb:133 Project::CandidateTestPaths.new(project_root: root, test_path:, code_relative:).resolve(code_file) plugins/os-sdlc/lib/os_sdlc/project/candidate_test_paths.rb:12 mirrored = "#{test_path}/#{code_relative}" plugins/os-sdlc/lib/os_sdlc/project/changes.rb:37 code_paths.flat_map { |path| @project.tests_for(path) } plugins/os-sdlc/lib/os_sdlc/project/changes.rb:16 changed_paths.select { |path| path.start_with?(code_prefix) } ``` ## Reproduce ``` ruby -e 'p "app/lib/x.rb".sub("lib/", "")' # => "app/x.rb" ``` ## Expected ```ruby def test_tests_for_does_not_strip_an_interior_code_path_segment project = Project.new(root: dir, name: "x", code_path: "lib", test_path: "test") # a file under lib/ whose relative path contains "lib/" again keeps that interior segment end ``` ## Tasks - [ ] Failing test: interior occurrence of the code_path segment is not stripped - [ ] Move the resolution logic from `Project::CandidateTestPaths` into `Project#tests_for` - [ ] Strip with `delete_prefix("#{code_path}/")` - [ ] Keep the `HarnessError` for an unknown file - [ ] Delete `project/candidate_test_paths.rb`; move its tests into `project_test.rb` - [ ] `Project::Changes#mapped_test_paths` unchanged ## Acceptance criteria - Full suite green; no file named candidate_test_paths remains - ADR case: completes ADR-0174. No ADR work. ## Origin - Trigger: `/os-sdlc:responsibility-audit plugins/os-sdlc/lib/os_sdlc/project.rb` on 2026-09-13; analysis at `.sdlc/tmp/responsibility-audit/2026-09-13-project/analysis.yaml` - Improvised this session: none - Chain: mixed responsibilities in `OsSdlc::Project` ← DESIGN (`plugins/os-sdlc/lib/os_sdlc/project.rb`, ADR-0055/0074/0088/0169/0174) - Root candidate: this ticket - Where: `OsSdlc::Project`, `plugins/os-sdlc/lib/os_sdlc/project.rb` - Session: c72ac6a8-cb89-4b97-904d-155babd2a7a2 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/c72ac6a8-cb89-4b97-904d-155babd2a7a2.jsonl
Author
Owner

Work started via /os-sdlc:implement on branch ticket-565 (worktree). Map: #568.

Work started via /os-sdlc:implement on branch ticket-565 (worktree). Map: #568.
Author
Owner

Resolution

Done: Project#tests_for now resolves candidate test paths itself with delete_prefix (anchored). Project::CandidateTestPaths deleted; its resolution logic lives in private Project methods. HarnessError kept for an unknown file. Project::Changes#mapped_test_paths unchanged.

Evidence: Pipeline implementation 2 complete: 11 dispatches, 1 contract-auditor retry, suite green twice (1207+ runs). Branch ticket-565, merged to main via worktree finish. Tests: project_tests_for_test.rb (interior segment not stripped; unknown file raises). Completes ADR-0174; no ADR work.

Follow-ups: none. Note for the batch re-audit: six private tests_for_* helpers now sit on Project; the re-audit judges whether they want their own owner.

## Resolution **Done:** Project#tests_for now resolves candidate test paths itself with delete_prefix (anchored). Project::CandidateTestPaths deleted; its resolution logic lives in private Project methods. HarnessError kept for an unknown file. Project::Changes#mapped_test_paths unchanged. **Evidence:** Pipeline implementation 2 complete: 11 dispatches, 1 contract-auditor retry, suite green twice (1207+ runs). Branch ticket-565, merged to main via worktree finish. Tests: project_tests_for_test.rb (interior segment not stripped; unknown file raises). Completes ADR-0174; no ADR work. **Follow-ups:** none. Note for the batch re-audit: six private tests_for_* helpers now sit on Project; the re-audit judges whether they want their own owner.
jared closed this issue 2026-09-14 00:06:01 +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#565
No description provided.