# Runbook: the docs site (launch and after) Launching docs.gurbanisoul.com and keeping it honest: one-time setup, the staging walkthrough, DNS cut-over, production smoke, rollback, and the routine after launch. - Page: - Updated: 2026-10-12 - Verified against: `4adc454` 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. --- The wiki (`docs-site/` over `docs/`, [ADR-0012](https://docs.gurbanisoul.com/adr/0012-docs-site/)) deploys through `deploy-docs.yml` with the product's posture: CI builds, deploys, smokes by commit, promotes, rolls back; the Vercel project's own git deployments are off. This runbook is the launch checklist and the routine that follows it. ## 1. One-time setup (owner) The Vercel project, the domain, the staging alias and the two secrets — steps 1–5 of [the deploy runbook's wiki section](https://docs.gurbanisoul.com/process/runbooks/deploy/#the-wiki-docsgurbanisoulcom--one-time-setup-owner). Until they exist, every push to `integration` still runs the full `docs` job (the required check), and `deploy-staging` fails clearly at preflight. Then apply the ruleset that makes the docs job required: ```bash REPO=Algorythmos-AI/sggs-platform bash scripts/gh/apply_rulesets.sh --dry-run # prints the change REPO=Algorythmos-AI/sggs-platform bash scripts/gh/apply_rulesets.sh ``` ### The pin bot's token (`DOCS_BOT_TOKEN`) `docs-pins` opens its pull request with a fine-grained personal access token, so the required checks run on it (a pull request opened with the workflow's own token would trigger none). At : | Field | Value | |---|---| | Resource owner | **Algorythmos-AI** — not your personal account (it cannot be changed later) | | Repository access | Only select repositories → `sggs-platform` | | Repository permissions | **Contents** and **Pull requests**: Read and write (nothing else) | | Expiration | 90 days, with a renewal reminder | If the organization requires approval, approve it under Organization settings → Personal access tokens → Pending requests. Then `gh secret set DOCS_BOT_TOKEN --repo Algorythmos-AI/sggs-platform` (a hidden prompt; never paste a token into chat or a file). Before it writes anything, the workflow checks the token with a dry-run push. A token that authenticates but cannot push — a personal resource owner, pending approval, read-only Contents — does not fail silently: the bump arrives as the issue *docs: the sibling docs changed — bump the pins*, which names the cause. An expired or removed token does the same. ## 2. Staging walkthrough (before the first promotion) Most of this walkthrough is automated now. After every staging deploy `deploy-docs` runs the **live suite** (`docs-site/e2e-live/live.spec.ts`) against the staging alias with the real API: the status strip, the search simulator, the Ang explorer on Angs 1, 712 and 1256, a line read from `/api/ang/1` verifying as `VERIFIED_EXACT`, the API console, ⌘K search, axe on five pages as deployed, and no Content-Security-Policy errors. Run it by hand with `make docs-live URL=https://docs.gurbanisoul.com` (add `VERCEL_BYPASS=…` for the staging alias). What stays human is taste: one visual pass on a new release. Open `https://sggs-docs-staging.vercel.app` logged in to Vercel. Run the visual pass the build cannot judge — the same list the repository's QA script shoots (`cd docs-site && node scripts/visual-qa.mjs ` writes light, dark and phone screenshots of every section under `docs-site/qa-shots/`): | Check | Where | |---|---| | The landing, the live status strip shows production's version and health | `/` | | A poster walkthrough: Next, arrow keys, a step chip, Full size, Escape returns focus | `/architecture/request-lifecycle/` | | The Ang explorer on Angs 1, 712, 1256; the English toggle; the outage message with the network off | `/data/line-record/` | | The search simulator with `sat nam`, `waheguru`, `mercy`, a Gurmukhi query; the tier lights on poster 05 | `/architecture/search-waterfall/` | | The verify playground with a line from memory; the rung lights on poster 07 | `/search/verification-engine/` | | The API console: an Ang, a search, copy as curl | `/api/` | | A pinned page's canonical banner and edit link | `/data/architecture/database-schema/`, `/ios/nitnem/spec/` | | Hover-cards and a quiz; the two cited lines stack Gurmukhi, transliteration, citation | `/scripture/what-sggs-is/` | | A learning path's ticks survive a reload | `/learning-paths/platform-engineer/` | | Search (⌘K) finds a technical term and a scripture term | any page | | Dark mode on every section; phone width with no horizontal scroll | every section | | The 404 page; a page's Open Graph card (`/og/.png`) | `/nope/`, `/og/index.png` | Anything wrong is a normal pull request into `integration`; the site redeploys to staging on merge. ## 3. Production 1. Cloudflare: `CNAME docs →` Vercel's recommended target, DNS-only. Vercel: the domain is attached to `sggs-docs` and shows *Valid Configuration*. 2. Merge the next release PR `integration → main` (a merge commit, as always). `deploy-docs`'s `deploy-production` job records the deployment the domain serves now (the rollback target), builds, deploys unaliased, smokes by commit, promotes to `docs.gurbanisoul.com`, smokes again — and rolls back if the public smoke fails. Every failure opens an issue that says whether production changed. > [!NOTE] > **A project's first Vercel deployment became production.** On 2026-09-26 the docs > project's first deployment — a *preview* from `deploy-staging` — was recorded as production, > and `docs.gurbanisoul.com` attached to it, so the domain served an `integration` build until > the 1.3.10 release. `deploy-staging` now refuses to run while the project has no production > deployment (`scripts/ci/vercel_api.py has-production`) and fails if its own deployment is not > a preview. For a new Vercel project, the first deployment must come from `deploy-production`. 3. Prove it: ```bash python3 scripts/ci/docs_smoke.py https://docs.gurbanisoul.com --commit ``` The smoke checks the landing, a Mermaid render, the search index, `/api/health` and `/api/meta` through the rewrite, the poster the architecture page links as "open full size" and an Open Graph card, and that `` equals the commit. Nothing may redirect: a redirect on `/api` is how the widgets broke before launch (`trailingSlash`). 4. Owner walkthrough of section 2 on the public domain, once. ## 4. Rollback The job rolls back on its own when the public smoke fails **after** promotion, to the deployment the domain served before the run (read from Vercel by the domain, never "the newest production deployment": a build deployed with `--skip-domain` whose smoke failed is also a READY production deployment, but it never served anyone). A failure before promotion changes nothing and says so in its issue. By hand: `python3 scripts/ci/vercel_api.py live docs.gurbanisoul.com` names the serving deployment; Vercel → `sggs-docs` → Deployments → the previous good deployment → *Instant Rollback*, or `vercel rollback --scope ` with the docs project's token. After a rollback Vercel stops assigning the domain automatically; the next `deploy-production` run's `vercel promote` restores it. The wiki carries no state, so a rollback costs nothing but the newer pages. **If the PDFs fail a build.** `build-pdfs` names the page that could not be rendered, and `check-pdf` names the file and the promise it broke (most seriously: Gurmukhi set outside Sant Lipi, which means the font did not load). Fix the page or the stylesheet; never deploy around the gate. To ship without PDFs while that is investigated, remove `DOCS_PDF: "1"` from the three build steps of `deploy-docs.yml`: the build then makes none, the menu shows no *Download PDF*, and the build stops announcing `pdf1`. **Switching the page menu off.** If the page menu (copy, view as Markdown, the assistant links, print) ever misbehaves, it can be removed without a code change: set `DOCS_PAGE_ACTIONS=0` in the docs project's build environment and deploy again. The build then leaves the menu out of every page and stops announcing `actions1`, so the live checks do not look for it (`docs-site/plugins/features.mjs`, [ADR-0013](https://docs.gurbanisoul.com/adr/0013-outbound-assistant-links/)). ### Visual baselines The `docs` check compares a picture of the top of six kinds of page (wide and phone, light and dark: 24 pictures in `docs-site/e2e-visual/__screenshots__/`) with what the pull request's build draws. The sidebar, "On this page", the dates under a title and live widgets are masked, so a new page or a re-verified stamp does not trip it; a change to the layout, the type or one of the six pages' opening content does. When it fails: 1. Open the run, download the **`visual-baselines`** artifact. `__screenshots__/` holds what this build drew; `test-results/` holds expected, actual and diff for each picture that changed. 2. If the change is a mistake, fix it. If it is meant, replace the files in `docs-site/e2e-visual/__screenshots__/` with the artifact's and commit them in the same pull request, saying in the description what changed to the eye. Never commit a baseline made on your own machine: fonts differ between systems, and the comparison runs only in the pinned Playwright container (`PW_IMAGE` in `deploy-docs.yml`; raise it together with the `playwright` version in `docs-site/package.json`, and refresh the baselines in that pull request). ## 5. After launch: the routine | When | What | |---|---| | every pull request | the `docs` check (required on `integration` and `main`): gates, build, links, budgets, CSP, e2e, axe on every page, the visual baseline, Lighthouse on five pages (all twenty nightly and on `main`) — about nine minutes | | a dataset bump lands | nothing to do: the docs job installs the pinned database and re-verifies the cited lines | | every 15 minutes | `uptime` probes the wiki's landing page and `/api/health` through its rewrite; issue *uptime: docs site probe failing* | | every 6 hours | `docs-watch`: the domain serves the last deployed commit, the smoke and the live suite pass; issue *docs-watch: production wiki check failing* (after a manual rollback it stays open until the next release — expected) | | every night | the `docs` job re-runs on `integration`; issue *docs: nightly wiki build failing* while red | | Mondays | `docs-links` checks every external link (issue *docs: broken external links*); `security` scans the history; `docs-pins` opens *docs(wiki): bump the sibling docs pins* when a sibling changed a published file (or issue *docs: the sibling docs changed — bump the pins* until `DOCS_BOT_TOKEN` exists); `docs-freshness` rewrites issue *docs: wiki pages to re-verify* | | a page is in *wiki pages to re-verify* | re-read it against the code it cites, fix what drifted, move its `verified` stamp; the issue closes itself when every page is fresh | | a scholar review is due | `make review-pack` writes one PDF — Scripture 101, the glossary's scripture and script sections, the contributors — with a sign-off sheet | | a new page | frontmatter, links, the scripture rule and the fallback rule are gated; a poster or a widget follows [the contributing guides](https://docs.gurbanisoul.com/contributing/) | ## 6. Sibling README links Done (2026-09-26): both sibling READMEs open with a line pointing at the wiki's published copy of their docs ([sggs-data#11](https://github.com/Algorythmos-AI/sggs-data/pull/11), [gurbani-soul-ios#19](https://github.com/Algorythmos-AI/gurbani-soul-ios/pull/19)). ## 7. Open review items - The scholar review of [Scripture 101](https://docs.gurbanisoul.com/scripture/), the glossary's scripture terms and the contributors chronology. `make review-pack` builds the PDF with a sign-off sheet. - Resolved: sggs-data's `database-schema.md` now states the database's counts (4,527 compositions, `max(comp_id)` 5,376), and `pipeline/tests/test_docs_facts.py` there holds them to the database.