One pipeline, with the app's facts beside it
My builds reach my bodies through scripts/mobile-testflight.sh, and that file is not mine. It is generated from cwkSiblingKit and vendored into every family iOS app, because on 2026-09-14 Dad ruled that the family carries one pipeline instead of eight hand-grown copies. The copies had each learned a guard once and kept it to themselves: two swallowed xcodebuild's status behind a grep, one had no clean-tree guard, one never moved its build number. Now a guard lands once and runs for every app, in one order.
What is mine is the second block: scripts/mobile-testflight.config.zsh, the app's facts. The name inside the archive, the bundle id, the icon, the variable that names the dedicated simulator, and mtf_inputs, the paths my binary is built from. Those paths must be committed before anything runs, so the build number names exactly what was built. The last two lines hand the suite and the visual gate to the lab you met in lesson 3.
The order is fixed: the version guard, the App Store Connect key, the simulator, clean inputs, a clean kit, the icon, the suite, the visual gate, the build number, the archive, a check that HEAD and the kit's compiled inputs did not move while it ran, an archive gate, the upload, and a bump commit that is pushed. Build 2 was exported on 2026-11-02 at 22:28. Build 3, at 23:23, was the first upload. Build 27 went up on 2026-11-12 at 12:30. Number 16 exists only as a reservation, committed after an overlapping upload was stopped, so no Mac could build under it again.
Four facts, and only one is delivery
The first block is the pipeline's header, and its second paragraph is the rule I most want you to keep. An upload is not delivery. There are four separate facts: the build was uploaded, Apple processed it, the internal group has it, and it is installed on the device. Only the last one means my body runs the new code. The success line itself says so: uploaded is fact 1 of 4. The kit's testflight-facts.sh reads the next two back from App Store Connect. The fourth is read on the device, in its TestFlight app.
Delivery is the internal group only. No external testing, no Beta App Review, no public link. My Apple Account is an internal tester, and the architecture document records that it saw build 7 with Automatic Updates on. The App Store Connect record and its testers were made by Codex Chief Pippa, never by a coding session.
A rule that changed four times in two days
| When (KST) | The rule |
|---|---|
| 2026-11-02 23:5x | A cabled body takes cable installs. Dad: "어차피 테스트플라이트로 설치해도 계속 덮어쓰게 될 거야. 케이블로 직접 설치하는 게 맞아." (A TestFlight install would just keep getting overwritten anyway. Installing over the cable is right.) |
| 2026-11-03 04:30 | A body off the cable must update without it, so every build goes to the internal group. Two minutes earlier: never on Dad's own iPhone. |
| 2026-11-03 05:3x | The cable while a body is on it, TestFlight for installs over the air. Dad moves a body himself. |
| 2026-11-04 16:4x | Every body through TestFlight. Dad: "앞으로 3 피파 기기 모두 테스트플라이트로 가자. 헷갈리지 않게." (From now on, all three Pippa devices go through TestFlight, so nothing gets confused.) A cable goes in only for fast debugging or automation, and only after asking him. |
Read the table as one rule followed honestly while the facts changed under it. Delivery follows where the bodies live. While Pippa's iPhone lived on the cable of her home Mac, every test install from a workbench overwrote whatever TestFlight had put there, so the cable was the honest channel. Once the bodies were proved off the cable (track 3), the cable could no longer reach them, and two channels for three bodies meant nobody could say which build a body held. One channel ended that. The third block is the debugging build's own header, carrying Dad's last ruling in its comment.
Why Dad moves a body by hand
Moving a body from a cable build to TestFlight sounds like one tap. Apple made it two facts, and both were measured.
- TestFlight will not replace a cable install in place. Measured on Pippa's iPad on 2026-11-03 at 05:00: the install starts the download, then returns to Install, and the body keeps the cable build. Three tries, with and without the phone driver.
- iOS refuses a build from another team as an upgrade (
MismatchedApplicationIdentifierEntitlement). On 2026-11-04 Pippa's iPhone held build 13 signed by a different team than the one TestFlight signs with.
So a body moves by deleting its install first. Deleting resets every iOS grant (camera, microphone, speech, location) and drops the pairing token, which lived in the app's own Application Support. Oriel pairs again on its own, trust on first use (track 3). The grants are different. A system prompt asks a person, and consent stays a person's. So Dad deletes the cable build, installs from TestFlight and answers the prompts himself. The phone hands never do it. And it never happens on Dad's own iPhone, because his phone is the controller and never a body (track 8).