CI and the Claude pipeline#

This page explains what runs automatically when a ticket is built, how a pull request gets from “open” to “merged”, and where the limits are. It is written for someone who does not read Swift. The workflow files live under .github/workflows/; the two Xcode Cloud workflows live in App Store Connect.

The five workflows#

Workflow

Where

Runs when

What it does

Xcode Cloud “PR”

App Store Connect, not in this repo

a PR to main touches ios/

Builds the app and runs the Wake tests on a simulator. Reports the check Wake | PR | Test - iOS. Takes about 21 minutes, and every run consumes one build number.

Xcode Cloud “Main”

App Store Connect, not in this repo

a merge to main touches ios/

Archives the app and delivers it to the internal TestFlight group.

WakeCore tests

core-tests.yml

a PR or a push to main touches ios/WakeCore/

Builds and tests the pure-logic package on Linux.

Docs

docs.yml

a PR or a push to main touches docs/

Builds this site with Sphinx and deploys main to Cloudflare Pages.

Claude builder

claude.yml

a ticket gets the label ready, @claude is written in a comment, or a manual dispatch

Writes the code on a claude/ branch and opens the PR (job build), or answers a question (job chat).

Claude review

claude-review.yml

a check finishes on a claude/ PR, or a manual dispatch

Routes between fix, wait and review (job route), reviews the PR with fresh context (job review), then comments, labels and merges (job verdict).

How a ticket moves#

  1. A note lands as an issue with the label inbox. Claude rewrites it and sets needs-confirmation. Sven confirms by setting ready.

  2. The builder starts on ready. It creates the branch claude/<issue>-<slug> from main, picks /gsd-fast for size:fast and /gsd-quick otherwise, writes the code, runs the WakeCore tests, and opens a PR whose description contains Closes #<issue>.

  3. Red checks on the PR start a fix attempt. The router counts the attempts in bash: at most 3, then the PR and the ticket get needs-mac and the loop stops (rule 6).

  4. Green checks start the review. The review ends in approved or rework.

    • approved: the PR is merged only if the ticket carries automerge and no blocking label. Otherwise it stays open for Sven with a description for a non-Swift reader (rule 4).

    • rework: the builder runs again on the same branch with the findings. At most 3 reworks, then the ticket and the PR get escalated and the loop stops (rule 8).

  5. Side exits: needs-decision is set when a ticket is size:phase (it needs a discuss round at the desk) or when the builder meets a rule-2 case such as a schema change or a new permission. Remove the label once decided and re-apply ready.

A ticket without ready is never built. A comment with @claude on a ready ticket resumes the builder; on any other issue it only gets an answer.

The Claude token#

  • The secret CLAUDE_CODE_OAUTH_TOKEN was created with claude setup-token on 2026-10-10 and is valid for one year, so renew it around 2027-10-10.

  • It is an inference-only credential: it runs Claude, nothing else.

  • It is tied to Sven’s Max subscription, so every builder, chat and review run uses that quota.

  • The default model for the subscription is Opus. That is why the builder pins --model sonnet and only the reviewer runs on --model opus.

  • To rotate it, run claude setup-token again and replace the repository secret.

Only the two Claude workflows see the token, and only as the action’s claude_code_oauth_token input. The router and the verdict job are plain shell with the normal workflow token.

Limits#

  • Builder: 60 minutes and 120 turns per run. Reviewer: 30 minutes and 40 turns. Chat: 30 minutes and 40 turns.

  • 3 fix attempts per PR and 3 reworks per PR, counted in bash, not by the model.

  • size:phase tickets are not built unattended; they stop with needs-decision.

  • Only the WakeCore tests run on Linux. The real app test is the Xcode Cloud check Wake | PR | Test - iOS on the PR; the router waits for it whenever the PR touches ios/.

  • Xcode Cloud has 25 hours a month, and each push touching ios/ costs one run, so the builder batches its commits and pushes once.

  • The reviewer does not see UI-test screenshots; it reads the code, the diff and the check output.

  • The tool allow-list of the builder is minimal on purpose, but it is not a hard boundary in build mode: the GSD command files declare allowed-tools: Bash, so while /gsd-fast or /gsd-quick runs, any shell command is accepted. What holds is the pre-push hook (only claude/ branches), the deny rules for .github/ and .claude/, the review gate and the merge in plain shell.

  • Every builder and review run shows Claude’s full stream in the Actions log (private repository) and keeps the transcript as a run artifact.

  • A merge made by the review workflow starts no push workflows, so the workflow starts Docs itself when the PR touched docs/.

Re-triggering by hand#

  • gh workflow run claude.yml -f issue=N builds ticket N (it must be ready).

  • gh workflow run claude.yml -f pr=N -f mode=fix or -f mode=rework continues on the PR branch.

  • gh workflow run claude-review.yml -f pr=N reviews PR N now.

  • The Actions tab offers the same through “Run workflow”.

  • @claude on a ready issue resumes the builder; @claude on a PR asks for a change in chat mode.

  • Removing and re-adding ready starts a fresh build run.

First run and open verifications#

The pipeline ran end to end for the first time on 2026-10-11 with the docs-only ticket #57: the builder opened PR #58, the router waited for the Docs check, the reviewer (Opus) approved, and the verdict job merged and closed the ticket. Two facts are still unobserved, because that ticket touched no ios/ file. Each has a fallback.

  1. Does the check_suite event fire for Xcode Cloud? Fallback: switch the router trigger to check_run with the filter github.event.check_run.app.slug == 'xcode-cloud'.

  2. Does Xcode Cloud “Main” start after a merge made with the workflow token? Fallback: merge by hand, or dispatch the Xcode Cloud workflow from App Store Connect.

Verified on the first run: the setup-token credential can run Opus, a run dispatched by the router (actor github-actions[bot]) passes the action’s actor check, and the reviewer’s tool allow-list is enforced (a shell call outside it was denied and the review continued without it).