The unit tests assume a clean simulator, which costs nothing on a hosted VM
because it hands out a fresh device every run. This runner reuses the same two
devices, so app containers, caches and logs survive between runs and six suites
fail on the previous run's leftovers rather than on anything in the code.
LoggingTests is the clearest: it expects no log files and finds nine, in the same
device container each time.
Only the two devices the tooling pins are erased, resolved by UDID. A blanket
erase would take out simulator state belonging to other work on a machine the
release pipeline shares.
Every check was red on every pull request. Upstream declares permissions: {} in its
workflows, which costs nothing on a public repo and is fatal here: the token is
limited to Metadata:read, so actions/checkout cannot read an internal repo and dies
with a misleading "Repository not found". Repo settings cannot fix it — an explicit
permissions block always overrides the default.
Each job now gets the minimum it needs. Danger also reads the pull request through
the issues API, so it needs issues:read alongside pull-requests:write, and zizmor's
SARIF upload needs actions:read on a private repo.
The unit tests move to the org's Mac Studio: GitHub-hosted macos-26 could not finish
inside the hour and bills at 10x. Preview tests are skipped there, using the flag
upstream already uses for its own enterprise tests — they render a snapshot per
preview and never once finished on either runner. The trade is that preview snapshot
regressions are no longer caught in CI.
Workflows that only make sense upstream are gated off rather than deleted, so they
keep rebasing cleanly. The enterprise tests need Element's bot token and their
private submodule. Three nightlies fail at checkout every weeknight — this repo is a
detached copy rather than a network fork, so GitHub never disabled their schedules —
and one of them posts to Element's Codecov on every run.
Task 6 Step 3 asserted the guard must fail before xcodes runs. It cannot:
fastlane's before_all always executes before the lane body, and the plan puts
xcodes in before_all and the guard in the lane. The acceptance criterion was
unmeetable on any machine.
The guard still does what it exists for — aborting before the ~30 minutes of
signing — so the wording was wrong, not the pipeline. Also record that the step
needs the xcodes CLI, since without it the lane dies earlier with an error that
reads like a config problem.
Task 11 described the skill's sections rather than showing them, the one place
the plan told an implementer what to write instead of what to type. Replace it
with the complete SKILL.md.
Writing it surfaced a broken convention worth not copying: the legacy skill
documents healthchat-v1.5.0-rc1 style tag suffixes, but the workflow derives the
marketing version by stripping only the healthchat-v prefix, so the suffix reaches
CFBundleShortVersionString, which Apple caps at three integers. Tags are numeric
only here, and test builds go through workflow_dispatch with the marker in the
release notes.
Task-by-task plan for the three PRs in the release design: the classic migration
fix, the fastlane pipeline, and the releasing-healthchat skill.
The migration fix gets a real test cycle — an existing upstream test,
classicAppAccountRequiresBackup, encodes the behaviour we're changing, so it is
rewritten first and drives the view model change.
Spec for bringing App Store release capability to element-x-ios, targeting the
existing HealthChat record that the legacy element-ios fork ships to today.
Both apps share bundle id ch.healthchat.element and team 6G37K72W82, so element-x
arrives as an in-place update rather than a new listing. That drives the design:
the identity already aligns and 26.07.3 clears the legacy's 1.4.1, but the repo
has no release machinery at all, and upstream's Xcode Cloud path is unavailable
because it needs the private Enterprise submodule.
Records three parts: repointing CLASSIC_APP_* at HealthChat's own identifiers so
existing users keep their sessions and encrypted history, a fork-owned fastlane
pipeline ported from the legacy fork, and a releasing-healthchat skill.
Revised after an adversarial review of every factual claim; 26 issues found, none
rejected. Most consequential: the migration fix is not monotonic, because making
loadAccounts() succeed exposes a backup-instructions sheet whose only button opens
element://open — a scheme this app does not register and whose legacy owner has
been replaced in place.
Applies the HealthChat product identity through the fork seams upstream already
provides, so no upstream logic files are touched and merges stay clean.
- Variants/HealthChat/healthchat.yml holds the identity scalars (display name,
bundle id ch.healthchat.element, app group, development team) and is included
permanently from project.yml. APP_NAME stays ElementX so the module name and
@testable import ElementX keep working.
- ElementX/Sources/AppHooks/HealthChat/ registers an AppSettings hook pinning
accountProviders to healthchat.ch with other providers disabled, push through
sygnal.healthchat.ch and a custom-scheme OIDC redirect; plus a Compound hook
recolouring the accent token family to the HealthChat blue.
- Entitlements are edited directly in ElementX/SupportingFiles/target.yml rather
than in the variant, because XcodeGen concatenates arrays on include-merge and
a variant can override scalars but cannot remove array entries. Drops the
Element Classic app group and keychain group, and narrows associated domains
to app.healthchat.ch, keeping matrix.to for permalinks.
- Info.plist CFBundleName now follows APP_DISPLAY_NAME — it, not
CFBundleDisplayName, is what the web authentication sign-in alert shows.
- App logo and app icon assets swapped in place; the onboarding welcome title
moves to a new screen_onboarding_welcome_title_ios untranslated string.
* implemented checkSourceSafetyIfNeeded in the timeline media preview
* added a subtitle to the spinner when scanning and an animated drag gesture for the error and loading states
* pr suggestions
* pr suggestions part 2
* Allow the AppIconBanner tool to be called with purely the build number.
And use that for the Element Pro Nightly.
* Use a build setting to determine Nightly builds.
* implement a MediaView to delegate at the view level the usage of the content scanner service, to determine how to render the media.
* fix for a small design mismatch
* fix for greedy error state
* updated naming
* just making some screenshot that have been affected by a string change on Localazy
* pr suggestions + updated strings
* implemented content scanning in the timeline reply view
* pr suggestions
* implement a MediaView to delegate at the view level the usage of the content scanner service, to determine how to render the media.
* fix for a small design mismatch
* fix for greedy error state
* updated naming
* just making some screenshot that have been affected by a string change on Localazy
* pr suggestions + updated strings
- Drop the redundant searchService coordinator/view model parameter; derive it from the client proxy.
- Rename the globalSearch avatar size to search.
- Lift the query observation streams out of their tasks.
- Restore the appeared and updateVisibleRange comments.
- Use CompoundIcon defaults and a scaledFrame for media previews.
- Simplify the result mapping with compactMap(makeResult) and drop unsupported events by returning nil.
- Move the preview helpers beneath the previews.
The SDK now surfaces polls, stickers, redactions and live location shares
alongside plain messages. Map them to their preview content instead of
dropping them as unsupported events.
Split global search into Rooms and Messages tabs. The Messages tab lists
matching events with the room's name and avatar, a sender-prefixed preview,
rich media (image/video/audio/file) previews, and a loading indicator, then
paginates as you scroll. Queries are debounced and the previous one is
superseded so late results can't clobber a newer search.
Wrap the SDK's SearchService in a SearchServiceProxy that publishes typed
message results and paginates on demand, expose it on the ClientProxy, and
enable the search index store on the session. Also opens up the timeline item
factory's message content builder so the proxy can reuse it.
* implemented the content scanner service with caching and a way to use in the state store context
* fix swiftformat comment enforcing
* pr suggestions
The Observation migration (2d486ece3) ported the preview fulfillment loop
from Combine publishers to async sequences but didn't carry over the
publisher path's 1s timeout. Without it, a preview whose expected state never
arrives loops forever.
* Stop reserving space for the non-existant icon background in ListRowLabel on iOS 26.
* Use the .avatar label style for NotificationSettingsEditScreenRoomCell.
* Fall back to the AppLock PIN code when there's no passcode.
* Add extra tests on the LinkNewDeviceFlowCoordinator.
* Misc tweaks for clarification.
* Regenerate snapshots.
* Limit conversations a message can be forwarded to
* Review fixups
* fix: remove the unused constant argument
---------
Co-authored-by: Mauro Romito <mauro.romito@element.io>
* use observation in unit and preview tests instead of the async stream
* use a parsing approach that is independent of the sourcery annotation
* Fix observation deferred fulfillments not receiving the initial value
Observation's observe only emits transitions, not the value present at subscription time. Drop the leading initial value from the affected transitionValues sequences and assert it directly with #expect instead.
* improved docs