feat(tooling): add anti-slop Oxlint rules - #63
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Contributor
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR adds a substantial vendored Oxlint plugin with custom AST/type-analysis logic and enables six rules across the repository's shared lint configuration. Although production runtime behavior is unchanged, the new repository-wide lint gates and maintenance surface warrant human review. You can add or adjust custom eligibility rules. Learn more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
anti-slopOxlint plugin at upstream commit6d538555cb15.Why
The reference implementation vendors these rules instead of adding a runtime dependency. Keeping the plugin under
tools/oxlintpreserves Agent Zero's existing lint-tooling boundary and makes future upstream synchronization explicit and reproducible.An all-rules audit found 504 pre-existing diagnostics across the remaining nine rules. Those rules are listed as
offso this tooling change does not hide a broad, unrelated source refactor; they can be enabled incrementally as their baselines are addressed.Verification
aube run check:repoaube run lint:ciaube run typecheckaube testaube run buildThe full build passed as
NODE_OPTIONS=--max-old-space-size=4096 aube run build; the default 2 GB heap completed 15 of 16 tasks but exhausted memory during the docs Nitro build. A temporary violating TypeScript fixture also confirmed that Oxlint reportsanti-slop/no-chained-type-assertionsthrough the shared configuration.Safety and compatibility
observemode as read-only, or explained the policy change above.This is a contributor-tooling-only change. It does not modify runtime behavior, state transitions, repository-write policy, or package architecture. No persistent test was added because the behavior is configuration loading; the integration fixture and complete lint run exercise that path directly.
Agent context
git,gh, and the repository'saubechecks.newt-appreference, vendored and configured the rules, audited the existing baseline, updated documentation, and ran every verification command listed above. The contributor selected the reference implementation and requested the integration; reviewers should verify the upstream pin and staged rule rollout.Reviewer notes
Six rules are enabled at
error; nine are deliberately present but disabled because enabling them together would introduce 504 existing findings. The vendored README records the upstream pin and update policy. The initial default-heap docs build failure was environmental and passed when rerun with a 4 GB Node heap.Note
Add vendored
anti-slopOxlint plugin with type-safety lint rulesReflect.apply/Reflect.getusage, and module mocking.no-chained-type-assertions,no-reflect-apply,no-reflect-get,no-shape-in-symbol-names,no-unknown-type-aliases,no-widen-then-assert); remaining rules are turned off for staged adoption.@oxlint/pluginsdependency and updatesknip.jsonc,turbo.jsonc, and.oxfmtignoreto integrate the vendored plugin into the build and tooling pipeline.Reflect.apply/Reflect.getcalls, identifiers containing "shape", type aliases resolving tounknown, and const widen-then-assert flows.📊 Macroscope summarized 1395818. 25 files reviewed, 12 issues evaluated, 12 issues filtered, 0 comments posted
🗂️ Filtered Issues
tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts — 0 comments posted, 1 evaluated, 1 filtered
isConditionalEmptyObjectSpreadunwraps parentheses only around the whole spread argument, not around either conditional branch. A spread such as{ ...(enabled ? ({}) : fields) }therefore has aParenthesizedExpressionconsequent and is not reported, even though it is exactly the conditional empty-object omission pattern this rule is intended to ban. [ Out of scope (post-validation triage) ]tools/oxlint/anti-slop/rules/no-known-value-widening.ts — 0 comments posted, 2 evaluated, 2 filtered
hasParentAssertiononly recognizes an immediately adjacent assertion parent, even though this rule otherwise treatsParenthesizedExpressionandTSNonNullExpressionas transparent. Consequently a chain such as(({ a: 1 } as object)) as unknown(or one separated by!) reports both the inner and outer assertion, while the equivalent unparenthesized chain reports only once. Transparent wrappers should be walked before deciding whether an assertion is nested, otherwise harmless parentheses produce duplicate diagnostics. [ Out of scope (post-validation triage) ]reportFlowexempts an empty object used as a dictionary accumulator only when the incoming expression is directly anObjectExpression. BecausehasKnownEvidencedeliberately follows stableconstaliases, equivalent code such asconst empty = {}; const values: Record<string, string> = empty;is reported even thoughconst values: Record<string, string> = {};is exempt. This creates an inconsistent false positive whenever the legitimate empty accumulator is factored into a constant. [ Out of scope (post-validation triage) ]tools/oxlint/anti-slop/rules/no-object-parameters.ts — 0 comments posted, 2 evaluated, 2 filtered
resolvesToObjectcarries the function'sshadowedAliasesset into the body of a module-level alias. For example, withtype T = object; type Input = T; function f<T>(value: Input) {},Inputstill resolves to the module-levelT, but recursion rejectsTbecause the unrelated function type parameter has the same name. The rule therefore misses a broad-object parameter whenever an intermediate alias references a module alias shadowed only at the use site. [ Out of scope (post-validation triage) ]Program.bodydeclarations. A valid nested alias such asfunction outer() { type Input = object; function inner(value: Input) {} }is never added toaliases, soinnerevades the rule even though its parameter resolves to the prohibitedobjecttype. The rule needs scope-aware collection of nestedTSTypeAliasDeclarationnodes (and corresponding cleanup/shadowing). [ Out of scope (post-validation triage) ]tools/oxlint/anti-slop/rules/no-unknown-parameters.ts — 0 comments posted, 1 evaluated, 1 filtered
checkParametersonly reports when the outer annotation node is exactlyTSUnknownKeyword. Semantically equivalent explicit inputs such asvalue: unknown | stringorvalue: (unknown)therefore bypass the rule, even though the union collapses tounknownand still leaves the parameter unparsed. The companion return rule resolves parenthesized and union forms, so this creates a concrete enforcement gap inno-unknown-parameters. [ Out of scope (post-validation triage) ]tools/oxlint/anti-slop/rules/no-unknown-returns.ts — 0 comments posted, 2 evaluated, 2 filtered
referencedAliasNamerejects every type reference with type arguments, soresolvesToUnknowncannot follow generic aliases. For example,type Result<T> = T; function read(): Result<unknown>produces no diagnostic even though the function's explicit return contract resolves directly tounknown, defeating the rule for a common owner-type pattern. [ Out of scope (post-validation triage) ]resolvesToUnknowntreats any identifier spelledPromiseorPromiseLikeas the global async type without checking whether that name is locally declared or imported. Valid code such asinterface Promise<T> { status: string }followed byfunction f(): Promise<unknown>is therefore reported even though the return type is the local contract and does not expose an unknown value. [ Out of scope (post-validation triage) ]tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts — 0 comments posted, 1 evaluated, 1 filtered
resolvesToUnknownnever examinesTSUnionTypemembers, so an enabled rule misses aliases such astype Hidden = unknown | string. TypeScript normalizes that union tounknown(the top type absorbs every other union member), meaning this alias conceals exactly the type the rule claims to ban but produces no diagnostic. Recursively checking union members forunknown/aliases would catch it. [ Below severity threshold ]tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts — 0 comments posted, 1 evaluated, 1 filtered
isPlainAliasConsumerUsesuppresses every unapplied alias reference, including generic aliases whose defaults resolve to an unsafe dictionary. For example,type Dict<T = unknown> = Record<string, T>; let value: Dictis not reported: the declaration cannot classify bareTas unsafe, while the only node where the default is substituted is discarded here. This lets precisely the unsafe dictionary contract the rule targets pass without a diagnostic. [ Out of scope (post-validation triage) ]tools/oxlint/anti-slop/shared/dictionary-types.ts — 0 comments posted, 1 evaluated, 1 filtered
isBroadMappedKeyrequires every union member to be broad, but a mapped key such asstring | "special"is still broad because thestringconstituent already admits every string key. Consequently a non-generic alias liketype Bag = { [K in string | "special"]: unknown }is not classified as an open dictionary, sono-known-value-wideningmisses widening assertions to this type when the rule is enabled. The union check should recognize a union as broad when any constituent covers a broad key domain (while still handling fully broad unions). [ Out of scope (post-validation triage) ]tools/oxlint/anti-slop/shared/lexical-type-parameters.ts — 0 comments posted, 1 evaluated, 1 filtered
collectInferTypeParameterNames(current.extendsType, ...)collectsinferdeclarations from nested conditional types as though they belonged to the outer conditional. An innerinfer Objectis scoped only to that inner conditional's true branch, so for code such astype Object = unknown; type R<T, U> = T extends (U extends infer Object ? Object : never) ? (() => Object) : never, the function return'sObjectresolves to the module alias. This helper nevertheless marks it shadowed, causing rules such asno-unknown-returnsto miss the violation. Traverse the outerextendsTypewithout descending into nestedTSConditionalTypescopes (except where their own binders are actually in scope). [ Out of scope (post-validation triage) ]