# Release Process
The steps that take a green integration branch to a tagged production release.
- Page:
- Updated: 2026-10-13
- Verified against: `a661527` on 2026-09-26
- From the Sri Guru Granth Sahib Ji — Knowledge Base wiki. Sri Guru Granth Sahib Ji is quoted verbatim, cited by Ang, and never altered.
---
1. On `integration`, ensure everything is green on staging.
2. Bump the unified version: `make release VERSION=1.2.0` (runs `bump.py` +
`check_versions.py`). Fill in the CHANGELOG section it stubs.
3. If the dataset changed: it was rebuilt, proven and published in sggs-data first; here the
release carries the reviewed `dataset.lock.json` bump and the regenerated contract (`make contract`).
4. Open a **release PR `integration → main`**, labelled `release`. All gates must pass.
5. Merge (a merge commit). `deploy-production` deploys the exact commit, verifies it, and only
then tags `vX.Y.Z` and cuts a GitHub Release from the CHANGELOG section. The tag names the
commit, and that commit's `dataset.lock.json` names the exact database it serves.
6. Confirm with `make verify-prod` (commit identity, health, Ang 712 heading, web deployment).
The release that moves production's API contexts onto their own functions (a later one, after
the app is live; [services-production](https://docs.gurbanisoul.com/process/runbooks/services-production/) steps 2–3) also owes the
performance gate, from the same vantage as the baseline:
`python3 tools/perf_baseline.py --base https://gurbanisoul.com --from "" --compare docs/perf/baseline-2026-09.json`
must PASS (p95 within +10 % per context), and each context's `X-Service` must name itself.
7. **iOS is part of every release.** After `main` is tagged `vX.Y.Z`, the app repository
([`Algorythmos-AI/gurbani-soul-ios`](https://github.com/Algorythmos-AI/gurbani-soul-ios)) vendors this release (`make vendor-sync-platform REF=vX.Y.Z`), sets
`MARKETING_VERSION` to `X.Y.Z`, tags its own `vX.Y.Z` and uploads the App Store binary
(`make testflight TEAM_ID=… BUILD=1 CHANNEL=appstore UPLOAD=1`; the archive refuses an
`appstore` upload not built against platform `vX.Y.Z`). It commits its ledger. The release
is **complete** only when `scripts/release/check_release_complete.py X.Y.Z` passes.
## Versioning
SemVer, **one unified version number across web, API and iOS** — the footer, `/api/meta`,
`/api/health`, the iOS About screen and the App Store all read the same X.Y.Z, and they are
never allowed to differ. **Every release is re-archived at the same version** and uploaded as
iOS `X.Y.Z (1)` from the release tag, even when the change is web-only — so the number in the
App Store always equals the live site. The iOS **build number restarts at 1 per version** and
only increments for a re-upload of the *same* version (e.g. an App Review fix). Never ship a
one-sided hotfix: web or iOS fixes bump the **patch** everywhere. **MAJOR** = breaking
API/data-model (e.g. a `comp_id` renumber); **MINOR** = features or data corrections; **PATCH**
= fixes. RCs on `integration` are `vX.Y.Z-rc.N`. The DB keeps its own decoupled `meta.version`.
See [ADR-0004](https://docs.gurbanisoul.com/adr/0004-unified-semver/).