Skip to content

Ship & operate

Branching & Delivery Flow

3 min readUpdated Verified against d2b3999 on 2026-09-26

Page actions

ChatGPT and Claude are third-party services. The link gives them this page's public address and a short instruction. This site records nothing.

The branch model: feature branches into integration, release pull requests into main, required checks and pull-request hygiene.

The whole journey, from a branch to production and the App Store, as the pipeline runs it — press Next on the site to walk it:

The delivery pipeline Work branches from integration, passes the local gates and a pull request’s checks, and is squash-merged; the push runs deploy-staging, which builds the API functions and the web at that commit, aliases the staging site and verifies it. A release is a merge-commit pull request from integration to main; deploy-production waits for every required check plus playwright on the exact commit, deploys the API and verifies it by commit, builds the web unaliased and smokes it, promotes it with a public smoke and a rollback edge, then tags vX.Y.Z. The app re-archives at the tag so web, API and App Store share one version. Afterwards the data canary and uptime keep watching. 11 · The delivery pipeline From a branch to staging, to a release on main, to production and the App Store— every step gated by CI, verified by commit, and reversible Ship to stagingRelease to production An engineer — a branch from integrationAn engineera branch from integrationLocal gates — make ci · make docs-checkLocal gatesmake ci · make docs-checkThe pull request — every check on the commitThe pull requestevery check on the commitSquash merge — by the ownerSquash mergeby the ownerdeploy-staging — gates, preflight, builddeploy-staginggates, preflight, buildStaging verified — then a person looksStaging verifiedthen a person looksThe release pull request — a merge commit into mainThe release pull requesta merge commit into mainGates and preflight — every check, every secretGates and preflightevery check, every secretThe Render standby — deployed, verified by commitThe Render standbydeployed, verified by commitWebsite + API functions — built unaliased, provenWebsite + API functionsbuilt unaliased, provenPromote — smoked on the domain, or backPromotesmoked on the domain, or backThe way back — vercel rollback; revert a pinThe way backvercel rollback; revert a pinTag vX.Y.Z — only after a verified deployTag vX.Y.Zonly after a verified deployThe app follows — X.Y.Z (1), from the tagThe app followsX.Y.Z (1), from the tagAfter the release — make verify-prod · the data canary every 6 h · uptime every 15 minAfter the releasemake verify-prod · the data canary every 6 h · uptime every 15 minThe wiki follows the same chain — deploy-docs.yml: integration → staging; main → smoked, promotedThe wiki follows the same chaindeploy-docs.yml: integration → staging; main → smoked, promoted a personcomponent in this projectgate: fails the build or the deployproven, verified stateexternal systemexplanationflow: data or control moves this wayreference: pins, reads or derives from Source of truth: .github/workflows/deploy-production.yml ·.github/workflows/deploy-staging.yml · docs/process/branching.md ·docs/process/release.md v1.3.10 · verified 2026-09-27 · d85c2adSri Guru Granth Sahib Ji — Knowledge Base · engineering wiki
Poster 11 — The delivery pipeline: ship into integration and onto staging, release into main and onto production, tag, the app follows, then the canary and uptime watch · open full size
Branch modelA feature branch from integration is squash-merged back into it as one commit, which deploys staging. A release merges integration into main with a merge commit and is tagged after a verified production deploy. A hotfix branches from main, is squashed into main, and main is merged back into integration.mainintegrationfeat/changehotfix/urgentmaintrunkworkreview fixesfeat (squashed): stagingvX.Y.Zfixfix (squashed)vX.Y.(Z+1)
Branch modelA feature branch from integration is squash-merged back into it as one commit, which deploys staging. A release merges integration into main with a merge commit and is tagged after a verified production deploy. A hotfix branches from main, is squashed into main, and main is merged back into integration.mainintegrationfeat/changehotfix/urgentmaintrunkworkreview fixesfeat (squashed): stagingvX.Y.Zfixfix (squashed)vX.Y.(Z+1)
Branch model

Rules

  • integration is the trunk. Branch feature/* or fix/* from it; open a PR back into integration. Merging to integration deploys staging (the web with the API as Vercel functions inside it, ADR-0011). TestFlight uploads are manual and happen in the app repository (gurbani-soul-ios) — no merge here uploads a build.
  • main is production. It only ever receives a release PR from integration (or hotfix/*). The version-consistency gate enforces the source branch. Merging to main tags vX.Y.Z, cuts a GitHub Release, and deploys production.
  • hotfix/* branches from main for urgent production fixes, then is back-merged into integration.

Merge strategy (important)

PRMerge methodWhy
feature/*, fix/* → integrationSquashone clean commit per change on the trunk
integration → main (release)Merge commitmain stays a descendant of integration, so branches never diverge and no back-merge is needed
hotfix/* → mainSquash, then merge main → integrationkeeps the trunk current
wiki PRs that move a verified stamp → integrationMerge commita stamp names a commit on the PR branch; a squash drops it from history and the stamp stops resolving

main therefore does not require linear history (a squash/rebase release would rewrite SHAs and make every later release PR conflict).

Required checks

Applied from .github/rulesets/ via scripts/gh/apply_rulesets.sh (the JSON files are the source of truth — keep this list in step with them). The script merges each file into the live ruleset (scripts/gh/merge_ruleset.py): keys GitHub added that the file does not mention are kept, never reset to a weaker default. Run it with --dry-run first; it prints exactly what would change.

  • main: PR + 1 approval + CODEOWNERS + resolved threads + strict status checks python, frontend, integrity, versions, secrets, static-analysis, docs; no deletion or force-push. docs gates the merge only: deploy-production waits on every other required check but not on the wiki’s (wait_for_checks.py --exclude docs), which deploys the wiki on its own. No linear-history rule (see Merge strategy).
  • integration: PR + CODEOWNERS + checks python, frontend, integrity, secrets, docs (the wiki job, ADR-0012); no deletion or force-push.
  • Tags v*: no deletion or force-push.
  • playwright and the iOS jobs are not required checks, but deploy-staging / deploy-production wait for playwright on the exact commit before deploying.

PR hygiene

Conventional-commit titles (fix(data): …), small diffs, Closes #NN, the scripture-safety checklist in the PR template completed for any search, verify, display or dataset-pin change.