os-sdlc: one owner for the base commit and changes since it (#1638 part A) #1639

Closed
opened 2026-11-08 15:58:26 +00:00 by jared · 2 comments
Owner

Problem

The survivor gate needs the ticket's base commit and the files the ticket changed since that commit. No public object owns the base commit: Gate::UnchangedCheck::BaseCheckout#merge_base is private. Project::Changes compares only the working tree with the index, so it misses committed ticket work.

Approach

Design: docs/system-design-format/proof/2026-11-08-os-sdlc-mutation-survivors.md, sections 2 and 3 (roles "base source" and "change source"), build slice 2. Parent: #1638.

Required work:

  1. Move the merge-base query to a public Project::BaseBranch#merge_base. It rescues OsGit::Error (including the detached-primary error from name) and raises HarnessError, as BaseCheckout#within does today. Gate::UnchangedCheck::BaseCheckout asks Project::BaseBranch for it — check: absent def merge_base in plugins/os-sdlc/lib/os_sdlc/gate/unchanged_check/base_checkout.rb
  2. FastGate::ChangedFiles asks Project::BaseBranch#merge_base instead of its own private merge_base, so one object owns the base commit — check: absent def merge_base in plugins/os-sdlc/lib/os_sdlc/fast_gate/changed_files.rb
  3. Add Project::Changes#since(ref). It returns a new Project::Changes whose path lists come from OsGit::Repository#changes(against: ref, renames: false): committed, staged, unstaged, and untracked files, without deleted files. Pass the ref through a keyword with a default (against: :index), so the existing two-argument callers do not change. code_diff and test_diff still compare against HEAD; since changes the path lists only — check: present def since in plugins/os-sdlc/lib/os_sdlc/project/changes.rb

Acceptance criteria

  1. Given a branch with commits after the base branch, when Project::BaseBranch#merge_base is asked, then it returns the merge-base commit of HEAD and the base branch.
  2. Given a base branch that does not exist, when Project::BaseBranch#merge_base is asked, then it raises HarnessError as BaseCheckout does today.
  3. Given one committed, one staged, one unstaged, and one untracked code file since the merge base, when Project::Changes#since(merge_base) is asked for code_paths, then all four files are listed.
  4. Given those changes, when test_paths is asked, then it lists the changed test files and the existing test files mapped to the changed code files.
  5. Given a file changed outside the target path, when code_paths is asked, then that file is not listed.
  6. Given a file deleted since the merge base, when code_paths is asked on since(merge_base), then that file is not listed.
  7. Given the unchanged gate and the fast gate, when they run, then their behavior is the same as before.

Owner sketch

class Project::BaseBranch
  def name
  def merge_base
end

class Project::Changes
  def since(ref)
  def code_paths
  def test_paths
end

Origin

  • Trigger: domain-object design docs/system-design-format/proof/2026-11-08-os-sdlc-mutation-survivors.md (commit f0e172b7) for parent #1638
  • Improvised this session: none
  • Chain: survivor gate needs one owner per fact ← design section 3 ← DESIGN (docs/system-design-format/proof/2026-11-08-os-sdlc-mutation-survivors.md)
  • Root candidate: none found; searched mutation, mutineer, mutant, survivor, merge_base, changes since
  • Where: lib/os_sdlc/project/base_branch.rb, lib/os_sdlc/project/changes.rb
  • Session: ff703e34-8046-4b24-af68-6ef76d8aa338
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os-plugins-os-sdlc/ff703e34-8046-4b24-af68-6ef76d8aa338.jsonl

VERDICT: CREATE

## Problem The survivor gate needs the ticket's base commit and the files the ticket changed since that commit. No public object owns the base commit: `Gate::UnchangedCheck::BaseCheckout#merge_base` is private. `Project::Changes` compares only the working tree with the index, so it misses committed ticket work. ## Approach Design: `docs/system-design-format/proof/2026-11-08-os-sdlc-mutation-survivors.md`, sections 2 and 3 (roles "base source" and "change source"), build slice 2. Parent: #1638. Required work: 1. Move the merge-base query to a public `Project::BaseBranch#merge_base`. It rescues `OsGit::Error` (including the detached-primary error from `name`) and raises `HarnessError`, as `BaseCheckout#within` does today. `Gate::UnchangedCheck::BaseCheckout` asks `Project::BaseBranch` for it — check: absent `def merge_base` in `plugins/os-sdlc/lib/os_sdlc/gate/unchanged_check/base_checkout.rb` 2. `FastGate::ChangedFiles` asks `Project::BaseBranch#merge_base` instead of its own private `merge_base`, so one object owns the base commit — check: absent `def merge_base` in `plugins/os-sdlc/lib/os_sdlc/fast_gate/changed_files.rb` 3. Add `Project::Changes#since(ref)`. It returns a new `Project::Changes` whose path lists come from `OsGit::Repository#changes(against: ref, renames: false)`: committed, staged, unstaged, and untracked files, without deleted files. Pass the ref through a keyword with a default (`against: :index`), so the existing two-argument callers do not change. `code_diff` and `test_diff` still compare against HEAD; `since` changes the path lists only — check: present `def since` in `plugins/os-sdlc/lib/os_sdlc/project/changes.rb` ## Acceptance criteria 1. Given a branch with commits after the base branch, when `Project::BaseBranch#merge_base` is asked, then it returns the merge-base commit of HEAD and the base branch. 2. Given a base branch that does not exist, when `Project::BaseBranch#merge_base` is asked, then it raises `HarnessError` as `BaseCheckout` does today. 3. Given one committed, one staged, one unstaged, and one untracked code file since the merge base, when `Project::Changes#since(merge_base)` is asked for `code_paths`, then all four files are listed. 4. Given those changes, when `test_paths` is asked, then it lists the changed test files and the existing test files mapped to the changed code files. 5. Given a file changed outside the target path, when `code_paths` is asked, then that file is not listed. 6. Given a file deleted since the merge base, when `code_paths` is asked on `since(merge_base)`, then that file is not listed. 7. Given the `unchanged` gate and the fast gate, when they run, then their behavior is the same as before. ## Owner sketch ```ruby class Project::BaseBranch def name def merge_base end class Project::Changes def since(ref) def code_paths def test_paths end ``` ## Origin - Trigger: domain-object design `docs/system-design-format/proof/2026-11-08-os-sdlc-mutation-survivors.md` (commit f0e172b7) for parent #1638 - Improvised this session: none - Chain: survivor gate needs one owner per fact ← design section 3 ← DESIGN (`docs/system-design-format/proof/2026-11-08-os-sdlc-mutation-survivors.md`) - Root candidate: none found; searched mutation, mutineer, mutant, survivor, merge_base, changes since - Where: lib/os_sdlc/project/base_branch.rb, lib/os_sdlc/project/changes.rb - Session: ff703e34-8046-4b24-af68-6ef76d8aa338 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os-plugins-os-sdlc/ff703e34-8046-4b24-af68-6ef76d8aa338.jsonl VERDICT: CREATE
Author
Owner

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

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

Resolution

Done: Project::BaseBranch#merge_base is the one public owner of the base commit (HarnessError on any git failure); Gate::UnchangedCheck::BaseCheckout and FastGate::ChangedFiles ask it. Project::Changes#since(ref) returns a Changes whose paths are committed, staged, unstaged, and untracked changes since ref, without deleted files.

Evidence: Commit d457c181 on ticket-1639 via implementation 263 (8 dispatches, all pass; suite gate green in 54.8 s). New tests: tests/project/base_branch_merge_base_test.rb, tests/project/changes_since_test.rb.

Follow-ups: none; #1640 (part B) is now unblocked

## Resolution **Done:** Project::BaseBranch#merge_base is the one public owner of the base commit (HarnessError on any git failure); Gate::UnchangedCheck::BaseCheckout and FastGate::ChangedFiles ask it. Project::Changes#since(ref) returns a Changes whose paths are committed, staged, unstaged, and untracked changes since ref, without deleted files. **Evidence:** Commit d457c181 on ticket-1639 via implementation 263 (8 dispatches, all pass; suite gate green in 54.8 s). New tests: tests/project/base_branch_merge_base_test.rb, tests/project/changes_since_test.rb. **Follow-ups:** none; #1640 (part B) is now unblocked <!-- os-backlog kind: resolution -->
jared closed this issue 2026-11-08 16:09:20 +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#1639
No description provided.