Proposal (rationale)
WI-075 closes the reproduced integration-branch corruption, but land also moves the configured
green marker and creates, overwrites or deletes a parked integ/... branch through raw
update-ref. Both auxiliary refs are ordinary refs/heads names and can be checked out. Exact
disposable probes reproduced the same split state: HEAD moved to the verified merge while the
holder’s index and files stayed at the old commit, so Git reported a staged reversion. The parked
case is more than hypothetical because a refusal explicitly tells the operator to recover and fix
the merge from that ref.
The successful path then deletes the deterministic parked ref without checking whether a worktree holds it. That contradicts ADR-0009’s “never destroy, only refuse” rule and can leave a linked worktree attached to a missing branch. Fixing only the integration ref would therefore make the most visible F-048 path safe while leaving the same mutation primitive unsafe one line later.
Decision
accepted — 2026-09-05, during the user-authorised Arena Lab bootstrap. Treat every branch ref
that land can mutate as operator-visible state. A checked-out integration or green ref is a
refusal; a parked merge is written only to an unheld, direct ref without overwriting another
recoverable merge; and Rungs never deletes a parked branch. Advance the integration and green refs
in one compare-and-swap transaction so neither can move without the other.
Implementation boundary — 2026-09-05, retained after independent review. “Concurrent change” in
the transaction guarantee means an OID change caught by expected-old CAS or an identity/holder
change visible to the final validation. A raw process can still replace a direct ref with a
same-OID symbolic ref after that validation but before Git takes its locks, or create a dangling
symbolic recovery candidate after allocator validation but before its create-only write. Git’s
public update-ref transaction cannot CAS direct-versus-symbolic type; supported Git versions
disagree on whether create-only treats a dangling symref as absent and replaces its name or refuses
it as occupied. no-deref protects the target in either case. These measured micro-races are the
explicit raw-Git exception below, not silently claimed as closed.
Plan
Requirements
- Resolve integration, green and existing parked branch names as exact, direct stored refs; reject case aliases, symbolic refs and any identity that cannot be enumerated safely.
- Refuse before mutation when an existing integration or green ref is checked out, and revalidate both identities and holder sets after arbitrary gate code runs.
- Advance integration and green atomically with expected-old values. A concurrent OID change to either ref must prevent Rungs from partially writing the pair and park the verified merge; identity/holder changes visible at the final validation must do the same.
- Never overwrite or delete a checked-out parked ref, and never discard an existing parked merge. Choose and report an unheld collision-free recovery ref when the preferred name is occupied.
- Use no-dereference ref writes so a symbolic-ref swap cannot redirect a checked operation into a different branch between validation and mutation.
- Preserve WI-075’s clean/dirty/late-holder behavior and ADR-0009’s detached scratch worktree.
Impacts
src/concurrency.tsref discovery, recovery-ref allocation and atomic update transaction;test/core.test.jsadversarial worktree/ref interleavings.- The concurrency guide and command output: a parked merge may receive a collision suffix, and cleanup is explicitly operator-owned rather than an automatic ref deletion.
- ADR-0009 is clarified only if necessary; its existing never-destroy rule already selects the behavior. No push, rebase, worktree removal or automatic operator checkout is introduced.
Approach
Generalize WI-075’s canonical direct-ref/holder state helper to distinguish a required existing ref
from a creatable marker. Capture both integration and green old values before verification, then
repeat identity and holder checks at the final mutation boundary. Use one git update-ref --stdin
transaction with compare-and-swap expectations and no dereferencing for the integration and green
advance; on any verification or transaction failure, Rungs writes neither ref. The measured
same-OID ref-type race in the recorded implementation boundary remains outside that guarantee.
Replace deterministic parked-ref overwrites with a recovery allocator. Reuse the preferred parked
ref only when it already names the same merge and is safe; otherwise create an unheld direct ref
whose name includes the verified merge identity, using create-only semantics. Never delete a parked
ref after success. worktrees already reports merged branches so the operator can decide when a
recovery ref is no longer needed.
Acceptance criteria / tests
- With
green/mainchecked out in another worktree,landrefuses without running gates or changing integration, green, parked refs, holderHEAD, index, status or bytes. - If the green ref becomes checked out, symbolic or concurrently advanced while gates run, the final transaction refuses, both integration and green remain at their captured values, and the verified merge is recoverable from a newly reported unheld ref.
- An existing checked-out parked ref is never advanced or deleted. Introduced-failure and CAS
paths preserve its
HEAD, index, status and bytes and allocate a distinct recovery ref. - An existing unheld parked ref naming different work is not overwritten; repeated parking of the same verified merge is idempotent and does not create unbounded duplicate refs.
- Successful landing moves integration and green together or neither, retains prior recovery refs, and keeps the current attribution, conflict, locking and detached-worktree behavior.
- Focused tests, full
npm test, package dry-run,git diff --checkand all registered Rungs gates pass. The exact pushed SHA passes all six OS/Node matrix cells and the site job.
Out of scope
- Preventing an uncooperative raw Git process from beginning a checkout, swapping a direct ref for
a same-OID symbolic ref, or creating a dangling symbolic recovery candidate in the unavoidable
intervals between final validation and Git’s locks. Git’s public expected-old/create protocol
compares OIDs or absence, not ref type, and Git versions disagree on dangling-symref absence;
no-derefprotects the symbolic target but cannot assert that the named ref stayed direct. The early/late checks and Rungs land lock remain the cooperative repository contract. - Automatically switching, resetting, removing or deleting an operator’s worktree or branch; those are deliberately operator-owned under ADR-0009.
- WI-073 path containment, WI-076 archive containment, WI-077 ejection, WI-078 exemptions, F-034 line-ending measurement, publication or Arena adoption.
Execution
Started from exact green main commit 65932f3 on
feature/WI-079-land-managed-ref-safety, after WI-075 landed. The red-first managed-ref matrix
produced 15 expected failures: green holders and aliases were ignored, parked refs were overwritten
or deleted, and a blocked green update occurred after integration had already advanced. The first
implementation made that matrix green and the pre-integration full suite passed 93 tests with one
platform-specific skip.
Integrated exact green main commit 9d362f8 after the local repair stabilized. The backlog merge
kept WI-073 archived from main and WI-079 in progress here. Independent diagnostic review then
found three real gaps, all reproduced before correction: dangling symrefs omitted by
for-each-ref (including the reftable backend), malformed loose refs being trusted as OIDs, and a
last-resort scratch retained at the reset base rather than the verified merge. Exact regressions
now cover each correction. A GitHub-reviewed APFS advisory also disproved the inherited assumption
that NFD plus Unicode simple folding covers macOS storage aliases; the ref key now uses NFKD and
full locale-independent case conversion, with sharp-S and ligature regressions. The same limitation
in WI-073’s emitted-path comparator is recorded separately as release-blocking F-051
rather than widening this item. Review also measured the public-Git same-OID ref-type race and its
create-only dangling-symref analogue, both recorded in the Decision and Out of scope; closing them
would require a new cooperative transaction protocol, so the plan is narrowed explicitly rather
than overstating no-deref.
Pre-reconciliation verification passed locally. The broad land/ref-safety slice passed 44/44; full
npm test passed 111 tests with three platform skips (114 total); npm pack --dry-run included
110 files in a 354.8 kB package; and git diff --check was clean. The first integrated gate run
correctly rejected the stale generated site snapshot after concurrency moved from 1.3.0 to 1.4.0.
After the prescribed npm run claims regeneration, all 30 registered gates passed, and the
snapshot was regenerated once more from that green run so it records 30 pass / 0 fail. No land,
branch deletion or worktree removal is part of this item.
After exact candidate b6916c3 passed all seven jobs in Actions run 33970794192, integrated exact
green main commit 2358993 (main run 33970797719) as required. The two textual conflicts retained
both the WI-079 and WI-076 changelog entries, kept WI-072 and WI-076 archived from main, and kept
WI-079 in progress. On the reconciled tree, the focused land/ref-safety suite passed 44/44; full
npm test passed 121 tests with three platform skips (124 total); all 30 Rungs gates passed;
npm pack --dry-run included 111 files in a 367.7 kB package; and git diff --check was clean.
No land, branch deletion or worktree removal is part of this item.
Review
Independent review approved the first pushed live diff with no remaining implementable WI-079 blocker.
It independently reran full npm test (111 pass, three platform skips), the narrowed new regression
slice (15/15), and git diff --check, and verified the dangling files/reftable symrefs, malformed
loose refs, merged scratch preservation, NFKD/full-fold aliases, canonical reserved-name filtering,
atomic two-ref CAS, recovery preservation, and honest limitation language. Its only non-blocking
hardening note is that a future tri-state symbolic-ref probe could distinguish an ordinary
non-symbolic exit from a fatal Git error more precisely. The first pushed candidate, 497b633, is
superseded as final evidence: Actions run
33970295165 exposed Git-version-dependent dangling-symref behavior in the diagnostic test on every
matrix host, plus one redundant LF-only retained-file assertion on Windows. Production already
handled both create outcomes; the regression now proves the portable target-preservation invariant,
and the retained-worktree check compares its exact pre-refusal bytes and clean status. This CI
diagnostic repair awaits exact-tip re-review and exact-SHA CI evidence.
The next candidate, 3be07f6, is also superseded as final evidence. Actions run 33970613191 made
both Ubuntu cells and the site job green, but macOS and Windows showed that Git’s registered
worktree path can be a filesystem alias of the path Rungs reports. The retained-worktree regression
now compares realpath identities from NUL-delimited git worktree list --porcelain -z output,
preserving the registration invariant without assuming one textual path spelling. Production
behavior was unchanged. Independent review approved the corrected exact tip b6916c3 with no
remaining implementable blocker, and Actions run 33970794192 passed all six OS/Node cells plus the
site job. That candidate is now superseded only by the required merge of exact green main
2358993; final reconciled-tip review and CI evidence remain pending.
Exact reconciled candidate a0ad1c4 passed all seven jobs in Actions run 33971138069, but final
review then reproduced one more managed-holder gap before approval: raw branch-string equality
missed a dangling worktree on GREEN/main when green/main was creatable, and did the same for a
dangling INTEG/... recovery holder. On a case-insensitive filesystem, creating the colliding
spelling moved the holder’s resolved HEAD behind its unchanged index and files. The shared holder
checks now use the same Windows/APFS storage-collision relation as ref identity and allocation.
Exact green and parking regressions preserve the dangling holder snapshot and prove the colliding
spelling is not created. The a0ad1c4 CI run is therefore diagnostic evidence only; review and
exact-SHA CI must repeat on the corrected tip.
The same final audit found an older-Git compatibility error in the new stored-ref scanner. Git can
echo an unrecognized --show-ref-format option and exit zero, which the first implementation
mistook for an unsupported declared backend instead of its intended files-only fallback. Ref-format
output now has a direct unit seam: query failure and that exact legacy echo select files, the two
known backends remain accepted, and every genuinely unknown backend still fails closed.
On the combined corrected worktree, the two holder-alias regressions and the legacy-format parser
regression passed 3/3; full npm test passed 124 tests with three platform skips (127 total); all
30 registered Rungs gates passed; npm pack --dry-run included 111 files in a 368.7 kB package;
and git diff --check was clean. Independent review approved exact tip 58d6bd4 with no remaining
blocker, and Actions run 33972167916 passed all six OS/Node jobs plus the site job. The reviewed
implementation landed on main at 9b993503; F-051 remains a separate open release blocker.