# How the no-mistakes Pipeline Handles Parked Runs Awaiting Agent Approval

> Discover how the no-mistakes pipeline manages parked runs awaiting agent approval. Learn about timestamp tracking, response handling, and observable automation for crash-resistant systems.

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

---

**The no-mistakes pipeline marks runs as parked by writing a Unix timestamp to the `awaiting_agent_since` column in SQLite, clears the marker after the agent responds via `axi respond`, and exposes this state through IPC fields and CLI output for observable, crash-resistant automation.**

The `kunchenguid/no-mistakes` repository implements a durable approval-gate mechanism that pauses pipeline execution when steps require human or automated agent verification. When a run reaches an `awaiting_approval` or `fix_review` gate, the system transitions into a **parked** state that persists in the database, allowing external tools to poll status without maintaining active connections to the executor.

## Database Schema for the Parked State

The parked signal relies on a single nullable column in the `runs` table. The `awaiting_agent_since` field stores the current Unix-seconds timestamp when a run is parked, and remains `NULL` during normal execution. This schema design allows atomic updates and provides a natural way to calculate wait duration by subtracting the stored timestamp from the current time.

## Executor Flow: Setting and Clearing Parked Markers

The coordinator inside [`internal/pipeline/executor.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/executor.go) manages the lifecycle of the parked state around blocking approval waits.

### Entering the Approval Gate

Before blocking for agent input, the executor marks the run as parked. This ensures that even if the CLI queries status milliseconds later, it sees the awaiting state immediately:

```go
// internal/pipeline/executor.go
if dbErr := e.db.SetRunAwaitingAgent(run.ID); dbErr != nil {
    slog.Warn("failed to set awaiting-agent marker in db", …)
}

```

The `SetRunAwaitingAgent` function defined in [`internal/db/run.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/run.go) executes an atomic UPDATE statement:

```go
// internal/db/run.go
func (d *DB) SetRunAwaitingAgent(id string) error {
    ts := now()
    _, err := d.sql.Exec(`UPDATE runs SET awaiting_agent_since = ?, updated_at = ? WHERE id = ?`,
        ts, ts, id)
    …
}

```

### Blocking for Agent Response

Immediately after setting the marker, the executor invokes `e.waitForApproval`, which blocks until the agent sends an `axi respond` command, the context is cancelled, or an error occurs. While blocked, the run remains parked in the database, allowing external observers to query how long the run has waited without accessing executor memory.

### Exiting the Gate

Once `waitForApproval` returns, the executor clears the parked marker regardless of the outcome. This guarantees that the run never remains marked as awaiting an agent after the gate resolves:

```go
// internal/pipeline/executor.go (after wait returns)
if dbErr := e.db.ClearRunAwaitingAgent(run.ID); dbErr != nil {
    slog.Warn("failed to clear awaiting-agent marker in db", …)
}

```

The corresponding database function sets the column back to `NULL`:

```go
// internal/db/run.go
func (d *DB) ClearRunAwaitingAgent(id string) error {
    _, err := d.sql.Exec(`UPDATE runs SET awaiting_agent_since = NULL, updated_at = ? WHERE id = ?`,
        now(), id)
    …
}

```

## IPC Protocol Exposure

The IPC layer exposes the parked state to external clients through the `RunInfo` struct in [`internal/ipc/protocol.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/ipc/protocol.go). This structure mirrors the database schema exactly:

```go
// internal/ipc/protocol.go
AwaitingAgent      bool   `json:"awaiting_agent,omitempty"`
AwaitingAgentSince *int64 `json:"awaiting_agent_since,omitempty"`

```

When the daemon broadcasts `run_updated` events, these fields populate from the current database row. Any client—including the `axi status` CLI—can determine the parked state and duration with a single RPC call without polling the executor's internal state machine.

## CLI Rendering of Parked Status

The command-line interface translates the raw timestamp into human-readable output. In [`internal/cli/axi_render.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/axi_render.go), the formatter appends an `awaiting_agent` field only when the run is parked and not in a terminal state:

```go
// internal/cli/axi_render.go
if rv.AwaitingAgentSince != nil && !terminalStatus(rv.Status) {
    fields = append(fields, toon.Field{
        Key: "awaiting_agent",
        Value: formatParkedFor(*rv.AwaitingAgentSince),
    })
}

```

The `formatParkedFor` helper converts the Unix timestamp into a duration string such as **"parked 2m30s"**, making the signal instantly visible to operators monitoring pipeline status.

## Crash Recovery and Stale State Cleanup

To prevent false positives after daemon restarts, the `RecoverStaleRuns` function in [`internal/db/run.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/run.go) executes on startup. This routine clears any lingering `awaiting_agent_since` values for runs that are being marked failed, ensuring that a crashed daemon never reports a completed or dead run as still parked and awaiting input.

## Summary

- **Database marker**: The `awaiting_agent_since` column stores a Unix timestamp when parked and `NULL` otherwise, enabling atomic state transitions in [`internal/db/run.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/run.go).
- **Executor coordination**: `SetRunAwaitingAgent` and `ClearRunAwaitingAgent` bracket the blocking `waitForApproval` call in [`internal/pipeline/executor.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/executor.go).
- **External visibility**: The IPC protocol exposes `AwaitingAgent` and `AwaitingAgentSince` fields in [`internal/ipc/protocol.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/ipc/protocol.go) for poll-based observation.
- **User experience**: The CLI renders human-readable parked durations via `formatParkedFor` in [`internal/cli/axi_render.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/axi_render.go).
- **Crash safety**: `RecoverStaleRuns` cleans up stale markers on daemon startup to prevent phantom parked states.

## Frequently Asked Questions

### How does the no-mistakes pipeline detect if a run is currently parked?

External tools query the `RunInfo` struct via the IPC protocol. If the `AwaitingAgentSince` field is non-nil and the status is non-terminal, the run is parked. The CLI performs this check in [`internal/cli/axi_render.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/axi_render.go) before displaying the "awaiting_agent" field.

### What happens to the parked state if the daemon crashes while a run is awaiting approval?

On startup, the daemon invokes `RecoverStaleRuns` in [`internal/db/run.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/run.go), which clears `awaiting_agent_since` for any runs being processed. This prevents the system from reporting a run as parked when it is actually failed or completed, ensuring consistency between the database and executor state.

### How long can a run remain in the parked state?

The pipeline imposes no hard timeout on the parked state itself; the run remains parked indefinitely until the agent sends an `axi respond` command or the context is explicitly cancelled. Operators can monitor wait duration by checking the `AwaitingAgentSince` timestamp, which exposes how many seconds the run has awaited approval.

### Where does the CLI get the information to display "parked 2m30s"?

The `axi status` command receives a `RunInfo` message from the daemon via IPC defined in [`internal/ipc/protocol.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/ipc/protocol.go). The formatter in [`internal/cli/axi_render.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/axi_render.go) passes the `AwaitingAgentSince` timestamp to `formatParkedFor`, which calculates the delta between the stored time and current time, rendering it as a human-readable duration string.