Skip to content
xiv

linear-implement

The core workflow: take a Linear issue, plan it, then iterate implement → validate → review until the work passes, and commit it on a dedicated branch. Everything stays local — no push, no PR.

  • Triggered by: xiv implement <issueId> (alias li)
  • Also runs as a subflow inside: linear-to-pr (behind xiv ship) and stack-build (once per stack entry)
  • Source: pack/workflows/linear-implement.tsx

Task graph

Loading diagram...

What each task does

TaskKindWhat it does
fetch-issueagentFetches the Linear issue (key, title, description, acceptance criteria, comments) via MCP into linearIssueSchema. Sets fetched: false on any auth/not-found/transport failure.
fetch-gatedeterministicHard gate: throws when the issue was not actually retrieved. Without it, a fetch error's text would flow downstream and be faithfully "implemented" as if it were the issue.
planagentProduces { summary, steps[] } from the issue and its criteria. With tdd: true the plan must start with test steps.
impl:implementagentWrites the code, following the plan and citing evidence per acceptance criterion. On later iterations the prompt appends the previous attempt's validation failures and review findings.
impl:validateagentRuns the repo's own lint/test/typecheck commands and reports { allPassed, failingSummary }. It arbitrates on exit codes, not judgement.
impl:reviewagentLocal code review of the working-tree diff against the acceptance criteria (the judgment is the xiv-review-core skill): a verdict (approve / comment / request_changes) plus prioritized findings.
finalizeagentCommits all work on the branch feat/<issue-key-lowercase> (created if needed), and reports branch, head SHA, issue key/title, criteria, and a summary. Explicitly never pushes or opens a PR.
compose-outputdeterministicMerges the finalize report with the final review's verdict and unfixed findings into the workflow result.

Loop exit condition

The loop exits when done computes true for the current iteration:

done = validation passed AND review did not request changes

Validation is the arbiter; the review is advisory. The review may only ever withhold done, never grant it:

  • request_changes → not done; the findings are fed into the next implement pass.
  • comment → non-blocking; done if validation passed (the findings still ride along as feedback if the loop runs again for another reason).
  • approve → done if validation passed.
  • Review skipped, crashed, or unparseable → the iteration simply has no review and validation decides alone. The review task is continueOnFail and its verdict field has no default, so a reviewer that cannot produce a real verdict fails its task rather than fabricating one — and a failed review can never wedge the loop.

Each iteration is judged on its own outputs: the done reads are deliberately unpinned to the current iteration, so a request_changes from iteration 1 cannot block iteration 3, and a stale approve cannot wave a later broken attempt through.

Iteration cap

Default 3 passes of implement → validate → review, from the maxIterations input (--max-iterations on the CLI), clamped to 1–10. On hitting the cap the loop returns the last attempt (onMaxReached="return-last") instead of failing the run — whatever the last review still reported becomes the result's knownGaps. A clean issue exits after one pass, so raising the cap only costs anything for issues that actually fail a pass.

Inputs and outputs

Input (inputSchema):

FieldDefaultMeaning
issueId""The Linear issue key to implement.
tddfalseForce a tests-first plan and implementation order.
skipAcceptanceReviewfalseSkip impl:review entirely; validation still gates every pass.
maxIterations3Loop cap, clamped 1–10.

Output (finalizeSchema — the table named output, which is what subflows consume):

FieldMeaning
branch, headShaWhere the committed work lives. Branch is derived from the input issueId (feat/<key>) so stacked builds stay deterministic even if the fetched issue output is unreadable.
issueKey, title, acceptanceCriteriaEchoed issue facts.
summaryWhat was implemented.
reviewVerdictThe final review's verdict, or null when no review ran.
knownGaps[]Findings the final review raised that the run shipped without fixing — { severity, title, detail, path }. Line numbers are deliberately dropped: these outlive the diff they were raised against.

Model assignment

A balanced mixed pipeline by default (cheap tier shown; every step is overridable per-run via XIV_*_ENGINE, and XIV_TIER=quality raises the three steps where capability matters — see /concepts/model-tiers):

  • fetch-issue, impl:validate — light codex model at low effort: retrieval and running commands, no reasoning to pay for.
  • plan — the codex planning model; tiered by effort (low → xhigh), since plan quality most determines the rest of the run.
  • impl:implement — heavy Claude model (Sonnet 5 cheap / Opus 5 quality): sustained multi-file editing.
  • impl:review — the codex review model at high effort: a false approval ends the loop, so findings must be trustworthy.
  • finalize — pinned to Sonnet 5 with autonomous git permissions and a hard never-merge rule; committing is mechanical, so it never rides the tier ladder.

Failure semantics

  • fetch-gate fails the run hard on a bad fetch — fix Linear MCP auth and re-run.
  • impl:review is continueOnFail; nothing else in the loop is. A crashed implement or validate task fails its attempt normally.
  • The heavy tasks carry 30-minute timeouts with 10-minute heartbeats.
  • Runs are resumable: fetch-issue and plan execute once, before the loop (their reads are pinned to iteration 0), so a resumed run does not refetch or replan.