A tool listed in dependencies does not establish that every check is enabled. This article follows ChatCrystal’s actual commands, Biome switches, and client Flat Config, then verifies target diagnostics offline. The goal is a demonstrable scope, rather than adding tools.
Read scripts, then tool switches
The current root package.json runs Biome first for pnpm lint, then client ESLint if it succeeds. These are current script excerpts:
{
"lint": "pnpm exec biome check . --diagnostic-level=error && pnpm --filter ./client lint",
"lint:fix": "pnpm exec biome check --fix . && pnpm --filter ./client lint -- --fix"
}
biome.json enables the linter, while formatter and assist are both false. Current biome check –fix therefore does not establish repository-wide formatting. VCS ignores and ignoreUnknown also affect coverage; a dot in the command does not mean every file type receives identical checks.
Root dependencies do not integrate Prettier, husky, or lint-staged, and there is no .husky commit hook. Editor formatting and local commit checks remain possible additions. Teaching installation instructions from the original draft cannot be presented as existing project features.
Flat Config controls matching, parsing, and rules separately
client/eslint.config.js uses ESLint Flat Config. It globally ignores dist and combines these configurations for **/*.{ts,tsx}:
| Configuration | Current role |
|---|---|
| @eslint/js recommended | Common JavaScript problem rules |
| typescript-eslint recommended | TypeScript syntax and recommended rules |
| react-hooks flat recommended | React Hooks rules |
| react-refresh vite | Export conventions for Vite refresh |
languageOptions uses ecmaVersion:latest and browser globals, rather than the original draft’s fixed 2020. React’s set-state-in-effect rule is disabled; that override does not disable every Hooks rule. Global ignores differ from matching only particular file types; see the official configuration guide.
The typescript-eslint recommended configuration here does not establish type-aware linting. Current settings do not wire type information through projectService or equivalent options. ESLint cannot be described as performing all compiler type analysis or enforcing enum naming by default. Rules needing type information have separate configuration requirements.
Verify rules through specific diagnostics
Install ESLint 10.10.0, matching the current client, in a new temporary directory. Save lesson.mjs and run with Node 24+. This independent JavaScript teaching configuration loads no ChatCrystal configuration and includes no React or TypeScript rules.
npm init -y
npm install --ignore-scripts --no-audit --no-fund eslint@10.10.0
import assert from 'node:assert/strict';
import { ESLint } from 'eslint';
const lint = new ESLint({
overrideConfigFile: true,
overrideConfig: [
{
files: ['**/*.js'],
languageOptions: { globals: { console: 'readonly' } },
rules: { 'no-unused-vars': 'error', 'no-undef': 'error' },
},
],
});
const [bad] = await lint.lintText('const unused = 1;', { filePath: 'fixture.js' });
assert.equal(bad.errorCount, 1);
assert.equal(bad.messages[0].ruleId, 'no-unused-vars');
const [good] = await lint.lintText("console.log('ready');", { filePath: 'fixture.js' });
assert.equal(good.errorCount, 0);
const [missing] = await lint.lintText('missing();', { filePath: 'fixture.js' });
assert.equal(missing.messages[0].ruleId, 'no-undef');
console.log('specific diagnostics and repaired input pass');
Change no-unused-vars from error to off and the first error-count assertion should fail; restore it and the program passes. Checking ruleId also avoids confusing a parse error with the intended rule. lintText returns diagnostics; the API call does not automatically set process status from the error count. Assertions determine success or failure in this lesson.
Integration checks should cover a matched file, an ignored file, and a file with a rule override. From client, pnpm exec eslint --print-config src/App.tsx shows the merged configuration. Array order, file matching, and override values establish more than the mere presence of plugins.
Formatting needs its own implementation choice
To add formatting, first choose Biome or Prettier and specify supported file types, commands, and editor settings. Enabling Biome formatter or adding Prettier are future options; this article changes neither configuration.
eslint-config-prettier disables a set of potentially conflicting formatting rules. It does not run Prettier or disable every ESLint quality rule. Existing ESLint rules need not conflict with formatting, so the presence of two tools alone does not establish incompatibility.
Save-time fixes and CI checks serve different purposes: the former changes fixable items, while the latter verifies committed content. Automatic fixes cannot guarantee removal of every error; inspect the diff and check again. Commit hooks can be bypassed. Whether CI blocks merging also depends on repository required checks, which cannot be inferred from a workflow file alone.
Check commands and failure exits
Current npm and Electron publishing jobs in release.yml run pnpm lint and pnpm test before building. That release workflow triggers from tags or manual dispatch. It does not establish identical gates on every ordinary pull request.
Accept a check only after verifying the target diagnostic, passing repaired input, intended ignores, and a failure that actually stops downstream work. Type and runtime checks have separate roles in TypeScript monorepo configuration; passing lint does not replace a testing strategy either.