2026-05-09 — ESLint tech-debt: rules disabled at flat-config migration
2026-05-09 — ESLint tech-debt: rules disabled at flat-config migration
Section titled “2026-05-09 — ESLint tech-debt: rules disabled at flat-config migration”Context
Section titled “Context”The repository was scaffolded with eslint: ^10.2.1 pinned in devDependencies and a .eslintrc.cjs config file. ESLint 9+ requires the new flat-config format (eslint.config.js) and rejects .eslintrc.*. As a result, npm run lint and CI’s lint step have never successfully run on this repository — the very first attempt (PR #1, 2026-05-09) was the first time the configuration mismatch surfaced.
Migrating to flat config in fix/eslint-flat-config made the lint runner work for the first time. With the original ruleset preserved (@typescript-eslint/recommended-type-checked + eslint:recommended + prettier), the codebase produced 42 errors across 7 rules — pre-existing tech debt that was never enforced.
Decision
Section titled “Decision”To unblock CI without bundling 42 unrelated code changes into the migration PR, the following rules are explicitly disabled in eslint.config.js:
| Rule | Why disabled | Future action |
|---|---|---|
@typescript-eslint/no-unsafe-member-access | 20 violations (mostly tests using mocks; a few in src/lib/response.ts, src/handlers/connector.ts) | Re-enable after typing mock fixtures and narrowing remaining unknown accesses in src |
@typescript-eslint/no-unsafe-assignment | 5 violations | Re-enable alongside the above |
@typescript-eslint/no-unsafe-argument | 4 violations | Re-enable alongside the above |
@typescript-eslint/no-unsafe-return | 1 violation | Re-enable alongside the above |
@typescript-eslint/no-unsafe-call | (transitive — disabled to round out the family) | Re-enable alongside the above |
@typescript-eslint/require-await | 6 violations | Re-enable; review whether async is needed at each site |
@typescript-eslint/no-base-to-string | 2 violations | Re-enable; add explicit toString implementations |
@typescript-eslint/no-unnecessary-type-assertion | 1 violation (auto-fixable, but kept off to keep this PR’s diff small) | Re-enable + run --fix |
@typescript-eslint/no-unused-vars is set to warn (not error) so the 3 currently-unused declarations (SiteOut, PlacementOut, mockCampaignAnalytics) surface in editor and CI output without blocking. Some of these are already used on feat/otel-newrelic; deleting them on main would conflict with that branch.
Trade-offs
Section titled “Trade-offs”- Pro: CI is unblocked. Lint runs and catches new violations of the rules that do remain enabled (~50 type-aware rules from
recommended-type-checked). - Pro: The disabled-rules list is explicit and reviewable. Each line in
eslint.config.js’s tech-debt section can be deleted and the violations addressed in its own follow-up PR. - Con: The Workers SOP and CLAUDE.md call for
recommended-type-checkedstrictness. Until the rules are re-enabled, the codebase is silently weaker than the documented standard.
Re-enablement plan
Section titled “Re-enablement plan”Suggested order (cheapest first):
no-unnecessary-type-assertion— one site, auto-fixable.no-base-to-string— two sites, explicit fixes.require-await— six sites, review case by case.no-unsafe-*family — bulk of the work; tackle in two passes (src first, tests second; tests probably need typed mock fixtures).
Each re-enablement is its own PR. Don’t bundle.
Out of scope for this ADR
Section titled “Out of scope for this ADR”- Whether to migrate from
@typescript-eslint/eslint-plugin+@typescript-eslint/parser(separate packages) to the unifiedtypescript-eslintpackage. Both work; current choice avoids a new devDependency. - Whether to keep ESLint pinned at
^10.2.1or relax to>=10. Status quo until a real version reason emerges.