VoiceDebrief iOS / develop

Build, test and release

How this repository works. The short version: CI is the compiler. Most of the work here is done by agents in Linux containers that cannot build Swift, so every change is compiled and tested on a Mac by GitHub Actions, and a failure is read from the job summary.

Layout

Apps/VoiceDebrief/            the app: a hand-written Xcode project, sources, resources, tests
Packages/SGitVault/           the vault engine (copied; see its SOURCE.md)
Config/                       Local.xcconfig.example, for building on your own device
.github/workflows/            ios.yml, ios-vault.yml, site.yml
.github/scripts/              select Xcode, pick a simulator, copy errors into the job summary
website/                      this site: markdown sources, the builder and the gate
issues/                       open and done work, one file each
version                       the version, one line

The Xcode project uses folder-synchronised groups: a Swift file added under Apps/VoiceDebrief/Sources/ is part of the app without editing the project file. Resources are copied flat into the bundle, so two resource files with the same name break the build.

On a Mac

Xcode 26 or later. Open Apps/VoiceDebrief/VoiceDebrief.xcodeproj and run the VoiceDebrief scheme on a simulator. To run on your own device, copy Config/Local.xcconfig.example to Config/Local.xcconfig and set your team.

cd Packages/SGitVault && swift test          # the vault engine, on the Mac itself

What CI does

WorkflowWhenWhat
ios.ymlA push to dev or a pull request that touches Apps/, Packages/ or the workflowOne Mac job: the vault engine's tests, a simulator build, a Release device build (unsigned), then the app's unit tests on an iPhone and an iPad simulator
ios-vault.ymlA change to the vault write path, every Monday, or by handSwift writes vaults on a Mac; sgit-ai on Linux clones them, verifies every signature and compares every file
site.ymlEvery push and pull requestThe gate, then (on dev) the release tag and the site

Builds and tests are unsigned. The last step of ios.yml uploads to TestFlight on a green push to dev: scripts/release/testflight.sh archives unsigned, signs the app with its entitlements, and exports with automatic signing through the App Store Connect API key. It needs the secrets ASC_KEY_ID, ASC_ISSUER_ID, ASC_KEY_P8_BASE64 and the variable APPLE_TEAM_ID, and skips itself while any is missing.

Reading a failure without a Mac. Each build and test step runs through .github/scripts/run-and-summarize.sh, which copies compiler errors and failed tests into the job summary and repeats them as the last lines of the step, so they can be read through the Actions API. Full logs are kept as the ios-logs artifact.

What CI cannot do

No simulator has Apple Intelligence's models, a real microphone, or a passkey in iCloud Keychain. Speech, the debrief, the passkey and the share sheet will have to be tested on a device by a person, against a written checklist. None of that exists yet.

The site

Each page is an index.md; index.html beside it is generated and committed.

node website/admin/build.mjs        # regenerate the pages, sitemap.xml and robots.txt
node website/admin/validate.mjs     # the gate CI runs

The gate fails on a page that is out of date, a broken link or anchor, a key-shaped string anywhere in the repository, an em-dash, or a script on a page. A page added, moved or removed updates llms.txt in the same commit.

A release

  1. Set the next version in the version file (a push to dev is the next patch number: v0.1.0 then v0.1.1).
  2. Add a row to the release history and update What is built for anything that changed.
  3. Run the builder and the gate, commit, push to dev.

CI tags the commit with the version if that tag does not exist yet, and publishes the site. A push that does not change the version is not a release: it is built, tested and published, but not tagged.

Rules that are easy to break