Twinned from https://github.com/cfsh/ninefold/issues/818 by tools/beads/import_github.py's reconcile pass.
Filed by the CTO after a live incident on 2026-08-10.
nf-dev-merge-reset-guard, verifying a reviewer finding on #810, ran git checkout main in /root/nf/ninefold — the shared base clone every devs/<topic> worktree on the box hangs off — to construct the specific scenario its own fix needs to handle (main checked out somewhere other than where you're syncing from). That succeeded and left the shared clone on branch main instead of detached. Its own attempt to self-correct was refused by its permission classifier (checking out/detaching is exactly the kind of shared-state git operation that should get a second look), so it escalated rather than guessing. The CTO restored it by hand (git checkout --detach, same commit) in the same session.
Why this mattered beyond tidiness: a git worktree can only have a given branch checked out in *one* place across the whole family sharing one .git. With main checked out in the shared base clone, git fetch origin +main:main — the exact mechanism #810 ships, for every session's post-merge sync — would have been refused for everyone else on the box: fatal: 'main' is already used by worktree at /root/nf/ninefold. The shared clone was, for a window, blocking the fix in flight.
/root/nf/ninefold does two jobs at once today, and only one of them is load-bearing:
devs/<topic> worktree attaches to via git worktree add — this is the entire reason it exists, so a new chunk worktree doesn't redownload the repo's full history. spawn_dev.py fetches into it (git -C ~/nf/ninefold fetch origin --prune) before every spawn, and because worktrees share objects/refs by construction, *any* worktree's fetch (including a plain dev worktree's, including merge_pr.py's new sync_main()) updates this same shared store regardless of which worktree ran it. There is no separate "keep the base clone synced" step needed beyond what already happens.A bare repo provides job 1 with none of job 2's hazard: git worktree add off a bare repo shares objects/refs identically, but a bare repo has no working tree or HEAD to occupy a branch with — this class of mistake becomes structurally impossible rather than a rule to remember.
The CTO currently runs cd /root/nf/ninefold && ./presubmit.py --suite python (or --all) directly against the shared clone to verify main is green before every spawn. That needs an actual checked-out working tree, which a bare repo doesn't have. Going bare needs a dedicated, permanent, read-only worktree alongside it (naming your call — something like ~/nf/ninefold-ro) that:
origin/main (refresh via git fetch && git reset --hard origin/main before each use, or equivalent),devs/*-shaped worktrees as disposable chunk work — it isn't one, don't let a reap sweep it.A — land immediately, safe, no coordination needed, normal PR:
One guardrail line in notes/merging.md (or wherever this tier's git conventions live): never git checkout/switch branches in the shared base clone; create a scratch worktree for any test that needs "a branch checked out somewhere else," and remove it after. Prevents a repeat regardless of when the rest of this lands.
B — the actual bare-repo migration. ⚠️ NOT a normal spawned-dev chunk.
A session claiming this issue and working it from its own devs/<topic> worktree would be restructuring the ground its own worktree stands on — that worktree hangs off the exact clone being converted. This piece has to be executed directly by the CTO (or a --standing-slot session created specifically for it, holding no other chunk), at a moment when it's safe to:
devs/* chunk worktrees are disposable by construction (CLAUDE.md) and don't block this on their own — any live ones just need to have pushed and finished first, the normal reap bar.argo and bork are Aedan-directed standing slots, not disposable chunk worktrees — they also hang off /root/nf/ninefold today and would need to be recreated against the new bare store to keep their live branch state. Scheduling that is not the CTO's call to make unilaterally; it needs Aedan's go-ahead on timing for those two specifically.spawn_dev.py's existing commands (git -C ~/nf/ninefold fetch origin --prune, git worktree add -b feat/<topic> ... origin/main) still behave identically against the bare store before trusting it — they should, since bare-ness doesn't change worktree/fetch semantics, but prove it rather than assume it.C — runbooks/provision-a-box.md in cfsh/nf-office, not this repo. So every *future* box gets a bare shared clone from first provisioning, decoupled from migrating this one. Office-repo prose, the CTO's own document — not a cfsh/ninefold PR, mentioned here only so whoever picks up B knows C is the CTO's to do in parallel, not part of this issue's own deliverable.
Re-litigating whether the board (#398) needs a different datastore — separate conversation, not this issue.
None.
No comments.