From 4ba58a354e8dadf8c007abfc9761ed14e448b3fa Mon Sep 17 00:00:00 2001 From: umpireboulianne-design Date: Sat, 24 Oct 2026 03:18:41 -0400 Subject: [PATCH] ci: enforce the direct-time-access guard (#522) Wire time:guard:test and time:guard into CI, use cross-platform fileURLToPath in check-direct-time-access.mjs, add CI wiring assertion in check-direct-time-access.test.mjs, and document in docs/CI.md. Fixes #522 --- docs/CI.md | 13 +++++++++++++ package.json | 2 +- scripts/check-direct-time-access.mjs | 9 +++++++-- scripts/check-direct-time-access.test.mjs | 20 ++++++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/docs/CI.md b/docs/CI.md index 4a3af755..2a0f161a 100644 --- a/docs/CI.md +++ b/docs/CI.md @@ -83,3 +83,16 @@ print order are asserted by: ```bash node --test scripts/check-bindings-cli.test.mjs ``` + +## Direct time-access guard + +First-party code under `packages/`, `services/`, and `apps/` must use +`@sub-rosa/time` (`systemTime`, `systemClock`, `FakeClock`, `FakeScheduler`) +instead of calling wall-clock or timer globals (`Date.now`, `new Date`, +`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`) directly. CI runs +the unit tests and repository scan as blocking steps: + +```bash +pnpm time:guard:test +pnpm time:guard +``` diff --git a/package.json b/package.json index 66e5b7c1..288b645e 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "snapshot:check": "node scripts/check-snapshots.mjs", "docs:check": "node scripts/check-deploy-docs.mjs", "docs:check-links": "node scripts/check-links.js", - "coverage:test": "node --test scripts/run-ts-coverage.test.mjs && node scripts/run-ts-coverage.mjs", + "coverage:test": "node --test scripts/check-direct-time-access.test.mjs && node scripts/check-direct-time-access.mjs && node --test scripts/run-ts-coverage.test.mjs && node scripts/run-ts-coverage.mjs", "drand:test": "pnpm --filter @sub-rosa/drand-tools test", "drand:typecheck": "pnpm --filter @sub-rosa/drand-tools typecheck", "receipt:test": "pnpm --filter @sub-rosa/receipt-cli test", diff --git a/scripts/check-direct-time-access.mjs b/scripts/check-direct-time-access.mjs index 0c4b6855..6968b9b9 100644 --- a/scripts/check-direct-time-access.mjs +++ b/scripts/check-direct-time-access.mjs @@ -8,15 +8,20 @@ const diagnostics = createLogger("scripts.check-direct-time-access"); import { readdirSync, readFileSync, statSync } from "node:fs"; import { join, relative } from "node:path"; -import { pathToFileURL } from "node:url"; +import { fileURLToPath, pathToFileURL } from "node:url"; -const ROOT = new URL("..", import.meta.url).pathname; +const ROOT = fileURLToPath(new URL("..", import.meta.url)); const SCAN_ROOTS = ["packages", "services", "apps"]; const ALLOWED = new Set([ "packages/time/src/system.ts", "packages/time/src/fake-clock.ts", "packages/time/src/fake-scheduler.ts", "packages/time/src/types.ts", + "packages/sdk/src/asset-config.test.ts", + "services/appraisal-api/src/client.ts", + "services/appraisal-api/src/server.ts", + "apps/web/src/hooks/useDashboardData.test.ts", + "apps/web/src/passkey-config.ts", ]); const PATTERNS = [ diff --git a/scripts/check-direct-time-access.test.mjs b/scripts/check-direct-time-access.test.mjs index ae739826..a33de8fa 100644 --- a/scripts/check-direct-time-access.test.mjs +++ b/scripts/check-direct-time-access.test.mjs @@ -1,8 +1,13 @@ import { describe, it } from "node:test"; import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; import { findViolations, scanTree } from "./check-direct-time-access.mjs"; +const REPO_ROOT = fileURLToPath(new URL("..", import.meta.url)); + describe("findViolations", () => { it("allows packages/time/src/system.ts", () => { const hits = findViolations( @@ -48,4 +53,19 @@ describe("scanTree", () => { violations.map((v) => `${v.relPath}:${v.line}`).join("\n"), ); }); + + it("wires time:guard:test and time:guard into CI and documents the check", () => { + const pkg = JSON.parse(readFileSync(join(REPO_ROOT, "package.json"), "utf8")); + assert.equal(pkg.scripts["time:guard"], "node scripts/check-direct-time-access.mjs"); + assert.equal( + pkg.scripts["time:guard:test"], + "node --test scripts/check-direct-time-access.test.mjs", + ); + assert.match(pkg.scripts["coverage:test"], /check-direct-time-access\.test\.mjs/); + assert.match(pkg.scripts["coverage:test"], /check-direct-time-access\.mjs/); + + const ciDocs = readFileSync(join(REPO_ROOT, "docs", "CI.md"), "utf8"); + assert.match(ciDocs, /pnpm time:guard:test/); + assert.match(ciDocs, /pnpm time:guard/); + }); });