Repository navigation
Description
Bug: laptop-repo-keeper's BUSY check never sees a merge/rebase in progress — it sealed a live merge as a "snapshot" commit
nixos/home/laptop-repo-keeper.sh:64-65 (current main, ba51bec):
gd="$(g rev-parse --git-dir 2>/dev/null)"
if [ -e "$d/.git/index.lock" ] || [ -e "$gd/MERGE_HEAD" ] || [ -d "$gd/rebase-merge" ] || [ -d "$gd/rebase-apply" ]; thengit -C <repo> rev-parse --git-dir prints a relative path (.git), relative to the target repo — but the script tests $gd/MERGE_HEAD relative to the keeper's own cwd. The systemd user unit (nixos/home/laptop-repo-keeper.nix) sets no WorkingDirectory, so the keeper runs with cwd $HOME, where $HOME/.git/MERGE_HEAD essentially never exists. The merge/rebase half of the BUSY guard therefore never fires (the index.lock half works because it is correctly prefixed with $d/). Symmetrically, if the keeper were ever launched from inside a merging repo, it would skip every repo as BUSY.
Verified reproduction (laptop probe, 2026-10-28)
Probe dir on the laptop: ~/tmp/probe-2026-10-28-laneA-keeper-mergehead/ — a scratch repo put mid-merge (conflict resolved and staged, MERGE_HEAD present, git status = M f.txt, HEAD = the branch tip), then the keeper run against a fake HOME containing only that repo:
- Before:
MERGE_HEAD=yes, HEAD subjectmain - Keeper output:
- merging: committed 1 tracked entries on main/- merging: ok (main, worktrees: 1)— no BUSY line - After:
MERGE_HEADgone; HEAD isrepo-keeper: snapshot merging 2026-10-28 13:39 CDTwith two parents (48ee2fd… + 74a4eeb…) — the keeper'sgit add -u && git commit(keeper script, commit block) completed and sealed the human's in-progress merge under a snapshot message. On the next hourly run it would also push that merge commit.
An unresolved-conflict merge is less lucky rather than safer: the same path reports COMMIT FAILED instead of BUSY — skipped, and a mid-rebase repo is not detected either (rebase-merge/rebase-apply use the same broken $gd).
Why it matters
The keeper runs hourly, unattended, against every primary clone on the laptop, and its documented contract is "a dirty primary … is reported, never forced" / BUSY repos are "skipped". Instead, the one state where an unattended git add -u && git commit is most destructive — a half-finished merge or rebase — is exactly the state its guard cannot see. It can finalize merges with a junk message, commit conflict resolutions the owner had not finished reviewing, and push the result.
Fix direction
Resolve the git dir absolutely, e.g. gd="$(g rev-parse --absolute-git-dir 2>/dev/null)" (or test $d/.git/… like the index.lock check, handling the .git-file/worktree case via the absolute path). Add a regression check: a fixture repo mid-merge (and one mid-rebase) must produce the BUSY … — skipped line and leave MERGE_HEAD and HEAD untouched.
Acceptance criteria
- A repo with
.git/MERGE_HEADpresent is reportedBUSY (lock/merge/rebase in progress) — skipped; its HEAD, index andMERGE_HEADare byte-identical after the run. - Same for a repo with
.git/rebase-merge(orrebase-apply) present. - A healthy dirty repo is still snapshotted and a clean repo still reports
ok(the existing behaviour the probe's control cases rely on). - The fix works regardless of the keeper's cwd (test with cwd = another merging repo: healthy repos must NOT be skipped).
Found by new-issue-creator-code-a, 2026-10-28 run. Probe preserved on the laptop at ~/tmp/probe-2026-10-28-laneA-keeper-mergehead/ (keeper2.out + the sealed merge commit in home/git/merging).
Activity
developing-today commentedon Oct 28, 2026
CODE-719
dezren39 commentedon Oct 28, 2026
🤖 Agent note · filed + self-assigned (guarded self-solve)
laptop-repo-keeper.sh:64-65tests$gd/MERGE_HEAD/$gd/rebase-*wheregdis the relative output ofgit rev-parse --git-dir, resolved against the keeper's cwd (the systemd user unit sets no WorkingDirectory, so $HOME) — the BUSY guard never fires for merges/rebases. Laptop probe (scratch repo mid-merge, conflict resolved+staged, fake HOME): keeper printed "committed 1 tracked entries on main" instead of BUSY, MERGE_HEAD disappeared, and HEAD became a two-parent merge commit titled "repo-keeper: snapshot merging …".--absolute-git-dir) plus probe re-run showing BUSY + untouched HEAD/MERGE_HEAD. Self-solving this run on branchcreator-a/1771-keeper-mergehead-guard(laptop worktree); if the PR cannot be merged verified this run, the issue returns to queue/queued with a packet.~/tmp/probe-2026-10-28-laneA-keeper-mergehead/(laptop, keeper2.out + sealed merge in home/git/merging)Merge pull request #1772 from developing-today/creator-a/1771-keeper-…
dezren39 commentedon Oct 28, 2026
🤖 Agent note · done (self-solved, merged)
creator-a/1771-keeper-mergehead-guard, laptop worktree~/git/fleet/1771-keeper-mergehead, commit 81fb4dd): one line —rev-parse --git-dir→rev-parse --absolute-git-dirinlaptop-repo-keeper.sh:64.BUSY … — skipped, MERGE_HEAD + HEAD untouched; mid-rebase repo: BUSY, untouched; healthy dirty repo: still snapshotted; healthy repo NOT skipped even with the keeper's cwd inside a merging repo.bash -nclean. (Pre-fix probe for contrast is in the issue body: the keeper sealed the merge as a two-parent snapshot commit.)pendingafter ~5 min — the stuck-runner condition already tracked as ci: Actions check runs stuck in queued across PRs — no runner pickup since ~05:00Z 2026-10-28 #1599, not a failure of this diff. Merged under the standing directive (change verified sound by probe) as merge commit 95a5c4a; verified the fix line is on origin/main afterwards. Issue closed by the PR's Fixes marker.