diff --git a/docs/superpowers/plans/2026-07-15-healthchat-appstore-release.md b/docs/superpowers/plans/2026-07-15-healthchat-appstore-release.md index ef4b3cf27..47013e3b7 100644 --- a/docs/superpowers/plans/2026-07-15-healthchat-appstore-release.md +++ b/docs/superpowers/plans/2026-07-15-healthchat-appstore-release.md @@ -253,7 +253,7 @@ Build the branch and install it **without deleting the legacy app** — same bun - [ ] **Step 5: Observe** Expected: the app finds the existing account and signs in without a manual login. -Watch the log for `ClassicAppManagerError` and for `"skipping loadAccounts"` — the latter means Step 1's trap caught you and the run is **void, not failed**. +Watch the log for `ClassicAppManagerError`. Two *different* lines end in `"skipping loadAccounts"` (`AuthenticationService.swift:52` and `:57`) and they mean opposite things — never match the bare substring. `"Already signed in to Element X before, skipping loadAccounts"` means Step 1's trap caught you and the run is **void, not failed**. `"Classic App not configured, skipping loadAccounts"` is the opposite: the `CLASSIC_APP_*` Info.plist keys are missing, `ClassicAppManager` never initialised, and migration is broken for every user — investigate, do not dismiss. - [ ] **Step 6: Exercise the `.requiresBackup` path if you can reach it** @@ -705,12 +705,21 @@ jobs: submodules: false persist-credentials: false + - name: Record toolchain + # HealthChat: nothing pins Xcode (the runner carries a single globally-selected one), so this + # log line is the only record of which compiler produced a given TestFlight build. + run: xcodebuild -version + - name: Resolve marketing version id: version + env: + # Read via env, never `${{ }}` inside the script: the input is attacker-controlled text + # that GitHub would splice into the shell before the regex below could reject it. + INPUT_VERSION: ${{ github.event.inputs.version }} run: | set -euo pipefail if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - VERSION="${{ github.event.inputs.version }}" + VERSION="$INPUT_VERSION" else VERSION="${GITHUB_REF_NAME#healthchat-v}" fi @@ -1097,12 +1106,13 @@ macOS, ARM64]`) shared with the legacy fork, not registered per-repo. - Network access to `developer.apple.com`, `api.appstoreconnect.apple.com`, `github.com`, `rubygems.org`. -**A release records no Xcode provenance.** With the pin gone, the runner's current -state is the only fact about which compiler built a shipped build, and -`release-healthchat.yml` does not log it — `unit-tests.yml` runs `xcodebuild --version`, the release path does not. Nothing in a release run's logs or artifacts -will tell you afterwards; read `xcodebuild -version` on the runner while it still -matches. +**A release's only Xcode provenance is its own log.** `release-healthchat.yml` runs +`xcodebuild -version` (`Record toolchain`) before every build, so each run records the +compiler that produced it. Nothing pins that version — upgrading the runner's Xcode +silently changes the compiler every subsequent release is built with, and that log +line is the only place it shows up. Do not reconstruct a past build's toolchain from +the runner: it answers for today, not for the build you are asking about. Read the +run's log. ## Renewing the Apple Distribution certificate (annual) @@ -1193,8 +1203,14 @@ place, and these are what stop existing users being silently logged out. - **`hasSignedInBefore` voids migration testing.** It lives in that shared suite, survives app deletion, and short-circuits `loadAccounts()`. Any device that has run an element-x HealthChat build shows a plain login screen *while migration - works perfectly*. Delete the app before testing; `"skipping loadAccounts"` in the - log means the run is **void, not failed**. + works perfectly*. Test on a device that has never run an element-x HealthChat + build, or clear the `group.ch.healthchat` suite first; + `"Already signed in to Element X before, skipping loadAccounts"` in the log + means the run is **void, not failed**. A + `"Classic App not configured, skipping loadAccounts"` line is the opposite — + the `CLASSIC_APP_*` Info.plist keys are missing, `ClassicAppManager` never + initialised, and migration is broken for every user. Investigate, do not + dismiss. ## Known branch divergence on rebase