# 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/-` 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 #`. 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).