No-Mistakes IPC Protocol: How the CLI and Daemon Communicate for Run Lifecycle Management

The no-mistakes CLI communicates with its background daemon via a JSON-RPC-style protocol over Unix domain sockets (or named pipes on Windows), implemented in the internal/ipc package, using methods like MethodRun and MethodRespond to manage run lifecycles.

The no-mistakes repository implements a client-server architecture where the command-line interface acts as a thin client and the heavy lifting occurs in a persistent background daemon. This IPC protocol between the no-mistakes CLI and daemon for run lifecycle management ensures atomic coordination of pipeline execution, human approvals, and real-time event streaming while keeping the daemon as the single source of truth for all run states.

IPC Architecture and Transport Layer

The IPC layer resides in internal/ipc and provides a lightweight RPC mechanism built on top of Unix domain sockets (or named pipes on Windows). All messages use JSON encoding with a length-prefixed framing protocol that both the ipc.Server and ipc.Client understand.

Key components:

  • internal/ipc/ipc.go – Defines core type definitions including RunInfo, StepResultInfo, Event, and request/response structs, plus method name constants.
  • internal/ipc/server.go – Implements the socket listener, request dispatch logic, and event broadcast system.
  • internal/ipc/client.go – Provides the client implementation with Dial, Call, and Subscribe methods.
  • internal/daemon/manager.go – Registers IPC handlers and integrates the protocol with the daemon's execution engine.

Core IPC Methods for Run Lifecycle

The protocol exposes five primary methods that map directly to run lifecycle operations. Each method accepts a typed params struct and returns a result struct or error.

MethodRun: Initiating Pipeline Execution

MethodRun triggers the daemon to start a new run. The CLI sends a RunParams payload containing repository path, branch, requested steps, and autofix preferences.

runParams := ipc.RunParams{
    RepoPath: "/path/to/repo",
    Branch:   "feature-xyz",
    Steps:    []string{"review", "rebase", "push"},
    AutoFix:  false,
}
var runInfo ipc.RunInfo
client.Call(ipc.MethodRun, runParams, &runInfo)

The daemon responds with a RunInfo struct containing a unique run ID, initial status (typically Running), and an empty steps list.

MethodRespond: Handling Human-in-the-Loop Approvals

When a step requires human or agent intervention, the daemon sets RunInfo.AwaitingAgentSince and emits an EventAwaitingApproval. The CLI uses MethodRespond to send the chosen action.

resp := ipc.RespondParams{
    RunID:    runInfo.ID,
    StepName: "review",
    Action:   ipc.ResponseAccept, // Accept, Reject, Edit, or Skip
}
var result ipc.RespondResult
client.Call(ipc.MethodRespond, resp, &result)

The daemon clears the awaiting state, updates the step result, and emits EventStepCompleted.

MethodAbort: Canceling Active Runs

MethodAbort forces a run into a terminal Aborted state. The request includes the run ID and optional reason string.

abort := ipc.AbortParams{
    RunID:  runInfo.ID,
    Reason: "user cancel",
}
var result ipc.AbortResult
client.Call(ipc.MethodAbort, abort, &result)

The daemon immediately terminates execution, clears AwaitingAgentSince, and broadcasts a final EventRunCompleted.

MethodSubscribe: Real-Time Event Streaming

MethodSubscribe opens a long-running connection for live updates. The TUI uses this to render progress bars and step status in real-time.

sub, err := client.Subscribe(ipc.MethodSubscribe, ipc.SubscribeParams{RunID: runInfo.ID})
for ev := range sub.Events {
    switch ev.Type {
    case ipc.EventStepStarted:
        fmt.Printf("▶ %s\n", ev.StepName)
    case ipc.EventAwaitingApproval:
        fmt.Printf("⏳ %s awaiting approval\n", ev.StepName)
    case ipc.EventRunCompleted:
        return
    }
}

MethodStatus: Polling Run State

MethodStatus fetches a snapshot of the current run state without establishing a subscription. Non-interactive commands use this to check progress or verify completion.

Protocol Flow and Data Structures

The run lifecycle follows a strict state machine communicated through the IPC protocol:

  1. Initialization – MethodRun creates RunInfo with Status: Running and a UUID-generated ID.
  2. Step Execution – The daemon emits EventStepStarted and EventStepCompleted for each pipeline step. Step results populate the Steps slice with StepResultInfo objects containing status, findings (JSON-encoded), and diff data.
  3. Awaiting State – When human input is required, the daemon sets AwaitingAgentSince to a Unix timestamp and emits EventAwaitingApproval.
  4. Resolution – MethodRespond transitions the step to completed, clears AwaitingAgentSince, and triggers the next step.
  5. Termination – MethodAbort or natural completion sets the final status and emits EventRunCompleted.

Critical data structures referenced in internal/ipc/ipc.go:

  • RunInfo – Contains ID, status, steps array, and AwaitingAgentSince timestamp.
  • StepResultInfo – Holds individual step outcomes, findings metadata, and execution duration.
  • Event – Union type for streaming updates with variants for step lifecycle and approval gates.

Implementing IPC Communication

Connecting to the daemon requires dialing the default socket path and invoking methods through the typed client API.

Establishing a client connection:

sock := ipc.DefaultSocketPath() // Typically $XDG_RUNTIME_DIR/no-mistakes.sock
client, err := ipc.Dial(sock)
if err != nil {
    log.Fatalf("failed to dial daemon: %v", err)
}
defer client.Close()

Complete run initiation example:

params := ipc.RunParams{
    RepoPath:   "/home/user/project",
    Branch:     "main",
    Steps:      []string{"lint", "test", "deploy"},
    AutoFix:    true,
}
var info ipc.RunInfo
if err := client.Call(ipc.MethodRun, params, &info); err != nil {
    log.Fatalf("run failed: %v", err)
}
fmt.Printf("Started run %s with status %s\n", info.ID, info.Status)

Handling interactive approval in the TUI:

The TUI implementation in internal/tui/view.go and internal/tui/pipeline.go combines MethodSubscribe with MethodRespond to create a responsive interface that pauses execution at approval gates and resumes upon user input.

Summary

  • The IPC protocol uses JSON-RPC over Unix domain sockets (or named pipes on Windows) to connect the CLI and daemon.
  • Five core methods manage the lifecycle: MethodRun, MethodRespond, MethodAbort, MethodSubscribe, and MethodStatus.
  • RunInfo serves as the single source of truth for run state, with AwaitingAgentSince tracking human-in-the-loop gates.
  • The internal/ipc package provides typed client/server implementations in ipc.go, client.go, and server.go.
  • internal/daemon/manager.go registers handlers and integrates IPC with the execution engine.
  • internal/tui/yolo_test.go demonstrates the complete request/response flow for testing purposes.

Frequently Asked Questions

What transport mechanism does the no-mistakes IPC protocol use?

The protocol transmits JSON messages over Unix domain sockets on Linux/macOS and named pipes on Windows. The ipc.Dial function in internal/ipc/client.go abstracts this transport, while ipc.NewServer in internal/ipc/server.go handles the socket creation and listener management.

How does the daemon handle multiple concurrent run requests?

The IPC server in internal/ipc/server.go accepts concurrent connections and dispatches each MethodRun request to a separate goroutine managed by the daemon's executor. Each run receives a unique UUID, and the RunInfo state is maintained in thread-safe storage accessible to all IPC handlers.

What happens if the CLI disconnects while a run is in progress?

Runs continue executing independently of CLI connections. The daemon persists run state internally, so temporary disconnections do not abort execution. The CLI can reconnect using MethodStatus to retrieve current state or MethodSubscribe to resume event streaming at any point in the lifecycle.

Can external tools integrate with the no-mistakes IPC protocol?

Yes. Any tool capable of connecting to the Unix domain socket can import the internal/ipc package (or implement the JSON message structures defined in internal/ipc/ipc.go) to call MethodRun, MethodRespond, or MethodStatus. The protocol is version-agnostic through explicit struct field naming, though callers must handle the specific error types defined in the IPC package.

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 →