One sign-up helper, because five copies had the same bug

Seven of 52 UI tests failed in one run with "sign-up did not complete". The
app was right and the tests were wrong, and the failure dump said so:

    signup.password  value: •                    ← one character
    signup.confirm   value: •••••••••••••••••    ← all seventeen
    signup.submit    Disabled

iOS puts "Use Strong Password?" over the first `.newPassword` field, and from
the first keystroke on it swallows what follows. Every sign-up helper in this
target called `dismissPasswordSuggestionIfPresent()` — at the end, after both
passwords had been typed, by which point there is nothing left to save. The app
then correctly refused a form whose two passwords disagreed, and the report
blamed sign-up for it.

It is non-deterministic, which is why it reads as flake: a fifth suite running
identical keystrokes passed in the same run. It is not flake and it is not
machine load.

There were five hand-rolled copies of that helper — four here, plus one I wrote
for the new two-simulator suite, which reproduced the bug immediately and is
what makes the point. Now there is one, `XCUIApplication.completeSignUp`, and it
dismisses the panel when the field takes focus, reads both fields back, repairs a
short one, and fails naming the cause instead of the app.

Two new suites:

  TwoSimulatorUITests — two simulators, two accounts, one relay. Both sides run
  the same test with the handles swapped, so neither has to start first; the
  peer lookup retries, which is the rendezvous. A direct message crosses in both
  directions and a group created on one device arrives on the other. This is
  weaker evidence than CrossPlatformUITests and says so in its own header: both
  halves are the same binary and will agree when both are wrong. What it proves
  that nothing else does is that a message crosses two devices, two Keychains
  and two ratchets and lands in the app's own UI.

  AppSurfaceUITests — the screens nothing was opening. Reactions, unsend,
  in-chat search, mute, the voice-note recorder, the stories strip, the groups
  section, the safety number and five of the seven settings screens had no test
  at all. Deliberately shallow and it says so: this catches a screen that
  crashes or fails to push, not one that is wrong.

Also here, each found while running the above:

- DEVELOPMENT_TEAM was "2W7DA3A8W2", the free personal team on the build Mac,
  while the App Store provisioning profile that already exists for
  net.flamenet.messenger belongs to M5923Y84P5 — the team that ships hittfitness
  and shepherdgiving, valid to 2027-06-04. A personal team cannot produce a
  distribution build, so every archive for TestFlight would have failed naming
  the certificate rather than the team ID that put it out of reach.

- Scripts/generate.sh, because .gitignore excludes the whole .xcodeproj and
  SwiftPM keeps Package.resolved inside it. Since the WebRTC dependency landed a
  fresh checkout had no pins at all: resolve against the network and get a
  different build tomorrow, or disable resolution and fail outright. Both
  unacceptable for something going to testers. The pins live in Scripts/ now.

- rounds.sh ran an iOS *build* and called it a round. The 45 XCUITest cases
  never ran. It runs them now — and refuses to score a run that executed zero
  tests as a pass, which xcodebuild will happily report after a runner crashes
  and relaunches:

      Test Suite 'TwoSimulatorUITests' passed
           Executed 0 tests, with 0 failures

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Paul Hitt
2026-08-25 15:55:19 -04:00
co-authored by Claude Opus 5
parent a213ad8d5d
commit a9ec8e98bb
+24 -1
View File
@@ -17,7 +17,7 @@ screen renders.
| Protocol engine | `Sources/E2E`, 15 files | `engine`, 10 files |
| App layer | ~9,700 lines, 45 files | accounts, contacts, chat, photos |
| Tests | XCUITest + JS interop | 114 unit + 37 cross-engine + 15 on-device + live round trips |
| Ships to | nobody (no signing team) | a debug APK that installs and runs |
| Ships to | TestFlight, once a distribution cert is minted (see below) | a debug APK that installs and runs; the release keystore is configured |
Android's engine is **verified against the web engine in both directions**
(`engine/test/interop-kotlin.mjs`), which is a stronger claim than the iOS app
@@ -334,3 +334,26 @@ for the app to settle before deciding anything.
first takes the message and the other reports zero, which reads as "nothing was
sent". Run a single `autoreply` at a time.
## Signing, on both sides (2026-08-25)
**iOS was pinned to the wrong Apple team, and that — not a missing membership —
is what made TestFlight unreachable.** `project.yml` carried
`DEVELOPMENT_TEAM: "2W7DA3A8W2"`, the free personal team attached to the iCloud
address on the build Mac. Meanwhile an **App Store provisioning profile for
`net.flamenet.messenger` already exists**, issued to team `M5923Y84P5` — the
team that ships hittfitness and shepherdgiving — and valid until 2027-06-04. A
personal team cannot produce a distribution build at all, so every archive would
have failed at signing while naming the certificate rather than the team ID that
put it out of reach. The team is now `M5923Y84P5`.
⚠️ **Still outstanding: no distribution certificate in the build Mac's
keychain.** `security find-identity -v -p codesigning` returns exactly one
identity, the personal-team *development* one. Signing in to Xcode with the App
Store Connect account mints the distribution certificate and the matching
profile; nothing in this repository can do it, and nothing here should try.
**Android needs none of that.** The release `signingConfig` reads
`~/.flamenet-android-signing.properties` — present on the build Mac since
2026-08-23 — so `:app:assembleRelease` produces a signed APK that can go
straight to a tester. That asymmetry is why Android reaches testers first, and
it has nothing to do with which app is more finished.