Verify and CI
bun run verify is the single pre-push command in this repo, and this page explains why it exists, what it actually gates, how CI mirrors it, and the one thing it deliberately does not cover: the real-Postgres integration lane. If you internalize one page in this section, make it this one.
The motivation is iteration cost. A CI round-trip through GitHub Actions is ~10 minutes; verify runs the same gate sequence locally in ~3–8 minutes and catches most failures in seconds. The repo’s stance (from CLAUDE.md): always run bun run verify from the root before pushing any branch that will trigger CI, and never treat a per-package check as a substitute — per-package runs miss cross-package type inference, the AppShell bundle budget, the fixture-key registry, the schema-audit error-class pattern, and tests that depend on workspace-built artifacts.
What verify runs
The verify script in the root package.json chains build, workspace typecheck, roughly forty gate scripts from scripts/check-*.ts, and the full test suite:
| Stage | Examples |
|---|---|
| Build + typecheck | turbo run build, turbo run typecheck |
| Package structure | check:boundaries, check:integration-contract, check:barrel-hygiene |
| Auth / authz | check:auth-method-lockstep, check:authz-endpoint-coverage, check:authz-authority-inventory, check:canonical-identity |
| Fund accounting | check:fund-accounting-write-cutover, check:account-code-semantics |
| Portal | display-only imports, canonical links, route health, trip-wire (boot+render smoke), bundle audit, a11y baseline, dev_local reachability, demo-fixture imports |
| Data safety | data-safety:fixtures, xlsx:safety, check:no-pii-logs-adjacent gates |
| Schema | schema:audit, schema:active-ci (active migration catalog + pglite schema contracts) |
| Tests | bun run test — the full Vitest suite (~2,000 test files) on pglite |
Copy-pasteable, from the repo root:
bun run verify # ~3-8 minutes; run before every pushHow CI mirrors it
.github/workflows/ci.yml runs on every PR and push to staging and main (docs/markdown paths are excluded), split into three parallel jobs:
The ci and gates jobs together cover roughly what verify covers (the gates job also runs the migration-immutability check, which needs full git history). The ci test phase caps TURBO_CONCURRENCY=4 and OPSDNA_TEST_MAX_WORKERS=4 so pglite (WASM Postgres) suites don’t oversubscribe the runner. Playwright e2e is not a merge gate — portal:trip-wire smoke-tests portal boot+render; the Playwright harness runs locally via bun run portal:e2e only.
verify ≠the postgres integration lane
bun run verify runs its tests on pglite, an in-process WASM Postgres. pglite silently passes things real Postgres rejects: EXECUTE grants, FORCE ROW LEVEL SECURITY, and cross-cutting catalog invariants like the identity-spine rule (every *_identity_id column must be uuid after migration 0311). Those live only in the separate postgres integration CI job, which runs against a real postgres:18 container — deliberately matching prod RDS PG 18, because postgres:16 gave false confidence. A green verify with a red postgres integration job is the classic tell that a schema change slipped the pglite lane.
When your change touches schema — a migration, a new *_identity_id column, RLS, grants — also run the lane locally against a real Postgres on :55432:
DATABASE_URL=postgres://opsdna:opsdna@localhost:55432/opsdna_dev \
bun run --cwd packages/persistence test:integrationOr target a single file:
bun x vitest run \
--config packages/persistence/vitest.integration.postgres.config.ts \
path/to/your.test.tsRelated
- Development workflow — daily commands and the local dev stack
- Branching and releases — what a green CI unlocks
- /entity-model — the invariants the schema gates enforce.