# How the No-Mistakes Branch Sync Service Handles Recovery After a Cancelled Push

> Learn how the no-mistakes branch sync service restores your repository after a cancelled push. Recover local work or revert to a safe state effortlessly.

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

---

**When a push is cancelled, the no-mistakes branch sync service preserves the repository state in the database and surfaces a recovery interface that lets users either apply the local worktree or revert to the gate head.**

The **no-mistakes** repository provides a guarded synchronization layer that protects local worktrees from data loss during interrupted Git operations. The branch sync service, implemented primarily in [`internal/branchsync/sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/branchsync/sync.go), detects cancellation signals and implements a robust recovery workflow that preserves repository integrity through state persistence and user-driven resolution.

## The Recovery Workflow

The recovery process follows a strict sequence from detection to resolution, ensuring that cancelled pushes never leave the repository in an inconsistent state.

### Step 1: Guarded Sync Initialization

When a user initiates synchronization via the TUI or CLI, the service creates a **`State`** struct in [`internal/branchsync/sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/branchsync/sync.go). This structure records the current branch, its upstream remote, and any pending worktree changes. The **`StartSync`** logic persists this state to the database before attempting any network operations, creating a restore point that survives process termination.

### Step 2: Push Execution and Cancellation Detection

The sync routine calls **`pushBranch`** to execute the Git push. If the user cancels the operation (Ctrl-C) or the process receives a termination signal, the underlying `exec.CommandContext` is killed. The **`handlePushError`** function interprets the resulting error as a *cancellation* rather than a hard failure, triggering the recovery pathway instead of treating it as a terminal error.

### Step 3: State Preservation in the Database

Upon detecting cancellation, the service **does not discard** the partially-pushed worktree. Instead, it stores the current sync state in the database via [`internal/db/run.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/run.go). The **`runs.custody_returned_at`** field remains nil, marking the run as *parked*. This preservation mechanism ensures the daemon can later reconstruct the exact state of the repository at the moment of interruption, including the relationship between the local head and the gate head.

### Step 4: Recovery Interface and User Decision

When the daemon (managed in [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go)) detects a parked sync—either after a restart or during a status check—it renders a recovery interface via [`internal/tui/branch_sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/tui/branch_sync.go). The UI presents two options: press **u** to apply the local worktree (keep changes) or **esc** to revert to the gate head (discard changes). This interactive decision point prevents automatic data loss while giving the user explicit control over the outcome.

### Step 5: Finalizing Recovery

If the user selects **apply**, the service invokes **`applyRecovery`** in [`internal/branchsync/sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/branchsync/sync.go), which executes `git reset --hard <saved-head>` to align the gate with the local worktree. If the user selects **revert**, **`revertRecovery`** checks out the saved gate head, discarding local changes. Both paths conclude by clearing the sync state from the database, setting `runs.custody_returned_at`, and releasing the branch lock that prevented concurrent operations.

## Key Implementation Files

The recovery mechanism spans several packages, each responsible for a specific layer of the system:

| File | Responsibility |
|------|----------------|
| [`internal/branchsync/sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/branchsync/sync.go) | Core sync logic, **`State`** struct definition, **`StartSync`**, **`pushBranch`**, **`handlePushError`**, **`applyRecovery`**, and **`revertRecovery`** functions. |
| [`internal/branchsync/recover_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/branchsync/recover_test.go) | Test suite verifying the recovery flow after various cancellation scenarios. |
| [`internal/cli/sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/sync.go) | CLI command definition and flag parsing for `--recover`, `--apply`, and `--revert` options. |
| [`internal/tui/branch_sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/tui/branch_sync.go) | Interactive UI rendering and key binding handling for the recovery prompt. |
| [`internal/db/run.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/run.go) | Database persistence for run state, including the **`custody_returned_at`** field used to track parked syncs. |
| [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go) | Daemon lifecycle management and final cleanup after recovery decisions are applied. |

## CLI Recovery Commands

While the TUI provides an interactive recovery flow, the CLI exposes explicit flags for scripting and automation:

```bash

# Start a guarded sync (interactive TUI)

no-mistakes sync

# If previously cancelled, recover by keeping local changes:

no-mistakes sync --recover --apply

# Or revert to the gate head, discarding local work:

no-mistakes sync --recover --revert

```

These commands correspond directly to the **`applyRecovery`** and **`revertRecovery`** functions, providing non-interactive paths for CI/CD pipelines or terminal-based workflows.

## Summary

- The no-mistakes branch sync service treats cancelled pushes as recoverable events, not terminal failures.
- Sync state is persisted to the database via the **`State`** struct and **`runs`** table fields before and during network operations.
- Cancellation is detected in **`handlePushError`** and triggers a *parked* state rather than cleanup.
- Users recover via **[`internal/tui/branch_sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/tui/branch_sync.go)** (interactive) or **[`internal/cli/sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/sync.go)** (flags) by choosing to apply local work or revert to the gate head.
- The daemon in **[`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go)** finalizes recovery by clearing state and releasing locks, ensuring branch consistency.

## Frequently Asked Questions

### What happens to my local changes if I cancel a push mid-operation?

Your local changes are preserved. The service stores the current state in the database (with **`custody_returned_at`** unset) and marks the sync as parked. The worktree remains intact until you explicitly choose to either apply the changes to the gate or revert to the previous gate head through the recovery interface.

### How does the service distinguish between a network failure and a user cancellation?

The **`handlePushError`** function in [`internal/branchsync/sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/branchsync/sync.go) inspects the error returned by the Git subprocess. Context cancellation errors (from `exec.CommandContext`) are interpreted as user-initiated cancellations, while non-cancellation errors (timeouts, auth failures, network errors) are treated as hard failures that do not trigger the recovery workflow.

### Can I automate recovery without using the interactive TUI?

Yes. The CLI supports the `--recover` flag combined with either `--apply` or `--revert` in [`internal/cli/sync.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/sync.go). Running `no-mistakes sync --recover --apply` programmatically accepts custody of the local worktree, while `--revert` restores the gate head, allowing full automation in shell scripts or CI environments.

### Where is the sync state stored during the recovery process?

The state is stored in the local database managed by [`internal/db/run.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/run.go). The **`runs`** table tracks the sync context, including branch names, commit SHAs, and the **`custody_returned_at`** timestamp. This persistence ensures that even if the daemon restarts, it can reconstruct the recovery context from the database without relying on in-memory state.