Repository navigation
ci(lint): enable oxlint suspicious and perf categories - #1414
Conversation
|
The review is blocked: this shallow checkout cannot resolve the requested three-dot diff ( The supplied PR description meets the testing-record requirement: it records a successful build and real-CLI runs against a scratch Please provide the exact three-dot diff or a checkout with sufficient history. No PR code was run, built, or installed. |
|
Review remains blocked: the requested three-dot diff fails with The supplied PR description meets the testing-record requirement: it documents a successful build followed by real-CLI runs against a scratch Please provide the exact three-dot diff or a checkout with sufficient history. No PR code was run, built, or installed. |
c1c43d4 to
fb9ca6d
Compare
|
No confident findings in the requested three-dot diff.
|
|
No confident findings in the requested three-dot diff.
|
Turn on the suspicious and perf rule categories per the plan in Tencent#836, with an off list for the rules that don't find real bugs here: - no-await-in-loop: sequential awaits are deliberate in a CLI (git, prompts, ordered writes); the rule can't tell an intentional ordering dependency from a parallelizable loop. - unicorn/no-array-sort: every prod sort site sorts a local array built in the same function, so the in-place mutation warning doesn't apply. - unicorn/consistent-function-scoping: moves closures to outer scope; style, not a bug class. - no-underscore-dangle: conflicts with this repo's own `_` convention (`_setLogFilePath`, `_resetState`). - oxc/no-map-spread: micro-optimization, not worth the churn. no-shadow stays on: real shadowing bugs were found during triage (see next commit). Also bump the TS target to ES2023 so `Array#toReversed()` and `Array#findLast()` (used by the fixes in the next commit) type-check; Node 20+ already supports them at runtime, and ES2023 adds no new syntax, so this only changes what the compiler's lib typings expose.
Fixes every warning oxlint's suspicious and perf categories report
with the off list from the previous commit applied, following each
rule's own meaning rather than a blanket find-replace:
- no-shadow: renamed the shadowing binding (never the outer one) at
every site. Removed 8 redundant `await import('./utils/logger.js')`
calls in index.ts; it already imports `log` at the top. Investigated
the two shadows the issue specifically called out as real bugs
(team-push.ts's destructured `dataHomeKey` hiding the imported
function, dashboard-collector.ts's inner `events`/`stopAt` hiding
the outer bindings): both are confined to their own block scope with
no cross-use today, so the rename is the complete fix, not a logic
change.
- no-unmodified-loop-condition: checked all 4 current hits (the issue
named 1; the codebase grew since it was filed). None is a real bug.
Each loop's exit condition is flipped by something the rule can't
see from inside the loop body: a setTimeout closure registered
before the loop (process-stop.ts, recall-attribution.test.ts), a
compound condition where the other operand changes
(resources/env.ts), or a property mutation on an object the rule
only tracks by reassignment (workflow-skill-transitions.test.ts).
Suppressed each with a narrow inline comment explaining why.
- preserve-caught-error: attached `{ cause }` to every rethrow, using
the error actually caught by the nearest enclosing catch (two sites
in local-agent.ts and one in resources/env.ts already attached the
wrong, outer error to a throw inside a nested catch).
- unicorn/no-array-reverse: switched to `Array#toReversed()` (dropping
any defensive `[...arr]` copy it made redundant); none of these
sites relied on the in-place mutation.
- unicorn/prefer-array-find: `.filter(p)[0]` / `.filter(p).pop()`
patterns became `.find(p)`; `.filter(p).at(-1)` / `.filter(p).pop()`
where the last match (not the first) was wanted became
`.findLast(p)` — using `.find()` there would have silently returned
the wrong element.
- unicorn/prefer-set-has: converted to `Set` + `.has()`, except
workflow/workspace.ts's `rest.includes(...)`, which is a string
substring check the rule mistook for array membership (false
positive, suppressed inline).
- no-useless-concat: merged adjacent string literals, except
redact.test.ts's DSN fixtures, left alone (see inline comment) to
not reassemble the exact secret-shaped literal its test author
deliberately split to dodge a secret scanner.
- no-useless-constructor, unicorn/require-module-specifiers,
oxc/no-accumulating-spread: removed the dead constructors, the
empty `export {}` placeholder (the file itself was then empty, so
deleted it), and the accumulator spread in workflow/drivers/skill/
tick.ts.
Verified: `npx tsc --noEmit`, `npx oxlint --type-aware --deny-warnings
--report-unused-disable-directives` (the project's `npm run lint`),
and `npx vitest run` (15137 passed / 899 failed / 191 skipped) all
clean relative to main — the failures are pre-existing Windows
environment flakiness (EPERM on temp-dir cleanup, 15s test timeouts
under load), confirmed by running the same files against an unmodified
main checkout and getting the same failures. `npm run build` succeeds
and the built CLI runs end to end (`doctor`, `env --help`, `models
--help`, `push --dry-run`) against a scratch HOME.
Enabling the suspicious/perf categories reaches the type-aware typescript/* rules in those categories too, not just the plain eslint/unicorn/oxc ones measured for Tencent#836. The real CI command (oxlint --type-aware --deny-warnings) surfaced ~3287 warnings from typescript/no-unsafe-type-assertion, no-unnecessary-type-assertion, no-unnecessary-type-conversion, consistent-return, no-unnecessary-boolean-literal-compare, no-unnecessary-template-expression, no-unnecessary-type-arguments, and no-unnecessary-type-parameters that my local verification missed because it ran oxlint without --type-aware. Type-aware linting beyond no-floating-promises/no-misused-promises is Tencent#863's separate, not-yet-merged scope. Turn those 8 rules off here, matching the existing pattern already used for the other out-of-scope typescript/* rules in this file. Verified with the actual CI command this time: `npm run lint` (oxlint --type-aware --deny-warnings --report-unused-disable-directives) is clean, and so is `npx tsc --noEmit` and `npm run build`.
Rebased onto upstream main (6 commits landed since this branch was cut, including Tencent#1346 which added these .reverse() calls). Same fix as the rest of this PR: switched to Array#toReversed(), since none of these sites reuse the original array reference after reversing it.
cdacfcd to
69d2993
Compare
|
No confident findings in the requested three-dot diff.
|
@STiFLeR7 Thanks, this is careful work. Notes for the maintainer, since this changes the lint gate and
The CI review reports no confident findings. It didn't list the extra off rules. |
Resolved 9 conflicting files, mostly our no-shadow renames colliding with unrelated upstream edits on the same lines; kept upstream's logic and reapplied our renamed identifiers. A few required judgment calls where upstream had refactored the surrounding code (resolveToolBaseDir removed in favor of resolveBaseDir, reportUsageToTeam renamed to reportUsage with new outcome tracking, judge.ts's answerFrom and profileIsolation restructured, session-work.ts's finished() extracted into its own function with unattended-mode support). Also fixed fresh oxlint suspicious/perf warnings (no-shadow, prefer-array-find, prefer-set-has, no-unmodified-loop-condition) that upstream's new commits introduced, since this PR's lint config now covers them.
|
No confident findings in the requested three-dot diff.
|
Re-checked
|
|
No confident findings in the requested three-dot diff.
|
@STiFLeR7 Re-checked
|
|
No confident findings in the requested three-dot diff.
|
@STiFLeR7 Re-checked
The earlier nits are unchanged and optional. @jeff-r2026 this is ready for your review. |
jeff-r2026
left a comment
There was a problem hiding this comment.
Verified 83df3d3 (contains main 299ce8e): tsc, lint (0 warnings) and build pass; the unit suite fails the same 33 root-only cases as main 299ce8e and nothing else. A built-CLI run (init over an insteadOf URL, status, pull delivering a skill, doctor) passes. A review of the 73 changed src/ files found no runtime behavior change beyond added error causes. Thanks, approving.
Part of #836 — the
suspicious/perfoxlint category item specifically (#863 type-aware linting and the anti-slop rules are separate, still-open items of the same issue, so this doesn't close it).What this does
Enables oxlint's
suspiciousandperfrule categories under the existing zero-warning CI gate, with the off list from the issue:no-await-in-loop— off: sequential awaits are deliberate in a CLI (git, prompts, ordered writes); the rule can't tell an intentional ordering dependency from a parallelizable loop.unicorn/no-array-sort— off: every prod sort site sorts a local array built in the same function; none mutates a caller's array.unicorn/consistent-function-scoping— off: moves closures to an outer scope; style, not a bug class.no-underscore-dangle— off: conflicts with this repo's own_convention (_setLogFilePath,_resetState).oxc/no-map-spread— off: micro-optimization, not worth the churn.no-shadow— kept on: real shadowing bugs were found during triage.Also bumps
tsconfig.json'stargettoES2023soArray#toReversed()/Array#findLast()(used by several fixes below) type-check. Node 20+ already runs these at runtime; ES2023 adds no new syntax, so this only changes which lib typings the compiler sees.Heads up on scope: the issue's counts are from when it was filed and the codebase has grown a lot since (big workflow/session-flow additions). Live
oxlinton currentmainwith this config reports 393 warnings, not the issue's 100 (51 prod/49 test) — the off-list rules are unaffected by the drift (they're off either way), but the rules that stay on scaled up roughly 3-10x along with the codebase. All 393 are fixed here, plus one more perf hit the issue didn't know about (oxc/no-accumulating-spreadinworkflow/drivers/skill/tick.ts).no-unmodified-loop-condition— investigated, not a bugThe issue flagged this rule specifically: "a loop whose condition never changes may be a real bug." There are 4 current hits (the issue named 1; the other 3 are new since). I checked each — none is a real bug, all are false positives where the condition is flipped by something outside the loop body that the (non-type-aware) rule can't see:
src/resources/env.ts— a one-timeconst existsguard in a compound condition; the other half of the condition (a directory walk) is what actually changes and ends the loop.src/tool-launch/process-stop.ts—escalatedflips inside asetTimeoutclosure registered before the loop starts (a deliberate SIGKILL-escalation timer).src/__tests__/recall-attribution.test.ts—settledflips inside a.finally()callback registered before the loop.src/__tests__/workflow-skill-transitions.test.ts—tailsgains a key via property assignment inside the loop body; the rule only tracks variable reassignment, not property mutation.Each got a narrow
oxlint-disable-next-linewith a one-line reason, not a restructure.Everything else
Fixed per each rule's actual meaning, not a blanket find-replace:
preserve-caught-error: attached{ cause }to every rethrow, using whichever error the nearest enclosingcatchactually holds — two spots inlocal-agent.tsand one inresources/env.tshad already attached{ cause: error }from an outer catch to a throw sitting inside a nested catch; fixed those to reference the inner error instead.unicorn/no-array-reverse: switched toArray#toReversed(), dropping any now-redundant defensive[...arr]copy.unicorn/prefer-array-find:.filter(p)[0]→.find(p). Where.filter(p).at(-1)or.filter(p).pop()was reaching for the last match, used.findLast(p)instead —.find()there would have silently returned the wrong element.unicorn/prefer-set-has: converted toSet+.has(), exceptworkflow/workspace.ts'srest.includes(...), which the rule mistook for array membership —restis a string and this is a substring check (false positive, suppressed inline).no-useless-concat: merged adjacent literals, except two DSN fixtures inredact.test.ts— left alone, with a comment, because merging would reassemble the exact secret-shaped literal its author deliberately split to dodge a repo secret scanner.no-shadow: renamed the shadowing (inner) binding at every site, never the outer one. Removed 8 redundantawait import('./utils/logger.js')calls inindex.ts(it already importslogat the top). The two sites the issue specifically called out as real bugs —team-push.ts's destructureddataHomeKeyhiding the importeddataHomeKey()function, anddashboard-collector.ts's innerevents/stopAthiding the outer bindings — are both confined to their own block scope with no cross-use today, so the rename is the complete fix; no logic changed.no-useless-constructor,unicorn/require-module-specifiers,oxc/no-accumulating-spread: removed two dead pass-through constructors, deleted anexport {}placeholder test file (which then trippedunicorn/no-empty-file, so the whole file went — nothing referenced it), and replaced a loop-accumulator object spread with direct property assignment.Testing
npx tsc --noEmit— clean.npx oxlint --type-aware --deny-warnings --report-unused-disable-directives(the repo'snpm run lint) — clean, 0 warnings.npm run build— succeeds; ran the built CLI end to end (doctor,env --help,models --help,push --dry-run) against a scratchHOME.npx vitest run(full suite, 857 files / 16227 tests) on this branch vs. an unmodifiedmaincheckout: 15137 passed / 899 failed vs. 15145 passed / 891 failed. Diffed the exact failing test names between both runs — the differences are generic Windows-environment flakiness (EPERMon temp-dir cleanup, 15s test timeouts under load), reproduced identically against unmodifiedmain. Also reran the 10 most heavily-touched test files in isolation on both branches: byte-identical result both sides (8 failed / 2 passed files, 74 failed / 1300 passed / 12 skipped tests) — confirms no regression from this change.Type-aware rules kept off (not this PR's scope)
Enabling
suspicious/perfviacategoriesalso reaches oxlint's type-awaretypescript/*rules in those categories, not just the plain ones listed above. Explicitly turned off in.oxlintrc.jsonbecause fixing them is issue #863's separate, not-yet-merged scope:typescript/no-unsafe-type-assertion,typescript/no-unnecessary-type-assertion,typescript/no-unnecessary-type-conversion,typescript/consistent-return,typescript/no-unnecessary-boolean-literal-compare,typescript/no-unnecessary-template-expression,typescript/no-unnecessary-type-arguments,typescript/no-unnecessary-type-parameters.