@@ -54,8 +54,15 @@ catches the known names; `web/scripts/prod-smoke.ts` catches the class. After
5454the Next build, ` build-web ` starts ` next start ` on the pinned Node against a
5555migrated Postgres container and drives one real client journey — session
5656admission, agent-run start, a streamed chat completion — with every external
57- HTTP call stubbed by ` web/scripts/prod-smoke-upstream.cjs ` . A request that
58- cannot complete end to end fails the job. The ` web ` Render service deploys
57+ HTTP call stubbed. A request that cannot complete end to end fails the job.
58+
59+ Both smokes are thin: ` packages/internal/src/prod-smoke/harness.ts ` owns the
60+ loopback-only database guard, the Bun-shim refusal, the seeded fixture (ad-data
61+ lifecycle row, user, session), staging a build outside the repo, the server
62+ spawn with log relay, ` /api/healthz ` polling with the optional runner gate, the
63+ authenticated request helper and teardown; ` upstream-stub.cjs ` beside it is the
64+ one table of stubbed upstream endpoints. An app's ` scripts/prod-smoke.ts `
65+ supplies only its port, launch command, environment, auth header and journey. The ` web ` Render service deploys
5966manually, so this makes ` main ` red before someone deploys it rather than
6067blocking the deploy itself.
6168
@@ -67,6 +74,31 @@ it refuses any other host):
6774cd web && DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/testdb bun run smoke:prod
6875```
6976
77+ ` build-freebuff-web ` has the same gate for the builder app,
78+ ` freebuff/web/scripts/prod-smoke.ts ` , and it matters more there: ` freebuff-web `
79+ auto-deploys from ` main ` . Two differences. The pin is that service's own
80+ ` freebuff/web/.node-version ` , not the repo root's, because Render applies the
81+ root file to every service and a root pin meant for ` web ` moved freebuff-web
82+ from 22.x to 20.15.1 on 2026-09-13, breaking its build on a Node 20 flag while
83+ CI stayed green; the job now installs that Node _ before_ the compile, since the
84+ app's build script runs ` node ` itself. And the journey is a chat turn behind a
85+ seeded database session cookie rather than an API-key completion, because that
86+ is the path that ` import() ` s ` @codebuff/sdk ` out of the standalone tree at
87+ request time and runs the agent in-process; the SDK's calls to the backend
88+ (` /api/v1/me ` , the run ledger, the model) all land in the stub's table.
89+ Hiding ` @codebuff/sdk ` from the standalone tree makes it fail, which is the
90+ proof that it exercises what the build's import check only approximates. The
91+ embedded agent runner is made _ required_ (as on a deployed instance) with
92+ placeholder Convex credentials, so a runner that cannot boot is a 503 from
93+ ` /api/healthz ` and a fast red rather than a warning; note that Next polyfills
94+ ` WebSocket ` on its server, so the runner's "Node >= 22 required" bail does not
95+ fire on Node 20 under this app — a Node downgrade is caught by building and
96+ smoking on the pinned version, not by that check.
97+
98+ ``` bash
99+ cd freebuff/web && DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/testdb bun run smoke:prod
100+ ```
101+
70102## What CI actually spends its time on
71103
72104` ci.yml ` is one flat fan-out: ` typecheck ` , ` build-web ` , and three test matrices,
0 commit comments