# How the no-mistakes Daemon Manages Disposable Git Worktrees for Secure CI Pipelines

> Learn how the no-mistakes daemon manages disposable Git worktrees for secure CI pipelines. It creates and removes isolated worktrees automatically, ensuring clean builds.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: how-to-guide
- Published: 2026-07-01

---

**The no-mistakes daemon creates isolated, disposable Git worktrees for every pipeline run by executing `git worktree add --detach` in a namespaced directory under `$NM_HOME`, then automatically removes them after execution or during periodic orphan recovery scans.**

The `no-mistakes` daemon (from the repository `kunchenguid/no-mistakes`) orchestrates CI pipelines inside ephemeral, isolated environments to prevent accidental mutations to the user's primary repository. By leveraging **disposable worktrees**, the daemon ensures each execution runs in a clean, detached sandbox that exists only for the duration of the job.

## Worktree Directory Structure and Naming Convention

All disposable worktrees reside under the daemon's data directory. The `Paths` helper in [`internal/paths/paths.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/paths/paths.go) constructs absolute paths using a deterministic hierarchy:

```go
// internal/paths/paths.go (lines 46-48)
func (p *Paths) WorktreeDir(repoID, runID string) string {
    return filepath.Join(p.root, "worktrees", repoID, runID)
}

```

This generates a path like `$NM_HOME/worktrees/<repoID>/<runID>/`, ensuring complete filesystem isolation between repositories and individual runs.

## Creating Worktrees via Git Commands

When `Manager.startRun` initiates a new pipeline, it materializes the worktree by invoking the Git CLI directly. The daemon executes `git worktree add --detach` to create a **linked worktree** that shares the bare gate repository's object database while maintaining an independent HEAD state.

According to [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go) (lines 308-319), the creation flow follows this pattern:

```go
// Conceptual excerpt from internal/daemon/manager.go
func (m *Manager) startRun(repo *Repo, headSHA string) (string, error) {
    runID := generateRunID()
    wtDir := m.paths.WorktreeDir(repo.ID, runID)
    
    // Line 308: Create worktree from the gate bare repo
    cmd := exec.CommandContext(ctx, "git", "worktree", "add", "--detach", wtDir, headSHA)
    if out, err := cmd.CombinedOutput(); err != nil {
        // Lines 312-319: Persist error and abort
        m.db.UpdateRunError(runID, fmt.Sprintf("create worktree: %s", err))
        return "", fmt.Errorf("create worktree: %w (%s)", err, out)
    }
    return runID, nil
}

```

If the command fails, the error is persisted via `m.db.UpdateRunError` and the run aborts immediately.

## Configuration and Isolation Guarantees

Immediately after creation, the daemon configures a **deterministic Git identity** to ensure traceable commits. As implemented in [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go) (lines 317-319), it sets `user.name` and `user.email` to bot-specific values to differentiate automated commits from user commits.

The worktree operates in **detached HEAD** state. The [`execution_context.go`](https://github.com/kunchenguid/no-mistakes/blob/main/execution_context.go) file (lines 5-21) documents that the `.git` entry inside the worktree is a pointer file linking back to the bare repository, preventing agents from accidentally writing to the user's original checkout. The executor explicitly warns agents to restrict all writes to this directory ([`internal/pipeline/executor.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/executor.go), lines 111-119).

Before execution, the daemon fetches the remote's default branch into the worktree to provide a **trusted view** of the repository. If this fetch fails, the run proceeds with a warning but without the trusted configuration (manager.go lines 333-334).

## Execution and Automatic Cleanup

The manager delegates pipeline execution to a background goroutine via `executor.Run`. Cleanup responsibility is tracked through `trackStartFailure` and `trackStopFailure` flags to ensure the manager knows when it owns the worktree removal.

When the pipeline terminates, the executor triggers removal via a deferred cleanup routine:

```go
// Cleanup logic from internal/daemon/daemon.go (lines 287-292)
func (d *Daemon) cleanupWorktrees() {
    // ...
    if err := os.RemoveAll(worktreeDir); err != nil {
        slog.Warn("failed to remove worktree", "path", worktreeDir, "error", err)
    }
}

```

If the initial removal fails, the daemon falls back to `os.RemoveAll` and logs the failure without crashing the daemon process.

## Orphan Detection and Crash Recovery

To handle crashes or unclean shutdowns, the daemon implements a **recover-servers** task that scans the worktrees directory during startup. As exercised in [`internal/daemon/subscribe_recover_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/subscribe_recover_test.go) (lines 353-401), this routine:

1. Lists all directories under `Paths.WorktreesDir()`
2. Checks for corresponding active run entries in the database via `isRunActive()`
3. Removes orphaned directories that lack active run records

This best-effort pruning prevents disk space exhaustion from abandoned worktrees left by previous daemon instances.

## Summary

- **Directory Structure**: Worktrees are stored under `$NM_HOME/worktrees/<repoID>/<runID>/` via `Paths.WorktreeDir()` in [`internal/paths/paths.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/paths/paths.go) (lines 46-48).
- **Creation**: The daemon invokes `git worktree add --detach` in `Manager.startRun` (manager.go lines 308-319) to create isolated, linked worktrees.
- **Isolation**: Detached HEAD state and pointer-file `.git` entries prevent writes to the user's original checkout (execution_context.go lines 5-21).
- **Cleanup**: Automatic removal occurs via deferred `os.RemoveAll` after pipeline completion, with fallback error logging (daemon.go lines 287-292).
- **Recovery**: A periodic scan removes orphaned worktrees that persist after crashes (subscribe_recover_test.go lines 353-401).

## Frequently Asked Questions

### Where does the no-mistakes daemon store disposable worktrees?

The daemon stores all worktrees under the `worktrees/` subdirectory of its data root (`$NM_HOME`). The `Paths.WorktreeDir()` function in [`internal/paths/paths.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/paths/paths.go) (lines 46-48) constructs paths following the pattern `$NM_HOME/worktrees/<repoID>/<runID>/`, ensuring each run receives a unique filesystem location.

### How does the daemon ensure worktrees are cleaned up after a crash?

The daemon runs an orphan detection routine during startup that scans `Paths.WorktreesDir()` for directories without corresponding active run entries in the database. As tested in [`internal/daemon/subscribe_recover_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/subscribe_recover_test.go) (lines 353-401), this routine performs best-effort deletion of stale worktrees to reclaim disk space.

### Why does no-mistakes use `git worktree add --detach` instead of a simple clone?

Using `git worktree add --detach` creates a **linked worktree** that shares the bare repository's object database, eliminating redundant storage of Git history while enforcing isolation through a detached HEAD state. This approach, documented in [`internal/pipeline/steps/execution_context.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/execution_context.go) (lines 5-21), prevents pipeline agents from accidentally pushing to or modifying the user's default branches.

### What happens if the `git worktree add` command fails?

If the command fails, `Manager.startRun` captures the combined output, persists the error via `m.db.UpdateRunError`, and aborts the run immediately (manager.go lines 312-319). The error is surfaced to the user through the run's status, and no partial worktree directory is left behind because the Git command is atomic.