Skip to content

feat(tooling): add anti-slop Oxlint rules - #63

Merged
RedStar071 merged 1 commit into
mainfrom
t3code/setup-oxlint-anti-slop-symlinks
Aug 22, 2026
Merged

feat(tooling): add anti-slop Oxlint rules#63
RedStar071 merged 1 commit into
mainfrom
t3code/setup-oxlint-anti-slop-symlinks

Conversation

@RedStar071

@RedStar071 RedStar071 commented Aug 22, 2026

Copy link
Copy Markdown
Member

Summary

  • Vendor the MIT-licensed anti-slop Oxlint plugin at upstream commit 6d538555cb15.
  • Load the local JavaScript plugin from the shared Oxlint config and enforce the six rules the repository already passes.
  • Register the vendored source with Turbo, Knip, Oxfmt, the lockfile, and contributor documentation.

Why

The reference implementation vendors these rules instead of adding a runtime dependency. Keeping the plugin under tools/oxlint preserves 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 off so 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:repo
  • aube run lint:ci
  • aube run typecheck
  • aube test
  • aube run build

The 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 reports anti-slop/no-chained-type-assertions through the shared configuration.

Safety and compatibility

  • I added or updated deterministic tests for changed behavior.
  • I preserved observe mode as read-only, or explained the policy change above.
  • Runtime commands and target-repository writes remain inside the runner boundary.
  • I did not expose secrets, tokens, personal data, or untrusted output in logs.
  • I updated documentation and Agent Skills when workflows or boundaries changed.

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

  • Agent/tools used: Codex in T3 Code, with git, gh, and the repository's aube checks.
  • What the agent did, and what you changed or verified yourself: Codex inspected the requested newt-app reference, 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-slop Oxlint plugin with type-safety lint rules

  • Introduces a local Oxlint plugin at tools/oxlint/anti-slop/index.ts exposing ~14 rules that target TypeScript anti-patterns such as type widening, unsafe dictionary types, Reflect.apply/Reflect.get usage, and module mocking.
  • Shared helpers in tools/oxlint/anti-slop/shared/dictionary-types.ts provide type classification and evidence utilities reused across multiple rules.
  • tooling/oxc/base.oxlintrc.json registers the plugin and enables six rules as errors (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.
  • Adds @oxlint/plugins dependency and updates knip.jsonc, turbo.jsonc, and .oxfmtignore to integrate the vendored plugin into the build and tooling pipeline.
  • Risk: existing code may now produce lint errors for the six enabled rules; check for chained type assertions, Reflect.apply/Reflect.get calls, identifiers containing "shape", type aliases resolving to unknown, 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
  • line 20: isConditionalEmptyObjectSpread unwraps parentheses only around the whole spread argument, not around either conditional branch. A spread such as { ...(enabled ? ({}) : fields) } therefore has a ParenthesizedExpression consequent 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
  • line 129: hasParentAssertion only recognizes an immediately adjacent assertion parent, even though this rule otherwise treats ParenthesizedExpression and TSNonNullExpression as 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) ]
  • line 156: reportFlow exempts an empty object used as a dictionary accumulator only when the incoming expression is directly an ObjectExpression. Because hasKnownEvidence deliberately follows stable const aliases, equivalent code such as const empty = {}; const values: Record<string, string> = empty; is reported even though const 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
  • line 80: resolvesToObject carries the function's shadowedAliases set into the body of a module-level alias. For example, with type T = object; type Input = T; function f<T>(value: Input) {}, Input still resolves to the module-level T, but recursion rejects T because 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) ]
  • line 103: Alias collection only scans direct Program.body declarations. A valid nested alias such as function outer() { type Input = object; function inner(value: Input) {} } is never added to aliases, so inner evades the rule even though its parameter resolves to the prohibited object type. The rule needs scope-aware collection of nested TSTypeAliasDeclaration nodes (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
  • line 59: checkParameters only reports when the outer annotation node is exactly TSUnknownKeyword. Semantically equivalent explicit inputs such as value: unknown | string or value: (unknown) therefore bypass the rule, even though the union collapses to unknown and still leaves the parameter unparsed. The companion return rule resolves parenthesized and union forms, so this creates a concrete enforcement gap in no-unknown-parameters. [ Out of scope (post-validation triage) ]
tools/oxlint/anti-slop/rules/no-unknown-returns.ts — 0 comments posted, 2 evaluated, 2 filtered
  • line 19: referencedAliasName rejects every type reference with type arguments, so resolvesToUnknown cannot 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 to unknown, defeating the rule for a common owner-type pattern. [ Out of scope (post-validation triage) ]
  • line 59: resolvesToUnknown treats any identifier spelled Promise or PromiseLike as the global async type without checking whether that name is locally declared or imported. Valid code such as interface Promise<T> { status: string } followed by function 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
  • line 31: resolvesToUnknown never examines TSUnionType members, so an enabled rule misses aliases such as type Hidden = unknown | string. TypeScript normalizes that union to unknown (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 for unknown/aliases would catch it. [ Below severity threshold ]
tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts — 0 comments posted, 1 evaluated, 1 filtered
  • line 70: isPlainAliasConsumerUse suppresses 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: Dict is not reported: the declaration cannot classify bare T as 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
  • line 397: isBroadMappedKey requires every union member to be broad, but a mapped key such as string | "special" is still broad because the string constituent already admits every string key. Consequently a non-generic alias like type Bag = { [K in string | "special"]: unknown } is not classified as an open dictionary, so no-known-value-widening misses 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
  • line 55: collectInferTypeParameterNames(current.extendsType, ...) collects infer declarations from nested conditional types as though they belonged to the outer conditional. An inner infer Object is scoped only to that inner conditional's true branch, so for code such as type Object = unknown; type R<T, U> = T extends (U extends infer Object ? Object : never) ? (() => Object) : never, the function return's Object resolves to the module alias. This helper nevertheless marks it shadowed, causing rules such as no-unknown-returns to miss the violation. Traverse the outer extendsType without descending into nested TSConditionalType scopes (except where their own binders are actually in scope). [ Out of scope (post-validation triage) ]

@vercel

vercel Bot commented Aug 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-zero-dashboard Ready Ready Preview Aug 22, 2026 2:23pm
agent-zero-docs Ready Ready Preview Aug 22, 2026 2:23pm
agent-zero-marketing Ready Ready Preview Aug 22, 2026 2:23pm

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​oxlint/​plugins@​1.79.01001009996100

View full report

@macroscopeapp

macroscopeapp Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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.

@RedStar071
RedStar071 merged commit 172fe26 into main Aug 22, 2026
18 of 21 checks passed
@RedStar071
RedStar071 deleted the t3code/setup-oxlint-anti-slop-symlinks branch August 22, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant