# Turn Linear Issues Into Pull Requests for an ASP.NET Core App

Yes. Label a Linear issue and an agent implements it in your ASP.NET Core API, adds xUnit tests, runs dotnet test and opens a pull request that links the issue.

[Run this on Rush](https://cloud.getrush.ai/run/linear-issue-to-pr/aspnet-core/), in a Project that has your repository.

## What the agent does

The agent starts from the issue, not from a generic .NET layout. In a server shaped like bitwarden/server it reads global.json first, which pins the SDK to 10.0.103 with rollForward set to latestFeature, then Directory.Build.props, where every project targets net10.0, TreatWarningsAsErrors is true and RestorePackagesWithLockFile is on, so a new package reference also changes a packages.lock.json. The same file fixes the test stack for the whole tree: xUnit 2.6.6, NSubstitute 5.1.0 and AutoFixture 4.18.1. The issue text decides where the change goes. A ticket about folders or ciphers lands in src/Api/Vault/Controllers, where CiphersController is routed at ciphers and FoldersController at folders under the Application authorization policy, with the logic behind them in src/Core/Vault as Commands, Queries, Repositories and Services. A ticket about login, two-factor or SSO lands in src/Identity, whose IdentityServer folder holds the RequestValidators, ClientProviders and ProfileService.

The controllers are thin on purpose, and the agent keeps them that way. FoldersController resolves the caller with GetProperUserId on IUserService, reads through IFolderRepository, saves through ICipherService and hands bulk deletes to IDeleteManyFoldersCommand, with a check that refuses more than 500 ids on the cloud host but allows them when GlobalSettings.SelfHosted is true. A new behaviour follows that shape: a command or query interface in src/Core/Vault, its implementation registered in VaultServiceCollectionExtensions, and a controller action that validates the request model and calls it. The agent does not put repository calls or business rules into the controller.

Tests follow the files they cover. test/Api.Test/Vault/Controllers/FoldersControllerTests.cs is the pattern: the class is decorated with ControllerCustomize for the controller under test and SutProviderCustomize, each test is a Theory with BitAutoData, and SutProvider hands out the controller with every dependency substituted, so a test sets GetProperUserId to return a user id and asserts that the command received the right ids. Command logic gets its own file under test/Core.Test/Vault/Commands, as DeleteManyFoldersCommandTest.cs does. When the ticket spans the auth pipeline or real database state, test/INTEGRATION_TEST.md says to drive it over HttpClient through a WebApplicationFactory with the in-memory SQLite database the factory owns, and to put that test in the matching Test project or in Api.IntegrationTest.

The agent runs what CI runs. .github/workflows/test.yml calls dotnet test ./test --configuration Debug, which builds every test project through test/Bitwarden.Tests.proj, a Microsoft.Build.Traversal project, and then runs the same command against bitwarden_license/test. That workflow also installs a Rust 1.97.1 toolchain before testing, so the agent installs one in the sandbox rather than skipping the projects that need it. With the suite green it opens a pull request whose description links the Linear issue and quotes the test command and its summary. The pull request link then goes back onto the issue as a comment.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Implement the Linear issue in this run's resources in this ASP.NET Core codebase. Read the issue and its comments first. Start from global.json and Directory.Build.props for the SDK, target framework and test packages, then find the owning area: src/Api/Vault with src/Core/Vault for vault work, src/Identity for authentication work. Extend the existing controller, command, query or validator rather than adding a parallel one, keep the controller thin, and add xUnit tests beside the code you change, using the SutProvider and BitAutoData pattern in test/Api.Test and test/Core.Test, or an HttpClient integration test as test/INTEGRATION_TEST.md describes when the change crosses the auth pipeline or needs database state. Run dotnet test ./test --configuration Debug as .github/workflows/test.yml does, and bitwarden_license/test as well. Do not change the SDK version, the target framework or any workflow file. Open a pull request whose description links the issue, lists the files and quotes the test output, and comment the pull request URL on the issue.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    },
    {
      "url": "https://linear.app/your-workspace/issue/ENG-42"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request on its own branch, and the pull request URL as a comment on the Linear issue. The diff is the production files the issue pointed at, usually one controller under src/Api and one command, query or validator under src/Core or src/Identity, plus the matching test file, and a packages.lock.json only when a package reference changed. The description links the issue, lists each file with why it changed, and quotes the dotnet test command with its pass and fail counts from the sandbox.

The session read names the repository and the pull request, and the transcript keeps every step: the issue read, the files opened, each dotnet build and dotnet test invocation and its output. A reviewer does not have to re-run the traversal project to see which test projects ran.

Before merging, a reviewer checks that the new test fails on the old code and passes on the branch, that the controller still resolves the user through IUserService rather than trusting a request field, that TreatWarningsAsErrors is still on, and that the pull request did not touch global.json, Directory.Build.props or .github/workflows to make the build pass.

## Run it on every Linear issue labelled agent

Add an automation to the Project with this prompt and a linear trigger on linear.issue.labeled with the label you reserve for the agent in its labels. Each labelled issue starts one run with the issue as its context, on its own branch; changes made by an app integration never fire the trigger, so a comment the agent posts does not start a second run. From your own server, the same run starts with this request and the issue URL in resources, and your handler forwards the pull request URL to Linear when the run completes.

Give the API key a webhook URL and Rush POSTs run.completed, run.failed or run.cancelled when the turn ends, signed with the whsec_ secret, with data.session_url to read for the pull request. A key runs at most max_concurrent sessions at once, 4 by default, and answers 429 past that, so a label applied across a backlog should be queued.

## Why run it on Rush?

A Bitwarden-sized solution is slow to set up on a laptop: the .NET 10 SDK global.json asks for, a Rust toolchain for the projects that need it, and a test run that builds dozens of projects through one traversal file. The agent does that in its own sandbox with its own clone, and the sandbox parks with its disk when the turn ends, so a follow-up on the review picks up where the build left off.

The proof is the pull request plus the transcript, not a claim that the tests passed. The exact dotnet test output and the test file the new case landed in are kept on the session. Several labelled issues run at once, each in its own sandbox with its own pull request, up to the key's concurrency limit.

## Frequently asked questions

### Will it add a new NuGet package to make the change easier?

Only when the issue needs one, and then the lockfile changes with it because RestorePackagesWithLockFile is on. The test stack is fixed in Directory.Build.props, so a new test uses xUnit, NSubstitute and AutoFixture as every existing test does, not a second mocking library.

### Does it run the licensed test projects too?

Yes. CI runs dotnet test on ./test and then on ./bitwarden_license/test, and the prompt asks for both. A failure in either is reported in the pull request rather than hidden by running only the open-source tree.

### What if the issue touches the identity server?

Then the change goes in src/Identity, most often a request validator under IdentityServer/RequestValidators, with a test in test/Identity.Test/IdentityServer beside the existing validator tests. When the flow spans the token endpoint and an API resource, the agent writes an HttpClient integration test as test/INTEGRATION_TEST.md asks.

### How does the issue text reach the agent?

The Linear URL in resources puts the issue in the prompt, read through your Linear connection, and the trigger passes the labelled issue the same way. The repository is cloned through the Rush GitHub App installation; a repository no installation covers answers 422 RESOURCE_NOT_ACCESSIBLE.

### Will it bump the SDK or target framework?

No. global.json and Directory.Build.props stay as they are, and the prompt forbids editing them. If the toolchain they name is not available in the sandbox, the answer says so instead of rolling the version forward.
