Ship & operateEngineering handbook
Delivery, versioning & release
Page actions
- View as Markdown (opens in a new tab)
- Ask ChatGPT (opens in a new tab)
- Ask Claude (opens in a new tab)
ChatGPT and Claude are third-party services. The link gives them this page's public address and a short instruction. This site records nothing.
Preview: the published page may be older or missing.
How work reaches users: the versioning convention, the integration to staging to production loop, delivery tooling and the iOS release.
https://docs.gurbanisoul.com/engineering/delivery/
How work reaches users. Read before shipping.
Versioning convention (important)
APP_VERSIONinwebapp/serve.pyis the source of truth for the running build. Bump it on every search-logic/UI release.- One number, everywhere. The web footer,
/api/meta,/api/health, the iOS About screen (MARKETING_VERSION) and the App Store all report the same X.Y.Z; they are never allowed to differ (check_versions.pygates this repository’s 5 strings; the app repository requires itsMARKETING_VERSIONto equal the platform release it vendors, and its ledger never to lead it). Every release is re-archived at the same version and uploaded as iOSX.Y.Z (1)from the release tagvX.Y.Z, even a web-only change — so the App Store number always equals the live site. The build number restarts at 1 per version (only a re-upload of the same version increments it). A release is complete only whenscripts/release/check_release_complete.py X.Y.Zpasses (prod web+API at the tag commit and anappstoreledger entry of X.Y.Z from that commit). Never ship a one-sided hotfix — patch-bump both surfaces. - The DB’s own build version is returned separately as
db_versionand is not shown in the UI. This decoupling is intentional: the data releases on its own cadence (see below). - Keep
README.md,CHANGELOG.md, andMASTER-INDEX.mdin step when you bump (scripts/release/bump.pydoes it). - The dataset versions separately. It is built and versioned in
Algorythmos-AI/sggs-data: every build stamps the database’smeta.version(served asdb_version) with sggs-data’sDATASET_VERSION, anddataset.lock.jsonrecords it asdataset_version. The database pinned today was built on 2026-09-18, before that rule, so/api/metareportsdb_version1.1.4 while the lock says 1.0.0; the next rebuild makes them one number. This repository serves exactly the objectdataset.lock.jsonpins; a new dataset arrives as a reviewed lock bump, never with an unrelated app change.
Delivery SOP — how work reaches users (read before shipping)
Environments: integration = trunk → auto-deploys staging (sggs-staging.vercel.app, SSO-protected; the API runs as Vercel functions inside it). main = production (web on the canonical domain gurbanisoul.com; www and the legacy *.vercel.app alias 308→apex; the API runs as Vercel functions inside it from the release after 1.3.9, with the Render single API …onrender.com kept deploying as the rollback target). Both deploy only through CI (deploy-staging.yml / deploy-production.yml); the platforms’ own git auto-deploys are OFF and Render auto-deploy is OFF. Never deploy by hand except a documented pipeline-outage hotfix.
The loop (use the tooling below; do not improvise it):
- Ship: branch from
integration(fix/…,feat/…), run local gates, open a PR intointegration, watch CI green. Corpus/DB change? It happens in sggs-data first (scripture proofs); here it arrives as a revieweddataset.lock.jsonbump. You cannot merge — hand the user the exact--admincommand (one line, no comments). - The user merges → the push auto-deploys staging. Review on
sggs-staging.vercel.app(open logged in to Vercel). - Release: preflight → PR
integration→mainas a merge commit (never squash —mainmust stay a descendant ofintegration) → the merge runs the gated production deploy → verify withmake verify-prodand confirm thevX.Y.Ztag.
Invariants: a deploy is verified by the running commit (/api/health.commit), not just the version. The release tag is cut only after a verified deploy. Secrets live in GitHub Environments (production/staging) — never type or echo a value; if one is pasted into chat, treat it as leaked and have the user rotate it. Full detail: docs/process/runbooks/deploy.md, docs/process/branching.md, ADR-0005.
Delivery tooling
Tested scripts that encode the gotchas learned shipping v1.1.x. Prefer them to ad-hoc commands:
| Step | Command | Script |
|---|---|---|
| Watch a PR’s checks to completion | make pr-checks PR=<n> | scripts/ci/wait_pr_checks.sh |
| Release preflight (trunk green, versions unified, CHANGELOG section) | make release-preflight | scripts/release/release_preflight.sh |
| Watch the production deploy gate by gate | make watch-deploy [SHA=<sha>] | scripts/release/watch_deploy.sh |
| Prove what production serves (commit, health, Ang 712 heading, web deployment) | make verify-prod [ARGS="--commit <sha> --version X.Y.Z"] | scripts/ops/verify_prod.py |
| Install / check the pinned database | make dataset · make dataset-check | scripts/data/fetch_dataset.py |
iOS app (Gurbani Soul)
The app lives in Algorythmos-AI/gurbani-soul-ios. It builds against this repository’s golden contract, vendored at a pinned platform release (vendor.lock.json there), and its MARKETING_VERSION must equal that release’s APP_VERSION — the one-number policy, enforced by its CI. After a release is tagged vX.Y.Z here, that repository runs make vendor-sync-platform REF=vX.Y.Z, sets MARKETING_VERSION, and uploads X.Y.Z (1) with make testflight … CHANNEL=appstore (the archive refuses an App Store upload whose contract was not vendored from vX.Y.Z). Its ledger records the platform commit each binary was built against; scripts/release/check_release_complete.py X.Y.Z reads it from there to prove web, API and the binary report the same number from the same commit. TestFlight/App Store runbooks, plans and the store listing live in that repository.
Sri Guru Granth Sahib Ji — Knowledge Base wiki. Generated from commit c3d9360. Updated 27 September 2026. Verified against f25ab97 on 2026-09-25. This copy may be out of date; the page is at https://docs.gurbanisoul.com/engineering/delivery/