From 1232214501dfc31782eb116002349e84833633df Mon Sep 17 00:00:00 2001 From: garrytan Date: Sat, 31 Oct 2026 18:31:49 +0000 Subject: [PATCH] fix(lint ratchet): copy the comparison instead of importing typecheck-test.ts (paid-selection closure); docs recommend the eslint-disable form /review tags Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- CONTRIBUTING.md | 3 ++- changelog.d/gsta203-pr4-oxlint-ratchet.md | 2 +- scripts/lint-ratchet.ts | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ff086b9a..031ab5d14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,8 @@ with `LINT_RATCHET_FIXED` until `bun run lint --write-baseline` locks it in (tha shrinks the baseline). The baseline records the oxlint version, which must match `package.json`'s exact pin; after bumping oxlint, run `bun run lint --write-baseline --oxlint-upgrade` in the same change. Findings are keyed by file, so renaming or splitting a file moves its findings and they count as -new: fix them, or mark an intended one with `// oxlint-disable-next-line -- `. oxlint +new: fix them, or mark an intended one with `// eslint-disable-next-line -- ` (oxlint +honors it, and /review lists it as a gate edit). oxlint picks files by extension, so the extensionless Bun scripts in `bin/` are not linted yet. Editing `lib/cso/*.ts`? Run `bun run format:cso` before committing; CI runs `format:cso:check`. All four run in the required `free-tests` check. diff --git a/changelog.d/gsta203-pr4-oxlint-ratchet.md b/changelog.d/gsta203-pr4-oxlint-ratchet.md index d65ea5f68..dd5f703ba 100644 --- a/changelog.d/gsta203-pr4-oxlint-ratchet.md +++ b/changelog.d/gsta203-pr4-oxlint-ratchet.md @@ -1,4 +1,4 @@ -**gstack now lints its own `.ts` and `.js` files with oxlint, held to a ratchet (GSTA-203 PR 4).** CI had no JS/TS linter, and the one real error oxlint found (a second `import * as os` in `browse/test/commands.test.ts`) slipped past the product typecheck. `bun run lint` runs oxlint's correctness rules in well under a second and fails only on findings that are new; the 971 existing ones are recorded in a baseline that can only shrink. +**gstack now lints its own JavaScript and TypeScript files with oxlint, held to a ratchet (GSTA-203 PR 4).** CI had no JS/TS linter, and the one real error oxlint found (a second `import * as os` in `browse/test/commands.test.ts`) slipped past the product typecheck. `bun run lint` runs oxlint's correctness rules in well under a second and fails only on findings that are new; the 971 existing ones are recorded in a baseline that can only shrink. ### Added - **`bun run lint`** (`scripts/lint-ratchet.ts`, `.oxlintrc.json`, `scripts/lint-baseline.json`): oxlint 1.90.0, pinned exactly, with its correctness rules over the repository, skipping `design-html/vendor` and `test/fixtures`. oxlint picks files by extension, so the extensionless Bun scripts in `bin/` are not linted yet. Findings are keyed by file, rule and message, not line, so moving code does not churn the baseline. Each failure prints a stable code: `LINT_RATCHET_NEW` (file:line, rule, "Fix them; do not add them to the baseline" and a reproduce command), `LINT_RATCHET_FIXED` (run `--write-baseline`, which only shrinks), `LINT_BASELINE_INVALID` (an unreadable or malformed baseline, or one written by another oxlint version; never rewritten on its own), `OXLINT_MISSING` (run `bun install`) and `OXLINT_CRASH` (with the oxlint version and config path). After an oxlint bump, `bun run lint --write-baseline --oxlint-upgrade` rebuilds the baseline in the same change. diff --git a/scripts/lint-ratchet.ts b/scripts/lint-ratchet.ts index c6dd0b551..10d7244f2 100755 --- a/scripts/lint-ratchet.ts +++ b/scripts/lint-ratchet.ts @@ -23,12 +23,12 @@ import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; import { bunIsolationArgs } from '../lib/bun-isolation'; -import { compareDiagnostics, type DiagnosticCounts } from './typecheck-test'; export const BASELINE_FILE = 'scripts/lint-baseline.json'; export const CONFIG_FILE = '.oxlintrc.json'; const ROOT = path.resolve(import.meta.dir, '..'); +export type DiagnosticCounts = Record; export interface LintBaseline { oxlint: string; findings: DiagnosticCounts } export interface Findings { counts: DiagnosticCounts; lines: Record } export interface OxlintRun { status: number | null; signal?: string | null; error?: Error; stdout: string; stderr: string } @@ -106,6 +106,17 @@ export function runOxlint(root: string): OxlintRun { } } +/** The same comparison as scripts/typecheck-test.ts, copied rather than imported: lib/pregate/checks/guards.ts names `bun run lint`, so an import would put the test-typecheck script in every paid case's derived closure. */ +function compareFindings(baseline: DiagnosticCounts, current: DiagnosticCounts) { + const added: Array<{ identity: string; baseline: number; current: number }> = [], fixed: typeof added = []; + for (const identity of new Set([...Object.keys(baseline), ...Object.keys(current)])) { + const before = baseline[identity] ?? 0, now = current[identity] ?? 0; + if (now > before) added.push({ identity, baseline: before, current: now }); + else if (now < before) fixed.push({ identity, baseline: before, current: now }); + } + return { added, fixed }; +} + function writeBaseline(file: string, oxlint: string, findings: DiagnosticCounts): void { fs.writeFileSync(file, JSON.stringify({ version: 1, oxlint, findings: sorted(findings) }, null, 2) + '\n'); } @@ -162,7 +173,7 @@ export function runLint(options: { write?: boolean; upgrade?: boolean; root?: st out.push(`lint ratchet: rewrote ${BASELINE_FILE} for oxlint ${pin} (was ${baseline.oxlint}; ${total} findings, ${Object.keys(current.counts).length} identities). Review the growth in the diff.`); return done(0); } - const { added, fixed } = compareDiagnostics(baseline.findings, current.counts); + const { added, fixed } = compareFindings(baseline.findings, current.counts); if (added.length) { err.push(`LINT_RATCHET_NEW: ${added.length} new or more frequent oxlint finding(s). Fix them; do not add them to the baseline.`); for (const d of added) err.push(` + ${describe(d.identity, current.lines[d.identity])} (${d.baseline} → ${d.current})`);