<!-- reviewed: 2026-06-27 | repo-wide consistency audit | canonical facts: docs/VERIFICATION-ANALYTICAL-DATA.md -->

# RUNBOOK — Cosmogonic Quantum Mechalogodrom

The one canonical ops doc: install, run, gate, build, deploy, release, roll back, troubleshoot.
For _what the project is_ see [AUDIT-LOG.md](AUDIT-LOG.md); for architecture see
[docs/ARCHITECTURE-2026-06-26.md](ARCHITECTURE-2026-06-26.md); for change history see [CHANGELOG.md](../CHANGELOG.md).

> **Path caveat:** this repo's absolute path contains `[ ]`. In PowerShell use `-LiteralPath` (or the
> dedicated file tools); plain globbing will mis-parse the brackets. Always run commands with the
> current directory **inside the repo** — Bun's HTML-import bundler fails when invoked from elsewhere.

## Prerequisites

- **Bun** `1.3.14` (pinned; the lockfile is `bun.lock`). Install from <https://bun.sh>.
- A modern WebGL2 browser for the app. No backend/database is required for the static demo.

## Install (clean checkout)

```bash
bun install                 # local
bun install --frozen-lockfile   # CI / reproducible (fails if lockfile drifts)
```

## Local ↔ GitHub sync (binding)

**Goal:** your checkout always matches `origin/main` before you run the app.

| Situation                 | Command                                                 | What it does                                                     |
| ------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------- |
| Start dev (automatic)     | `bun dev`                                               | runs `bun run guard` first via `predev`                          |
| Check sync only           | `bun run guard`                                         | ff-down if behind; push if ahead; never destroys work            |
| Pull only (ff-down)       | `bun run pull`                                          | fast-forward to `origin/main` when clean                         |
| Behind + want GitHub wins | `git fetch origin main && git reset --hard origin/main` | **destructive to local-only commits** — use when diverged        |
| Stuck rebase / diverged   | `bun run guard --force`                                 | preserves tip to `recovery/guard-*` then resets to `origin/main` |
| Opt out of auto-sync      | `CQM_NO_SYNC=1 bun dev`                                 | skip guard on dev start                                          |

**Symptoms:**

- Renamed docs missing locally → you are **behind** `origin/main`. Run `bun run pull` or `bun run guard`.
- `git status` shows dozens of `M` files with empty diff → CRLF phantom (Windows). `git reset --hard origin/main` re-checkouts as LF.
- `sync-guard: stuck rebase` on a clean tree → stale rebase state; `bun run guard --force` or `git rebase --abort` if a real rebase is active.

Post-commit hook auto-pushes **HEAD → `origin/main`** (opt out: `git config hooks.autopush false`). Work on `main` only — no PRs.

## Run locally

```bash
bun dev      # bun --hot server.ts — app at http://localhost:3000  (diagrams at /docs, spec at /spec, lab at /lab)
bun start    # non-hot production server (bun server.ts)
```

## The full gate (run before EVERY commit)

```bash
bun run check   # prettier --check → tsc --noEmit → oxlint → verify:receipts → sync:check → verify:facts → build
```

All stages must pass. Current baseline: prettier clean · tsc 0 · oxlint 0 · **3,631-test canonical floor** ·
`verify:receipts` clean · `sync:check` clean · build green. Individual stages:
`bun run format:check`, `bun run typecheck`, `bun run lint`, `bun run verify:receipts`,
`bun run sync:check`, `bun run verify:facts`, `bun run build`.

## Build & assemble the static site

```bash
bun run build   # → dist/ (minified bundle for index.html + docs.html + specs.html)
bun run pages   # bun run build + scripts/build-pages.ts → site/  (the GitHub Pages artifact)
```

`scripts/build-pages.ts` rewrites the absolute nav links (`/docs`→`docs.html`, `/spec`→`specs.html`,
`/lab`→`lab/`) to subpath-relative for the project-Pages host, drops the `/lab` artifact at
`site/lab/index.html`, and neutralizes the server-only `/api/audit` poll. Both `dist/` and `site/` are
**git-ignored** and wiped before each assembly — never commit them.

## Deploy (GitHub Pages, via Actions)

Pushing to `main` triggers `.github/workflows/pages.yml`, which builds and deploys to GitHub Pages.
Live site: <https://0thernes.github.io/cosmogonic-quantum-mechalogodrom/>.

**GitHub repo hygiene (one-person, direct-main, no PRs):**

- **Truth branch:** `main` only. The repository hook runs the full gate before commit and then pushes
  `HEAD:main`; do not open PRs or leave automation branches as alternate truth.
- **CI:** push to `main` runs `.github/workflows/ci.yml` (Linux + Windows full gate) + `pages.yml` (deploy).
  Diagnose: `gh run list --branch main --limit 5` · `gh run view <id> --log-failed`.
- **Pages gotcha:** `github-pages` environment must allow `main` deploys (see below).

- **Gotcha (already fixed, keep in mind):** the `github-pages` _environment_ deployment-branch policy
  must allow `main`, or every deploy is silently **rejected** at the deploy step while CI stays green.
  Re-apply with:
  `gh api --method POST repos/0thernes/cosmogonic-quantum-mechalogodrom/environments/github-pages/deployment-branch-policies -f name=main -f type=branch`
- Diagnose a stale deploy: `gh run list --workflow=pages.yml` (look for `failure`), then
  `gh run view <id>` and read the **annotations** (the env-protection rejection shows there, not in the logs).

## Cut a release

```bash
# Bump package.json + date the CHANGELOG [x.y.z] section, then commit the exact reviewed scope.
git add package.json CHANGELOG.md <changed files>
git commit -m "chore(release): vX.Y.Z — …"
# The fail-closed pre-commit gate runs; post-commit pushes direct main. After hosted CI succeeds,
# auto-tag.yml creates the lightweight vX.Y.Z tag at that exact SHA and dispatches release.yml.
```

Verify: `gh release view vX.Y.Z`, `gh run list --workflow=release.yml`, and confirm the tag resolves to
the CI-approved main SHA. The internal `V##` numbers are not semver; `package.json` is authoritative.

## Roll back

- **Code/release:** `git checkout vX.Y.Z` (automated releases use lightweight tags) — the last known-good is the
  newest `v*` tag. To re-deploy an older state, push that state to `main` (Pages always deploys `main`).
- **Pages only:** the GitHub Pages "deployments" list keeps prior successful deploys; the previous one is
  the rollback target if a bad deploy ships.

## Troubleshoot

| Symptom                                                             | Cause                                                              | Fix                                                                                  |
| ------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `bun dev` shows `initCenterHud is not a function` or a stale layout | `bun --hot` cached a broken intermediate bundle during rapid edits | Restart `bun dev`. The committed code compiles (`tsc` 0) and builds clean.           |
| Live Pages site looks stale after a push                            | `github-pages` env rejected the `main` deploy                      | See the Deploy "Gotcha" above; check `gh run list --workflow=pages.yml` annotations. |
| Gate fails on `format:check`                                        | unformatted files                                                  | `bunx prettier --write <files>` then re-run `bun run check`.                         |
| `dist/` huge / many orphaned chunks                                 | (fixed) build now wipes `dist/` first                              | Re-run `bun run build`; if an old tree persists, `rm -rf dist site` and rebuild.     |
| Determinism test fails                                              | `Math.random`/`Date.now` crept into `src/sim/**`                   | Route all sim randomness through the seeded `Rng` (`src/math/rng.ts`).               |

## Determinism & license invariants (do not break)

- All simulation randomness flows through the seeded `Rng` (`src/math/rng.ts`); `Math.random`/`Date.now`
  are **banned** in `src/sim/**` (enforced by `tests/determinism-law.test.ts`).
- License is **proprietary / UNLICENSED** by design — never relicense to MIT/open-source.
- `legacy/` is preserved verbatim — never edit it.

**Ralph Wiggum Loop (MANDATORY per mandate):** If any tool, push, MCP, check, or external call hangs or errors, sleep 15s then retry the exact command. Continue non-stop. Bank green, persist plan, forward progress. Used for all Tsotchke wiring + this session.
