# How the no-mistakes Pipeline Executor Handles Findings and Auto-Fix Logic

> Discover how the no-mistakes pipeline executor manages findings and auto-fix logic. Learn how it classifies issues and routes them for automated correction or human review.

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

---

**The no-mistakes pipeline executor collects findings from each step, classifies them using helper functions in [`internal/types/findings.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/types/findings.go), and routes actionable items to either an automated correction loop or a human-review queue depending on the `Action` field.**

The `kunchenguid/no-mistakes` repository implements a deterministic CI/CD system where the pipeline executor serves as the central orchestrator. This component manages the lifecycle of findings—from collection through classification to resolution—ensuring that only safe, pre-approved changes are applied automatically while mandatory review items pause the workflow.

## Findings Representation and Classification

All findings travel through the system as a strongly-typed `types.Findings` struct defined in [[`internal/types/findings.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/types/findings.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/types/findings.go). Each finding carries metadata including `Severity`, `Description`, `Action`, and `RequiresHumanReview`.

Two helper functions drive the executor’s routing decisions:

- **`AutoFixableFindings(findings)`** – filters the list to return only items where `Action == "auto-fix"` (implemented around line 156 in [[`findings.go`](https://github.com/kunchenguid/no-mistakes/blob/main/findings.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/types/findings.go#L156))
- **`HasAskUserFindings(findings)`** – returns `true` if any finding has `Action == "ask-user"` (line 220 in the same file)

These pure functions allow the executor to make deterministic routing decisions without inspecting individual finding internals directly.

## Executor Initialization and Configuration

The executor is instantiated via **`pipeline.NewExecutor`** (see [[`internal/pipeline/executor.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/executor.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/executor.go#L69)). During construction, it receives:

- A database handle (`*db.DB`) for persisting run and round state
- A configuration object (`*config.Config`) containing the global `auto_fix` defaults from [[`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go)
- A slice of pipeline steps to execute
- An event broadcast channel to push updates to the UI and daemon

The executor also initializes memory structures to track `awaiting_agent_since` timestamps when human review is required.

## Step Execution and Finding Normalization

When an agent step completes, the executor receives a `StepResult` containing raw findings. Before routing, the executor:

1. **Normalizes IDs** – calls `NormalizeFindings` (line 104 in [[`findings.go`](https://github.com/kunchenguid/no-mistakes/blob/main/findings.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/types/findings.go#L104)) to ensure every finding has a deterministic identifier, guaranteeing stable merge behavior across rounds
2. **Persists the round** – writes the findings JSON to the database round record so the UI can render them on resume
3. **Broadcasts state** – emits a `StepCompleted` event to listeners, including the TUI renderer tested in [[`internal/tui/viewport_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/tui/viewport_test.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/tui/viewport_test.go)

## Decision Logic: Auto-Fix vs Human Review

After collecting normalized findings, the executor applies a strict priority check:

```go
if types.HasAskUserFindings(findings) {
    // Human review required: park the run and set awaiting_agent_since
    exec.parkRun(runID, findings)
    return
} else if autoFix := types.AutoFixableFindings(findings); len(autoFix.Items) > 0 {
    // Safe to auto-fix: enter the correction loop
    exec.runAutoFixLoop(ctx, runID, autoFix)
}

```

If **any** `ask-user` findings exist, the executor immediately parks the run (updating `awaiting_agent_since` in the database as seen in [[`internal/db/run.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/run.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/db/run.go#L23)) and waits for external approval via the daemon’s `HandleRespond` method (line 848 in [[`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go#L848)).

## The Auto-Fix Loop Implementation

When findings contain only `auto-fix` actions (or mixed `no-op` items), the executor enters a loop that applies corrections iteratively:

```go
for {
    // Create a fix-specific round referencing the original finding ID
    fixRound := exec.createFixRound(ctx, runID, finding)
    
    // Invoke the step's agent with a fix prompt
    output, err := agent.Fix(ctx, finding)
    
    // Merge any new findings emitted by the agent
    merged := types.MergeUserOverrides(current, nil, output.Findings.Items)
    // MergeUserOverrides preserves user-added items with source:"user" (line 170 in findings.go)
    
    // Persist the updated state
    db.SaveRound(fixRound.ID, merged)
    
    // Continue until no auto-fixable items remain
    if len(types.AutoFixableFindings(merged).Items) == 0 {
        break
    }
}

```

Each iteration creates a new round in the database, allowing the UI to display progress and enabling recovery if the process restarts mid-fix. The loop respects the global `auto_fix` configuration flag; if disabled, `AutoFixableFindings` are treated as standard findings requiring human approval.

## Integration with the Daemon and UI

The executor operates within the daemon context managed by [[`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go). Key integration points include:

- **Event Broadcasting** – The executor pushes `StepCompleted` and `AwaitingApproval` events to the TUI, which renders the "awaiting approval" badge tested in [[`viewport_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/viewport_test.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/tui/viewport_test.go)
- **User Responses** – When a user approves findings via the `axi` CLI, `HandleRespond` routes the approval back to the specific executor instance, which then resumes the pipeline
- **Cancellation** – The manager can signal cancellation to the executor, which gracefully stops the auto-fix loop after the current agent invocation completes

## Edge Cases and Guarantees

| Scenario | Executor Behavior |
|----------|-------------------|
| **No findings** | Proceeds to next step immediately |
| **Only `no-op` findings** | Treated as non-actionable; step marked approved |
| **Mixed `ask-user` and `auto-fix`** | Presence of any `ask-user` forces parking; auto-fix deferred until human items resolved |
| **Missing finding IDs** | `NormalizeFindings` generates deterministic UUIDs pre-routing |
| **User-added findings** | `MergeUserOverrides` stamps with `source:"user"`, preventing re-auto-fix |
| **Executor restart** | Reloads last persisted round from DB; resumes auto-fix loop or awaiting-approval state |

## Summary

- The **no-mistakes pipeline executor** operates as a finite state machine that transitions between running steps, awaiting human approval, and auto-fixing findings
- **`AutoFixableFindings`** and **`HasAskUserFindings`** (lines 156 and 220 in [`findings.go`](https://github.com/kunchenguid/no-mistakes/blob/main/findings.go)) provide the classification logic that determines routing
- **Auto-fix loops** are iterative, creating new database rounds for each correction attempt until no actionable items remain
- **Human-review findings** trigger an immediate park state via `awaiting_agent_since` (line 23 in [`run.go`](https://github.com/kunchenguid/no-mistakes/blob/main/run.go)), integrating with the daemon’s `HandleRespond` endpoint (line 848 in [`manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/manager.go))
- **Deterministic IDs** via `NormalizeFindings` (line 104) ensure stable merge behavior across pipeline restarts

## Frequently Asked Questions

### How does the executor prevent auto-fixing items that require human judgment?

The executor checks **`HasAskUserFindings`** before entering the auto-fix loop. If this function returns `true`—indicating any finding has `Action: "ask-user"`—the executor immediately parks the run and sets `awaiting_agent_since` in the database. This guarantees that human-review items block the pipeline regardless of whether other `auto-fix` items exist in the same result set.

### What happens if the no-mistakes daemon restarts during an auto-fix loop?

The executor persists every round—including intermediate fix rounds—to the database via the `*db.DB` handle. On restart, the executor reloads the last persisted round for the active run, inspects the findings using `AutoFixableFindings`, and either resumes the fix loop or restores the awaiting-approval state. This design ensures exactly-once semantics for fix attempts and prevents duplicate agent invocations.

### Can multiple auto-fix passes occur for a single original finding?

Yes. The auto-fix loop continues invoking the agent and creating new rounds until **`AutoFixableFindings`** returns zero items. If an agent’s fix attempt introduces a new detectable issue (for example, a lint error in the generated code), that new finding enters the next iteration. The loop breaks only when the output contains exclusively `no-op` or non-actionable findings.

### Where is the auto-fix behavior configured, and can it be disabled per-run?

Global defaults reside in [[`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go)](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go) under the `auto_fix` key. If this configuration is set to `false`, the executor treats findings with `Action: "auto-fix"` as requiring human review, effectively disabling the automatic correction loop. The executor checks this flag before calling `runAutoFixLoop`, ensuring the behavior is consistent across all pipeline steps.