Files
bot-agents/.agents/memory/lint-config.md
T
Danny Kim 063eb09750 Replace NodeJS dict aliases with maps and named field reads
Co-authored-by: Danny Kim <temeddix@gmail.com>
Co-committed-by: Danny Kim <temeddix@gmail.com>
2026-10-20 05:01:49 +00:00

5.3 KiB


description: Lint config shape: every non-nursery Oxlint category on, plugins via all, a blacklist of offs in configs.project, per-file exceptions only in projectOverrides, ignorePatterns only for generated output

Policy (2026-10-19): a blacklist, not a whitelist. Replaces the older category-subset and "no overrides" notes.

  • Categories: correctness, suspicious, pedantic, perf, style, restriction at error. nursery stays off (no line for it).
  • Plugins are enabled whole through their all config (explicitTypes.configs.all.rules, importStructure.configs.all.rules).
  • Blacklist: repo-wide offs and option tweaks live in projectLint.configs.project.rules in scripts/project-lint.ts, spread last in rules, each with a one-line reason. Never re-list a rule a category already turns on. bot-agents runs on Node everywhere, so import/no-nodejs-modules, unicorn/no-process-exit, and no-console are off repo-wide.
  • Per-file exceptions only via projectOverrides, the whole overrides array exported from scripts/project-lint.ts: the Node code entry (vite.config.ts, scripts/**, nodeRules) first, then one entry per exception with a why-comment. vite.config.ts has exactly overrides: projectOverrides.
  • No allowFiles options in custom rules, and no disable comments (lint-no-disable-comments).
  • ignorePatterns only for generated or build output, never hand-written code. A hand-written file is linted, with override entries for only what it legitimately breaks. bot-agents has no generated code, so none.
  • Precedence: categories < rules (a later spread wins) < overrides (a later entry wins).
  • Size and complexity limits (complexity, max-lines, ...) are set to the smallest value the current code passes.
// scripts/project-lint.ts
export const projectOverrides: OxlintOverride[] = [
  { files: ["vite.config.ts", "scripts/**"], rules: nodeRules },
  // Why: src/example.ts must ... (a per-file exception, if one is ever needed)
  // { files: ["src/example.ts"], rules: { "some-rule": "off" } },
];

// vite.config.ts
rules: {
  ...explicitTypes.configs.all.rules,
  ...importStructure.configs.all.rules,
  ...projectLint.configs.project.rules,
},
overrides: projectOverrides,

Rule decisions (2026-10-19, all repos)

  • typescript/promise-function-async is on with require-await and typescript/return-await. A function that returns a promise is async and awaits it: async f() { const x = await g(); return x; }. Test doubles do the same (const resolved = await Promise.resolve(v); return resolved;), and expect(promise).rejects takes the promise itself. src/processes/child.ts is exempt in projectOverrides: exitCode must return the very promise it marks handled, or a failed spawn would surface as an unhandled rejection before callers await it.
  • init-declarations is on: an uninitialized let becomes let x: T | null = null; (unknown already includes null); a try that assigns and a catch that exits still narrow it.
  • no-use-before-define is on with only { functions: false }.
  • no-await-in-loop is off (sequential awaits are intentional) and unicorn/no-array-reduce is on: sequential steps are plain for loops, never reduce chains or recursion.
  • consistent-return, typescript/switch-exhaustiveness-check, and default-case are off: TypeScript's checks cover them, and no default: branch is added to an exhaustive switch.
  • unknown is allowed; any is banned and the typescript/no-unsafe-* rules are on. Untrusted data is unknown plus a type guard.
  • unicorn/no-array-for-each (use for...of), unicorn/prefer-structured-clone, and typescript/prefer-nullish-coalescing (defaults) stay on.
  • explicit-types upper-snake-string-unions is on (enum-values-upper-snake); unicorn/custom-error-definition is on, so a custom error has a constructor that sets this.name.
  • src/post/ and src/profiles/ (code and tests) are exempt from project/prefer-ascii in projectOverrides: posted Gitea text shows emoji verdict marks and a middle dot.

Rule decisions (2026-10-20, all repos)

  • unicorn/number-literal-case is off: Oxfmt lowercases hex digits back.
  • typescript/consistent-return is off too: it misfires on complete switches, like consistent-return.
  • On with defaults, no offs: prefer-object-spread, unicorn/prefer-spread, unicorn/no-useless-spread, typescript/no-misused-spread, no-plusplus (x += 1), unicorn/no-array-method-this-argument, typescript/method-signature-style ("property"), typescript/unified-signatures.
  • unicorn/import-style uses its default: import nodePath from "node:path" and nodePath.join(...) (nodePath, since path is a common variable).
  • no-template-curly-in-string is on; only tests/workflow.test.ts, which checks literal Gitea ${{ }} expressions, is exempt in projectOverrides.
  • Rule defaults are preferred over option tweaks unless a tweak is needed.
  • fmt matches deno fmt: printWidth: 80, quoteProps: "preserve", embeddedLanguageFormatting: "off". max-lines-per-function is 124 after the narrower width.

A new category hit is fixed in the code; an off or exception is added only for a real collision or churn, with its reason. Rules: project-lint-plugin.