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 |
Builds the app and runs the Wake tests on a simulator. Reports the check |
Xcode Cloud “Main” |
App Store Connect, not in this repo |
a merge to |
Archives the app and delivers it to the internal TestFlight group. |
WakeCore tests |
|
a PR or a push to |
Builds and tests the pure-logic package on Linux. |
Docs |
|
a PR or a push to |
Builds this site with Sphinx and deploys |
Claude builder |
|
a ticket gets the label |
Writes the code on a |
Claude review |
|
a check finishes on a |
Routes between fix, wait and review (job |
How a ticket moves#
A note lands as an issue with the label
inbox. Claude rewrites it and setsneeds-confirmation. Sven confirms by settingready.The builder starts on
ready. It creates the branchclaude/<issue>-<slug>frommain, picks/gsd-fastforsize:fastand/gsd-quickotherwise, writes the code, runs the WakeCore tests, and opens a PR whose description containsCloses #<issue>.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-macand the loop stops (rule 6).Green checks start the review. The review ends in
approvedorrework.approved: the PR is merged only if the ticket carriesautomergeand 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 getescalatedand the loop stops (rule 8).
Side exits:
needs-decisionis set when a ticket issize: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-applyready.
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_TOKENwas created withclaude setup-tokenon 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 sonnetand only the reviewer runs on--model opus.To rotate it, run
claude setup-tokenagain 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:phasetickets are not built unattended; they stop withneeds-decision.Only the WakeCore tests run on Linux. The real app test is the Xcode Cloud check
Wake | PR | Test - iOSon the PR; the router waits for it whenever the PR touchesios/.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-fastor/gsd-quickruns, any shell command is accepted. What holds is the pre-push hook (onlyclaude/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=Nbuilds ticket N (it must beready).gh workflow run claude.yml -f pr=N -f mode=fixor-f mode=reworkcontinues on the PR branch.gh workflow run claude-review.yml -f pr=Nreviews PR N now.The Actions tab offers the same through “Run workflow”.
@claudeon a ready issue resumes the builder;@claudeon a PR asks for a change in chat mode.Removing and re-adding
readystarts 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.
Does the
check_suiteevent fire for Xcode Cloud? Fallback: switch the router trigger tocheck_runwith the filtergithub.event.check_run.app.slug == 'xcode-cloud'.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).