How Branch Synchronization Handles Recovered Pipeline Custody in no-mistakes
Branch synchronization in kunchenguid/no-mistakes returns custody of a pipeline-owned branch to the user by verifying terminal run status, anchoring preserved commits through a private gate repository, and applying either a strict fast-forward or atomic keep-local swap.
Branch synchronization is the safety mechanism in the kunchenguid/no-mistakes repository that manages how control of a branch transfers between the user and the pipeline. When a pipeline run terminates without publishing its head, the branch remains locked in the run's custody; the recovery process safely returns ownership while preserving commit integrity.
Understanding Pipeline Custody States
A branch enters StatePipelineOwned when a run finishes—whether completed, failed, or cancelled—without publishing its pipeline head. In this state, the branch is considered "stranded" and requires explicit recovery to return custody to the user. The internal/branchsync package implements this logic, with the primary entry point being Service.Recover in internal/branchsync/sync.go.
The Recovery Workflow (Service.Recover)
The recovery process follows a strict sequence of validations and safety checks to ensure data integrity.
State Inspection and Early Exit
The inspect function loads the work-tree, retrieves the last run for the branch, and determines if the branch is pipeline_owned. If the run already has CustodyReturnedAt set, Service.Recover marks Recovered as true and exits early without modifying state.
// Early return path in sync.go
if run.CustodyReturnedAt != nil {
return State{Recovered: true}, nil
}
Terminal Run Validation
Recovery is permitted only for terminal runs. The terminalRunStatus helper blocks attempts to recover active runs, ensuring that in-progress pipelines cannot have their custody forcibly removed.
Fast-Path for Reachable Commits
If the preserved commit (run.HeadSHA) is already present locally and the local HEAD is equal to or an ancestor of it, anchorReachablePreserved anchors these commits locally and recovery completes immediately. This avoids unnecessary network operations when the work-tree already contains the required history.
Gate Verification for Missing Commits
When the preserved commit is not reachable locally, the system consults the gate repository (the private GateDir). The gate's branch head is read via git rev-parse. If the gate head diverges from the preserved commit, recovery is blocked to prevent data loss.
Anchoring Preserved Commits
If the gate head matches the preserved commit (or a previous keep-local recovery anchored it), the system fetches the commits into a private ref at refs/no-mistakes/recover/<runID>. This ensures the commits become reachable locally before any branch modifications occur.
The Recovery Decision Matrix
The relationship between the work-tree and the preserved head determines the recovery path, as documented in the comment block of the Recover function:
- Behind + Clean: Fast-forward recovery
- Behind + Dirty: Blocked (unless
--keep-local) - Diverged: Blocked (unless
--keep-local)
Fast-Forward Recovery
When the local branch is behind the preserved head and the work-tree is clean, recoverFastForward executes a strict fast-forward merge (git merge --ff-only). This is the default behavior and the safest path when no local work exists.
# Default fast-forward recovery
no-mistakes axi sync --recover
Keep-Local Recovery
The recoverKeepLocal path—triggered by the --keep-local flag—never mutates the work-tree. Instead, it performs an atomic compare-and-swap of the gate branch to the local HEAD and stages the local head into the gate via fetch. This path is essential when the branch has diverged or when preserving local work takes priority.
# Preserve local work and only move the gate
no-mistakes axi sync --recover --keep-local
Finalizing Custody Return
After successful fast-forward or keep-local operations, finishRecover calls SetRunCustodyReturned to persist the custody-return timestamp. The returned state contains Recovered: true and Changed: true, indicating the branch now behaves as a normal, un-owned branch.
Safety Mechanisms and Gate Protection
The gate repository acts as a safety buffer between the local work-tree and the remote pipeline state. Before allowing any recovery that requires fetching missing commits, the system verifies that the gate head matches the expected run.HeadSHA. This prevents scenarios where the pipeline state has changed unexpectedly since the run completed, protecting against race conditions and potential history rewrites.
Practical Usage Examples
Inspect the current branch status without network operations:
no-mistakes axi sync --check
Programmatic access from Go components:
ctx := context.Background()
svc, close, _ := branchsync.OpenCurrent()
defer close()
state := svc.Recover(ctx, false) // false == no --keep-local
fmt.Printf("recovered=%v, changed=%v, err=%s\n",
state.Recovered, state.Changed, state.Error)
Key Implementation Files
| File | Description |
|---|---|
internal/branchsync/sync.go |
Full implementation of synchronization, inspection, refresh, apply, and recovery logic including Service.Recover, recoverFastForward, and recoverKeepLocal. |
internal/branchsync/recover_test.go |
Unit tests covering fast-forward, keep-local, and blocked recovery scenarios. |
internal/branchsync/sync_test.go |
General tests for state classification, relation detection, and safety flags. |
Summary
- Branch synchronization protects branch integrity by maintaining a clear custody model between users and pipeline runs.
- Recovery requires terminal status: Only completed, failed, or cancelled runs can be recovered; active runs are blocked.
- Dual-path recovery: The system prefers fast-forward merges when safe, but offers
--keep-localfor diverged or dirty work-trees. - Gate verification: The private gate repository ensures preserved commits match expectations before local anchoring occurs.
- Atomic operations: Keep-local recovery uses compare-and-swap semantics to prevent race conditions when updating remote state.
Frequently Asked Questions
What happens if I try to recover a pipeline run that is still active?
Recovery is blocked. The terminalRunStatus validation in internal/branchsync/sync.go explicitly prevents recovery of non-terminal runs. This ensures that active pipelines cannot have their custody interrupted, maintaining the integrity of ongoing operations.
What is the difference between fast-forward and keep-local recovery?
Fast-forward (recoverFastForward) updates the local branch head to match the preserved pipeline commits when the local branch is behind and the work-tree is clean. Keep-local (recoverKeepLocal) preserves the current local HEAD and work-tree entirely, instead atomically swapping the gate repository's branch pointer to the local HEAD. Use keep-local when you have uncommitted changes or when the branch has diverged from the pipeline history.
Where are the preserved commits stored during the recovery process?
Preserved commits are initially recorded as run.HeadSHA in the run metadata. During recovery, if not already reachable locally, they are fetched into a private Git reference at refs/no-mistakes/recover/<runID>. This anchoring ensures the commits remain reachable even if the recovery operation is interrupted or fails partway through.
How does the gate repository prevent data loss during recovery?
The gate repository serves as a consistency checkpoint. Before fetching missing commits or updating branch pointers, the system verifies that the gate's current head matches the expected run.HeadSHA. If the gate has diverged—indicating the pipeline state changed unexpectedly since the run completed—the recovery is blocked, preventing the local branch from being reset to an outdated or conflicting state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →