From d9a57e0c0356fe6ca66161ec034c237cedce0657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl?= Date: Thu, 16 Jul 2026 00:14:02 +0200 Subject: [PATCH] Resync the release plan's embedded workflow and skill with the shipped files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plan embeds verbatim copies of two shipped files and both had drifted. The shipped files are correct, so the copies are now byte-identical to them (verified by cmp against `git show`). Task 8's workflow still spliced `${{ github.event.inputs.version }}` straight into the shell, so anyone re-deriving the workflow from the plan reintroduced a script injection that the regex below it cannot prevent — the input is interpolated before the check ever runs. It now reads the input via the `INPUT_VERSION` env var, and gains the `Record toolchain` step that is a release's only record of which compiler built it now that nothing pins Xcode. Task 11's skill claimed deleting the app clears `hasSignedInBefore`. It does not: the flag lives in the shared `group.ch.healthchat` UserDefaults suite, which survives deletion, so that advice yields a void migration test that looks like a pass — the precise false negative the section exists to prevent. Task 3 Step 5 carried the same fault in a subtler form: it matched the bare substring "skipping loadAccounts", but two lines in `AuthenticationService.swift` (:52 and :57) end that way and mean opposite things. Reading them as one told you to dismiss "Classic App not configured" — migration broken for every user — as a void run. Step 1 already prescribed the correct remedy and is unchanged. --- .../2026-07-15-healthchat-appstore-release.md | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) 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