Skip to content

Local CI

The engine’s CI runs locally. bin/ci is the gate, wired to a pre-push hook, and the hosted GitHub Actions workflow is opt-in (workflow_dispatch).

The hosted workflow was billing-gated and had been reporting nothing for days — every job refused before it started, so main sat red and the signal was worthless. Worse, its only real check was a single shell test, run redundantly across nine billed jobs; the generate and verify steps were stubs that echoed and exited.

Everything worth checking here is reproducible on a laptop in seconds, so that is where it lives.

Terminal window
bin/ci # fast stages — what the pre-push hook runs
bin/ci --full # also builds the landing + docs sites
bin/ci --stage links # run one stage
bin/ci --list # list stages

Exit code is 0 if everything passed, 1 otherwise.

StageWhat it proves
source-hashThe generate/ digest helper hasn’t drifted. This is the one real check the hosted workflow ran.
shellEvery .sh parses (bash -n) and passes shellcheck -S error.
pythonEvery .py compiles.
pathsNo foreign absolute home paths (/Users/<name>/…, /home/<name>/…).
vault-refsEvery llm-wiki-* name the engine mentions is either a real vault or an allowlisted placeholder.
linksCross-vault links are well formed — the visible vault-short: matches the vault= param — and targets resolve.
buildThe Astro landing + docs sites build. Only in --full; auto-skips when Node or deps are missing.

paths and vault-refs exist because the same two regressions kept coming back: another machine’s absolute paths, and real private vault names leaking into examples. They are regression guards, not style checks.

Terminal window
bin/install-hooks # sets core.hooksPath=.githooks (idempotent)

install.sh does this automatically. core.hooksPath is local git config, so each clone runs the installer once.

Bypass when you need to:

Terminal window
git push --no-verify # skip once
LLM_WIKI_SKIP_CI=1 git push # skip via env
  • vaults/ — each vault is its own git repo with its own history.
  • .private/ — the operator overlay. Naming real vaults is its whole job, so linting it as engine content produces nothing but false positives.
  • golden-corpus/ — a frozen regression fixture. Its content is the fixture; “fixing” it would invalidate the baselines.
  • .ralph/prd-*.json — dated records of completed work.

If a doc needs an example vault that doesn’t exist, add it to .ci/vault-placeholders.txt. That file is the allowlist, which is what stops a real private vault name from quietly becoming an example.

The Golden Corpus workflow still exists and can be triggered by hand from the Actions tab, or:

Terminal window
gh workflow run "Golden Corpus"

GitHub Pages deployment (deploy.yml) is still an Actions workflow, because publishing to Pages needs an OIDC token minted by the runner. While Actions is billing-gated the docs site does not update — it has been frozen since 2026-08-10 — so the page you are reading may be behind main.

bin/ci --full builds both sites locally, which proves the build is not broken; it simply cannot publish.

This is postponed rather than forgotten. The diagnosis, the two viable fixes (settle the billing, or switch Pages to deploy-from-branch) and the reason local CI cannot cover it are written up in issue #60.