Infra and tooling
Workflows, deploy configuration, repository scripts and the root tooling that enforce the rules.
There is no packages/infra. What this page calls infra is the repository's delivery layer: .github/workflows, infra/, render.yaml, scripts/, and the root tooling (ESLint, Prettier, dependency-cruiser, commitlint, husky, coverage thresholds). By the ownership map these paths belong to the QA area (packages/e2e/**, infra/**, .github/**, scripts/** and the root tooling files).
Heavy work (builds, test suites, browsers, screenshots) runs in GitHub Actions. This page says what runs where; the full workflow map is in Operators: CI and deploy pipeline.
Root tooling
Prop
Type
The dependency rule
.dependency-cruiser.cjs allows these imports and treats every other cross-package import as an error:
| Package | May import |
|---|---|
shared | nothing |
engine | shared |
payments | shared, engine |
voice | shared |
server | shared, engine, payments, voice |
web | shared, voice |
A second rule, engine-no-node-io, stops the engine from importing fs, net, http, child_process, os and the other I/O modules (except under src/testing and src/node). npm run check:deps runs the cruiser, then scripts/check-engine-purity.mjs (no direct wall-clock or randomness in the engine) and the payments package's own dependency check. The e2e package is not in the rule.
Scripts
| Script | What it checks or does |
|---|---|
check-ascii.mjs | Fails on a non-ASCII character in checked files |
check-banned-words.mjs | Fails on a banned word; the list comes from a CI secret or a local file |
check-branch-pr.mjs | Branch name and pull request title rules (see Contributing) |
check-ownership.mjs | A pull request may touch one area only, unless it has the cross-area label |
check-interface-label.mjs | Regenerates the API reports; a changed report needs the interface label |
check-bundle-secrets.mjs | Scans a built web bundle for secret names with values, token-like strings and canary values |
check-no-live-host.mjs | Fails when source or configuration names a live PayPal host |
check-engine-purity.mjs | No direct clock or randomness in engine sources |
check-coverage.mjs | Per-package coverage floors |
check-docs-format.mjs | Lints docs/DECISIONS.md and docs/GATES.md against their formats |
check-gates-result.mjs | A flag in gates-result.json may be on only with a closed entry in docs/GATES.md |
check-fixtures.mjs | Audio sidecars, WAV format and size, recorded speech results |
check-audit.mjs | Dependency audit with expiring waivers |
classify-change.mjs | Decides whether a pull request changes only documentation |
smoke.sh and smoke-dist.mjs | Post-deploy smoke of the API host; smoke of the built server |
gate-g3-early.mjs | Early G3 evidence from older sandbox authorizations (read, or probe) |
The scripts have their own tests (scripts/tests, eslint.rules/tests), run by npm run test:tools.
Workflows
The workflow files are in .github/workflows. Grouped by purpose, with the trigger as written in the file:
| Workflow | What it does |
|---|---|
ci | The main pipeline. Jobs: changes, typecheck, lint, format-check, unit, coverage, property, scenarios, deps, secrets, ascii-banned, docs-format, commitlint, ownership, voice, e2e-fixtures, reference-python, differential, tools, package-checks, prod-build and the required roll-up. On a documentation-only change the build, test and type jobs are skipped and the cheap checks still run |
pr-meta | Validates branch name and title using the script from the base branch; the pull request code is never checked out |
interface-check | API reports and the interface label |
quality-tools | Runs when scripts, lint rules, hooks or root package.json change |
audit | Dependency audit on lockfile changes, on main, and weekly |
engine-readiness, server-postgres, payments-evidence, sim-multiday, load, voice-eval, voice-browser, browser-specs, ui-screens | Path-filtered: each runs when the files it covers change |
Deploy configuration
render.yaml is the Render blueprint for the single Node host: one instance (numInstances: 1) because the stores are files owned by one process, a persistent disk of 1 GB mounted at /var/data, health check /healthz, and autoDeploy: false (deploys come from the workflows through the deploy hook). The build is npm ci --include=dev --ignore-scripts && npm run build -w @gavel/server; the start command is npm run start:prod -w @gavel/server.
The server validates its environment before it starts. A missing or empty required variable, or a PAYPAL_ENV other than sandbox, ends the process with exit code 78 and one JSON line per problem that names the variable and the reason, never a value. Required everywhere: PUBLIC_URL, TOKEN_SECRET, ADMIN_KEY, PAYMENTS, PAYPAL_ENV; with PAYMENTS=sandbox also PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET and PAYPAL_WEBHOOK_ID.
Three GitHub environments exist: staging, production and sandbox-nightly. Secrets live in environment secrets and in the Render and Cloudflare dashboards; .env.example lists names with empty values.
Invariants
- Secrets are never printed or committed. Workflow values reach steps through
envonly.gitleaks-history, thesecretsjob inci, andcheck-bundle-secrets.mjsscan for them. - Only the PayPal sandbox.
check-no-live-host.mjsfails on a live PayPal host and the server refuses anyPAYPAL_ENVother thansandbox. - A gate flag follows its record.
check-gates-result.mjstiesgates-result.jsonto a closed entry indocs/GATES.md. - Coverage floors only rise. The coverage job fails when a threshold is lowered.
- Workflows are pinned. Third-party actions are referenced by commit hash.
Tests that guard it
scripts/tests/*.test.mjs and eslint.rules/tests/*.test.mjs (run by npm run test:tools in the tools job of ci), and packages/e2e/test/repo-contracts.test.ts, which reads workflow and script files to check they stay wired.
- .github/workflows/ci.ymlthe main pipeline and the required roll-up
- .dependency-cruiser.cjspackage dependency rule
- commitlint.config.cjscommit header rules
- scripts/ownership.map.jsonwhich paths belong to which area
- render.yamlRender blueprint
- infra/environments.mdenvironments and secrets by name
- infra/rollback.mdproduction deploy and rollback
- infra/uptime.mduptime probe and warmup
Last updated on