No-Mistakes Lifecycle Guard: Preventing Daemon Stops and Restarts During Active Runs
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 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.
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 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 vialifecycle.RunList, causing the CLI to exit with a non-zero status - Active runs present with
--force: Writes a warning tostderrdisplaying the active runs, then returnsnilto 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. 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: ContainsActiveRunsfor database queries andRunListfor formatting outputinternal/cli/daemon_cmd.go: HousesguardDestructiveDaemonLifecycleand the command handlers that enforce the guardinternal/cli/daemon_lifecycle_test.go: Provides test coverage throughTestDaemonStopRefusesWithActiveRunsAndListsThemandTestDaemonRestartRefusesWithActiveRuns, verifying that commands fail appropriately without--forceand 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:
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:
$ 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:
$ 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:
$ 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
ActiveRunsininternal/lifecycle/guard.goqueries the SQLite database for runs withRunPendingorRunRunningstatusguardDestructiveDaemonLifecycleenforces the guard logic ininternal/cli/daemon_cmd.go, returning errors unless--forceis specified- The
--forceflag bypasses the guard after printing a warning tostderr, 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 via the ActiveRuns function, while the enforcement logic lives in 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →