How the no-mistakes Pipeline Handles Parked Runs Awaiting Agent Approval
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 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:
// 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 executes an atomic UPDATE statement:
// 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:
// 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:
// 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. This structure mirrors the database schema exactly:
// 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, the formatter appends an awaiting_agent field only when the run is parked and not in a terminal state:
// 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 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_sincecolumn stores a Unix timestamp when parked andNULLotherwise, enabling atomic state transitions ininternal/db/run.go. - Executor coordination:
SetRunAwaitingAgentandClearRunAwaitingAgentbracket the blockingwaitForApprovalcall ininternal/pipeline/executor.go. - External visibility: The IPC protocol exposes
AwaitingAgentandAwaitingAgentSincefields ininternal/ipc/protocol.gofor poll-based observation. - User experience: The CLI renders human-readable parked durations via
formatParkedForininternal/cli/axi_render.go. - Crash safety:
RecoverStaleRunscleans 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 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, 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. The formatter in 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.
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 →