How the Destructive Daemon Lifecycle Guard Prevents Stopping the Daemon During Active Runs

The destructive daemon lifecycle guard prevents accidental daemon shutdowns by querying the local SQLite database for pending or running pipeline runs and refusing the daemon stop or daemon restart commands unless the operator explicitly overrides the protection with the --force flag.

The no-mistakes repository implements a safety mechanism to protect long-running pipeline executions from abrupt termination. When administrators attempt to stop or restart the daemon, the destructive daemon lifecycle guard intervenes to check for active work, ensuring that in-flight pipelines are not abandoned and state corruption is avoided.

How the Guard Detects Active Pipeline Runs

The guard’s protection begins with an atomic check against the daemon’s state database. Before any shutdown logic executes, the system verifies whether the daemon is currently coordinating pipeline executions.

Querying the SQLite Database

The guard calls lifecycle.ActiveRuns(p) defined in internal/lifecycle/guard.go, which opens the local SQLite database via p.DB() from internal/paths/paths.go. It executes a query against the runs table to retrieve any rows where the status is pending or running.

This database-driven approach ensures the guard always checks the ground truth of daemon state, regardless of process signals or memory cache inconsistencies.

Run Status Evaluation

The ActiveRuns function returns a slice of active run records. The guard then evaluates this slice to determine if a destructive lifecycle change is safe:

  • Empty result: No active runs exist, allowing immediate shutdown
  • Non-empty result: Active pipelines are in progress, triggering the protective refusal logic

Decision Logic and User Interaction

The guardDestructiveDaemonLifecycle function implements a three-tier decision tree that balances safety with operational flexibility.

Safe Shutdown Path

When no active runs exist, the guard returns nil immediately, allowing the CLI command to proceed to daemonStopFn or daemonStartFn:

$ no-mistakes daemon stop
  ✓ daemon stopped

The Force Override Mechanism

If active runs exist, the guard checks for the --force flag:

Without --force, the guard returns an error containing a refusal message and a formatted list of active runs (lifecycle.RunList), causing the command to abort before any shutdown occurs:

$ no-mistakes daemon stop
Error: refusing daemon stop because 2 active pipeline runs are in progress;
feature-a  aaa111  main  a1b2c3d4
feature-b  bbb222  main  d4c3b2a1

With --force, the guard prints a warning message detailing the active runs but returns nil, permitting the operation to proceed:

$ no-mistakes daemon stop --force
FORCE: daemon stop will stop/restart the daemon while 2 active pipeline runs are in progress
active pipeline runs:
  feature-a  aaa111  main  a1b2c3d4
  feature-b  bbb222  main  d4c3b2a1
  ✓ daemon stopped

Integration with CLI Commands

The guard is invoked from newDaemonStopCmd and newDaemonRestartCmd in internal/cli/daemon_cmd.go. These command constructors wrap the actual shutdown functions (daemonStopFn and daemonStartFn) with the protective check.

If guardDestructiveDaemonLifecycle returns an error, the CLI propagates that error immediately and never reaches the underlying shutdown logic. This architectural pattern ensures that the guard acts as an impenetrable gatekeeper—there is no code path that allows daemon termination without first passing the active-run verification.

func stopDaemon(p *paths.Paths, force bool) error {
    // Guard prevents accidental shutdown during active runs.
    if err := guardDestructiveDaemonLifecycle(p, os.Stderr, "daemon stop", force); err != nil {
        return err
    }
    return daemonStopFn(p) // actual shutdown only executes after guard passes
}

Audit Logging and Compliance

Regardless of whether the guard permits or denies the operation, the command logs the invocation via logLifecycleInvocation. When --force is used, the system records the override in <NM_HOME>/logs/cli.log along with caller attribution including PID, PPID, and parent command line.

The test suite in internal/cli/daemon_lifecycle_test.go verifies this behavior, ensuring that:

  • The guard refuses stopping when active runs exist
  • The --force flag correctly overrides the protection
  • Audit trails capture force-override events with proper caller attribution

Summary

  • The destructive daemon lifecycle guard queries the SQLite runs table via internal/lifecycle/guard.go to detect pending or active pipeline executions.
  • Without active runs, daemon stop and daemon restart commands execute immediately.
  • When active runs exist, the guard returns an error and aborts the command unless --force is specified.
  • The guard is integrated into internal/cli/daemon_cmd.go before any shutdown function calls, ensuring zero bypass paths.
  • All lifecycle events are audited to <NM_HOME>/logs/cli.log with caller attribution for compliance and forensics.

Frequently Asked Questions

What happens if I try to stop the daemon while a pipeline is running?

The CLI outputs an error message listing the active runs and aborts the command. The daemon continues running, and no shutdown logic executes. You must either wait for the pipelines to complete or use the --force flag to override the protection.

How do I force the daemon to stop despite active runs?

Append the --force flag to your command: no-mistakes daemon stop --force or no-mistakes daemon restart --force. The guard will print a warning showing the active runs and proceed with the shutdown. This action is logged with full caller attribution including PID and command line for audit purposes.

Where does the guard check for active runs?

The guard checks the local SQLite database via internal/lifecycle/guard.go, specifically the ActiveRuns function. It reads from the runs table (defined in internal/db/run.go) to identify rows with status pending or running.

Is there a way to see which runs are blocking the shutdown?

Yes. When the guard refuses a shutdown, it prints a formatted list of active runs showing the feature name, run ID, branch, and commit hash. When using --force, this same list appears in the warning message before the daemon stops.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →