# Ship code with engineering tickets

> Take a ticket from Analyze to Release: approve the plan, follow the pull request and release the change.

An engineering ticket is a bug, a feature or a change to the code of your product. The Engineer takes each ticket
through five steps, and stops for you only where your word counts. This page tells you how to make a ticket, approve
its plan, follow its pull request and release the change.

## Before you begin

- An Engineer in the troop. Owners and admins hire one. See [Hire and manage teammates](/docs/teammates).
- GitHub, connected, with a test command for each repo. See [GitHub](/docs/apps/github).
- A Claude plan. The Engineer reads and writes code with Claude Code, so it needs a Claude subscription or an
  Anthropic API key. See [Choose AI plans and models](/docs/models).
- A QA tester, if you want a second review and a test of each change. See
  [Test your product with the QA tester](/docs/tests).

## The five steps

| Step        | Who does it               | What happens                                                                                               |
| ----------- | ------------------------- | ---------------------------------------------------------------------------------------------------------- |
| **Analyze** | The Engineer              | It reads the ticket and the code. It writes the analysis: the problem, and the checks under **Done when**. |
| **Plan**    | The Engineer              | It writes the plan: the approach, the decisions, the steps, the files and a test for each check.           |
| **Build**   | The Engineer              | It changes the code on a branch of its own, runs the tests of the repo and opens a pull request.           |
| **Test**    | GitHub and the QA tester  | GitHub's checks run, reviewers read the change, and the QA tester tests it.                                |
| **Release** | A person, or auto-release | Troopy merges the commit that passed every gate into the default branch.                                   |

The status of a ticket still says who has it now. A ticket that waits for its release is at **Release** and
**In review**. A released ticket is **Done**.

On **Tasks**, the **Engineering** tab shows the statuses of all its tasks. Select **Steps** to see the steps as
columns: **Backlog**, **Analyze**, **Plan**, **Build**, **Test**, **Ready to release** and **Released**. **Released**
shows the tickets of the last 14 days, and only tickets show there. A card shows the state of GitHub's checks, such as **Waiting for checks**,
and the try of the change, such as **Try 2 of 4**.

## Make a ticket

1. Open **Tasks**, and select the **Engineering** tab.
2. Select **New task**.
3. Under **What is it?**, select **Bug**, **Feature** or **Change**.
4. Write a title. Answer the questions that the brief box shows.
5. Select **Create task**.

The ticket goes to the Engineer that has the fewest tickets at work, and starts at **Analyze**. If you set its status
to **Backlog**, it waits there until you move it to **Analyze**. With no Engineer in the troop, the Chief of Staff
proposes to hire one.

Tickets also come from these places:

- An engineering task that you give to an Engineer. A sub-task, a task with sub-tasks, and the task of a routine or of
  a chat stay out of the steps.
- **Fix it** on an incident. See [Catch the errors of your apps](/docs/incidents).
- **Make a bug ticket** on a problem of a test run, and the bug ticket of a place whose checks begin to fail. See
  [Test your product with the QA tester](/docs/tests).

## Answer questions and approve the plan

At **Analyze**, the Engineer asks all of its questions at one time. The ticket then waits in
[Needs you](/docs/needs-you). When you answer all of them, the Engineer goes on.

When the plan is ready, the ticket shows **Approve the plan**, and Needs you shows **Plan to approve**.

1. Open the ticket from **Needs you**, or from the board.
2. Read the **Analysis** and the **Plan**.
3. If the plan is correct, select **Approve the plan**.
4. If it is not correct, select **Ask for changes**. Write what must change, then select **Ask for these changes**.

After your yes, the Engineer starts to change the code. The analysis and the plan lock. To change them again, move the
ticket back to **Plan**.

You can edit the analysis and the plan where they stand. Each change saves by itself. While the Engineer works on the
ticket, the two are read-only. A yes approves the version that you read. If the plan changed while you read it, Troopy
refuses the yes. Read the plan again, then approve it.

> [!NOTE]
> A ticket of an incident, and work that an event from outside Troopy started, always wait for a person's yes to the
> plan. The setting **Ask a person to approve the plan** does not change this.

## Follow the build

At **Build**, the Engineer changes the code in a copy of the repo, on the computer of the workspace. Each ticket
has its own branch, like `troopy/vir-12-fix-the-signup-bug`. Troopy pushes only branches under `troopy/`. A build can
take up to 40 minutes.

Troopy runs the test command of the repo on each change. Only a change that passes those tests becomes a pull request.
The pull request opens as a draft. Troopy marks it ready for review when every gate passed.

The **Code** section of the ticket shows the repo and the branch, the result of the tests, the files that changed and
the pull request. Troopy refuses a commit that holds something with the shape of a secret, or that changes
`.github/workflows`.

## Follow the gates at Test

At **Test**, gates read the change. Troopy reads the pull request every 2 minutes.

- **GitHub's checks.** With **GitHub's checks must pass** on, the ticket waits for the checks of the repo. A failed
  check sends it back to **Build** with what failed. A repo that shows no checks for 3 minutes after a push has none.
- **Reviews on GitHub.** A review that asks for changes sends the ticket back to **Build** with the words of the
  reviewer. Only the reviews of the owner of the repo, the members of its organization and its collaborators count.
- **The QA tester.** With a QA tester in the troop, it tests the change after the checks. A pass moves the ticket on.
  A fail sends the ticket back to **Build** with the report.
- **A new commit.** A push to the pull request sends the ticket back to **Test**. The gates then read the new commit.

The **Checks** panel of the ticket lists each check of GitHub, with a link to it. The **Test** section shows the latest
test run of the QA tester, with **Open the report**.

Each send-back to **Build** counts one try. After the number of tries in **Tries before it asks you**, the Engineer
stops, and the ticket waits for you. Without a QA tester, the ticket goes on when GitHub's checks pass.

## Release the change

When every gate passed, the ticket moves to **Ready to release**. How it goes live depends on
**How changes go live**:

- **A person presses Release**, the default. On the ticket or in Needs you, select **Release**. A merge on GitHub
  releases the ticket too.
- **People merge on GitHub**. Merge the pull request on GitHub. Troopy marks the ticket **Released** when GitHub merges
  it.

Before **Release** merges, Troopy reads the gates again. It merges only the commit that passed them, with the method of
**How Release merges**. GitHub's rules for the repo still apply, such as a required review. After the merge, Troopy
deletes the branch of the ticket.

To close a ticket with no merge, set its status to **Done**. Only the people who can release do this.

## Move a ticket between steps

Only a person moves a ticket between steps. Use the **Step** property of the ticket, or drag its card on the board.
A move to the step where a stopped ticket is starts that step again.

| Move                            | What happens                                                                       | Who                          |
| ------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------- |
| Back to **Analyze** or **Plan** | The analysis and the plan unlock. The plan needs a new yes.                        | Members, owners and admins   |
| Back to **Build**               | The Engineer works on the change again. Your move is the reason that it reads.     | Members, owners and admins   |
| Forward to **Build**            | It approves the current plan in your name.                                         | The people who approve plans |
| To **Test**                     | The gates read the change again. It needs an open pull request whose tests passed. | Members, owners and admins   |
| To **Ready to release**         | It skips the gates at **Test**. Auto-release never releases such a ticket.         | The people who can release   |
| **Take it out of the steps**    | The ticket becomes a plain task. You cannot do it while a teammate works on it.    | Members, owners and admins   |

Nothing drops on **Released**. A ticket goes there with **Release**, with a merge on GitHub, or with auto-release.

## Settings > Engineering

Owners and admins change these settings in **Settings** > **Engineering**. Each change ends an auto-release grant.

| Setting                              | Choices                                                             | Default                       |
| ------------------------------------ | ------------------------------------------------------------------- | ----------------------------- |
| **Ask a person to approve the plan** | **For every ticket**, **For medium and large changes** or **Never** | **For every ticket**          |
| **Who approves plans**               | **Everyone in the workspace** or **Owners and admins**              | **Everyone in the workspace** |
| **How changes go live**              | **A person presses Release** or **People merge on GitHub**          | **A person presses Release**  |
| **Who can release**                  | **Owners and admins** or **Everyone in the workspace**              | **Owners and admins**         |
| **How Release merges**               | Squash, merge commit or rebase                                      | Squash                        |
| **GitHub's checks must pass**        | On or off                                                           | On                            |
| **Tries before it asks you**         | 1 to 5 send-backs                                                   | 3                             |

## Turn on auto-release

Under auto-release, Troopy releases each ticket that passed every gate, with no person, for a time that you pick.
Owners and admins turn it on. Before the grant, these must be true:

- **How changes go live** is **A person presses Release**.
- **GitHub's checks must pass** is on.
- GitHub is connected, and its owner accepted the new permissions of Troopy's app.
- People released 3 changes in the last 30 days.

1. Open **Settings** > **Engineering**, and go to **Auto-release**.
2. Under **For**, pick 1, 3, 7, 14 or 30 days.
3. Under **Changes a day, at most**, pick a number from 1 to 10.
4. Under **Which changes**, pick the largest size that it releases.
5. Select **Turn on auto-release**.

Auto-release merges only a commit that went through **Test** and that GitHub's checks passed. It never releases these
tickets:

- a ticket with no size, or larger than the size of the grant
- a ticket that a person moved past **Test**
- a ticket of an incident, or of work that an event from outside Troopy started

The grant stops when its date comes, when someone changes the engineering settings, or when its granter is no longer an
owner or admin. It also stops when GitHub refuses a merge for a reason that a person must handle. A new incident whose
possible cause auto-release shipped stops it too. The Engineer says why in the troop chat. To stop it yourself, select
**Turn off auto-release**.

## Problems

| Message                                                                    | Why                                                                           | What to do                                                                              |
| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| The pull request was closed on GitHub.                                     | Someone closed it with no merge.                                              | Reopen it on GitHub, or move the ticket back to **Build**.                              |
| Only an Engineer analyzes, plans and builds a ticket.                      | Another teammate holds the ticket.                                            | Give the ticket to an Engineer.                                                         |
| The Engineer finished without a pull request that passed the repo's tests. | The tests of the repo failed on each try.                                     | Write to the Engineer on the ticket, then select **Try again**.                         |
| Testing sent it back 3 times, and it failed again.                         | The tries ran out.                                                            | Read the last report, then move the ticket to **Build**.                                |
| It conflicts with the latest code on the default branch.                   | The default branch changed under the pull request.                            | Resolve the conflict on GitHub, then try again.                                         |
| Troopy can't read GitHub's checks yet.                                     | The GitHub account did not accept the Checks and Commit statuses permissions. | An owner of the GitHub account accepts them on GitHub. See [GitHub](/docs/apps/github). |
| GitHub wants an approving review first.                                    | The repo asks for a review before a merge.                                    | Approve the pull request on GitHub, then select **Release** again.                      |
| The repo doesn't allow squash merges.                                      | The method of **How Release merges** is off in the repo.                      | Change **How Release merges** in **Settings** > **Engineering**.                        |
| It was behind the default branch.                                          | The repo asks for an up-to-date branch.                                       | Wait. GitHub updates the branch, and the new commit goes to **Test**.                   |
| The Engineer writes code with Claude Code.                                 | The organization has no Claude plan that can take the work.                   | Add a Claude plan in **Settings** > **AI**.                                             |

## Who can do what

| Action                                                                 | Who                                                                                                                          |
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Make a ticket, answer questions, move a ticket between steps           | Members, owners and admins                                                                                                   |
| Approve a plan                                                         | Everyone in the workspace, or only owners and admins, as **Who approves plans** says. Approvers too, when everyone approves. |
| Release a ticket, or close it by hand                                  | Owners and admins, or everyone, as **Who can release** says. Approvers too, when everyone releases.                          |
| Change **Settings** > **Engineering**, and turn auto-release on or off | Owners and admins                                                                                                            |
| Read tickets, plans and gates                                          | Everyone in the workspace, viewers too                                                                                       |

Teammates never move a ticket between steps, and never release one.

## Related

- [GitHub](/docs/apps/github)
- [Test your product with the QA tester](/docs/tests)
- [Catch the errors of your apps](/docs/incidents)
- [Answer what needs you](/docs/needs-you)
- [Stay in control of your troop](/docs/control)
