Skip to content

chore(lint): adopt oxlint 1.67 canonical rule additions (selective, library posture)#99

Merged
Goosterhof merged 4 commits into
mainfrom
chore/oxlint-1.67-canonical-adoption
Jun 1, 2026
Merged

chore(lint): adopt oxlint 1.67 canonical rule additions (selective, library posture)#99
Goosterhof merged 4 commits into
mainfrom
chore/oxlint-1.67-canonical-adoption

Conversation

@Goosterhof
Copy link
Copy Markdown
Contributor

@Goosterhof Goosterhof commented May 26, 2026

Summary

Selective adoption of the war-room canonical template's oxlint 1.55→1.67 rule additions (template commit 321da5f), respecting fs-packages' deliberate library Correctness-only posture (per CLAUDE.md §Lint Rules: "library posture is Correctness-only, opt-in per-rule for anything else").

This is not a wholesale port — Vue/jsx-a11y/type-aware bulk rules are deliberately out of scope and the skip rationale is documented inline in .oxlintrc.json.

Standalone — no longer stacked. This PR was originally drafted stacked on #98 (Supply Corps sweep, which carried the oxlint 1.67 binary). #98 was closed without merging (2026-05-29); oxlint 1.67 is already on main regardless (package.json pins "oxlint": "^1.67"), so this PR stands alone, is MERGEABLE against main directly, and needs nothing merged first.

Gating note: the CI lint step runs bare npm run lint (oxlint .) — not --deny-warnings. The three warn-severity rules below (no-unnecessary-type-parameters, no-useless-iterator-to-array, import-style) are therefore advisory in CI: they surface in output but exit 0. They are documentation-of-intent, not enforcement, unless/until the CI gate adds --deny-warnings. The four error-severity rules gate normally.

Rules adopted (7)

Rule Severity Rationale
no-implied-eval error Core ESLint security — fires on setTimeout("...") / setInterval("...") / new Function("...") callsites. No posture friction.
prefer-regex-literals error Core ESLint correctness — new RegExp("foo")/foo/.
getter-return error Graduated from nursery → correctness (1.61). Catches getters with missing return.
typescript/no-unnecessary-type-conversion error Library code benefits from minimal String(x)/Number(x)/Boolean(x) noise on already-typed values.
typescript/no-unnecessary-type-parameters warn (advisory in CI) Type parameter used only once. API-surface smell.
unicorn/no-useless-iterator-to-array warn (advisory in CI) Pure performance — flags [...someArray] when result is iterated again.
unicorn/import-style warn (advisory in CI) Pushes the canonical import shape per node builtin (e.g. import path from 'node:path', not import {join}). Complements prefer-node-protocol.

Deliberately NOT adopted (documented inline in .oxlintrc.json)

Skipped Why
vue/* No SFCs in fs-packages source; published Vue-aware packages expose factories/composables only.
jsx-a11y/* No JSX in this monorepo.
typescript/no-unsafe-* family Library API surface intentionally exposes unknown/any at adapter boundaries (fs-adapter-store generic constraints).
unicorn/prefer-import-meta-properties Dual ESM+CJS build target via tsdown — import.meta in CJS bundles cascades to build errors.
no-useless-assignment Library code intentionally pre-declares values for type inference.
vitest/valid-expect-in-promise, vitest/prefer-strict-boolean-matchers Test gates already enforce strict matcher discipline via Stryker mutation testing at 90% threshold.
Vue 2→3 deprecation guards, Composition-API safety, Options-API correctness No Options API or template-API features used.
complexity, max-params, max-depth, max-nested-callbacks, max-lines-per-function Stryker mutation + 100% per-package coverage gates govern complexity at the test-discipline layer.

Sweep collateral (in-scope, ≤3 line fix per order §C)

scripts/lint-pkg.mjs line 25 — unicorn/import-style fires on import {join} from 'node:path' with diagnostic "Use default import for module node:path". Rule is bidirectional per-builtin (some builtins want named, some want default); for node:path it wants default. Switched to import path from 'node:path' with path.join(...) at the 4 call sites in this file. Behaviour unchanged. 5-line diff.

Note: the order's §A rationale for unicorn/import-style describes the rule as "pushes named imports for node builtins" — directionally wrong for node:path. Fix matches what the rule actually fires on. Flagged for canonical template maintainer in the execution report.

Verification (8-gate matrix, run locally)

Gate Status
1. npm audit (--high) PASS — 2 moderate transitive (qs/typed-rest-client) below --audit-level=high; pre-existing baseline carried from Supply Corps sweep
2. npm run format:check PASS — 145 files, no diffs
3. npm run lint PASS — zero output; --deny-warnings also exits 0 today (no current warn-rule violations), but --deny-warnings is NOT the CI gate
4. npm run build PASS — all 11 packages built clean
5. npm run typecheck PASS — all 11 packages clean
6. npm run lint:pkg FAIL — PRE-EXISTING BASELINE (22 fails: sideEffects advisory × 11 [queue #70] + attw 0.18.2 "Cannot read properties of undefined (reading 'filename')" × 11 [PR #98 commentary]). Reproduced identically on parent branch — not introduced here.
7. npm test PASS — 528/528 tests across 22 test files
8. npm run test:coverage PASS — per-package 100% thresholds enforced and met

Mutation testing not run — config-only change does not move the mutation surface; per-package Stryker config unchanged. 7/8 gates green; Gate 6 carries the documented pre-existing baseline that PRs #87, #88 and the 2026-05-15 wave already flagged.

Residual baseline carried forward (NOT introduced)

Commit shape

Single commit: chore(lint): adopt oxlint 1.67 canonical rule additions selectively.
2 files changed, 26 insertions, 6 deletions.

Execution report

reports/fs-packages/execution/2026-05-26-armorer-oxlint-1.67-adoption.md in the war-room repo. Includes per-skip rationale, full 8-gate matrix, and Notes-for-the-General on the rule-direction wording issue.

Test plan

  • npm run lint (oxlint w/ new rules) exits 0
  • npm run lint -- --deny-warnings also exits 0 (local check only — not the CI gate)
  • npm run build clean
  • npm run typecheck clean
  • npm test 528/528
  • npm run test:coverage clean
  • lint:pkg failure matches parent branch (confirmed pre-existing, not introduced)

🤖 Generated with Claude Code

Goosterhof and others added 2 commits May 26, 2026 15:28
npm update (lockfile-only, in-range minor/patch refresh):
- @vitest/coverage-v8 4.1.5 -> 4.1.7
- axios 1.16.0 -> 1.16.1
- oxlint 1.65.0 -> 1.67.0
- vitest 4.1.5 -> 4.1.7
- vue 3.5.33 -> 3.5.34
- vue-component-type-helpers 3.2.9 -> 3.3.2
- vue-router 5.0.6 -> 5.0.7

npm audit fix delta:
- closes brace-expansion GHSA-jxxr-4gwj-5jf2 (moderate, transitive)
- closes ws GHSA-58qx-3vcg-4xpx (moderate, transitive)
- closes js-cookie GHSA-qjx8-664m-686j (high, transitive, also cleared by npm update range-resolution)
- closes qs GHSA-q8mj-m7cp-5q26 partial — npm reports fixAvailable but the only nested copy is via @stryker-mutator/core -> typed-rest-client@2.3.1 -> qs@6.15.1; audit fix is a lockfile no-op without breaking Stryker's typed-rest-client pin

This obsoletes the 6 open Dependabot PRs (#81, #90, #91, #92, #96, #97) — all
were CI-red on the same baseline audit drift PR #95 was attempting to clear,
and this sweep is a strict superset of PR #95's audit-fix payload.

Verification (7 of 8 gates locally green):
- Gate 1 npm audit: 2 moderate (qs/typed-rest-client under Stryker; dev-tree only, see Commander-disposition note in mission report)
- Gate 2 format:check: PASS (145 files)
- Gate 3 lint: PASS (oxlint 1.67.0, 0 warnings)
- Gate 4 build: PASS (all 11 packages, dual ESM+CJS)
- Gate 5 typecheck: PASS (all 11 packages)
- Gate 6 lint:pkg: FAIL locally (documented queue #63 local-vs-CI parser disparity + queue #70 sideEffects Suggestion — CI is expected to pass per PR #95 precedent)
- Gate 7 test:coverage: 528/528 tests PASS, 100% per-package thresholds met
- Gate 8 test:mutation: PASS (all 11 packages >=90%; 100/94.81/97.18/100/97.30/92.50/91.20/92.73/96.67/98.36/93.33)

Step 2E clean install (npm ci): PASS — no ERESOLVE; all 11 @script-development/*
node_modules entries resolve to workspace symlinks (no nested registry copies,
cascade-tax discipline intact).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Library-territory Correctness-only posture preserved per CLAUDE.md §Lint Rules.
Adopts 7 rules from war-room canonical template (commit 321da5f):
- no-implied-eval, prefer-regex-literals, getter-return (core ESLint correctness/security)
- typescript/no-unnecessary-type-conversion, typescript/no-unnecessary-type-parameters (type-aware quality)
- unicorn/no-useless-iterator-to-array, unicorn/import-style (perf + node-builtin discipline)

Skipped additions documented inline in .oxlintrc.json (vue/*, jsx-a11y/*,
typescript/no-unsafe-*, prefer-import-meta-properties, no-useless-assignment,
vitest/* test-override additions, Vue 2->3 deprecation/Options-API rules,
complexity/max-* limits).

One trivial violation surfaced + fixed: scripts/lint-pkg.mjs node:path import
switched from named to default to match unicorn/import-style firing direction.
4 call sites updated, behaviour unchanged.

War-room canonical: templates/oxlintrc.json (commit 321da5f).
Branch parent: chore/supply-corps-sweep-2026-05-26 (carries the 1.67 binary).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 26, 2026

Deploying fs-packages with  Cloudflare Pages  Cloudflare Pages

Latest commit: 71ab0b4
Status: ✅  Deploy successful!
Preview URL: https://9c22a655.fs-packages.pages.dev
Branch Preview URL: https://chore-oxlint-1-67-canonical.fs-packages.pages.dev

View logs

@jasperboerhof
Copy link
Copy Markdown
Contributor

PR Reviewer · 9/10 · PASS

Findings

  • MINOR · .oxlintrc.json:24 — no-useless-assignment is named in two separate skip-rationale comments (line 21 and line 24) with mildly inconsistent justifications — line 21 says "intentionally pre-declares values for type inference", line 24 bundles it with complexity/max-* limits saying "Stryker + coverage gates govern complexity". Drop the duplicate mention on line 24 (keep it grouped with the complexity/max-* limits, or keep line 21 — but not both) so the skip-list reads cleanly.

Action

merge-ready

@Goosterhof Goosterhof requested a review from jasperboerhof May 29, 2026 09:16
@Goosterhof Goosterhof added the Agent Review Requested Requesting review of specialized AI review agents. label May 29, 2026
…onical-adoption

# Conflicts:
#	package-lock.json
Copy link
Copy Markdown
Contributor Author

@Goosterhof Goosterhof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers. Two concerns and two nits on a clean, well-documented config chore. The diff is exactly what the body advertises — 7 selectively-adopted oxlint rules with inline skip rationale, plus a 5-line node:path import-shape fix in scripts/lint-pkg.mjs. The library Correctness-only posture is respected and the skip table is honest about why the bulk rules stay out.

Concerns

The "Stacked on #98" narrative is stale and now actively misleading. The body says this PR is "Stacked on #98 (Supply Corps sweep — carries the oxlint 1.67 binary the new rules depend on)" and that "Once #98 merges this PR rebases onto fresh main." But #98 is CLOSED and was never merged (mergedAt: null, mergeCommit: null, closed 2026-05-29). The dependency the stacking narrative hangs on — oxlint 1.67 — is already on main regardless (package.json on main pins "oxlint": "^1.67"), and this PR reports MERGEABLE against main directly. So the stacking framing is no longer load-bearing: there's nothing to merge first and nothing to rebase onto. A reviewer reading the body will go looking for an open #98 and find a closed one. Rewrite the Summary to state the actual situation — #98 closed, oxlint 1.67 already on main, this PR stands alone — so the next reviewer doesn't have to reconstruct the merge history to trust the diff.

unicorn/import-style set to warn doesn't gate by default. Three of the adopted rules (no-unnecessary-type-parameters, no-useless-iterator-to-array, import-style) are warn, and the default lint script is oxlint . without --deny-warnings. So in normal CI these three are advisory only — they surface in output but exit 0. The body's verification matrix notes --deny-warnings also exits 0 today, which is the right thing to have checked, but unless --deny-warnings is the actual CI gate (not just a local spot-check), a future violation of these three lands green. Either confirm the CI lint step runs --deny-warnings (in which case warn and error are equivalent for gating and the severity split is purely cosmetic), or accept that these three are documentation-of-intent rather than enforcement. Worth one sentence in the body stating which it is.

Nits

The skip-rationale comment block lives inside the "rules": {} object as trailing // lines after the last real entry. It parses fine (oxlint reads JSONC and npm run lint passing proves it), but a trailing comment block with no following key is the kind of thing a future editor deletes by accident when adding the next rule. Consider a single leading block comment instead of a trailing one.

The node:path fix switches {join} to a default path import to satisfy import-style's per-builtin direction. Fine, behaviour-identical. The body already flags that the canonical template's §A rationale describes the rule as pushing named imports — directionally wrong for node:path — and routes that to the template maintainer. Good catch; nothing to change here.

Verdict

This is mergeable on substance. The only thing I'd want addressed before merge is the stale #98 narrative in the body — it's not a code defect but it's a correctness defect in the PR's own provenance story, and provenance is what a config-chore review trades on. Fix the body, confirm the --deny-warnings gating question, and ship.

Copy link
Copy Markdown
Contributor

@jasperboerhof jasperboerhof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — reviewed, no blockers.

@Goosterhof Goosterhof merged commit 5914698 into main Jun 1, 2026
2 checks passed
@Goosterhof Goosterhof deleted the chore/oxlint-1.67-canonical-adoption branch June 1, 2026 10:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Agent Review Requested Requesting review of specialized AI review agents.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants