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

> Prevent unexpected daemon stops during active runs with the no-mistakes lifecycle guard. Override with --force only when necessary. Secure your pipelines.

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

---

**The lifecycle guard in no-mistakes prevents destructive daemon stops, restarts, or updates while pipeline runs are active, requiring an explicit `--force` flag to override.**

The no-mistakes repository provides a machine-wide daemon that coordinates concurrent pipeline runs. The **lifecycle guard** is a safety mechanism that blocks shutdown commands when jobs are still pending or running, preventing data corruption and inconsistent worktree states.

## Why the Lifecycle Guard Exists

The daemon serves as a single process coordinating multiple concurrent runs. Stopping it while runs are `pending` or `running` would abruptly terminate those jobs, potentially leaving worktrees, locks, or temporary data in an inconsistent state and risking data loss. To avoid these race conditions, the guard forces users to explicitly acknowledge the danger and provide a `--force` flag before the daemon can be stopped, restarted, or updated.

## How the Lifecycle Guard Works

The guard implements a check-before-act pattern across all lifecycle commands. When a user issues `daemon stop`, `daemon restart`, or `update`, the system validates active run states before proceeding.

### Querying the ActiveRuns Registry

The guard first queries the daemon’s internal **ActiveRuns** registry defined in [`internal/lifecycle/active_runs.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/active_runs.go). If the registry contains any runs that are not yet finished, the command prints a concise list of those runs and aborts with a non-zero exit status. This check ensures the daemon cannot be destroyed while work is in progress.

### The Force Override Mechanism

The command only proceeds if the user supplies `--force`, which overrides the guard after the active runs list has been displayed. This explicit opt-out ensures the user is aware of the impact before terminating active jobs.

## Logging and Audit Trails

Every lifecycle request, whether allowed or blocked, is logged to `<NM_HOME>/logs/cli.log` with the caller’s PID, PPID, and the full command line. This audit trail is essential for post-mortem analysis of accidental daemon shutdowns, allowing administrators to trace who attempted to stop the daemon and when.

## Integration and Implementation Details

The guard logic lives in **[`internal/lifecycle/guard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/guard.go)** and is invoked from the CLI command handlers in **[`internal/cli/daemon_lifecycle_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/daemon_lifecycle_test.go)**, which also contains the corresponding tests. The daemon’s startup and shutdown entry points in **[`internal/daemon/daemon.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/daemon.go)** consult this guard before executing destructive operations.

The guard is deliberately *disabled by default* for the `auto_fix.review` feature, because review-related stops are handled separately through a different code path.

## Practical Examples

Attempting to stop the daemon while runs are active results in a blocked operation:

```bash
$ no-mistakes daemon stop
Active runs:
  • run 42 – pending (branch: feature/foo)
  • run 43 – running (branch: bugfix/bar)
Refusing to stop daemon while runs are active. Use --force to override.

```

To force the daemon to stop despite active runs:

```bash
$ no-mistakes daemon stop --force
Daemon stopped successfully.

```

The same protection applies to restart and update commands:

```bash
$ no-mistakes daemon restart
Active runs detected; aborting. Add --force to restart anyway.

$ no-mistakes update
Active runs present – refusing to replace the running daemon.
Run `no-mistakes update -y --force` to proceed anyway.

```

## Summary

- The **lifecycle guard** prevents accidental daemon stops that would terminate active pipeline runs and corrupt data.
- It queries the **ActiveRuns** registry in [`internal/lifecycle/active_runs.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/active_runs.go) to check for pending or running jobs before executing `stop`, `restart`, or `update` commands.
- Users must provide the `--force` flag to override the guard, ensuring explicit acknowledgment of the risk.
- All lifecycle attempts are logged to `<NM_HOME>/logs/cli.log` with caller PID and PPID for forensic analysis.
- The core implementation resides in [`internal/lifecycle/guard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/guard.go) with integration points in [`internal/daemon/daemon.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/daemon.go) and [`internal/cli/daemon_lifecycle_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/daemon_lifecycle_test.go).
- The guard is disabled by default only for the `auto_fix.review` feature.

## Frequently Asked Questions

### What happens if I try to stop the daemon without --force while runs are active?

The command queries the ActiveRuns registry and, if any runs are pending or running, prints a list of those runs and aborts with a non-zero exit code. The daemon remains running to protect active jobs from abrupt termination.

### Where is the lifecycle guard logic implemented in the no-mistakes codebase?

The core implementation resides in [`internal/lifecycle/guard.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/guard.go). It is invoked from CLI handlers in [`internal/cli/daemon_lifecycle_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/daemon_lifecycle_test.go) and consulted by the daemon startup and shutdown entry points in [`internal/daemon/daemon.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/daemon.go).

### How does the guard track active runs?

The guard consults the **ActiveRuns** registry maintained in [`internal/lifecycle/active_runs.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/lifecycle/active_runs.go), which maintains an in-memory list of all pipeline runs that have not yet finished, including their status and branch information.

### Is the lifecycle guard ever disabled?

The guard is disabled by default only for the `auto_fix.review` feature, as review-related stops are handled separately. For standard lifecycle operations, you cannot permanently disable the guard; you must use the `--force` flag to override it on a per-command basis.