chore(lint): add anti-slop oxlint plugin (pinned, all rules off) (#20726)

* chore(lint): add anti-slop oxlint plugin (all rules off)

Vendors dmmulroy/anti-slop (MIT) plus no-call-only-assertions and
no-pass-through-type-alias from maharshi365/deslop (MIT). Every rule starts
"off"; each follow-up PR fixes one rule's violations and flips it to "error".

* fix(lint): actually exclude the vendored plugin from the anti-slop audit

oxlint does not honour ignorePatterns supplied via --config, so the
config/oxlint-plugins/anti-slop/** entry never matched and the vendored rule
source was being linted as first-party code (505 violations). Move the exclusion
to the --ignore-pattern CLI flag in audit:anti-slop, which does work, and drop
the entry that gave a false sense of coverage.

Keeping vendored source unlinted matters because anti-slop is updated by
three-way merge against the upstream snapshot; reformatting it locally would
conflict on every update.

* chore(lint): pin anti-slop instead of vendoring it; drop deslop

Replaces the ~5k vendored lines with a git-pinned devDependency:
  oxlint-plugin-anti-slop: github:dmmulroy/anti-slop#c44ef22

anti-slop ships raw .ts with no build step, and Node refuses to type-strip
anything under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), so
oxlint cannot load it from there -- which is why upstream says to vendor it. A
postinstall step copies the pinned package's source to .anti-slop-plugin/
(gitignored), which Node will type-strip because it sits outside node_modules.
Upgrading is now a SHA bump rather than a re-vendor and three-way merge.

Verified byte-identical rule output to the vendored copy across all 16 rules
that fire.

Drops maharshi365/deslop and its two rules (no-call-only-assertions,
no-pass-through-type-alias). It is not on npm either, so it would need a second
git pin and copy step, and it is a 5-star single-maintainer repo that is itself
a re-namespaced copy of anti-slop. One upstream is enough.

* ci(lint): run audit:anti-slop in PR CI

config/scripts/pr-workflow-lint-parity.test.mjs requires every step in
`pnpm lint` to have a matching step in .github/workflows/pr.yml; adding
audit:anti-slop to lint without the workflow step failed that ratchet.

Also makes audit:anti-slop sync the plugin itself before linting. The generated
.anti-slop-plugin/ directory is gitignored and otherwise only created by
postinstall, so a cached install that skips postinstall would leave oxlint
unable to load the plugin.
This commit is contained in:
Neil
2026-09-14 21:42:37 -07:00
committed by GitHub
parent ef39f32d4f
commit 11180fa532
7 changed files with 124 additions and 4 deletions
+3
View File
@@ -133,6 +133,9 @@ jobs:
- name: Lint
run: pnpm exec oxlint --format github
- name: Reject low-evidence patterns
run: pnpm run audit:anti-slop
- name: Enforce focused code-quality plugins
run: pnpm run audit:code-quality:native
+3
View File
@@ -181,3 +181,6 @@ tests/e2e/.cross-version-checkouts/
# IS committed). Also keeps oxfmt/oxlint, which honor this file, from walking
# vendored gems.
/mobile/vendor/
# Generated by config/scripts/sync-anti-slop-plugin.mjs from the pinned oxlint-plugin-anti-slop
.anti-slop-plugin/
+5 -1
View File
@@ -4,5 +4,9 @@
"semi": false,
"printWidth": 100,
"trailingComma": "none",
"ignorePatterns": ["cloud/**", ".github/actions/cloud-sql-rollout-lease/**"]
"ignorePatterns": [
"cloud/**",
".github/actions/cloud-sql-rollout-lease/**",
".anti-slop-plugin/**"
]
}
+60
View File
@@ -0,0 +1,60 @@
{
"$schema": "../node_modules/oxlint/configuration_schema.json",
"plugins": [],
"jsPlugins": [
{
"name": "anti-slop",
"specifier": "../.anti-slop-plugin/index.ts"
}
],
"categories": {
"correctness": "off",
"suspicious": "off",
"pedantic": "off",
"perf": "off",
"style": "off",
"restriction": "off",
"nursery": "off"
},
"ignorePatterns": [
"**/node_modules",
"**/dist",
"**/out",
"cloud/**",
"src/shared/rpc-contract/rpc-params-catalog.generated.ts",
"tests/e2e/.cross-version-checkouts"
],
"rules": {
"anti-slop/no-array-filter-map": "off",
"anti-slop/no-chained-type-assertions": "off",
"anti-slop/no-conditional-empty-object-spread": "off",
"anti-slop/no-known-value-widening": "off",
"anti-slop/no-module-mocking": "off",
"anti-slop/no-object-parameters": "off",
"anti-slop/no-reduce-accumulator-copy": "off",
"anti-slop/no-reflect-apply": "off",
"anti-slop/no-reflect-get": "off",
"anti-slop/no-runtime-typeof": "off",
"anti-slop/no-shape-in-symbol-names": "off",
"anti-slop/no-unknown-parameters": "off",
"anti-slop/no-unknown-returns": "off",
"anti-slop/no-unknown-type-aliases": "off",
"anti-slop/no-unsafe-dictionary-type": "off",
"anti-slop/no-widen-then-assert": "off",
"anti-slop/require-readable-spacing": "off",
"anti-slop/require-safety-comment-for-type-assertion": "off"
},
"overrides": [
{
"files": [
"**/*.test.{ts,tsx}",
"**/*.spec.{ts,tsx}",
"tests/**/*.{ts,tsx}",
"**/__mocks__/**"
],
"rules": {
"anti-slop/no-module-mocking": "off"
}
}
]
}
+20
View File
@@ -0,0 +1,20 @@
// anti-slop ships raw .ts with no build step, and Node refuses to type-strip anything
// under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), so oxlint cannot load
// it from there. Copy the pinned package's source out to a gitignored dir it can load.
import { cpSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
const repoRoot = resolve(import.meta.dirname, '../..')
const source = resolve(repoRoot, 'node_modules/oxlint-plugin-anti-slop/src')
const target = resolve(repoRoot, '.anti-slop-plugin')
rmSync(target, { recursive: true, force: true })
mkdirSync(target, { recursive: true })
cpSync(source, target, { recursive: true })
// Effect rules are opt-in upstream and this repo does not use Effect; tests would be linted.
rmSync(resolve(target, 'effect'), { recursive: true, force: true })
cpSync(
resolve(repoRoot, 'node_modules/oxlint-plugin-anti-slop/LICENSE'),
resolve(target, 'LICENSE')
)
writeFileSync(resolve(target, 'package.json'), '{ "type": "module" }\n')
+7 -3
View File
@@ -14,7 +14,7 @@
"audit:perf": "oxlint --config config/oxlint-performance-audit.json --format json src",
"test:perf:contracts": "vitest run --config config/vitest.performance.config.ts",
"format": "oxfmt --write .",
"lint": "oxlint && pnpm run audit:code-quality:native && pnpm run audit:code-quality:type-aware && pnpm run check:reliability-gates && pnpm run check:dead-classes && pnpm run check:max-lines-ratchet && pnpm run check:ts-nocheck-ratchet && pnpm run check:runtime-electron-ratchet && pnpm run check:readme-local-links && pnpm run verify:rpc-params-catalog && pnpm run verify:bundled-skill-guides && pnpm run verify:skill-bundle-manifest && pnpm run verify:localization-catalog && pnpm run verify:localization-runtime-catalog && pnpm run verify:localization-extraction && pnpm run verify:localization-coverage",
"lint": "oxlint && pnpm run audit:anti-slop && pnpm run audit:code-quality:native && pnpm run audit:code-quality:type-aware && pnpm run check:reliability-gates && pnpm run check:dead-classes && pnpm run check:max-lines-ratchet && pnpm run check:ts-nocheck-ratchet && pnpm run check:runtime-electron-ratchet && pnpm run check:readme-local-links && pnpm run verify:rpc-params-catalog && pnpm run verify:bundled-skill-guides && pnpm run verify:skill-bundle-manifest && pnpm run verify:localization-catalog && pnpm run verify:localization-runtime-catalog && pnpm run verify:localization-extraction && pnpm run verify:localization-coverage",
"audit:code-quality": "pnpm run audit:code-quality:native && pnpm run audit:code-quality:type-aware && pnpm run audit:react-doctor",
"audit:code-quality:native": "oxlint --config config/oxlint-code-quality-native-plugins.json src config tests mobile --deny-warnings",
"audit:code-quality:type-aware": "oxlint --type-aware --config config/oxlint-code-quality-type-aware.json src config tests --deny-warnings",
@@ -97,7 +97,7 @@
"build": "pnpm run build:desktop && pnpm run build:native",
"build:release": "pnpm run build:relay && pnpm run build:native && pnpm run verify:computer-native && pnpm run build:cli && pnpm run build:electron-vite && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer",
"build:release:parallel": "pnpm run build:relay && pnpm run build:native && pnpm run verify:computer-native && pnpm run build:cli && pnpm run build:electron-vite:parallel && pnpm run verify:built-skills-cli && pnpm run build:web-from-renderer",
"postinstall": "node config/scripts/rebuild-native-deps.mjs",
"postinstall": "node config/scripts/rebuild-native-deps.mjs && node config/scripts/sync-anti-slop-plugin.mjs",
"rebuild:electron": "node config/scripts/rebuild-native-deps.mjs",
"reclaim:electron-dists": "node config/scripts/reclaim-electron-dists.mjs",
"reclaim:dev-bundles": "node config/scripts/reclaim-dev-electron-bundles.mjs",
@@ -164,7 +164,9 @@
"test:e2e:remote-bulk-open-freeze": "pnpm run ensure:electron-runtime && pnpm exec playwright test tests/e2e/remote-session-bulk-open-freeze-repro.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1",
"test:e2e:ssh-docker-bulk-open-freeze": "node config/scripts/run-ssh-docker-bulk-open-freeze-e2e.mjs",
"repro:live-remote-bulk-open-freeze": "node config/scripts/live-remote-bulk-open-freeze-repro.mjs",
"repro:live-remote-realistic-freeze": "node config/scripts/live-remote-realistic-freeze-repro.mjs"
"repro:live-remote-realistic-freeze": "node config/scripts/live-remote-realistic-freeze-repro.mjs",
"audit:anti-slop": "node config/scripts/sync-anti-slop-plugin.mjs && oxlint --config config/oxlint-anti-slop.json src config tests mobile --deny-warnings",
"sync:anti-slop-plugin": "node config/scripts/sync-anti-slop-plugin.mjs"
},
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "0.3.251",
@@ -199,6 +201,7 @@
"@electron-toolkit/tsconfig": "^2.0.0",
"@electron/rebuild": "^4.2.0",
"@monaco-editor/react": "^4.7.0",
"@oxlint/plugins": "1.80.0",
"@playwright/test": "^1.59.1",
"@sanity/diff-match-patch": "^3.2.0",
"@shadcn/lint": "^0.1.0",
@@ -265,6 +268,7 @@
"monaco-editor": "^0.55.1",
"oxfmt": "^0.65.0",
"oxlint": "^1.80.0",
"oxlint-plugin-anti-slop": "github:dmmulroy/anti-slop#c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b",
"oxlint-plugin-react-doctor": "0.9.1",
"oxlint-tsgolint": "7.0.2001",
"pdfjs-dist": "^6.3.289",
+26
View File
@@ -213,6 +213,9 @@ importers:
'@monaco-editor/react':
specifier: ^4.7.0
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
'@oxlint/plugins':
specifier: 1.80.0
version: 1.80.0
'@playwright/test':
specifier: ^1.59.1
version: 1.59.1
@@ -411,6 +414,9 @@ importers:
oxlint:
specifier: ^1.80.0
version: 1.80.0(oxlint-tsgolint@7.0.2001)
oxlint-plugin-anti-slop:
specifier: github:dmmulroy/anti-slop#c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b
version: https://codeload.github.com/dmmulroy/anti-slop/tar.gz/c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b
oxlint-plugin-react-doctor:
specifier: 0.9.1
version: 0.9.1
@@ -1940,6 +1946,14 @@ packages:
cpu: [x64]
os: [win32]
'@oxlint/plugins@1.78.0':
resolution: {integrity: sha512-Ypt8KeRYw+4jUtlPirfcHWMrn5ms12VrrFPD+Mds477/7tJxG1Kcz2Yrg2nVcTQEUx/GdlhS+BUg1kmxNm04Ug==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
'@oxlint/plugins@1.80.0':
resolution: {integrity: sha512-QRgH1XqQEYNHa4f1vvPQ5fAdNdncHGIUG1ZWLlGIZHky3qwCEeAKYitZNbZMtaXtAQAAFFTOwqUfzESvimqZNA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
'@parcel/watcher-android-arm64@2.5.6':
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
engines: {node: '>= 10.0.0'}
@@ -6059,6 +6073,10 @@ packages:
vite-plus:
optional: true
oxlint-plugin-anti-slop@https://codeload.github.com/dmmulroy/anti-slop/tar.gz/c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b:
resolution: {gitHosted: true, integrity: sha512-Vj/M0k5Bt1Q2pGdfXZ24wGXycBcvFHwJ/pjHIFl1hV8soaZKHl1lba5UXlQFY5xgwQ5TNfEWiKfsEt0yyRyVUg==, tarball: https://codeload.github.com/dmmulroy/anti-slop/tar.gz/c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b}
version: 0.1.2
oxlint-plugin-react-doctor@0.9.1:
resolution: {integrity: sha512-yCW8USbiuszbVsUMN4fL1iU7mRu3Ae3w96+k/xqCyWvW6bF6DzCMtLy5N/w6WLRX78iQsWxVqW/SEgzRBXLfsA==}
engines: {node: ^20.19.0 || >=22.13.0}
@@ -8605,6 +8623,10 @@ snapshots:
'@oxlint/binding-win32-x64-msvc@1.80.0':
optional: true
'@oxlint/plugins@1.78.0': {}
'@oxlint/plugins@1.80.0': {}
'@parcel/watcher-android-arm64@2.5.6':
optional: true
@@ -13162,6 +13184,10 @@ snapshots:
'@oxfmt/binding-win32-ia32-msvc': 0.65.0
'@oxfmt/binding-win32-x64-msvc': 0.65.0
oxlint-plugin-anti-slop@https://codeload.github.com/dmmulroy/anti-slop/tar.gz/c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b:
dependencies:
'@oxlint/plugins': 1.78.0
oxlint-plugin-react-doctor@0.9.1:
dependencies:
'@typescript-eslint/types': 8.60.0