# No-Mistakes Lifecycle Guard: Preventing Daemon Stops and Restarts During Active Runs

> Prevent daemon stops and restarts during active runs with Lifecycle Guard. Learn how it uses SQLite to block destructive operations unless force is specified.

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

---

**The lifecycle guard blocks destructive daemon operations by querying the SQLite database for runs with `RunPending` or `RunRunning` status, refusing to stop or restart the daemon unless the `--force` flag is explicitly provided.**

The no-mistakes daemon manages pipeline executions that can span minutes or hours, requiring protection against accidental interruption. The **lifecycle guard** is a safety mechanism implemented in the Go source code that prevents the daemon from stopping, restarting, or updating while any pipeline run remains active. This design protects worktrees, database consistency, and external resources from corruption due to premature termination.

## How the Lifecycle Guard Works

The guard operates through a three-layer defense that detects active runs, enforces protection rules, and integrates with the CLI command handlers.

### Active Run Detection

The `ActiveRuns` function in [`internal/lifecycle/guard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/guard.go) opens the local SQLite database and queries for incomplete operations. It returns all runs where the `status` column equals `RunPending` or `RunRunning`, providing the foundation for the guard's decision-making logic.

```go
func ActiveRuns(p *paths.Paths) ([]*db.Run, error)

```

This function accepts a `*paths.Paths` struct to locate the database file via `p.DB()`, then filters the runs table for active states. If no database exists or no active runs are found, it returns an empty slice.

### Guard Enforcement Logic

The `guardDestructiveDaemonLifecycle` function in [`internal/cli/daemon_cmd.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/daemon_cmd.go) implements the enforcement policy. Called before any destructive operation, it evaluates the active run list and the `force` boolean parameter to determine whether to proceed, refuse, or warn.

The logic follows these rules:

- **Empty run list**: The operation proceeds immediately
- **Active runs present without `--force`**: Returns an error containing the formatted run list via `lifecycle.RunList`, causing the CLI to exit with a non-zero status
- **Active runs present with `--force`**: Writes a warning to `stderr` displaying the active runs, then returns `nil` to allow the operation

The `RunList` helper function formats the active runs into a human-readable multi-line string showing run IDs, statuses, branches, and commit hashes.

### CLI Integration

The guard is wired directly into the Cobra command handlers for `daemon stop`, `daemon restart`, and `update --force` in [`internal/cli/daemon_cmd.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/daemon_cmd.go). Both `newDaemonStopCmd` and `newDaemonRestartCmd` invoke `guardDestructiveDaemonLifecycle` before executing their destructive actions, ensuring the check occurs at the entry point of every relevant CLI operation.

## Implementation Details

The lifecycle guard spans three critical files in the repository:

- **[`internal/lifecycle/guard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/guard.go)**: Contains `ActiveRuns` for database queries and `RunList` for formatting output
- **[`internal/cli/daemon_cmd.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/daemon_cmd.go)**: Houses `guardDestructiveDaemonLifecycle` and the command handlers that enforce the guard
- **[`internal/cli/daemon_lifecycle_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/daemon_lifecycle_test.go)**: Provides test coverage through `TestDaemonStopRefusesWithActiveRunsAndListsThem` and `TestDaemonRestartRefusesWithActiveRuns`, verifying that commands fail appropriately without `--force` and succeed with it

The guard specifically checks for the status constants `RunPending` and `RunRunning` defined in the database package, ensuring that only truly active operations trigger the protection.

## Practical Usage Examples

### Checking for Active Runs Programmatically

You can leverage the lifecycle guard's detection logic in your own Go code to implement custom pre-flight checks:

```go
import (
    "fmt"
    "github.com/kunchenguid/no-mistakes/internal/lifecycle"
    "github.com/kunchenguid/no-mistakes/internal/paths"
)

func validateSafeShutdown() error {
    p, _ := paths.New()
    runs, err := lifecycle.ActiveRuns(p)
    if err != nil {
        return fmt.Errorf("database error: %w", err)
    }
    if len(runs) == 0 {
        return nil // Safe to proceed
    }
    return fmt.Errorf("cannot stop daemon: %d active runs in progress\n%s",
        len(runs), lifecycle.RunList(runs))
}

```

### Default Safe Behavior (CLI)

Without additional flags, the CLI refuses destructive operations when pipelines are active:

```bash
$ no-mistakes daemon stop
refusing daemon stop because 2 active pipeline runs are in progress;
active pipeline runs:
  aaa111  pending  feature-a  a1b2c3d4
  bbb222  running  feature-b  b2c3d4e5

```

The command exits with a non-zero status code, preventing automation scripts from accidentally interrupting running work.

### Overriding the Guard with Force

When you must stop or restart the daemon despite active runs, use the `--force` flag:

```bash
$ no-mistakes daemon stop --force
FORCE: daemon stop will stop the daemon while 2 active pipeline runs are in progress
active pipeline runs:
  aaa111  pending  feature-a  a1b2c3d4
  bbb222  running  feature-b  b2c3d4e5
daemon stopped

```

The same pattern applies to restarts:

```bash
$ no-mistakes daemon restart --force
FORCE: daemon restart will stop/restart the daemon while 1 active pipeline run is in progress
active pipeline runs:
  ccc333  running  feature-c  c3d4e5f6
daemon stopped
daemon started

```

## Summary

- The **lifecycle guard** in no-mistakes prevents daemon stops, restarts, and forced updates while pipeline runs are active
- **`ActiveRuns`** in [`internal/lifecycle/guard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/guard.go) queries the SQLite database for runs with `RunPending` or `RunRunning` status
- **`guardDestructiveDaemonLifecycle`** enforces the guard logic in [`internal/cli/daemon_cmd.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/daemon_cmd.go), returning errors unless `--force` is specified
- The **`--force`** flag bypasses the guard after printing a warning to `stderr`, allowing intentional overrides
- This mechanism protects worktrees, database state, and external resources from inconsistency caused by premature daemon termination

## Frequently Asked Questions

### What statuses trigger the lifecycle guard?

The guard triggers when any run has the status `RunPending` or `RunRunning`. These constants represent operations that have been queued but not started, or are currently executing. Completed, failed, or canceled runs do not trigger the protection.

### Where is the lifecycle guard implemented in the source code?

The detection logic resides in [`internal/lifecycle/guard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/guard.go) via the `ActiveRuns` function, while the enforcement logic lives in [`internal/cli/daemon_cmd.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/daemon_cmd.go) within `guardDestructiveDaemonLifecycle`. The guard is invoked by the `daemon stop`, `daemon restart`, and `update --force` command handlers in the same file.

### How do I stop the daemon when active runs are stuck?

Use the `--force` flag with your stop or restart command: `no-mistakes daemon stop --force`. This prints a warning listing the active runs but proceeds with the operation. Only use this when you understand that interrupting active runs may leave worktrees or external resources in an inconsistent state.

### Does the lifecycle guard affect the `update` command?

Yes, the `update --force` command also invokes `guardDestructiveDaemonLifecycle` to prevent updates from restarting the daemon while pipelines are running. Without the `--force` flag on the update command, the guard blocks the update operation if active runs exist.