Files
deepseek-harness/eslint.config.mjs
T
Tianyi Cui cb6bee3d03 Add ESLint: typescript-eslint strict-type-checked + stylistic formatting
Flat config with two layers. Correctness (type-checked): the headline
rules for this codebase are no-floating-promises / no-misused-promises
(a lost promise in the agent loop is our primary bug class),
switch-exhaustiveness-check (we switch over merge-extensible unions
everywhere), no-unnecessary-condition, require-await, and
no-explicit-any. Style (@stylistic): 2-space, no semicolons, single
quotes, trailing commas, max-len 140 — the existing house style, now
enforced instead of drifting between agents. vendor/ is excluded
(vendored source keeps upstream style); tests relax the rules that
fight test ergonomics (non-null assertions after expects, async mock
signatures, non-Error throws).

Code adjusted to pass: registry disposers wrap ctx.effect's
promise-returning disposer behind a sync () => void (our public API),
BlockAssembler gains an invariant-checking mustGet instead of non-null
assertions, lastTurnNumber uses findLast, waterfall tails return
Promise.resolve instead of async-without-await arrows, and the two
deliberate suppressions (non-exhaustive derivation switch, unbound
execute pass-through) carry justification comments.

yarn lint / yarn lint:fix added.
2026-06-11 14:17:58 +08:00

134 lines
5.2 KiB
JavaScript

import stylistic from '@stylistic/eslint-plugin'
import tseslint from 'typescript-eslint'
/**
* ESLint flat config. Two layers:
*
* 1. typescript-eslint strict-type-checked — correctness rules that need the
* type checker. The headline rules for this codebase: no-floating-promises
* and no-misused-promises (an un-awaited promise in the agent loop is our
* primary bug class), switch-exhaustiveness-check (we switch over
* merge-extensible unions everywhere).
* 2. @stylistic — formatting (2-space, no semicolons, single quotes, trailing
* commas), so style is enforced rather than drifting between agents.
*
* vendor/ is linted lightly (style only stays OFF — vendored code keeps
* upstream style; only a few safety rules apply there) and examples/tests are
* linted with relaxed unsafe-* rules where mocks intentionally bend types.
*/
export default tseslint.config(
{
ignores: [
'**/lib/**',
'**/node_modules/**',
'vendor/**', // vendored source keeps upstream style and idioms
'**/*.js',
'**/*.mjs',
],
},
// --- our packages: full strictness -------------------------------------
{
files: ['packages/*/src/**/*.ts', 'examples/**/*.ts', 'scripts/**/*.ts'],
extends: [
...tseslint.configs.strictTypeChecked,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.typecheck.json'],
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
// The bug class this repo cares most about: lost promises in the loop.
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/require-await': 'error',
'@typescript-eslint/switch-exhaustiveness-check': ['error', {
considerDefaultExhaustiveForUnions: true,
}],
'@typescript-eslint/no-unnecessary-condition': ['error', {
allowConstantLoopConditions: true,
}],
// `any` requires a justification comment — enforced as: no bare casts.
'@typescript-eslint/no-explicit-any': 'error',
// Style points where the codebase intentionally diverges from preset:
'@typescript-eslint/no-namespace': 'off', // Cordis Config-namespace idiom
'@typescript-eslint/no-empty-object-type': 'off', // merge-extensible maps
'@typescript-eslint/no-invalid-void-type': 'off', // event signatures
'@typescript-eslint/restrict-template-expressions': ['error', {
allowNumber: true,
allowBoolean: true,
}],
// `void foo()` in arrow listeners is our idiom for intentional fire-and-forget
'no-void': 'off',
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
}],
},
},
// --- examples: demo code conforms to async interfaces without awaiting ---
{
files: ['examples/**/*.ts'],
rules: {
'@typescript-eslint/require-await': 'off',
},
},
// --- tests: same rules, minus the friction that fights test ergonomics --
{
files: ['packages/*/tests/**/*.ts'],
extends: [
...tseslint.configs.strictTypeChecked,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.typecheck.json'],
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-non-null-assertion': 'off', // assertions follow expect()s
'@typescript-eslint/no-unnecessary-condition': 'off',
'@typescript-eslint/require-await': 'off', // mock execute() signatures
'@typescript-eslint/no-empty-function': 'off', // stub agents
'@typescript-eslint/only-throw-error': 'off', // testing non-Error throws
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/restrict-template-expressions': 'off',
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
}],
},
},
// --- formatting (everything we own) -------------------------------------
{
files: ['packages/**/*.ts', 'examples/**/*.ts', 'scripts/**/*.ts', 'eslint.config.mjs'],
plugins: { '@stylistic': stylistic },
rules: {
'@stylistic/indent': ['error', 2],
'@stylistic/semi': ['error', 'never'],
'@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
'@stylistic/comma-dangle': ['error', 'always-multiline'],
'@stylistic/eol-last': ['error', 'always'],
'@stylistic/no-trailing-spaces': 'error',
'@stylistic/object-curly-spacing': ['error', 'always'],
'@stylistic/arrow-parens': ['error', 'as-needed', { requireForBlockBody: true }],
'@stylistic/member-delimiter-style': ['error', {
multiline: { delimiter: 'none' },
singleline: { delimiter: 'semi', requireLast: false },
}],
'@stylistic/max-len': ['error', { code: 140, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: true }],
},
},
)