How the Pipeline Executor Orchestrates Review, Test, Lint, Push, PR, and CI Steps in no-mistakes
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), 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 (lines 62‑75) accepts the database connection, path helpers, configuration, an agent implementation, the ordered step slice, and an optional event-stream callback.
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.
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). For each iteration, the executor checks for cancellation signals, respects user-defined skip flags, and delegates actual execution to executeStep (lines 38‑66).
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) wraps each step’s business logic with database bookkeeping, log streaming, and approval coordination. The lifecycle follows four distinct phases:
- Start – The executor marks the step as running via
db.StartStepand emits aStepStartedevent to any connected UI. - Execute – The step’s
Execute(*StepContext)method runs. Every concrete step implements thepipeline.Stepinterface, which requiresName()andExecute()methods. - Approval Handling – If a step returns a
NeedsApprovalflag, the executor blocks one.approvalCh. The UI or an external agent later callsExecutor.Respondto unblock the flow with anApprovalAction. - Finish – On completion,
CompleteStepWithStatuspersists the result, and aStepCompletedevent is emitted. If the step returnsskipRemaining=true, the executor marks all subsequent steps asSkipped.
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 (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 (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.
// 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.
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.goinitializes 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. - 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.
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 →