# 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.

- URL: https://letsship.ai/blog/claude-code-on-linear-issues
- Author: Önder Ceylan
- Published: 2026-09-12

To get [Claude Code](https://code.claude.com/docs/en/overview) 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](https://letsship.ai/docs).

| Stage          | What it adds                                                                                                                 | Where you see it                             |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| Planner        | A plan written from the issue, the repository and the acceptance criteria, before any code exists                            | The Linear issue                             |
| Builder        | The change, made in an isolated sandbox, with your formatter, linter, typecheck and tests run before it pushes               | The pull request                             |
| CI             | Your own pipeline as a gate, with a failure sent back to the builder along with its logs                                     | The pull request's checks                    |
| Reviewer       | A read of the diff against the plan, raising blocking and non-blocking findings                                              | The Linear issue                             |
| Preview deploy | The change running on a live environment, [deployed by your pipeline with your credentials](https://letsship.ai/docs/configuration/deployments) | The preview URL                              |
| QA             | The acceptance criteria exercised against that preview, with proof of what it checked                                        | The 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](https://letsship.ai/blog/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](https://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](https://letsship.ai/docs/getting-started/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](https://letsship.ai/docs/getting-started/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](https://letsship.ai/docs/getting-started/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
# 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](https://letsship.ai/docs/configuration/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](https://letsship.ai/blog/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](https://letsship.ai/data/swe-in-a-team-per-task.csv). 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](https://letsship.ai/docs/configuration/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.

```md
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](https://linear.app/docs/agents-in-linear), 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](https://letsship.ai/docs/cli) or the [API](https://letsship.ai/docs/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:

```yaml
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](https://letsship.ai/docs/configuration/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](https://letsship.ai/docs/configuration/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:

```bash
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](https://letsship.ai/docs/api/missions)).

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](https://letsship.ai/blog/codex-reviewer-for-claude-code).

## 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.
