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

> Discover the no-mistakes IPC protocol. Learn how the CLI and daemon communicate using JSON-RPC over sockets for seamless run lifecycle management. Get insights into MethodRun and MethodRespond.

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

---

**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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/ipc/ipc.go)** – Defines core type definitions including `RunInfo`, `StepResultInfo`, `Event`, and request/response structs, plus method name constants.
- **[`internal/ipc/server.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/ipc/server.go)** – Implements the socket listener, request dispatch logic, and event broadcast system.
- **[`internal/ipc/client.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/ipc/client.go)** – Provides the client implementation with `Dial`, `Call`, and `Subscribe` methods.
- **[`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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.

```go
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.

```go
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.

```go
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.

```go
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`](https://github.com/kunchenguid/no-mistakes/blob/main/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:**

```go
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:**

```go
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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/tui/view.go) and [`internal/tui/pipeline.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/ipc.go), [`client.go`](https://github.com/kunchenguid/no-mistakes/blob/main/client.go), and [`server.go`](https://github.com/kunchenguid/no-mistakes/blob/main/server.go).
- **[`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go)** registers handlers and integrates IPC with the execution engine.
- **[`internal/tui/yolo_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/ipc/client.go) abstracts this transport, while `ipc.NewServer` in [`internal/ipc/server.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/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.