Skip to content

Commit 0adae72

Browse files
committed
feat: add database check scripts and improve environment variable handling
- Introduced `db-check-optional.mts` and `db-check-required.mts` for database connectivity checks. - Updated `cleanup_expired_vine_sessions.mts` and other scripts to use bracket notation for environment variables. - Enhanced logging for production environment checks. - Added new guardrail checks for script contracts and runtime I/O. - Created fixtures and tests for new guardrail checks and ts-coverage validations. - Updated TypeScript configuration files for improved type checking and module resolution.
1 parent a7cbda8 commit 0adae72

72 files changed

Lines changed: 1059 additions & 178 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 4 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,7 @@ on:
99
jobs:
1010
ci:
1111
runs-on: ubuntu-latest
12-
services:
13-
postgres:
14-
image: postgres:16
15-
env:
16-
POSTGRES_USER: postgres
17-
POSTGRES_PASSWORD: postgres
18-
POSTGRES_DB: cherry_test
19-
ports:
20-
- 5432:5432
21-
options: >-
22-
--health-cmd="pg_isready -U postgres -d cherry_test"
23-
--health-interval=5s
24-
--health-timeout=5s
25-
--health-retries=20
2612
env:
27-
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/cherry_test?schema=public
2813
NODE_OPTIONS: "--conditions=development"
2914
PATH: "/usr/bin:/bin:/usr/local/bin"
3015
steps:
@@ -39,55 +24,13 @@ jobs:
3924
node-version: '22'
4025
cache: 'npm'
4126

42-
- name: Install Postgres client
27+
- name: Install toolchain
4328
run: |
4429
sudo apt-get update
45-
sudo apt-get install -y postgresql-client ripgrep
30+
sudo apt-get install -y ripgrep
4631
4732
- name: Install dependencies
4833
run: npm ci
4934

50-
- name: Check clean working tree
51-
run: npm run check:clean
52-
53-
- name: Wait for Postgres
54-
run: |
55-
ready=0
56-
for i in {1..30}; do
57-
if pg_isready -h localhost -p 5432 -U postgres -d cherry_test; then
58-
echo "Postgres is ready"
59-
ready=1
60-
break
61-
fi
62-
echo "Waiting for Postgres... ($i)"
63-
sleep 2
64-
done
65-
if [ "$ready" -ne 1 ]; then
66-
echo "Postgres never became ready"
67-
exit 1
68-
fi
69-
70-
- name: Apply Prisma migrations to test DB
71-
run: |
72-
npx prisma generate --schema=prisma/schema.prisma
73-
npx prisma migrate deploy --schema=prisma/schema.prisma
74-
75-
- name: Verify Prisma migration status
76-
run: npx prisma migrate status --schema=prisma/schema.prisma
77-
78-
- name: Dump migration status on failure
79-
if: failure()
80-
run: |
81-
echo "DB: $DATABASE_URL" | sed -E 's#(//[^:/@]+):[^@]+@#\\1:***@#'
82-
npx prisma migrate status --schema=prisma/schema.prisma || true
83-
psql "$DATABASE_URL" -c "\\dt" || true
84-
85-
86-
- name: Test
87-
run: npm test
88-
89-
- name: Build
90-
run: npm run build
91-
92-
- name: Check (composite)
93-
run: npm run check
35+
- name: Verify CI truth
36+
run: npm run ci:verify

‎.github/workflows/env-checks.yml‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: env-checks
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
9+
jobs:
10+
env:
11+
runs-on: ubuntu-latest
12+
if: ${{ secrets.DATABASE_URL != '' }}
13+
env:
14+
DATABASE_URL: ${{ secrets.DATABASE_URL }}
15+
CHERRY_STRICT: "1"
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Setup Node
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: '22'
26+
cache: 'npm'
27+
28+
- name: Install dependencies
29+
run: npm ci
30+
31+
- name: Run env checks
32+
run: npm run check:env

‎docs/ci-and-guardrails.md‎

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Status: Draft
2-
Last updated: 2025-12-29
2+
Last updated: 2026-01-02
33

44
# CI and guardrails
55

@@ -8,21 +8,17 @@ Last updated: 2025-12-29
88
### Pipeline
99
- Runs on every push to `main` and all PRs via `.github/workflows/ci.yml`.
1010
- Steps (fail-fast):
11-
1) `npm ci`
12-
2) `npx prisma generate`
13-
3) `check:clean`
14-
4) Prisma migrate/generate
15-
5) `test` (runs `check:guardrails`, including `check:prisma-assumptions`)
16-
6) `build`
17-
7) `check` (composite superset, final semantic gate)
18-
- Tests run with `DATABASE_URL=file:./tmp/test.db` for isolation.
11+
1) `npm ci` (postinstall runs `prisma generate`)
12+
2) `npm run ci:verify` (composite truth gate: check + test + build)
13+
- Optional env lane (`.github/workflows/env-checks.yml`) runs `CHERRY_STRICT=1 npm run check:env` when secrets are available.
1914

20-
### Why CI Runs `npm run check`
15+
### Why CI Runs `npm run ci:verify`
2116

2217
- CI does not enumerate guardrails.
23-
- CI runs one authority: `npm run check`.
18+
- CI runs one authority: `npm run ci:verify`.
2419
- `check:guardrails` guarantees registry completeness, execution exclusivity, CI coverage, and ordering stability.
25-
- The last non-empty command in the CI job must be `npm run check`.
20+
- `check` stays pure (guardrails + lint + typecheck); env checks live in `check:env`.
21+
- The last non-empty command in the CI job must be `npm run ci:verify`.
2622

2723
> If CI ever runs individual guardrail scripts directly, the system is broken.
2824
@@ -34,12 +30,13 @@ Last updated: 2025-12-29
3430
- Guardrail files/tests must not be removed (offline evaluator, ingest, engine tests, Prisma assumptions).
3531
- Guardrail 5 (implicit config): `process.env` access is confined to `app/api/**` and `scripts/**`; load env into typed config via `initConfigFromEnv` and thread it explicitly. `check:config` must pass without allowlists.
3632
- Guardrail 6 (config immutability): server config is deep-frozen and locked after boundary load; `setServerConfig` rejects writes post-lock and loader registration fails once locked. `check:config-lock` must pass.
37-
- `check:ci-must-run-check` enforces the composite gate in CI.
33+
- `check:check-contract` enforces the `ci:verify` contract and keeps `check` pure.
34+
- `check:ci-must-run-check` enforces the single CI entrypoint (`ci:verify`).
3835
- `check:guardrails-core` exits non-zero on any deviation; CI treats that as a hard failure.
3936

4037
### How to run locally
4138

42-
Run the npm scripts: `check:guardrails`, `lint`, `typecheck`, `typecheck:scripts`, `test`.
39+
Run the npm scripts: `check` (pure), `test`, `build`, or the full gate `ci:verify`.
4340

4441
## Future/Target behavior
4542

‎docs/guardrails.md‎

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
Status: Active
2-
Last updated: 2026-01-03
2+
Last updated: 2026-01-02
33

44
# Guardrails
55

66
## Current behavior
77
- Guardrail and execution script registration is mandatory; registries are the only authority.
8-
- CI runs `npm run check` as the final composite gate; direct guardrail execution by path is forbidden.
8+
- CI runs `npm run ci:verify` as the sole truth gate; `check` remains pure (guardrails + lint + typecheck), and env checks live in `check:env`.
99
- Script conventions (no raw JSON.parse, no any, .mts only under scripts) live in `docs/script-standards.md`.
1010
- Guardrail checks now enforce JSON.parse bans in scripts and npm arg forwarding (`check:script-json-parse`, `check:npm-arg-forwarding`).
1111

@@ -149,11 +149,12 @@ Any duplication is a hard CI failure.
149149
- Guardrail names must map to canonical script filenames: `check:<name>` → `check-<name>.mts` with `:` normalized to `-` under `scripts/`.
150150
- Guardrail check: `check:guardrail-name-path-bijection`.
151151

152-
### Guardrail 22 — CI Composite Check Required
152+
### Guardrail 22 — CI Truth Entry Point
153153

154-
- CI must include a step that runs `npm run check`.
155-
- The last non-empty command in the CI job must be `npm run check`.
156-
- Guardrail check: `check:ci-must-run-check`.
154+
- CI must include a step that runs `npm run ci:verify`.
155+
- The last non-empty command in the CI job must be `npm run ci:verify`.
156+
- CI must not invoke other npm scripts directly; `ci:verify` is the only entrypoint.
157+
- Guardrail checks: `check:ci-must-run-check`, `check:ci-guardrail-coverage`.
157158

158159
### Guardrail 23 — Execution Registry Completeness
159160

@@ -202,6 +203,18 @@ Any duplication is a hard CI failure.
202203
- TS extension specifiers and `@/` aliases are forbidden in scripts.
203204
- Guardrails: `check:no-ts-extension-imports`, `check:no-script-alias-imports`.
204205

206+
### Guardrail 30 — Check Contract
207+
208+
- `ci:verify` must run `check`, `test`, and `build` in order.
209+
- `check` must remain pure (no env-dependent scripts).
210+
- Guardrail: `check:check-contract`.
211+
212+
### Guardrail 31 — Script Runner Contract
213+
214+
- Package scripts that invoke files under `scripts/` must go through `npm run ts:esm`.
215+
- Direct `node`, `tsx`, or `ts-node` usage in script commands is forbidden.
216+
- Guardrail: `check:script-runner-contract`.
217+
205218
## Future/Target behavior
206219

207220
- TODO: Expand guardrail coverage and tests as new risk areas are identified.

‎docs/script-standards.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
Status: Active
2-
Last updated: 2025-12-29
2+
Last updated: 2026-01-02
33

44
# Script Standards
55

66
## Current behavior
77
- Scripts are ESM by extension; `.mts` only lives under `scripts/`, runtime code stays `.ts`.
88
- Guardrail entrypoints are registered in `scripts/guardrails/registry.mts` and must be reachable from `npm run check`.
99
- Execution entrypoints are registered in `scripts/execution/registry.mts` and run via `npm run ts:esm -- scripts/execution/run.mts <name>`.
10-
- CI must run `npm run check` and it must be the final non-empty command in the job.
10+
- CI must run `npm run ci:verify` and it must be the final non-empty command in the job.
1111
- JSON inputs must be parsed via `scripts/guardrails/lib/read-json.mts`; raw `JSON.parse` is forbidden outside that helper.
1212
- NPM script args must be forwarded with `--` (use `npm run <script> -- <args>`).
1313
- `any` is forbidden in scripts; use `unknown` plus explicit schema/type guards.
1414
- `catch` params must be typed as `unknown` and normalized before use.
1515
- Orphan scripts are forbidden; register or delete them.
16+
- Package scripts that invoke `scripts/` files must use the `ts:esm` wrapper; direct `node`, `tsx`, or `ts-node` invocations are forbidden.
17+
- Node scripts must use runtime extensions (`.js`/`.mjs`/`.cjs`) and avoid `@/` aliases.
1618

1719
## Future/Target behavior
1820
- TODO: Keep execution and guardrail registries fully derivable from documented standards.

‎eslint.config.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export default defineConfig([
106106
...nextVitals,
107107
...nextTs,
108108
globalIgnores([
109+
'.tmp/**',
109110
'.next/**',
110111
'out/**',
111112
'build/**',

‎package.json‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"dev": "next dev",
1212
"build": "npm run check:guardrails && next build",
1313
"start": "next start",
14+
"ci:verify": "npm run check && npm run test && npm run build",
1415
"check:clean": "npm run ts:esm -- scripts/execution/run.mts check:clean",
1516
"check:db-ready": "npm run ts:esm -- scripts/execution/run.mts check:db-ready",
1617
"check:routes": "npm run ts:esm -- scripts/guardrails/run.mts check:routes",
@@ -20,8 +21,8 @@
2021
"lint:scripts": "eslint scripts --max-warnings=0",
2122
"lint:tailwind": "npm run check:tailwind-conflicts",
2223
"ts:esm": "CHERRY_TSESM=1 npx tsx --tsconfig tsconfig.scripts.json",
23-
"typecheck": "tsc --noEmit",
24-
"typecheck:scripts": "tsc --project tsconfig.scripts.json --noEmit",
24+
"typecheck": "tsc -b tsconfig.typecheck.json --pretty false",
25+
"typecheck:scripts": "tsc -b tsconfig.scripts.typecheck.json --pretty false",
2526
"check:server-entropy": "npm run ts:esm -- scripts/guardrails/run.mts check:server-entropy",
2627
"check:ordering": "npm run ts:esm -- scripts/guardrails/run.mts check:ordering",
2728
"check:identity": "npm run ts:esm -- scripts/guardrails/run.mts check:identity",
@@ -36,6 +37,7 @@
3637
"check:loader-contract": "npm run ts:esm -- scripts/guardrails/run.mts check:loader-contract",
3738
"check:esm-loader-totality": "npm run ts:esm -- scripts/guardrails/run.mts check:esm-loader-totality",
3839
"check:prisma-mock-loader-totality": "npm run ts:esm -- scripts/guardrails/run.mts check:prisma-mock-loader-totality",
40+
"check:script-runner-contract": "npm run ts:esm -- scripts/guardrails/run.mts check:script-runner-contract",
3941
"check:no-script-alias-imports": "npm run ts:esm -- scripts/guardrails/run.mts check:no-script-alias-imports",
4042
"check:no-ts-extension-imports": "npm run ts:esm -- scripts/guardrails/run.mts check:no-ts-extension-imports",
4143
"check:guardrail-no-runtime-io": "npm run ts:esm -- scripts/guardrails/run.mts check:guardrail-no-runtime-io",
@@ -62,8 +64,10 @@
6264
"check:user-pages-runtime": "npm run ts:esm -- scripts/guardrails/run.mts check:user-pages-runtime",
6365
"check:config-lock": "npm run ts:esm -- scripts/guardrails/run.mts check:config-lock",
6466
"check:ts-coverage": "npm run ts:esm -- scripts/guardrails/run.mts check:ts-coverage",
67+
"check:check-contract": "npm run ts:esm -- scripts/guardrails/run.mts check:check-contract",
68+
"check:env": "npm run check:db:optional",
6569
"check": "npm run check:guardrails && npm run lint && npm run lint:scripts && npm run typecheck && npm run typecheck:scripts",
66-
"check:guardrails": "npm run check:ts-coverage && npm run check:side-effects && npm run check:side-effects:diff && npm run check:script-semantics && npm run check:script-json-parse && npm run check:npm-arg-forwarding && npm run check:loader-contract && npm run check:esm-loader-totality && npm run check:prisma-mock-loader-totality && npm run check:no-script-alias-imports && npm run check:no-ts-extension-imports && npm run check:guardrail-no-runtime-io && npm run check:implicit-boolean && npm run check:branded-literal && npm run check:guardrail-self && npm run check:guardrail-time && npm run check:guardrail-registry && npm run check:guardrail-name-path-bijection && npm run check:guardrail-doc-sync && npm run check:guardrail-execution && npm run check:guardrail-helpers-exclusive && npm run check:guardrail-subprocess-totality && npm run check:ci-must-run-check && npm run check:ci-guardrail-coverage && npm run check:execution-registry-completeness && npm run check:no-orphan-check-files && npm run check:no-orphan-scripts && npm run check:server-entropy && npm run check:ordering && npm run check:identity && npm run check:config && npm run check:config-init && npm run check:config-lock && npm run check:determinism && npm run check:engine-prisma && npm run check:engine-date && npm run check:authority-lint && npm run check:authority-invariants && npm run check:prisma-assumptions && npm run check:dev-ui-parity && npm run check:shell-boundaries && npm run check:route-collisions && npm run check:user-pages-runtime && npm run check:catch-unknown && npm run check:guardrails-core && npm run check:repo-guardrails && npm run check:routes && npm run check:engine-freeze && npm run check:migrations && npm run check:db",
70+
"check:guardrails": "npm run check:ts-coverage && npm run check:check-contract && npm run check:side-effects && npm run check:side-effects:diff && npm run check:script-semantics && npm run check:script-json-parse && npm run check:npm-arg-forwarding && npm run check:loader-contract && npm run check:esm-loader-totality && npm run check:prisma-mock-loader-totality && npm run check:script-runner-contract && npm run check:no-script-alias-imports && npm run check:no-ts-extension-imports && npm run check:guardrail-no-runtime-io && npm run check:implicit-boolean && npm run check:branded-literal && npm run check:guardrail-self && npm run check:guardrail-time && npm run check:guardrail-registry && npm run check:guardrail-name-path-bijection && npm run check:guardrail-doc-sync && npm run check:guardrail-execution && npm run check:guardrail-helpers-exclusive && npm run check:guardrail-subprocess-totality && npm run check:ci-must-run-check && npm run check:ci-guardrail-coverage && npm run check:execution-registry-completeness && npm run check:no-orphan-check-files && npm run check:no-orphan-scripts && npm run check:server-entropy && npm run check:ordering && npm run check:identity && npm run check:config && npm run check:config-init && npm run check:config-lock && npm run check:determinism && npm run check:engine-prisma && npm run check:engine-date && npm run check:authority-lint && npm run check:authority-invariants && npm run check:prisma-assumptions && npm run check:dev-ui-parity && npm run check:shell-boundaries && npm run check:route-collisions && npm run check:user-pages-runtime && npm run check:catch-unknown && npm run check:guardrails-core && npm run check:repo-guardrails && npm run check:routes && npm run check:engine-freeze && npm run check:migrations",
6771
"check:dev-ui-parity": "npm run ts:esm -- scripts/guardrails/run.mts check:dev-ui-parity",
6872
"check:shell-boundaries": "npm run ts:esm -- scripts/guardrails/run.mts check:shell-boundaries",
6973
"check:route-collisions": "npm run ts:esm -- scripts/guardrails/run.mts check:route-collisions",
@@ -82,7 +86,9 @@
8286
"cleanup:kill-alias-imports": "npm run ts:esm -- scripts/execution/run.mts cleanup:kill-alias-imports",
8387
"cleanup:vine-sessions": "npm run ts:esm -- scripts/execution/run.mts cleanup:vine-sessions",
8488
"check:prisma-assumptions": "npm run ts:esm -- scripts/guardrails/run.mts check:prisma-assumptions",
85-
"check:db": "npm run ts:esm -- scripts/guardrails/run.mts check:db",
89+
"check:db:optional": "npm run ts:esm -- scripts/execution/run.mts check:db:optional",
90+
"check:db:required": "npm run ts:esm -- scripts/execution/run.mts check:db:required",
91+
"check:db": "npm run check:db:optional",
8692
"check:migrations": "npm run ts:esm -- scripts/guardrails/run.mts check:migrations",
8793
"report:authority": "npm run ts:esm -- scripts/execution/run.mts report:authority",
8894
"report:bucket-balance": "npm run ts:esm -- scripts/execution/run.mts report:bucket-balance",

‎scripts/check-authority-invariants.mts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ import type {
1919
SimulateSpendParams,
2020
SimulatedAuthorityDecision,
2121
} from '../lib/authority/simulateSpendAuthority.js';
22+
import type { Digest } from '../lib/adapters/digest.js';
23+
import type { ServerConfig } from '../lib/config/server.js';
2224

2325
const fixedNowMs = 1704153600000;
2426
const fixedPeriodEndMs = 1706745600000;
25-
const digest = Sha256Digest;
27+
const digest = Sha256Digest as Digest;
2628
const PREFIX = 'check:authority-invariants';
2729
const FIX = 'Fix authority invariants or update the guardrail expectations.';
2830
const STATUS_SAFE = 'SAFE' as const;
@@ -58,7 +60,8 @@ async function main(): Promise<void> {
5860
process.env['NEXT_PUBLIC_SITE_VERSION'] = 'dev';
5961
}
6062
initConfigFromEnv(process.env);
61-
const engineVersion = getServerConfig().engineVersion;
63+
const serverConfig = getServerConfig() as ServerConfig;
64+
const engineVersion = serverConfig.engineVersion;
6265
const snapshot = buildSnapshot();
6366
const baseParams: SimulateSpendParams = {
6467
userId: 'user-1',

0 commit comments

Comments
 (0)