# How the Pipeline Executor Orchestrates Review, Test, Lint, Push, PR, and CI Steps in no-mistakes

> Understand how the no-mistakes pipeline executor orchestrates review, test, lint, push, PR, and CI steps for a streamlined workflow from local changes to merged pull requests.

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

---

**The pipeline executor in `no-mistakes` drives a deterministic, linear nine-step workflow that transforms local Git changes into merged pull requests, pausing for human approval via channels when steps require manual sign-off.**

The `no-mistakes` CLI automates the journey from uncommitted changes to production-ready code through a carefully orchestrated pipeline. At the center of this system sits the **pipeline executor** ([`internal/pipeline/executor.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/executor.go)), a Go component that coordinates review, testing, linting, and deployment phases while maintaining strict database-backed state consistency. Understanding how this executor orchestrates the Review, Test, Lint, Push, PR, and CI steps reveals the deterministic engine that turns developer intent into merged reality.

## Executor Construction and Run Initialization

Before executing any steps, the executor requires a fully configured environment. The `NewExecutor` constructor in [`internal/pipeline/executor.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/executor.go) (lines 62‑75) accepts the database connection, path helpers, configuration, an agent implementation, the ordered step slice, and an optional event-stream callback.

```go
func NewExecutor(database *db.DB, p *paths.Paths, cfg *config.Config,
    ag agent.Agent, steps []Step, onEvent EventFunc) *Executor

```

This constructor initializes the internal `approvalCh` channel that blocks steps when human input is required. When the CLI triggers `Execute`, the method first marks the run as *running* in the database, creates a dedicated log directory for the run, and inserts a `StepResult` row for every step in the pipeline so the UI can track progress in real time.

```go
if err := e.db.UpdateRunStatus(run.ID, types.RunRunning); err != nil { … }
logDir := e.paths.RunLogDir(run.ID)
…
for _, step := range e.steps { … e.db.InsertStepResult(run.ID, step.Name()) }

```

## Sequential Step Execution

The executor processes steps in strict order using the slice returned by `steps.AllSteps()` (defined in [`internal/pipeline/steps/common.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/common.go)). For each iteration, the executor checks for cancellation signals, respects user-defined skip flags, and delegates actual execution to `executeStep` (lines 38‑66).

```go
for i, step := range e.steps { … e.executeStep(ctx, step, sr, run, repo, workDir, logDir) }

```

This linear design ensures that **ReviewStep** always precedes **TestStep**, and **PushStep** cannot occur until **LintStep** succeeds. The executor maintains this sequence unless a step explicitly signals that remaining steps should be skipped or a fatal error terminates the run.

## Step Lifecycle and Approval Orchestration

The `executeStep` method (lines 78‑120 of [`executor.go`](https://github.com/kunchenguid/no-mistakes/blob/main/executor.go)) wraps each step’s business logic with database bookkeeping, log streaming, and approval coordination. The lifecycle follows four distinct phases:

1. **Start** – The executor marks the step as *running* via `db.StartStep` and emits a `StepStarted` event to any connected UI.
2. **Execute** – The step’s `Execute(*StepContext)` method runs. Every concrete step implements the `pipeline.Step` interface, which requires `Name()` and `Execute()` methods.
3. **Approval Handling** – If a step returns a `NeedsApproval` flag, the executor blocks on `e.approvalCh`. The UI or an external agent later calls `Executor.Respond` to unblock the flow with an `ApprovalAction`.
4. **Finish** – On completion, `CompleteStepWithStatus` persists the result, and a `StepCompleted` event is emitted. If the step returns `skipRemaining=true`, the executor marks all subsequent steps as `Skipped`.

This design allows high-risk steps like **ReviewStep** or **LintStep** to pause for human verification while maintaining automated throughput for routine operations.

## The Fixed Nine-Step Pipeline Definition

According to [`internal/pipeline/steps/common.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/common.go) (lines 24‑41), `AllSteps()` returns a hardcoded slice that defines the exact execution order. The pipeline executor orchestrates these nine steps sequentially:

- **IntentStep** – Parses the user’s high-level intent and determines the target branch.
- **RebaseStep** – Rebases the local branch onto the latest remote base to ensure linear history.
- **ReviewStep** – Runs agent-driven code review with structured risk assessment.
- **TestStep** – Executes the repository’s test suite and captures pass/fail metrics.
- **DocumentStep** – Generates documentation evidence for the changes.
- **LintStep** – Runs linters (golint, staticcheck, etc.) and produces structured findings.
- **PushStep** – Performs safe force-pushes with lease-guard logic to update the remote.
- **PRStep** – Opens or updates the pull request on GitHub, GitLab, or Bitbucket.
- **CIStep** – Monitors CI status, auto-fixes flaky checks, and merges when green.

This fixed ordering ensures that code quality gates (review, test, lint) always execute before publication (push, PR) and before the final CI monitoring phase.

## Inside a Step: Review Implementation Example

To understand how individual steps interact with the executor, examine the `ReviewStep` in [`internal/pipeline/steps/review.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/steps/review.go) (lines 31‑86). This step illustrates the standard pattern: resolve context, detect file changes, build a rich prompt, and invoke the agent with a strict JSON schema.

```go
// Resolve base SHA and check for relevant files
baseSHA, _ := resolveBranchBaseSHA(ctx, sctx.Repo, sctx.Config)
if len(files) == 0 {
    return &pipeline.StepOutcome{Findings: "low-risk"}, nil
}

// Build prompt and invoke agent
result, err := sctx.Agent.Run(ctx, agent.RunOpts{
    Prompt:     prompt,
    CWD:        sctx.WorkDir,
    JSONSchema: reviewFindingsSchema,
    OnChunk:    sctx.LogChunk,
})

```

The agent returns structured findings that the executor stores in the database. All other steps follow this template: they prepare a context, invoke either the agent or a local tool (like `go test` for the **TestStep**), and return a `StepOutcome` that may contain findings, summaries, or skip directives.

## Completion and Error Handling

After the loop finishes, the executor updates the run status to `RunCompleted` and emits a final `RunCompleted` event (lines 69‑75). If any step returns an error, the executor catches it and invokes `failRun`, which sets the run status to *failed* and halts execution immediately.

```go
if err := e.db.UpdateRunStatus(run.ID, types.RunCompleted); err != nil { … }
e.emitRunEvent(ipc.EventRunCompleted, run, repo)

```

This deterministic cleanup ensures that the database always reflects the terminal state of the pipeline, whether successful, failed, or partially completed due to skips.

## Summary

- The **pipeline executor** in [`internal/pipeline/executor.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/pipeline/executor.go) initializes runs by creating log directories and database records for every step.
- It executes a **fixed nine-step sequence** (Intent → Rebase → Review → Test → Document → Lint → Push → PR → CI) defined in [`steps/common.go`](https://github.com/kunchenguid/no-mistakes/blob/main/steps/common.go).
- Each step runs through a standardized lifecycle: start, execute, optional approval via `approvalCh`, and completion with database persistence.
- Steps like **ReviewStep** invoke the agent with JSON schemas, while utility steps like **LintStep** wrap external tools.
- The executor handles errors by marking runs as failed and supports early termination when steps return `skipRemaining=true`.

## Frequently Asked Questions

### How does the pipeline executor handle human approval during long-running steps?

When a step returns a `NeedsApproval` flag, the executor blocks on the internal `approvalCh` channel. The UI or an external process calls `Executor.Respond` with an `ApprovalAction` to unblock the step. This mechanism allows high-risk operations like deployment or final merges to require explicit human sign-off while maintaining automated execution for routine steps.

### What happens if the lint step fails in the no-mistakes pipeline?

If the **LintStep** returns findings with a severity that triggers early termination, it sets `skipRemaining=true` in the `StepOutcome`. The executor detects this flag and marks all subsequent steps (Push, PR, CI) as `Skipped` in the database. This prevents publishing code that fails quality gates while still recording the lint results for developer review.

### Can steps be skipped selectively without modifying the source code?

Yes. The executor accepts a `skips` map during initialization that contains step names to bypass. During the sequential loop, the executor checks if the current step name exists in `skips` and, if present, records it as skipped without invoking `executeStep`. This allows users to bypass specific phases like **DocumentStep** or **TestStep** when running locally or in demo mode.

### Where does the executor store logs and step results during execution?

The executor creates a dedicated log directory via `e.paths.RunLogDir(run.ID)` for each run. Step-specific output streams through the `OnChunk` callback to both the console and persistent files. Additionally, every step status update writes to the database via `InsertStepResult`, `StartStep`, and `CompleteStepWithStatus`, ensuring the UI can display real-time progress even if the CLI process disconnects.