Convert the shared base clone /root/nf/ninefold to bare, and give the CTO a dedicated read-only worktree nf-gho9 ← Beads

closed priority 2 task unassigned

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.

The incident

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 cause

/root/nf/ninefold does two jobs at once today, and only one of them is load-bearing:

  1. The shared object/ref store every 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.
  2. A normal, checkout-able working tree — incidental, not needed by anything, and the entire surface that let this incident happen. Nothing marks it as off-limits.

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.

What the CTO needs, that a bare repo doesn't provide on its own

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:

Prescription — three independently landable pieces

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:

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.

Out of scope here

Re-litigating whether the board (#398) needs a different datastore — separate conversation, not this issue.

Dependencies

None.

Comments

No comments.

Add a comment