Resync the release plan's embedded workflow and skill with the shipped files

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.
This commit is contained in:
2026-07-16 00:14:02 +02:00
parent 65d19d3274
commit 116f716d3a
@@ -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