Our SWE-in-a-team benchmark research is ready.Read the results.
← Blog
||Guides

Claude Code on Linear: assign an issue, get a tested pull request

Assign a Linear issue to SHIP and Claude Code plans it, builds it, clears your CI, reviews it and tests it on a preview before the pull request reaches you.

To get Claude Code working your Linear issues, connect Linear and GitHub to SHIP, add an Anthropic API key or subscription credential, and assign the issue to SHIP. The claude-code harness runs every role by default: it plans the change, builds it in an isolated sandbox, waits on your CI, reviews the diff and tests the acceptance criteria on a preview, keeping the proof. Cost is recorded per mission and per stage, and the pull request waits for you to merge. In the SWE-in-a-team benchmark, claude-code ran the planner, reviewer and tester in all 260 missions.

See what a mission adds beyond a draft

An agent that reads an issue and pushes a branch hands you a draft that nobody has planned, run through CI, reviewed or tried yet. A SHIP mission does that work in stages before the pull request reaches you, and each stage leaves something you can check. The table follows the order in the SHIP introduction.

StageWhat it addsWhere you see it
PlannerA plan written from the issue, the repository and the acceptance criteria, before any code existsThe Linear issue
BuilderThe change, made in an isolated sandbox, with your formatter, linter, typecheck and tests run before it pushesThe pull request
CIYour own pipeline as a gate, with a failure sent back to the builder along with its logsThe pull request's checks
ReviewerA read of the diff against the plan, raising blocking and non-blocking findingsThe Linear issue
Preview deployThe change running on a live environment, deployed by your pipeline with your credentialsThe preview URL
QAThe acceptance criteria exercised against that preview, with proof of what it checkedThe Linear issue and the mission's artifacts

A failing gate sends the work back to the builder with the feedback instead of ending the mission. Across the 260 missions in SWE-in-a-team, the loop corrected 40 of the 46 runs it objected to without a person stepping in.

Connect Linear and GitHub

Both connections are made once, during onboarding at letsship.ai:

  1. On the Connect step, click Connect Linear and complete Linear's OAuth flow. The grant makes SHIP an agent in your workspace that people can assign and mention, and lets it read issues, post updates and run agent sessions (Connect Linear).
  2. Click Connect GitHub and install the SHIP GitHub App on the repositories it may work in. It asks for Contents and Pull requests with read and write, and Checks and Actions with read only, which is what cloning, pushing the agent's branch, opening the pull request and reading a failing run's logs take (Connect GitHub).
  3. On the Project step, choose Linear as where the work is planned, select the team, and pick the repository. A project covers exactly one Linear team and one repository.

Check one thing in your CI before the first mission. SHIP learns about CI through check_suite.completed, so your workflows need to run on pull_request events. A workflow that only runs on pushes to the default branch produces nothing for a pull request to gate on.

Add an Anthropic key or subscription

On the Connect your AI providers step, add an Anthropic credential. An API key works, and so does a subscription credential. SHIP checks it with a live call to Anthropic when you save it, so a typo or a key from the wrong account shows up there instead of on your first mission (LLM credentials).

One credential is enough for the whole loop. Planner, builder, reviewer and QA all default to the claude-code harness, and the spend, rate limits and data handling stay on your own Anthropic account, since nothing is pooled across organizations.

The key never enters the sandbox where Claude Code runs. SHIP starts the agent's container with a placeholder and rewrites the credential on its way out to Anthropic, which means nothing the agent does, or is talked into doing, can read the key back. If that rewrite ever fails to happen, the placeholder reaches Anthropic and the call is refused.

Pin Claude Code and a model to each role

With no ship.yml in the repository, every role runs claude-code on the opus alias. To choose per role, add the file at the repository root:

# yaml-language-server: $schema=https://letsship.ai/schema/ship.yml.json
version: 1

deployments: {} # add a preview entry here to test against a live environment

agents:
  planner:
    harness: claude-code
    model: opus
  builder:
    harness: claude-code
    model: sonnet
    effort: high
  reviewer:
    harness: claude-code
    model: opus

A model alias (opus, sonnet or haiku) follows the newest model of its family, while a concrete versioned model id stays exactly where you put it. effort maps to Claude Code's own reasoning control and has to be one of low, medium, high, xhigh or max, or the stage fails before it starts. A role you leave out keeps the defaults, and deployments is required even when it is empty, because a file that fails validation takes the rest of its configuration down with it (Schema). SHIP reads the agents block from ship.yml on your main branch, so a change applies to missions that start after it merges.

For the builder, SWE-in-a-team has numbers. On the same 20 tickets, one run each, claude-code resolved all 20 as the builder on both claude-sonnet-5 ($4.31 per resolved ticket) and claude-opus-5 ($5.48), with cost counted API-equivalent from the per-run data. Moving a role up a model size is roughly a 5x decision on that role's spend, so the per-stage cost on your own missions is where to check whether it paid off.

If the repository already carries Claude Code sub-agents, you can reuse them. A file at .ship/agents/<role>.md has the same shape as .claude/agents/*.md, and SHIP appends its body after its own charter for that role, so an existing reviewer file can be copied in unchanged, or pointed at with prompt: .claude/agents/security.md under agents.reviewer. SHIP also reads CLAUDE.md at the repository root and resolves a standalone @AGENTS.md line in it. Frontmatter keys model, tools and harness in those files are ignored, with a warning on the mission, because ship.yml is the one place execution config lives (Agent prompts).

Assign the issue to SHIP

Write the issue the way you would for a colleague: what you want, and how you will know it is done. Acceptance criteria written as checkboxes are what QA verifies against, so that is the part to be specific about.

Export bookings as CSV for a date range

Accounting needs the bookings between two dates as a spreadsheet.

- [ ] GET /api/export?type=bookings returns CSV and requires a signed-in session
- [ ] Optional from and to parameters keep only bookings whose class starts inside the range, both ends inclusive
- [ ] Columns, in order: Starts, Class, Member, Email, Status

Then assign the issue to SHIP. The mission answers in Linear's agent session, the same place other agents reply, and posts its plan, review verdict and QA verdict there as it goes. The pull request it opens links back to the issue.

Mentioning @SHIP on an issue also starts a mission, and it is how you talk to the agents in a comment. Only a mention written in Linear's own composer counts, though. A comment posted through Linear's API does not raise the event that starts a mission, so a script that needs to start work should call the CLI or the API instead.

Steer the mission from the issue

Reply on the issue to change course. The agents pick the instruction up on the mission's open pull request, and a request aimed at what one stage produced reruns that stage without restarting the rest. Your reply also outranks the automated guards: if the mission keeps failing the same way, the instruction clears that history and the loop continues on what you said.

Three mentions control the run itself. @SHIP pause holds the next dispatch while the current stage finishes, @SHIP resume releases it, and @SHIP stop ends the mission and leaves the pull request open for later. The console has the same three as buttons, and from a terminal ship instruct SHIP-412 -m "..." reaches a ticket's mission as well.

To read the plan before any code exists, put a checkpoint on the planner:

agents:
  planner:
    checkpoint: true

The mission then holds once the plan is written and says so on the issue. Resume it, or reply with what you want changed and the planner plans again. The builder and reviewer can hold too, after CI has run and after a clean review; QA cannot, because the pull request at the end is already that gate (Human in the loop).

A mission that gets stuck stops by itself. After two consecutive rounds where review, QA or CI fails the same way, it shows Needs attention in the console, the Linear issue moves to In Review, and the reason is posted on the issue with the last round's feedback. A reply sends the agents back in and resets the counters (Retry limits).

Read the verdicts, proof and cost

When the mission finishes, the issue carries the plan, the review verdict and the QA verdict. The mission's artifacts hold what QA actually checked, such as the trace of a test session or a screenshot of the change working. With no preview configured, QA still runs: it builds and serves the app locally, loads a browser extension or exercises an API, whichever surface qaSurface detects in your project or you set explicitly.

Cost comes from list prices for the model that served each call, not from what the harness reports about itself, and SHIP attributes it per mission and per stage. By default a single mission stops at $5, and the organization has a $50 daily cap that you can change from Settings or the API. When the cap is reached, work already running finishes and new dispatches are refused with a message naming the cap and when it resets.

The same numbers are readable outside the console:

ship status SHIP-412 --json

For every stage attempt, GET /v1/missions/{issueId}/provenance returns the harness, model, provider, token usage, cost and duration. SHIP keeps those records for about 30 days, so snapshot them if you want a longer history (Missions API).

SHIP marks the pull request ready once review and QA are satisfied and stops there, so the merge is yours unless you have turned on auto-merge for the project or for that mission. If you want a second provider reading Claude Code's diffs before you do, put Codex on the reviewer.

Questions

Can Claude Code work on Linear issues?

Yes. Connect Linear to SHIP and assign an issue to it, and SHIP runs the claude-code harness, its default for every role, to plan the change, build it, review it and test it before a pull request reaches you.

How do I connect Claude Code to Linear?

In SHIP's onboarding, click Connect Linear and complete Linear's OAuth flow, install the SHIP GitHub App on your repositories, and add an Anthropic credential. Then create a project that points at one Linear team and one repository.

How do I trigger Claude Code from a Linear issue?

Assign the issue to SHIP, or mention @SHIP on it. A comment posted programmatically through Linear's API does not start a mission, so scripts should use the SHIP CLI or API instead.

Can I use a Claude subscription instead of an API key?

Yes. SHIP accepts an Anthropic subscription credential alongside an API key, verifies it with a live call when you save it, and never puts it inside the sandbox where Claude Code runs.

What does a Claude Code mission cost?

SHIP measures cost from list prices for the model that served each call and records it per mission and per stage, with a $5 per-mission and a $50 per-organization daily limit by default. In the SWE-in-a-team benchmark, a claude-code builder on claude-sonnet-5 resolved all 20 tickets for $4.31 per resolved ticket (API-equivalent).

Does SHIP merge the pull request by itself?

Not by default. SHIP marks the pull request ready once review and QA are satisfied and leaves the merge to a person, unless you turn on auto-merge for the project or the mission.

Assign your next Linear issue to SHIP

See Claude Code plan, build and test a ticket from your own backlog, and open a pull request that has already cleared your CI.