worktree finish refuses a committed linked entry; start excludes linked entries from git #789

Closed
opened 2026-09-28 11:58:04 +00:00 by jared · 1 comment
Owner

Problem

A worktree carries .cc-os as a symlink to the owning checkout's .cc-os directory. .gitignore listed .cc-os/, a directory-only rule, so the symlink was not ignored inside a worktree. Pipeline commits made with git add -A included the symlink. After the fast-forward to main, .cc-os in the main checkout became a symlink to itself, and every os-backlog command failed until the directory was rebuilt by hand. The same exposure exists for .sdlc/pipeline.db and every extra path listed under worktree-symlinks.

Observed

Commits 50947d21 (2026-09-26) and 1bfea020 (2026-09-27, the #786 CRC commit) each added .cc-os as a symlink with target /home/jared/dev/cc-os/.cc-os.

/home/jared/.claude/plugins/os-backlog/bin/os-backlog close 769 --done ... --evidence ... --follow-ups none
os-backlog: close: no valid tracker configured for /home/jared/dev/cc-os — run /os-backlog:route first
ls -la .cc-os
lrwxrwxrwx. 1 jared jared 28 Sep 28 07:27 .cc-os -> /home/jared/dev/cc-os/.cc-os

Reproduce

plugins/os/bin/worktree start demo && cd .claude/worktrees/demo && git add -A && git status --short | grep cc-os

With the old .cc-os/ ignore rule the symlink shows as staged. The 2026-09-28 change to .cc-os (no slash) stops this one path but not the other linked entries.

Approach

Required work:

  1. Worktree::FinishCommand refuses to merge when git diff --name-only <base>..<branch> contains any LinkedEntries path, and prints the offending paths — check: ruby plugins/os/tests/all.rb
  2. Worktree::StartCommand appends each linked entry to the worktree's .git/info/exclude when it creates the symlink — check: ruby plugins/os/tests/all.rb

Rejected: rely on .gitignore alone — the extras list in worktree-symlinks is per-machine config and cannot be covered by a committed ignore file.

Acceptance criteria

  1. Given a worktree whose branch commits a LinkedEntries path, when the user runs worktree finish, then finish exits nonzero, names the path, and main is unchanged.
  2. Given a fresh worktree, when git add -A runs inside it, then no linked entry is staged.

Owner sketch

class Worktree::FinishCommand
  def refuse_linked_entries_in_diff
end

class Worktree::StartCommand
  def exclude_linked_entries
end

Origin

  • Trigger: os-backlog close 769 ... during the 2026-09-28 diagnosis of the junk "T" tickets
  • Improvised this session: removed the self-symlink, recreated .cc-os/config from ~/.cc-os/projects.json, ran git rm --cached .cc-os, changed the .gitignore rule to .cc-os
  • Chain: tracker commands fail ← .cc-os is a self-symlink on main ← fast-forward merged a commit that added the worktree symlink ← .gitignore rule .cc-os/ matches directories only ← DESIGN (plugins/os/lib/worktree/start_command.rb symlink_owning_state, .gitignore line 26)
  • Root candidate: this ticket
  • Where: plugins/os/lib/worktree/start_command.rb, plugins/os/lib/worktree/linked_entries.rb
  • Session: 663377bc-4e1b-457f-bb56-7a415ac6e14f
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/663377bc-4e1b-457f-bb56-7a415ac6e14f.jsonl

VERDICT: CREATE

## Problem A worktree carries `.cc-os` as a symlink to the owning checkout's `.cc-os` directory. `.gitignore` listed `.cc-os/`, a directory-only rule, so the symlink was not ignored inside a worktree. Pipeline commits made with `git add -A` included the symlink. After the fast-forward to main, `.cc-os` in the main checkout became a symlink to itself, and every `os-backlog` command failed until the directory was rebuilt by hand. The same exposure exists for `.sdlc/pipeline.db` and every extra path listed under `worktree-symlinks`. ## Observed Commits 50947d21 (2026-09-26) and 1bfea020 (2026-09-27, the #786 CRC commit) each added `.cc-os` as a symlink with target `/home/jared/dev/cc-os/.cc-os`. ``` /home/jared/.claude/plugins/os-backlog/bin/os-backlog close 769 --done ... --evidence ... --follow-ups none ``` ``` os-backlog: close: no valid tracker configured for /home/jared/dev/cc-os — run /os-backlog:route first ``` ``` ls -la .cc-os lrwxrwxrwx. 1 jared jared 28 Sep 28 07:27 .cc-os -> /home/jared/dev/cc-os/.cc-os ``` ## Reproduce ``` plugins/os/bin/worktree start demo && cd .claude/worktrees/demo && git add -A && git status --short | grep cc-os ``` With the old `.cc-os/` ignore rule the symlink shows as staged. The 2026-09-28 change to `.cc-os` (no slash) stops this one path but not the other linked entries. ## Approach Required work: 1. `Worktree::FinishCommand` refuses to merge when `git diff --name-only <base>..<branch>` contains any `LinkedEntries` path, and prints the offending paths — check: `ruby plugins/os/tests/all.rb` 2. `Worktree::StartCommand` appends each linked entry to the worktree's `.git/info/exclude` when it creates the symlink — check: `ruby plugins/os/tests/all.rb` Rejected: rely on `.gitignore` alone — the extras list in `worktree-symlinks` is per-machine config and cannot be covered by a committed ignore file. ## Acceptance criteria 1. Given a worktree whose branch commits a `LinkedEntries` path, when the user runs worktree finish, then finish exits nonzero, names the path, and main is unchanged. 2. Given a fresh worktree, when `git add -A` runs inside it, then no linked entry is staged. ## Owner sketch ```ruby class Worktree::FinishCommand def refuse_linked_entries_in_diff end class Worktree::StartCommand def exclude_linked_entries end ``` ## Origin - Trigger: `os-backlog close 769 ...` during the 2026-09-28 diagnosis of the junk "T" tickets - Improvised this session: removed the self-symlink, recreated `.cc-os/config` from `~/.cc-os/projects.json`, ran `git rm --cached .cc-os`, changed the `.gitignore` rule to `.cc-os` - Chain: tracker commands fail ← `.cc-os` is a self-symlink on main ← fast-forward merged a commit that added the worktree symlink ← `.gitignore` rule `.cc-os/` matches directories only ← DESIGN (plugins/os/lib/worktree/start_command.rb `symlink_owning_state`, .gitignore line 26) - Root candidate: this ticket - Where: plugins/os/lib/worktree/start_command.rb, plugins/os/lib/worktree/linked_entries.rb - Session: 663377bc-4e1b-457f-bb56-7a415ac6e14f - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/663377bc-4e1b-457f-bb56-7a415ac6e14f.jsonl VERDICT: CREATE
Author
Owner

Resolution

Done: start writes linked entries to the shared info/exclude; finish refuses with exit 5 when the branch diff touches one

Evidence: commit ab25f09e merged 9f272c2f; three red tests in plugins/os/tests/worktree/; os suite 30 runs 0 failures; note: Required work 2 named the per-worktree exclude, which git ignores, so the shared common-dir exclude was used

Follow-ups: none

## Resolution **Done:** start writes linked entries to the shared info/exclude; finish refuses with exit 5 when the branch diff touches one **Evidence:** commit ab25f09e merged 9f272c2f; three red tests in plugins/os/tests/worktree/; os suite 30 runs 0 failures; note: Required work 2 named the per-worktree exclude, which git ignores, so the shared common-dir exclude was used **Follow-ups:** none
jared closed this issue 2026-09-28 16:02:52 +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#789
No description provided.