How the no-mistakes Pipeline Executor Handles Findings and Auto-Fix Logic
The no-mistakes pipeline executor collects findings from each step, classifies them using helper functions in 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). 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 whereAction == "auto-fix"(implemented around line 156 in [findings.go](https://github.com/kunchenguid/no-mistakes/blob/main/internal/types/findings.go#L156))HasAskUserFindings(findings)– returnstrueif any finding hasAction == "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#L69)). During construction, it receives:
- A database handle (
*db.DB) for persisting run and round state - A configuration object (
*config.Config) containing the globalauto_fixdefaults from [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:
- Normalizes IDs – calls
NormalizeFindings(line 104 in [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 - Persists the round – writes the findings JSON to the database round record so the UI can render them on resume
- Broadcasts state – emits a
StepCompletedevent 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)
Decision Logic: Auto-Fix vs Human Review
After collecting normalized findings, the executor applies a strict priority check:
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#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#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:
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). Key integration points include:
- Event Broadcasting – The executor pushes
StepCompletedandAwaitingApprovalevents to the TUI, which renders the "awaiting approval" badge tested in [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
axiCLI,HandleRespondroutes 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
AutoFixableFindingsandHasAskUserFindings(lines 156 and 220 infindings.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 inrun.go), integrating with the daemon’sHandleRespondendpoint (line 848 inmanager.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) 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.
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 →