From 6a87da6eafaa03b0da43189a28c6a68180d297ff Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 05:26:38 +0000 Subject: [PATCH 1/8] Scaffold Phase 1 with Bun and Node test matrix. Stand up the package, specHelper, and Postgres smoke tests. CI runs the same node:test files on Bun and Node 26 against a Postgres service. There is no docker-compose file; local tests only need DATABASE_URL. Bun uses --max-concurrency=1; Node uses --test-concurrency=1. Co-authored-by: Evan Tahler --- .env.example | 1 + .github/workflows/test.yaml | 76 +++++++++++++++++++ .nvmrc | 1 + CLAUDE.md | 10 +-- README.md | 2 +- __tests__/smoke.test.ts | 18 +++++ __tests__/utils/specHelper.ts | 38 ++++++++++ biome.json | 38 ++++++++++ bun.lock | 118 +++++++++++++++++++++++++++++ docs/plans/01-repo-scaffold.md | 107 ++++++-------------------- docs/plans/07-multiworker.md | 5 +- docs/plans/08-conformance-tests.md | 5 +- docs/plans/10-publish-and-ci.md | 6 +- package.json | 38 ++++++++++ src/index.ts | 1 + tsconfig.json | 13 ++++ 16 files changed, 381 insertions(+), 96 deletions(-) create mode 100644 .env.example create mode 100644 .github/workflows/test.yaml create mode 100644 .nvmrc create mode 100644 __tests__/smoke.test.ts create mode 100644 __tests__/utils/specHelper.ts create mode 100644 biome.json create mode 100644 bun.lock create mode 100644 package.json create mode 100644 src/index.ts create mode 100644 tsconfig.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..52524ab --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/pgboss_queue_test diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..418cb6f --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,76 @@ +name: Test + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - run: bun install --frozen-lockfile + - run: bun run lint + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - run: bun install --frozen-lockfile + - run: bun run build + + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + runtime: [bun, node] + services: + postgres: + image: postgres:16 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: pgboss_queue_test + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - uses: actions/setup-node@v4 + if: matrix.runtime == 'node' + with: + node-version-file: .nvmrc + - run: bun install --frozen-lockfile + - name: Test with Bun + if: matrix.runtime == 'bun' + run: bun run test + env: + DATABASE_URL: postgres://postgres:postgres@localhost:5432/pgboss_queue_test + - name: Test with Node + if: matrix.runtime == 'node' + run: bun run test:node + env: + DATABASE_URL: postgres://postgres:postgres@localhost:5432/pgboss_queue_test + + complete: + if: always() + needs: [lint, build, test] + runs-on: ubuntu-latest + steps: + - name: Require all test jobs to pass + run: | + if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + exit 1 + fi diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..6f4247a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +26 diff --git a/CLAUDE.md b/CLAUDE.md index 0ef1f8b..f42e5ed 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -45,21 +45,21 @@ A PR with no `docs/plans/` diff is only OK when the change is truly unrelated (t ## Tooling (once Phase 1 exists) -This is a **Bun + TypeScript** project. Use `bun`, never `npm` or `npx`, for install/test/run. Use `bunx` if you need a package runner. PRs must stay green on `.github/workflows/test.yaml` (lint, build, `bun test` against Postgres). +This is a **Bun + TypeScript** project. Use `bun`, never `npm` or `npx`, for install/lint/build. Use `bunx` if you need a package runner. Tests are `node:test` so CI can run them on both Bun and Node. PRs must stay green on `.github/workflows/test.yaml` (lint, build, Bun + Node Postgres matrix). ```bash bun install # install -bun test # bun:test, needs Postgres +bun test # node:test via bun, needs Postgres +bun run test:node # same files on Node 26+ bun run lint # biome check bun run format # biome write bun run build # tsc / bun build of src → dist bun docs:dev # VitePress (Phase 9) ``` -Local Postgres (Phase 1 `docker-compose.yml`): +Local Postgres: set `DATABASE_URL` (see `.env.example`). CI starts Postgres as a workflow service; there is no `docker-compose.yml`. ```bash -docker compose up -d postgres # DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/pgboss_queue_test ``` @@ -135,7 +135,7 @@ Do **not** accept `pkg: "ioredis"`, `redis: Redis`, or `database: number`. Those - **JSDoc on every public class, method, and exported type.** `@param` for each parameter (including edge cases), `@returns` when non-obvious, `@throws` when applicable. Match node-resque's documented Queue methods. - **No Python.** New scripts, CLIs, and tooling are Bun + TypeScript. - **Biome** for format/lint (keryx-style), not Prettier. -- **Tests use `bun:test`**, not Jest. Port node-resque tests faithfully: same `describe` / `test` names, same assertions, Postgres `specHelper` instead of Redis. +- **Tests use `node:test`**, not Jest, so the same files run on Bun and Node. Port node-resque tests faithfully: same `describe` / `test` names, same assertions, Postgres `specHelper` instead of Redis. - **Every behavior change ships with tests.** A PR with no test changes is a red flag unless it is docs-only. - **Do not add dependencies** unless a phase plan names them. Expected runtime deps: `pg-boss`, `pg`. Dev: `typescript`, `@types/pg`, `biome`, `bun` types. diff --git a/README.md b/README.md index 34d5f3f..1770a3e 100644 --- a/README.md +++ b/README.md @@ -302,7 +302,7 @@ Raise your Postgres pool `max` when using a large `maxTaskProcessors`. Events ma ## Requirements -- Node.js 20+ or [Bun](https://bun.sh) +- Node.js 26+ or [Bun](https://bun.sh) - PostgreSQL 13+ (`SKIP LOCKED`) ```bash diff --git a/__tests__/smoke.test.ts b/__tests__/smoke.test.ts new file mode 100644 index 0000000..e4c8b25 --- /dev/null +++ b/__tests__/smoke.test.ts @@ -0,0 +1,18 @@ +import assert from "node:assert/strict"; +import { after, describe, test } from "node:test"; +import { connect, disconnect } from "./utils/specHelper.ts"; + +describe("Postgres smoke test", () => { + after(disconnect); + + test("DATABASE_URL is defined", () => { + assert.ok(process.env.DATABASE_URL); + }); + + test("SELECT 1 returns 1", async () => { + const pool = await connect(); + const result = await pool.query<{ value: number }>("SELECT 1 AS value"); + + assert.equal(result.rows[0]?.value, 1); + }); +}); diff --git a/__tests__/utils/specHelper.ts b/__tests__/utils/specHelper.ts new file mode 100644 index 0000000..9ac71d7 --- /dev/null +++ b/__tests__/utils/specHelper.ts @@ -0,0 +1,38 @@ +import { Pool, type PoolConfig } from "pg"; + +const connectionString = process.env.DATABASE_URL; + +if (!connectionString) { + throw new Error( + "DATABASE_URL is required to run the test suite. See .env.example.", + ); +} + +export const connectionDetails: PoolConfig = { connectionString }; +export const timeout = 500; +export const queue = "default"; +export const schema = "pgboss_queue_test"; + +let pool: Pool | undefined; + +export async function connect(): Promise { + pool ??= new Pool(connectionDetails); + await pool.query("SELECT 1"); + return pool; +} + +export async function disconnect(): Promise { + if (!pool) return; + + await pool.end(); + pool = undefined; +} + +export async function cleanup(): Promise { + const connection = await connect(); + await connection.query("SELECT 1"); +} + +export async function popFromQueue(): Promise { + throw new Error("not implemented"); +} diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..31053f2 --- /dev/null +++ b/biome.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.5.10/schema.json", + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + } + }, + "files": { + "includes": [ + "**", + "!**/node_modules", + "!**/dist", + "!**/docs/.vitepress/dist", + "!**/docs/.vitepress/cache", + "!**/*.md" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 80 + }, + "linter": { + "enabled": true, + "rules": { + "preset": "recommended" + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "trailingCommas": "all" + } + } +} diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..b84ad40 --- /dev/null +++ b/bun.lock @@ -0,0 +1,118 @@ +{ + "lockfileVersion": 2, + "configVersion": 1, + "workspaces": { + "": { + "name": "pgboss-queue", + "dependencies": { + "pg": "^8.23.0", + }, + "devDependencies": { + "@biomejs/biome": "^2.5.10", + "@types/bun": "^1.4.0", + "@types/node": "^26.3.0", + "@types/pg": "^8.23.1", + "typescript": "^7.0.2", + }, + }, + }, + "packages": { + "@biomejs/biome": ["@biomejs/biome@2.5.10", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.10", "@biomejs/cli-darwin-x64": "2.5.10", "@biomejs/cli-linux-arm64": "2.5.10", "@biomejs/cli-linux-arm64-musl": "2.5.10", "@biomejs/cli-linux-x64": "2.5.10", "@biomejs/cli-linux-x64-musl": "2.5.10", "@biomejs/cli-win32-arm64": "2.5.10", "@biomejs/cli-win32-x64": "2.5.10" }, "bin": { "biome": "bin/biome" } }, "sha512-WRKXARA3kTuiV5sxqTpobJ/I0MVd4vk3pOL6wnp5az4LntFIhWTj1RWZq3DI9PCEN3lXcqy7p5aqUHzvq8AXyQ=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.5.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ItCrxKK6SXVT6flYs0qIuBd4AA3TTTl4d66Re6YI2FuGZnN85NmuYNzkiTJUyYw8qBLv69L5zTUB6uyWd++h3Q=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.5.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-yLsPU9pAmtChXDu8vhKAzErqe+LeeYuwuUB2FZMkRitsmdodxsYRa9KHrFispsUHzzOu+9HB3nP/TQxyia+Sjw=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.5.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-VG8uQW/86a1roLaIFvtIbEigxIdzdJ190oGyg1tV7VYeQtOS+x10sflk7WbuXgw91EtZX5DlIIIej1YqkNLlcg=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.5.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-t1QAKZwQJRB4dvgJSgFiQ4BNfNPChg69BNonz854qLVxnjT3UvDzQg9mbkTJRu35ZqU0Rw10A73J8Urgbg2RPw=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.5.10", "", { "os": "linux", "cpu": "x64" }, "sha512-4O6T0eq2heoHZN0a9UX+rWQoxXEBaKf+lRi2hbsGlHneUz9BWXM76nEWMK7Eeq8gzMxR1khQB6BFpAASpeXqGg=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.5.10", "", { "os": "linux", "cpu": "x64" }, "sha512-pgDDqp9JybHm2I0KRgzN6i4+lt8xu4iqxUwLzglUMmOmyRTU1AYBGKzh9sNMOtIjah7xoWvKHlLVetvyifzoiQ=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.5.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-pxAbxduPO4xq/Cvgaa2lOrs9BB0hEXmmDqfMNP4ZOffGOkUrD1/QGw9UAMpFQpX2P8MqTIIRuQKcmetum4Oa6A=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.5.10", "", { "os": "win32", "cpu": "x64" }, "sha512-M+2dgBsl3lXRiTfgPVc2p3anS4Tocojke4rzFLScZ2Y/wmF+36dRb1iHCLiyGqOzQGyTplZH1HnEYviiAqi3nA=="], + + "@types/bun": ["@types/bun@1.4.0", "", { "dependencies": { "bun-types": "1.4.0" } }, "sha512-K+lZULY23vRgK/CfTjFIV+tyifaNdSMlPh9j+6mQ/cLfpOznLyAuzgV/JQysyECpkBQLVMSyvjlr2fBUSA9wFQ=="], + + "@types/node": ["@types/node@26.3.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw=="], + + "@types/pg": ["@types/pg@8.23.1", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A=="], + + "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], + + "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], + + "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], + + "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], + + "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], + + "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], + + "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], + + "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], + + "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], + + "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], + + "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], + + "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], + + "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], + + "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], + + "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], + + "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], + + "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], + + "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], + + "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], + + "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], + + "bun-types": ["bun-types@1.4.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-iIKw23BspnQQYd3prITOBxeUsxBHnwzX6YJfGMuNOZzeNcMmVqzIIVGRm1l69ogaPQmb4wB6BN8mA5bE9YuC5Q=="], + + "pg": ["pg@8.23.0", "", { "dependencies": { "pg-connection-string": "^2.14.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.16.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg=="], + + "pg-cloudflare": ["pg-cloudflare@1.4.0", "", {}, "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A=="], + + "pg-connection-string": ["pg-connection-string@2.14.0", "", {}, "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg=="], + + "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], + + "pg-pool": ["pg-pool@3.14.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw=="], + + "pg-protocol": ["pg-protocol@1.16.0", "", {}, "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg=="], + + "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], + + "pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="], + + "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + + "postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="], + + "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + + "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + + "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], + + "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], + + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + } +} diff --git a/docs/plans/01-repo-scaffold.md b/docs/plans/01-repo-scaffold.md index 2e7314e..fb4a766 100644 --- a/docs/plans/01-repo-scaffold.md +++ b/docs/plans/01-repo-scaffold.md @@ -1,6 +1,6 @@ # Phase 1 — Repo scaffold, test harness, and CI -**Status:** not-started +**Status:** in-progress **Depends on:** Phase 0 ## Goal @@ -23,16 +23,16 @@ CI must exist **before** Queue/Worker code. An empty `expect(true)` that never o - `type`: `"module"` - `main` / `types`: `dist/index.js` / `dist/index.d.ts` - `exports` with `import` + types - - `engines.node`: `>=20` + - `engines.node`: `>=26` - `license`: `Apache-2.0` - - `scripts`: `"test": "bun test --concurrency=1"`, `build`, `lint`, `format` (docs scripts wait for Phase 9) + - `scripts`: `"test": "bun test --max-concurrency=1"`, `"test:node": "node --experimental-strip-types --test-concurrency=1 --test './__tests__/**/*.test.ts'"`, `build`, `lint`, `format` (docs scripts wait for Phase 9) - `devDependencies`: `typescript`, `@types/node`, `@types/pg`, `@biomejs/biome`, `@types/bun` - `dependencies`: `pg` now (smoke test uses it). Add `pg-boss` in Phase 2 if you want to keep this PR smaller — either is fine as long as CI is green. - `tsconfig.json` — `strict`, `ES2022`, `moduleResolution: bundler` or `nodenext`, `declaration`, `outDir: dist`, `rootDir: src` - `biome.json` — match keryx reasonably (indent 2, no unused imports) - `.gitignore` — `node_modules`, `dist`, `.env`, `docs/.vitepress/dist`, `*.log` - `LICENSE` — Apache-2.0 -- `.nvmrc` or `.node-version` — `20` +- `.nvmrc` or `.node-version` — `26` - `.env.example` — `DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/pgboss_queue_test` ### Source stub @@ -46,22 +46,12 @@ Keep the file compiling. Do not fake Queue/Worker yet. ### Local Postgres -`docker-compose.yml`: - -```yaml -services: - postgres: - image: postgres:16 - environment: - POSTGRES_PASSWORD: postgres - POSTGRES_DB: pgboss_queue_test - ports: - - "5432:5432" - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 5s - timeout: 5s - retries: 10 +No `docker-compose.yml`. CI starts Postgres as a GitHub Actions service. Locally, point `DATABASE_URL` at any Postgres 13+ you already run (homebrew, apt, a shared dev database, etc.). Tests must never target production. + +`.env.example` is the only local-DB contract: + +``` +DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/pgboss_queue_test ``` ### Test harness (not a dummy assert) @@ -80,70 +70,17 @@ services: - `SELECT 1` returns `1` through specHelper's pool - `bun run build` artifacts exist *or* that check lives in CI only (prefer CI `bun run build`) -Do **not** ship only `expect(true).toBe(true)`. +Do **not** ship only `expect(true).toBe(true)`. Tests use `node:test` + `node:assert/strict` so the same files run on Bun and Node. ### CI — full test workflow now -`.github/workflows/test.yaml` is the product gate from this PR onward. Copy this shape (keryx `test.yaml` + node-resque's Postgres-instead-of-Redis): - -```yaml -name: Test -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - - run: bun install - - run: bun run lint - - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - - run: bun install - - run: bun run build - - test: - runs-on: ubuntu-latest - services: - postgres: - image: postgres:16 - env: - POSTGRES_PASSWORD: postgres - POSTGRES_DB: pgboss_queue_test - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 - - run: bun install - - run: bun test - env: - DATABASE_URL: postgres://postgres:postgres@localhost:5432/pgboss_queue_test - - complete: - if: always() - needs: [lint, build, test] - runs-on: ubuntu-latest - steps: - - run: | - if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then - exit 1 - fi -``` +`.github/workflows/test.yaml` is the product gate from this PR onward. Jobs: `lint`, `build`, `test` (Postgres 16 service, **matrix `runtime: [bun, node]`**), `complete`. + +- Bun leg: `bun run test` (`bun test --max-concurrency=1`) +- Node leg: `actions/setup-node` from `.nvmrc` (26), then `bun run test:node` +- `DATABASE_URL=postgres://postgres:postgres@localhost:5432/pgboss_queue_test` on both legs + +See the workflow file for the YAML. Do not duplicate a second test pipeline in Phase 10. Rules: @@ -161,8 +98,8 @@ Rules: - `bun install` works locally - `bun run build` emits `dist/` - `bun run lint` is clean -- `docker compose up -d postgres` + `bun test` is green locally -- **GitHub Actions on this PR is green** (lint, build, Postgres test job, `complete`) +- `DATABASE_URL` pointing at a local Postgres + `bun test` and `bun run test:node` are green +- **GitHub Actions on this PR is green** (lint, build, Bun + Node Postgres test matrix, `complete`) - Smoke test fails if Postgres is down or `DATABASE_URL` is missing - No runtime exports claimed that do not exist @@ -173,3 +110,7 @@ A compiling package, a shared `specHelper`, and CI that will run every subsequen ## Lessons learned - 2026-08-26 (plan): Test CI and a real Postgres smoke test (`SELECT 1`) belong here, not in Phase 10. Later phases must stay green on this workflow. +- 2026-08-26: Pin Node to Current 26 (`engines.node` `>=26`, `.nvmrc` `26`) instead of the original `>=20` pin. `@types/node` already tracks 26. +- 2026-08-26: `bun test --concurrency=1` is not a Bun flag (`bun test --help` has `--concurrent` and `--max-concurrency`, not `--concurrency`). Bun still accepted the unknown flag and ran anyway. Use `--max-concurrency=1` on Bun and `--test-concurrency=1` on `node --test`. Tests are sequential by default unless `--concurrent` / `--parallel` is set. +- 2026-08-26: Dropped `docker-compose.yml`. CI Postgres is a GitHub Actions service; locally `DATABASE_URL` is enough. Compose would only wrap a database this repo does not otherwise orchestrate. +- 2026-08-26: Test matrix runs the same `node:test` files on Bun and Node 26. `bun:test` cannot run on Node, so the suite is `node:test` + `node:assert/strict` rather than `bun:test`. diff --git a/docs/plans/07-multiworker.md b/docs/plans/07-multiworker.md index ae8bd24..8461f23 100644 --- a/docs/plans/07-multiworker.md +++ b/docs/plans/07-multiworker.md @@ -31,7 +31,7 @@ Port `__tests__/core/multiWorker.ts` **in this PR**: - stays at min on blocking CPU jobs - failure events bubble -These tests are CPU-noisy; keep `jest.retryTimes` equivalent (`test.todo` is not acceptable). bun:test has retry — use it. +These tests are CPU-noisy; keep a `jest.retryTimes` equivalent (`test.todo` is not acceptable). Prefer rerunning the file in CI over weakening assertions. **CI:** green on `test.yaml` before merge. @@ -52,4 +52,5 @@ pg-boss `localConcurrency` is **not** a substitute. MultiWorker must spawn real ## Lessons learned -_None yet._ +- 2026-08-26: Phase 1 runs tests with `node:test`, not `bun:test`. Do not assume Bun-only retry APIs when this phase is implemented. + diff --git a/docs/plans/08-conformance-tests.md b/docs/plans/08-conformance-tests.md index 7e01cb8..b306fb9 100644 --- a/docs/plans/08-conformance-tests.md +++ b/docs/plans/08-conformance-tests.md @@ -29,7 +29,7 @@ If a row above is missing when you start this phase, that is a **bug in an earli | node-resque | pgboss-queue | | --- | --- | -| Jest + ts-jest | `bun:test` (`bun test --concurrency=1`) | +| Jest + ts-jest | `node:test` (`bun test --max-concurrency=1` and `node --test --test-concurrency=1`) | | `ioredis` specHelper | `__tests__/utils/specHelper.ts` | | `REDIS_HOST` | `DATABASE_URL` (CI injects it) | | `afterAll` disconnect redis | `afterAll` end pool + truncate or `DROP SCHEMA` | @@ -45,7 +45,7 @@ connect / disconnect / cleanup popFromQueue(): Promise ``` -**Isolation:** truncate + migrate once in `beforeAll` for speed, or per-file schema. Keep `--concurrency=1` until proven otherwise. +**Isolation:** truncate + migrate once in `beforeAll` for speed, or per-file schema. Keep Bun `--max-concurrency=1` and Node `--test-concurrency=1` until proven otherwise. Do not pass `bun test --concurrency=1` — that is not a Bun flag. ## Matrix @@ -169,3 +169,4 @@ Docs site can describe a real API. Phase 10 can trust tests that have been runni ## Lessons learned - 2026-08-26 (plan): This phase is an audit, not the first test suite. Tests ship with Phases 1–7; CI has been running since Phase 1. +- 2026-08-26: Phase 1 corrected the runner to `node:test` on a Bun + Node matrix. Isolation uses `--max-concurrency=1` / `--test-concurrency=1`, not `bun test --concurrency=1`. diff --git a/docs/plans/10-publish-and-ci.md b/docs/plans/10-publish-and-ci.md index 7747036..9474a78 100644 --- a/docs/plans/10-publish-and-ci.md +++ b/docs/plans/10-publish-and-ci.md @@ -94,7 +94,7 @@ jobs: `package.json` must include `"files": ["dist", "README.md", "LICENSE"]` so we do not publish tests or plans. -Optional later: Node 20/22/24 matrix for compiled `dist/` consumers. Add to `test.yaml` (Phase 1 file), not here. +The Phase 1 `test` job already matrices Bun and Node 26 against Postgres. Do not add a second matrix here. ## Versioning policy (CLAUDE.md) @@ -107,8 +107,7 @@ Do not publish `0.0.1` empty stubs. First intentional bump to `0.1.0` is the fir Port node-resque `examples/` in Phases 4–7. This phase can add a compose-based example command if missing: ```bash -docker compose up -d postgres -bun examples/example.ts +DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/pgboss_queue_test bun examples/example.ts ``` Optional: `examples/docker` like node-resque — not required for v1. @@ -134,3 +133,4 @@ README (user-facing): GitHub Test workflow badge (the Phase 1 workflow), npm ver ## Lessons learned - 2026-08-26 (plan): Test CI is Phase 1. This phase is only Pages + npm publish. +- 2026-08-26: Phase 1 already matrices Bun and Node 26 in `test.yaml`. Example runs use `DATABASE_URL`; there is no repo `docker-compose.yml`. diff --git a/package.json b/package.json new file mode 100644 index 0000000..2a9f405 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "pgboss-queue", + "version": "0.0.1", + "description": "A PostgreSQL-backed background job queue with the node-resque runtime model", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "engines": { + "node": ">=26" + }, + "license": "Apache-2.0", + "scripts": { + "test": "bun test --max-concurrency=1", + "test:node": "node --experimental-strip-types --test-concurrency=1 --test './__tests__/**/*.test.ts'", + "build": "tsc", + "lint": "biome check .", + "format": "biome check --write ." + }, + "dependencies": { + "pg": "^8.23.0" + }, + "devDependencies": { + "@biomejs/biome": "^2.5.10", + "@types/bun": "^1.4.0", + "@types/node": "^26.3.0", + "@types/pg": "^8.23.1", + "typescript": "^7.0.2" + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f8d266c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} From 98a1a61b321bea065a3570689e61bdddab69eef5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 05:28:03 +0000 Subject: [PATCH 2/8] Ban any in TypeScript and Biome. Set noImplicitAny in tsconfig, typecheck tests via tsconfig.test.json, and fail lint on explicit any. Co-authored-by: Evan Tahler --- CLAUDE.md | 2 +- biome.json | 5 ++++- docs/plans/01-repo-scaffold.md | 5 +++-- package.json | 2 +- tsconfig.json | 1 + tsconfig.test.json | 9 +++++++++ 6 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 tsconfig.test.json diff --git a/CLAUDE.md b/CLAUDE.md index f42e5ed..3f4c778 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -131,7 +131,7 @@ Do **not** accept `pkg: "ioredis"`, `redis: Redis`, or `database: number`. Those ## Coding conventions -- **TypeScript strict.** No `as any`. Use `@ts-expect-error` with a comment when the type system cannot express something. +- **TypeScript strict (`noImplicitAny`).** No `any` and no `as any`. Biome `noExplicitAny` is an error. Use `@ts-expect-error` with a comment when the type system cannot express something. - **JSDoc on every public class, method, and exported type.** `@param` for each parameter (including edge cases), `@returns` when non-obvious, `@throws` when applicable. Match node-resque's documented Queue methods. - **No Python.** New scripts, CLIs, and tooling are Bun + TypeScript. - **Biome** for format/lint (keryx-style), not Prettier. diff --git a/biome.json b/biome.json index 31053f2..82a0ad1 100644 --- a/biome.json +++ b/biome.json @@ -26,7 +26,10 @@ "linter": { "enabled": true, "rules": { - "preset": "recommended" + "preset": "recommended", + "suspicious": { + "noExplicitAny": "error" + } } }, "javascript": { diff --git a/docs/plans/01-repo-scaffold.md b/docs/plans/01-repo-scaffold.md index fb4a766..286a2df 100644 --- a/docs/plans/01-repo-scaffold.md +++ b/docs/plans/01-repo-scaffold.md @@ -28,8 +28,8 @@ CI must exist **before** Queue/Worker code. An empty `expect(true)` that never o - `scripts`: `"test": "bun test --max-concurrency=1"`, `"test:node": "node --experimental-strip-types --test-concurrency=1 --test './__tests__/**/*.test.ts'"`, `build`, `lint`, `format` (docs scripts wait for Phase 9) - `devDependencies`: `typescript`, `@types/node`, `@types/pg`, `@biomejs/biome`, `@types/bun` - `dependencies`: `pg` now (smoke test uses it). Add `pg-boss` in Phase 2 if you want to keep this PR smaller — either is fine as long as CI is green. -- `tsconfig.json` — `strict`, `ES2022`, `moduleResolution: bundler` or `nodenext`, `declaration`, `outDir: dist`, `rootDir: src` -- `biome.json` — match keryx reasonably (indent 2, no unused imports) +- `tsconfig.json` — `strict`, `noImplicitAny: true`, `ES2022`, `moduleResolution: bundler` or `nodenext`, `declaration`, `outDir: dist`, `rootDir: src`. `tsconfig.test.json` typechecks `__tests__` with `noEmit` (so implicit `any` in tests fails `build` too). +- `biome.json` — match keryx reasonably (indent 2, no unused imports). `suspicious/noExplicitAny` is `error` so `: any` and `as any` fail `lint`. - `.gitignore` — `node_modules`, `dist`, `.env`, `docs/.vitepress/dist`, `*.log` - `LICENSE` — Apache-2.0 - `.nvmrc` or `.node-version` — `26` @@ -114,3 +114,4 @@ A compiling package, a shared `specHelper`, and CI that will run every subsequen - 2026-08-26: `bun test --concurrency=1` is not a Bun flag (`bun test --help` has `--concurrent` and `--max-concurrency`, not `--concurrency`). Bun still accepted the unknown flag and ran anyway. Use `--max-concurrency=1` on Bun and `--test-concurrency=1` on `node --test`. Tests are sequential by default unless `--concurrent` / `--parallel` is set. - 2026-08-26: Dropped `docker-compose.yml`. CI Postgres is a GitHub Actions service; locally `DATABASE_URL` is enough. Compose would only wrap a database this repo does not otherwise orchestrate. - 2026-08-26: Test matrix runs the same `node:test` files on Bun and Node 26. `bun:test` cannot run on Node, so the suite is `node:test` + `node:assert/strict` rather than `bun:test`. +- 2026-08-26: Ban `any` in the whole tree: `noImplicitAny` in `tsconfig.json` (explicit even though `strict` already implies it), `tsc --noEmit -p tsconfig.test.json` so tests are included, and Biome `noExplicitAny` as an error. `tsc` has no `noExplicitAny` flag. diff --git a/package.json b/package.json index 2a9f405..6e465cc 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "scripts": { "test": "bun test --max-concurrency=1", "test:node": "node --experimental-strip-types --test-concurrency=1 --test './__tests__/**/*.test.ts'", - "build": "tsc", + "build": "tsc && tsc --noEmit -p tsconfig.test.json", "lint": "biome check .", "format": "biome check --write ." }, diff --git a/tsconfig.json b/tsconfig.json index f8d266c..e6f9ea0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "module": "ESNext", "moduleResolution": "Bundler", "strict": true, + "noImplicitAny": true, "declaration": true, "outDir": "dist", "rootDir": "src", diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..a664124 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": ".", + "allowImportingTsExtensions": true + }, + "include": ["src/**/*.ts", "__tests__/**/*.ts"] +} From 8ba9d290d0cc16e2365cdd099bdd128bca8d663b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 05:30:14 +0000 Subject: [PATCH 3/8] Run the suite on Bun; smoke-import the package on Node. Restore bun:test for Postgres tests and add scripts/assert-node-package.mjs so CI proves Node 26 can import dist without duplicating the suite. Co-authored-by: Evan Tahler --- .github/workflows/test.yaml | 29 +++++++++++++---------------- CLAUDE.md | 10 +++++----- __tests__/smoke.test.ts | 11 +++++------ docs/plans/01-repo-scaffold.md | 17 +++++++++-------- docs/plans/07-multiworker.md | 1 + docs/plans/08-conformance-tests.md | 5 +++-- docs/plans/10-publish-and-ci.md | 3 ++- package.json | 2 +- scripts/assert-node-package.mjs | 28 ++++++++++++++++++++++++++++ tsconfig.test.json | 2 +- 10 files changed, 68 insertions(+), 40 deletions(-) create mode 100644 scripts/assert-node-package.mjs diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 418cb6f..d30b8c6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -28,10 +28,6 @@ jobs: test: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - runtime: [bun, node] services: postgres: image: postgres:16 @@ -45,28 +41,29 @@ jobs: --health-retries 5 ports: - 5432:5432 + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - run: bun install --frozen-lockfile + - run: bun run test + env: + DATABASE_URL: postgres://postgres:postgres@localhost:5432/pgboss_queue_test + + node-package: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - uses: actions/setup-node@v4 - if: matrix.runtime == 'node' with: node-version-file: .nvmrc - run: bun install --frozen-lockfile - - name: Test with Bun - if: matrix.runtime == 'bun' - run: bun run test - env: - DATABASE_URL: postgres://postgres:postgres@localhost:5432/pgboss_queue_test - - name: Test with Node - if: matrix.runtime == 'node' - run: bun run test:node - env: - DATABASE_URL: postgres://postgres:postgres@localhost:5432/pgboss_queue_test + - run: bun run build + - run: bun run test:node-package complete: if: always() - needs: [lint, build, test] + needs: [lint, build, test, node-package] runs-on: ubuntu-latest steps: - name: Require all test jobs to pass diff --git a/CLAUDE.md b/CLAUDE.md index 3f4c778..e8d504c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -45,15 +45,15 @@ A PR with no `docs/plans/` diff is only OK when the change is truly unrelated (t ## Tooling (once Phase 1 exists) -This is a **Bun + TypeScript** project. Use `bun`, never `npm` or `npx`, for install/lint/build. Use `bunx` if you need a package runner. Tests are `node:test` so CI can run them on both Bun and Node. PRs must stay green on `.github/workflows/test.yaml` (lint, build, Bun + Node Postgres matrix). +This is a **Bun + TypeScript** project. Use `bun`, never `npm` or `npx`, for install/lint/build/test. Use `bunx` if you need a package runner. PRs must stay green on `.github/workflows/test.yaml` (lint, build, `bun test` against Postgres, Node package import). ```bash bun install # install -bun test # node:test via bun, needs Postgres -bun run test:node # same files on Node 26+ +bun test # bun:test, needs Postgres +bun run test:node-package # Node 26 imports dist/ (run after build) bun run lint # biome check bun run format # biome write -bun run build # tsc / bun build of src → dist +bun run build # tsc of src → dist, plus test typecheck bun docs:dev # VitePress (Phase 9) ``` @@ -135,7 +135,7 @@ Do **not** accept `pkg: "ioredis"`, `redis: Redis`, or `database: number`. Those - **JSDoc on every public class, method, and exported type.** `@param` for each parameter (including edge cases), `@returns` when non-obvious, `@throws` when applicable. Match node-resque's documented Queue methods. - **No Python.** New scripts, CLIs, and tooling are Bun + TypeScript. - **Biome** for format/lint (keryx-style), not Prettier. -- **Tests use `node:test`**, not Jest, so the same files run on Bun and Node. Port node-resque tests faithfully: same `describe` / `test` names, same assertions, Postgres `specHelper` instead of Redis. +- **Tests use `bun:test`**, not Jest. Port node-resque tests faithfully: same `describe` / `test` names, same assertions, Postgres `specHelper` instead of Redis. Node must still be able to import the compiled package (`bun run test:node-package`); do not run the Bun suite on Node. - **Every behavior change ships with tests.** A PR with no test changes is a red flag unless it is docs-only. - **Do not add dependencies** unless a phase plan names them. Expected runtime deps: `pg-boss`, `pg`. Dev: `typescript`, `@types/pg`, `biome`, `bun` types. diff --git a/__tests__/smoke.test.ts b/__tests__/smoke.test.ts index e4c8b25..bc269e2 100644 --- a/__tests__/smoke.test.ts +++ b/__tests__/smoke.test.ts @@ -1,18 +1,17 @@ -import assert from "node:assert/strict"; -import { after, describe, test } from "node:test"; -import { connect, disconnect } from "./utils/specHelper.ts"; +import { afterAll, describe, expect, test } from "bun:test"; +import { connect, disconnect } from "./utils/specHelper"; describe("Postgres smoke test", () => { - after(disconnect); + afterAll(disconnect); test("DATABASE_URL is defined", () => { - assert.ok(process.env.DATABASE_URL); + expect(process.env.DATABASE_URL).toBeDefined(); }); test("SELECT 1 returns 1", async () => { const pool = await connect(); const result = await pool.query<{ value: number }>("SELECT 1 AS value"); - assert.equal(result.rows[0]?.value, 1); + expect(result.rows[0]?.value).toBe(1); }); }); diff --git a/docs/plans/01-repo-scaffold.md b/docs/plans/01-repo-scaffold.md index 286a2df..2fc6915 100644 --- a/docs/plans/01-repo-scaffold.md +++ b/docs/plans/01-repo-scaffold.md @@ -25,7 +25,7 @@ CI must exist **before** Queue/Worker code. An empty `expect(true)` that never o - `exports` with `import` + types - `engines.node`: `>=26` - `license`: `Apache-2.0` - - `scripts`: `"test": "bun test --max-concurrency=1"`, `"test:node": "node --experimental-strip-types --test-concurrency=1 --test './__tests__/**/*.test.ts'"`, `build`, `lint`, `format` (docs scripts wait for Phase 9) + - `scripts`: `"test": "bun test --max-concurrency=1"`, `"test:node-package": "node scripts/assert-node-package.mjs"`, `build`, `lint`, `format` (docs scripts wait for Phase 9) - `devDependencies`: `typescript`, `@types/node`, `@types/pg`, `@biomejs/biome`, `@types/bun` - `dependencies`: `pg` now (smoke test uses it). Add `pg-boss` in Phase 2 if you want to keep this PR smaller — either is fine as long as CI is green. - `tsconfig.json` — `strict`, `noImplicitAny: true`, `ES2022`, `moduleResolution: bundler` or `nodenext`, `declaration`, `outDir: dist`, `rootDir: src`. `tsconfig.test.json` typechecks `__tests__` with `noEmit` (so implicit `any` in tests fails `build` too). @@ -70,15 +70,14 @@ DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/pgboss_queue_test - `SELECT 1` returns `1` through specHelper's pool - `bun run build` artifacts exist *or* that check lives in CI only (prefer CI `bun run build`) -Do **not** ship only `expect(true).toBe(true)`. Tests use `node:test` + `node:assert/strict` so the same files run on Bun and Node. +Do **not** ship only `expect(true).toBe(true)`. Tests use `bun:test`. Node compatibility is `scripts/assert-node-package.mjs`: after `bun run build`, Node 26 imports `package.json` `exports["."].import` (`dist/index.js`) and asserts `process.versions.bun` is unset. Later phases should import real public APIs in that script (do not re-run the Bun suite on Node). ### CI — full test workflow now -`.github/workflows/test.yaml` is the product gate from this PR onward. Jobs: `lint`, `build`, `test` (Postgres 16 service, **matrix `runtime: [bun, node]`**), `complete`. +`.github/workflows/test.yaml` is the product gate from this PR onward. Jobs: `lint`, `build`, `test` (Postgres 16 service, **Bun `bun:test`**), `node-package` (Node 26 imports the compiled package), `complete`. -- Bun leg: `bun run test` (`bun test --max-concurrency=1`) -- Node leg: `actions/setup-node` from `.nvmrc` (26), then `bun run test:node` -- `DATABASE_URL=postgres://postgres:postgres@localhost:5432/pgboss_queue_test` on both legs +- `test`: `bun run test` with `DATABASE_URL=postgres://postgres:postgres@localhost:5432/pgboss_queue_test` +- `node-package`: `actions/setup-node` from `.nvmrc` (26), `bun run build`, `bun run test:node-package` (no Postgres) See the workflow file for the YAML. Do not duplicate a second test pipeline in Phase 10. @@ -98,8 +97,9 @@ Rules: - `bun install` works locally - `bun run build` emits `dist/` - `bun run lint` is clean -- `DATABASE_URL` pointing at a local Postgres + `bun test` and `bun run test:node` are green -- **GitHub Actions on this PR is green** (lint, build, Bun + Node Postgres test matrix, `complete`) +- `DATABASE_URL` pointing at a local Postgres + `bun test` is green +- `bun run build` + `bun run test:node-package` is green on Node 26 +- **GitHub Actions on this PR is green** (lint, build, Bun Postgres tests, Node package import, `complete`) - Smoke test fails if Postgres is down or `DATABASE_URL` is missing - No runtime exports claimed that do not exist @@ -115,3 +115,4 @@ A compiling package, a shared `specHelper`, and CI that will run every subsequen - 2026-08-26: Dropped `docker-compose.yml`. CI Postgres is a GitHub Actions service; locally `DATABASE_URL` is enough. Compose would only wrap a database this repo does not otherwise orchestrate. - 2026-08-26: Test matrix runs the same `node:test` files on Bun and Node 26. `bun:test` cannot run on Node, so the suite is `node:test` + `node:assert/strict` rather than `bun:test`. - 2026-08-26: Ban `any` in the whole tree: `noImplicitAny` in `tsconfig.json` (explicit even though `strict` already implies it), `tsc --noEmit -p tsconfig.test.json` so tests are included, and Biome `noExplicitAny` as an error. `tsc` has no `noExplicitAny` flag. +- 2026-08-26: Reverted the suite to `bun:test`. Node coverage is `scripts/assert-node-package.mjs` (import compiled `exports` on Node 26, not a second copy of the Postgres tests). diff --git a/docs/plans/07-multiworker.md b/docs/plans/07-multiworker.md index 8461f23..2075e90 100644 --- a/docs/plans/07-multiworker.md +++ b/docs/plans/07-multiworker.md @@ -53,4 +53,5 @@ pg-boss `localConcurrency` is **not** a substitute. MultiWorker must spawn real ## Lessons learned - 2026-08-26: Phase 1 runs tests with `node:test`, not `bun:test`. Do not assume Bun-only retry APIs when this phase is implemented. +- 2026-08-26: Phase 1 reverted to `bun:test`. Bun retry APIs are available again; Node is only the compiled-package import check. diff --git a/docs/plans/08-conformance-tests.md b/docs/plans/08-conformance-tests.md index b306fb9..68105bd 100644 --- a/docs/plans/08-conformance-tests.md +++ b/docs/plans/08-conformance-tests.md @@ -29,7 +29,7 @@ If a row above is missing when you start this phase, that is a **bug in an earli | node-resque | pgboss-queue | | --- | --- | -| Jest + ts-jest | `node:test` (`bun test --max-concurrency=1` and `node --test --test-concurrency=1`) | +| Jest + ts-jest | `bun:test` (`bun test --max-concurrency=1`) | | `ioredis` specHelper | `__tests__/utils/specHelper.ts` | | `REDIS_HOST` | `DATABASE_URL` (CI injects it) | | `afterAll` disconnect redis | `afterAll` end pool + truncate or `DROP SCHEMA` | @@ -45,7 +45,7 @@ connect / disconnect / cleanup popFromQueue(): Promise ``` -**Isolation:** truncate + migrate once in `beforeAll` for speed, or per-file schema. Keep Bun `--max-concurrency=1` and Node `--test-concurrency=1` until proven otherwise. Do not pass `bun test --concurrency=1` — that is not a Bun flag. +**Isolation:** truncate + migrate once in `beforeAll` for speed, or per-file schema. Keep Bun `--max-concurrency=1` until proven otherwise. Do not pass `bun test --concurrency=1` — that is not a Bun flag. Node compatibility is the Phase 1 `node-package` job, not a second test runner. ## Matrix @@ -170,3 +170,4 @@ Docs site can describe a real API. Phase 10 can trust tests that have been runni - 2026-08-26 (plan): This phase is an audit, not the first test suite. Tests ship with Phases 1–7; CI has been running since Phase 1. - 2026-08-26: Phase 1 corrected the runner to `node:test` on a Bun + Node matrix. Isolation uses `--max-concurrency=1` / `--test-concurrency=1`, not `bun test --concurrency=1`. +- 2026-08-26: Phase 1 reverted the suite to `bun:test`. Node is covered by importing `dist/` (`test:node-package`), not by running this matrix on `node --test`. diff --git a/docs/plans/10-publish-and-ci.md b/docs/plans/10-publish-and-ci.md index 9474a78..a3e347a 100644 --- a/docs/plans/10-publish-and-ci.md +++ b/docs/plans/10-publish-and-ci.md @@ -94,7 +94,7 @@ jobs: `package.json` must include `"files": ["dist", "README.md", "LICENSE"]` so we do not publish tests or plans. -The Phase 1 `test` job already matrices Bun and Node 26 against Postgres. Do not add a second matrix here. +The Phase 1 `test` job runs `bun:test` against Postgres. The `node-package` job imports compiled `dist/` on Node 26. Do not add a second suite matrix here. ## Versioning policy (CLAUDE.md) @@ -134,3 +134,4 @@ README (user-facing): GitHub Test workflow badge (the Phase 1 workflow), npm ver - 2026-08-26 (plan): Test CI is Phase 1. This phase is only Pages + npm publish. - 2026-08-26: Phase 1 already matrices Bun and Node 26 in `test.yaml`. Example runs use `DATABASE_URL`; there is no repo `docker-compose.yml`. +- 2026-08-26: Phase 1 dropped the suite matrix. `test` is Bun; `node-package` only imports `dist/` on Node 26. diff --git a/package.json b/package.json index 6e465cc..2e9c5b4 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "license": "Apache-2.0", "scripts": { "test": "bun test --max-concurrency=1", - "test:node": "node --experimental-strip-types --test-concurrency=1 --test './__tests__/**/*.test.ts'", + "test:node-package": "node scripts/assert-node-package.mjs", "build": "tsc && tsc --noEmit -p tsconfig.test.json", "lint": "biome check .", "format": "biome check --write ." diff --git a/scripts/assert-node-package.mjs b/scripts/assert-node-package.mjs new file mode 100644 index 0000000..6fedb03 --- /dev/null +++ b/scripts/assert-node-package.mjs @@ -0,0 +1,28 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); + +assert.equal( + process.versions.bun, + undefined, + "this script must run on Node, not Bun", +); +assert.ok(process.versions.node, "expected a Node.js runtime"); + +assert.equal(typeof pkg.name, "string"); +assert.equal(typeof pkg.exports?.["."]?.import, "string"); + +const entry = pkg.exports["."].import; +const url = pathToFileURL(join(root, entry)).href; +const mod = await import(url); + +assert.equal(typeof mod, "object"); +assert.ok(mod); + +process.stdout.write( + `imported ${pkg.name} from ${entry} on node ${process.versions.node}\n`, +); diff --git a/tsconfig.test.json b/tsconfig.test.json index a664124..0d1996c 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -3,7 +3,7 @@ "compilerOptions": { "noEmit": true, "rootDir": ".", - "allowImportingTsExtensions": true + "types": ["bun", "node"] }, "include": ["src/**/*.ts", "__tests__/**/*.ts"] } From d5056392870b54e58c68a5fca4f5d1a4cee833ec Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 05:32:56 +0000 Subject: [PATCH 4/8] Run the Node package import check with node, not bun. The node-package job already sets up Node 26; invoke the smoke script with that binary so the check cannot run under Bun. Co-authored-by: Evan Tahler --- .github/workflows/test.yaml | 3 ++- CLAUDE.md | 4 ++-- docs/plans/01-repo-scaffold.md | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d30b8c6..f78838c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -59,7 +59,8 @@ jobs: node-version-file: .nvmrc - run: bun install --frozen-lockfile - run: bun run build - - run: bun run test:node-package + - run: node --version + - run: node scripts/assert-node-package.mjs complete: if: always() diff --git a/CLAUDE.md b/CLAUDE.md index e8d504c..e88a5a0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -50,7 +50,7 @@ This is a **Bun + TypeScript** project. Use `bun`, never `npm` or `npx`, for ins ```bash bun install # install bun test # bun:test, needs Postgres -bun run test:node-package # Node 26 imports dist/ (run after build) +node scripts/assert-node-package.mjs # after build; must use Node, not bun bun run lint # biome check bun run format # biome write bun run build # tsc of src → dist, plus test typecheck @@ -135,7 +135,7 @@ Do **not** accept `pkg: "ioredis"`, `redis: Redis`, or `database: number`. Those - **JSDoc on every public class, method, and exported type.** `@param` for each parameter (including edge cases), `@returns` when non-obvious, `@throws` when applicable. Match node-resque's documented Queue methods. - **No Python.** New scripts, CLIs, and tooling are Bun + TypeScript. - **Biome** for format/lint (keryx-style), not Prettier. -- **Tests use `bun:test`**, not Jest. Port node-resque tests faithfully: same `describe` / `test` names, same assertions, Postgres `specHelper` instead of Redis. Node must still be able to import the compiled package (`bun run test:node-package`); do not run the Bun suite on Node. +- **Tests use `bun:test`**, not Jest. Port node-resque tests faithfully: same `describe` / `test` names, same assertions, Postgres `specHelper` instead of Redis. Node must still be able to import the compiled package (`node scripts/assert-node-package.mjs`); do not run the Bun suite on Node. - **Every behavior change ships with tests.** A PR with no test changes is a red flag unless it is docs-only. - **Do not add dependencies** unless a phase plan names them. Expected runtime deps: `pg-boss`, `pg`. Dev: `typescript`, `@types/pg`, `biome`, `bun` types. diff --git a/docs/plans/01-repo-scaffold.md b/docs/plans/01-repo-scaffold.md index 2fc6915..6f6f5c6 100644 --- a/docs/plans/01-repo-scaffold.md +++ b/docs/plans/01-repo-scaffold.md @@ -77,7 +77,7 @@ Do **not** ship only `expect(true).toBe(true)`. Tests use `bun:test`. Node compa `.github/workflows/test.yaml` is the product gate from this PR onward. Jobs: `lint`, `build`, `test` (Postgres 16 service, **Bun `bun:test`**), `node-package` (Node 26 imports the compiled package), `complete`. - `test`: `bun run test` with `DATABASE_URL=postgres://postgres:postgres@localhost:5432/pgboss_queue_test` -- `node-package`: `actions/setup-node` from `.nvmrc` (26), `bun run build`, `bun run test:node-package` (no Postgres) +- `node-package`: `actions/setup-node` from `.nvmrc` (26), `bun run build`, then **`node scripts/assert-node-package.mjs`** (not `bun run`; no Postgres) See the workflow file for the YAML. Do not duplicate a second test pipeline in Phase 10. @@ -98,7 +98,7 @@ Rules: - `bun run build` emits `dist/` - `bun run lint` is clean - `DATABASE_URL` pointing at a local Postgres + `bun test` is green -- `bun run build` + `bun run test:node-package` is green on Node 26 +- `bun run build` + `node scripts/assert-node-package.mjs` is green on Node 26 - **GitHub Actions on this PR is green** (lint, build, Bun Postgres tests, Node package import, `complete`) - Smoke test fails if Postgres is down or `DATABASE_URL` is missing - No runtime exports claimed that do not exist @@ -116,3 +116,4 @@ A compiling package, a shared `specHelper`, and CI that will run every subsequen - 2026-08-26: Test matrix runs the same `node:test` files on Bun and Node 26. `bun:test` cannot run on Node, so the suite is `node:test` + `node:assert/strict` rather than `bun:test`. - 2026-08-26: Ban `any` in the whole tree: `noImplicitAny` in `tsconfig.json` (explicit even though `strict` already implies it), `tsc --noEmit -p tsconfig.test.json` so tests are included, and Biome `noExplicitAny` as an error. `tsc` has no `noExplicitAny` flag. - 2026-08-26: Reverted the suite to `bun:test`. Node coverage is `scripts/assert-node-package.mjs` (import compiled `exports` on Node 26, not a second copy of the Postgres tests). +- 2026-08-26: The `node-package` CI step must invoke `node` directly. `bun run test:node-package` can still shell out to `node`, but the workflow should not go through Bun for that check. From 762fd17313a6b9c03a016e133fcf41d12d6f7a7b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 05:35:08 +0000 Subject: [PATCH 5/8] Set noImplicitAny on the root and test TypeScript configs. Keep the flag explicit in tsconfig.json (not only implied by strict) and repeat it on tsconfig.test.json so tests stay covered if strict is relaxed. Co-authored-by: Evan Tahler --- docs/plans/01-repo-scaffold.md | 1 + tsconfig.json | 4 ++-- tsconfig.test.json | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/plans/01-repo-scaffold.md b/docs/plans/01-repo-scaffold.md index 6f6f5c6..3198a22 100644 --- a/docs/plans/01-repo-scaffold.md +++ b/docs/plans/01-repo-scaffold.md @@ -117,3 +117,4 @@ A compiling package, a shared `specHelper`, and CI that will run every subsequen - 2026-08-26: Ban `any` in the whole tree: `noImplicitAny` in `tsconfig.json` (explicit even though `strict` already implies it), `tsc --noEmit -p tsconfig.test.json` so tests are included, and Biome `noExplicitAny` as an error. `tsc` has no `noExplicitAny` flag. - 2026-08-26: Reverted the suite to `bun:test`. Node coverage is `scripts/assert-node-package.mjs` (import compiled `exports` on Node 26, not a second copy of the Postgres tests). - 2026-08-26: The `node-package` CI step must invoke `node` directly. `bun run test:node-package` can still shell out to `node`, but the workflow should not go through Bun for that check. +- 2026-08-26: `noImplicitAny` is set on both `tsconfig.json` and `tsconfig.test.json` so relaxing `strict` later still bans implicit `any` in src and tests. diff --git a/tsconfig.json b/tsconfig.json index e6f9ea0..7249ab3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "compilerOptions": { + "noImplicitAny": true, + "strict": true, "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", - "strict": true, - "noImplicitAny": true, "declaration": true, "outDir": "dist", "rootDir": "src", diff --git a/tsconfig.test.json b/tsconfig.test.json index 0d1996c..b8a2350 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -3,6 +3,7 @@ "compilerOptions": { "noEmit": true, "rootDir": ".", + "noImplicitAny": true, "types": ["bun", "node"] }, "include": ["src/**/*.ts", "__tests__/**/*.ts"] From 71c128497a51c4bf7a32c17e6f0b33a328840f9d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 05:37:33 +0000 Subject: [PATCH 6/8] Document required main checks: complete and Cursor Bugbot. The GitHub token for this agent cannot write branch protection or rulesets (403). Co-authored-by: Evan Tahler --- docs/plans/01-repo-scaffold.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/01-repo-scaffold.md b/docs/plans/01-repo-scaffold.md index 3198a22..fc238d2 100644 --- a/docs/plans/01-repo-scaffold.md +++ b/docs/plans/01-repo-scaffold.md @@ -86,7 +86,7 @@ Rules: - Do **not** wait for Phase 10 to add this file. Phase 10 is npm publish + GitHub Pages only. - Do **not** add a `docs` job yet (VitePress does not exist). Phase 9 appends it. - `complete` must fail the workflow if lint, build, or test failed. -- Branch protection (when maintainers can set it): require `complete`. +- Branch protection (maintainer UI; this agent cannot set it): require `complete` and `Cursor Bugbot`. ### README / CLAUDE.md From b4a8195cc2863d4d53dfb8f33151591fa87fec1a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 05:39:26 +0000 Subject: [PATCH 7/8] Note that requiring Bugbot on main needs GitHub admin. The agent token cannot write branch protection or rulesets. Co-authored-by: Evan Tahler --- docs/plans/01-repo-scaffold.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/plans/01-repo-scaffold.md b/docs/plans/01-repo-scaffold.md index fc238d2..83ff6ae 100644 --- a/docs/plans/01-repo-scaffold.md +++ b/docs/plans/01-repo-scaffold.md @@ -118,3 +118,4 @@ A compiling package, a shared `specHelper`, and CI that will run every subsequen - 2026-08-26: Reverted the suite to `bun:test`. Node coverage is `scripts/assert-node-package.mjs` (import compiled `exports` on Node 26, not a second copy of the Postgres tests). - 2026-08-26: The `node-package` CI step must invoke `node` directly. `bun run test:node-package` can still shell out to `node`, but the workflow should not go through Bun for that check. - 2026-08-26: `noImplicitAny` is set on both `tsconfig.json` and `tsconfig.test.json` so relaxing `strict` later still bans implicit `any` in src and tests. +- 2026-08-26: Required checks on `main` should be `complete` and `Cursor Bugbot`. The cloud agent GitHub token is not a repo admin (403 on branch protection and rulesets), so a maintainer must set that in the GitHub UI. From af8aef4034a0b871feed0224198725677088cefb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 05:46:25 +0000 Subject: [PATCH 8/8] Mark Phase 1 as done. Acceptance criteria are met on this PR; squash-merge is the remaining step. Co-authored-by: Evan Tahler --- docs/plans/01-repo-scaffold.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plans/01-repo-scaffold.md b/docs/plans/01-repo-scaffold.md index 83ff6ae..072a1b7 100644 --- a/docs/plans/01-repo-scaffold.md +++ b/docs/plans/01-repo-scaffold.md @@ -1,6 +1,6 @@ # Phase 1 — Repo scaffold, test harness, and CI -**Status:** in-progress +**Status:** done **Depends on:** Phase 0 ## Goal