Skip to content

laptop-repo-keeper: BUSY check never sees a merge/rebase in progress — rev-parse --git-dir is relative, so the keeper sealed a live merge as a 'snapshot' commit #1771

Closed

Description

@dezren39

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" ]; then

git -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 subject main
  • Keeper output: - merging: committed 1 tracked entries on main / - merging: ok (main, worktrees: 1) — no BUSY line
  • After: MERGE_HEAD gone; HEAD is repo-keeper: snapshot merging 2026-10-28 13:39 CDT with two parents (48ee2fd… + 74a4eeb…) — the keeper's git 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_HEAD present is reported BUSY (lock/merge/rebase in progress) — skipped; its HEAD, index and MERGE_HEAD are byte-identical after the run.
  • Same for a repo with .git/rebase-merge (or rebase-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

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    source/creatorFiled by the new-issue-creator job
    priority/highImportant and time-sensitive
    area/nixosNixOS modules and machine configuration
    on Oct 28, 2026
  2. developing-today commented on Oct 28, 2026

    @developing-today
    Contributor
  3. added
    work/self-assignedAn agent grabbed this item directly, outside the queue
    effort/xsTrivial: expected under 30 minutes of work
    on Oct 28, 2026
  4. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed + self-assigned (guarded self-solve)

    • Agent: new-issue-creator job (new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a run 2026-10-28 13:35 CDT
    • Found via: filed this run
    • Task: Filed after verifying on main ba51bec that laptop-repo-keeper.sh:64-65 tests $gd/MERGE_HEAD / $gd/rebase-* where gd is the relative output of git 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 …".
    • Expectation: effort/xs — one-line fix (--absolute-git-dir) plus probe re-run showing BUSY + untouched HEAD/MERGE_HEAD. Self-solving this run on branch creator-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.
    • Links: probe ~/tmp/probe-2026-10-28-laneA-keeper-mergehead/ (laptop, keeper2.out + sealed merge in home/git/merging)
  5. added a commit that references this issue on Oct 28, 2026
  6. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done (self-solved, merged)

    • Agent: new-issue-creator job (new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a run 2026-10-28 13:35 CDT
    • Task: Fixed in PR fix(keeper): BUSY check uses absolute git dir — keeper sealed a live merge as a snapshot (#1771) #1772 (branch 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-dir in laptop-repo-keeper.sh:64.
    • Verification: laptop probe re-run against the fixed script, 7/7 assertions pass — mid-merge repo: 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 -n clean. (Pre-fix probe for contrast is in the issue body: the keeper sealed the merge as a two-parent snapshot commit.)
    • CI note: PR checks (flake eval / treefmt / run) were still pending after ~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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area/nixosNixOS modules and machine configurationeffort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/highImportant and time-sensitivesource/creatorFiled by the new-issue-creator jobstatus:donetask/small-fixQueue packet kind: bounded fix with reproduction and targeted testswork/self-assignedAn agent grabbed this item directly, outside the queue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions