The hole
probePostgres() was catch { return false }. Every possible failure — a rotated service password, a service that never came up, an exhausted connection limit — read as "this machine has no Postgres", and the 14 files gated on it vanished into skip. A run with all of them gone still exits 0.
Measured on the suite as it stands, with DATABASE_URL pointed at a port nothing answers on:
| tests | pass | skip | exit | |
|---|---|---|---|---|
| Postgres reachable | 34 | 30 | 4 | 0 |
Postgres unreachable, CI=1 | 34 | 11 | 23 | 0 ← green |
19 live-Postgres assertions stop running and CI reports success.
This already happened
Every gate used to build its URL from process.env.USER, which on a runner is runner, so each probe failed password auth. From pg.ts's own docblock, written after that incident:
the gate read that as "no Postgres here", and the tests SKIPPED. CI stayed green while the guards for the two most severe bugs in the release never ran at all — the worst shape for a test to be in, because the green tick says the opposite.
The URL was fixed. The mechanism was not, so the next cause lands in the same silence.
public-surface-types.test.ts has the same story on its own dist/ gate — "the CI test job never built, so this returned early on every run and never checked anything" — and it already solved it properly: if (CI) throw, warn locally.
The change
Who supplies the precondition decides what its absence means. On a developer machine Postgres is optional and skipping is right — nobody should need it to change a string helper. In CI the workflow starts Postgres 17, waits for pg_isready, exports DATABASE_URL and builds before testing, so absence is a broken job.
test/gate.ts— lifts thepublic-surface-typesrule out so it is stated once and reachable from the next gate someone writes.QB_REQUIRE_LIVEoverrides either way:0for a fork with no service containers,1for CI strictness locally.test/pg.ts— an unreachable Postgres raises where one is provided, and the real driver error is surfaced instead of swallowed. Retries 3× only where failure is fatal: locally the connection is refused instantly and retrying per file is pure latency, while in CIpg_isreadyhas already run, so a retry covers a blip rather than boot.test/dist.config-singleton.test.ts— the one guard that can see a bundler binding-split (#1043) skipped on a missingdist/, i.e. precisely on the run where the build step had not executed.
public-surface-types.test.ts is left alone: it is already correct, and rewriting working code to share the helper would widen the diff for no behaviour change.
Verification
Both directions controlled, since only the pair is evidence — a gate that always raises would pass the first case while making the suite unrunnable for anyone without Postgres.
| mutation | result |
|---|---|
raise removed (if (false)) | the 2 cases asserting non-zero exit fail |
raise forced (if (true)) | the 2 skip cases fail |
On the real scenario — CI=1, unreachable Postgres, all 14 gated files — the run now exits 1 with 13 errors where it previously exited 0 with 23 skips. With a real Postgres under CI=1 it is unchanged at 30 pass / 0 fail, so no false positive. Both directions of the dist/ gate verified by moving dist/src/index.js aside: CI=1 → fail, no CI → skip, exit 0.
Full suite: 2512 pass, 0 fail (2508 before, +4 new gate tests). bun run lint 0 errors, none in the changed files. tsc clean.
🤖 Generated with Claude Code