# How Branch Synchronization Handles Recovered Pipeline Custody in no-mistakes

> Learn how branch synchronization in kunchenguid/no-mistakes recovers pipeline custody by verifying run status, anchoring commits, and using fast-forward or atomic swaps. Ensure your pipeline branch is safe.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: internals
- Published: 2026-07-17

---

**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`](https://github.com/kunchenguid/no-mistakes/blob/main/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.

```go
// 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.

```bash

# 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.

```bash

# 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:

```bash
no-mistakes axi sync --check

```

Programmatic access from Go components:

```go
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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/branchsync/recover_test.go) | Unit tests covering fast-forward, keep-local, and blocked recovery scenarios. |
| [`internal/branchsync/sync_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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-local` for 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`](https://github.com/kunchenguid/no-mistakes/blob/main/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.