[JUM-21][Refactor] Adopt Airbnb Extended ESLint 9 flat configuration (epic-wide, single-PR exception) - #545
Open
web2solutions wants to merge 9 commits into
Conversation
- eslint 9.39.5 in root, apps/frontend, apps/jumentix-website and packages/config-eslint (single major; eslint-config-airbnb-extended@3.2.0 declares peer eslint ^9 only, so JUM-6's own fallback rule applies and apps/frontend steps down from 10.x) - remove legacy ESLint 8 family from the root: eslint-config-airbnb-base, eslint-import-resolver-alias, eslint-plugin-import, eslint-plugin-jest, eslint-plugin-module-resolver, eslint-plugin-no-async-foreach, @typescript-eslint/* (superseded by @jumentix/config-eslint's own deps) - add prettier 3.9.9 + globals 17.12.0; apps/frontend declares cypress, apps/jumentix-website declares cypress and @jumentix/service-management (hoist-only latent gaps the legacy resolver could not see, JUM-16 class) - package.json script wiring for the new gates (JUM-13/JUM-19): lint runs through ci-cd/run-lint.js with --max-warnings=0; lint:frontend/lint:website per-surface; format/format:check; ci:gate chains all four - apps/frontend and apps/jumentix-website lint scripts drop 'next lint' (removed in Next.js 16) for direct eslint with --max-warnings=0 Verified: bun install --frozen-lockfile clean, deps:check-overrides exit 0, zero peer warnings in the ESLint family.
…he single flat-config source - packages/config-eslint/src exports composable flat-config profiles on eslint-config-airbnb-extended: base, typescript, node, vue, reactNextA11y, test, stylistic, each with a *Strict variant (strict is what every consumer runs) - Jumentix parity layer: no-console, no-async-foreach, dependency-declaration guard with workspace-union packageDirs, deliberate legacy relaxations with reasons; strict variant adds the four JUM-44 rules the owner accepted (no-cycle depth 1, arrow-body-style, prefer-default-export, jest/unbound-method) plus the JUM-9 type policies - typescript profile: per-workspace projectService resolution (JUM-9); untypedFiles fallback for trees no tsconfig covers (985 parse fatals at first measurement, now zero); resolverProjects wiring import-x resolution for @src/* and @jumentix/* aliases (JUM-12) - node profile: eslint-plugin-n with the Node 22 floor from root engines; n/no-sync off (type-aware crash on plain JS); Bun API allowances documented - vue profile: tiers + vuejs-accessibility (strict) + typed <script setup>; reactNextA11y: React/Hooks/jsx-a11y/@next core web vitals; react strict tier's arrow-function demand neutralized (recommended tier's declaration convention wins) - test profile: Jest scoped to test files only (JUM-619), docker-gated and bun:test and Cypress/Mocha exception blocks preserved from the legacy per-path overrides; jest padding family and two jest-29 additions off with reasons - stylistic profile: eslint-config-prettier last in every array (JUM-13) - contract suite (41 tests): positive/negative fixtures per profile, effective-config snapshots (committed JSON, runtime-neutral for bun test and jest), export resolution, empty-glob detection, external-consumption fixture project; bun coverage feeds coverage/config-eslint into the merge - real package.json (build via tsc ESM, lint/typecheck/test), README EN+pt-BR - codemod/strict follow-ups: controller dynamic resolution reads default exports first (RealtimeAPIBase/RestAPI); hoisting-safe firebase mocks in agent-registry suites; handler imports restored in handlers.test.ts; authenticateForHeader seed helper replaces dropped non-null spreads in integration suites (authenticate returns IServiceResponse with optional result — proving it exists, JUM-44 compliant) Verified: bun run --cwd packages/config-eslint build|lint|typecheck|test all exit 0; suite fails on a deliberately broken profile (rule drop, empty glob).
…website
- root eslint.config.mjs consumes @jumentix/config-eslint strict profiles,
reproducing every legacy ignorePattern and per-path override (Jest scoped
to test files, docker-gated integration suites, frontend bun:test suites,
Cypress/Mocha browser globals, restify allow-list); scoped JUM-18
overrides documented inline: service-management no-bundler runtime
(props:false / functions:false), browser service workers, Next.js App
Router named-export contracts, cli-init template mirrors (fidelity owned
by the freshness gate, not the resolver)
- vendored/generated bundles ignored deliberately (OASdoc, AsyncAPIdoc,
service-management/vendor, .browser-tests, _pagefind) — closing the
false-green class, not widening it
- apps/frontend rewritten onto the shared config (ESLint 10 to 9.39.5);
tsconfig.eslint.json exists because the import resolver only resolves
aliases for files a listed project covers and the app tsconfig excludes
test/** (JUM-760)
- apps/jumentix-website gains its own eslint.config.mjs consuming
baseStrict + typescriptStrict + reactNextA11yStrict — its first working
lint ever ('next lint' was removed in Next.js 16 and nothing in CI ran it)
- lint scripts run without --ext (flat config); root lint now covers
.ts/.tsx/.js/.mjs/.cjs (2,802 files vs 1,668 legacy .ts-only — coverage
regression: zero; extension gaps: closed)
- tsconfig target ES2020 -> ES2022: the airbnb strict tier enforces
prefer-object-has-own and Node 22 (root engines) supports it; resolves the
TS2550 that blocked ~17 backend suites
- tsconfig.build.json excludes config-eslint lint fixtures from the emit
build (deliberate violations by design, JUM-14)
Verified: bun run lint exit 0 (2,802 files, --max-warnings=0); file-count
parity with the JUM-5 baseline pasted in the epic's Project Updates.
…ss all surfaces Activates the *Strict config-eslint profiles on root, frontend and website consumers and remediates the measured 252,732 strict violations down to 0 errors / 0 warnings under --max-warnings=0, in per-surface waves: - apps/backend-template, apps/service-management(+api), apps/frontend, apps/jumentix-website, packages/*, ci-cd, tooling, sdk-clients, bin. - JUM-44 codemod: 309 modules converted to default exports for import-x/prefer-default-export (barrels kept as re-exports; 4 justified Next.js contract suppressions); arrow-body-style and jest/unbound-method enabled and remediated; import-x/no-cycle enabled at depth 1 (0 hits); @typescript-eslint/no-explicit-any (3,482 measured) and no-underscore-dangle (366 measured) documented as owner-approved OFF decisions in the typescript/parity profiles. - Class-split fix: 12 package tsconfigs resolve @jumentix/* to src for tests while tsconfig.build.json (no paths) keeps dist builds honest, eliminating dual class identity between src and dist (~50 test failures). - serverless.ts keeps module.exports (serverless framework contract) with a justified disable; dynamic controller resolution accepts both default and named exports after the codemod; jest.MockedFunction casts restored where bun:test cannot use jest.mocked (justified jest/prefer-jest-mocked disable). - serverHarness waits on child 'close' instead of 'exit' so EADDRINUSE in stderr is never misclassified under parallel load; stripped type annotations restored in the offline persistence matrix; smoke-test source markers aligned with the remediated canvas module (dead buildRoutedEdgePathD marker now points at roundedRoutePathD). Verified: bun ci-cd/run-lint.js exit 0 (root 2,803 files, frontend, website, all strict, 0/0); bun run ci:gate:task exit 0 (3,988+ tests).
…he repo Adds prettier.config.mjs (singleQuote, semi, trailingComma none, printWidth 100; YAML keeps double quotes so the check-ci-provider cron contract survives formatting) and .prettierignore (dependencies, build outputs, vendored bundles, generated cli-init templates and the release-owned CHANGELOG files), plus format / format:check scripts wired into ci:gate. This commit is the mechanical tail of the one-time 'prettier --write .' run: documentation, root markdown, CI workflow/pm2/spec configuration and generated CI artifacts. Code surfaces were formatted together with their remediation in 7d8def4; the ESLint integration itself (Stylistic profile without rule conflicts) landed with the config-eslint package in d095bcd. Verified: bun run format:check exit 0; bun ci-cd/check-ci-provider.js exit 0; CHANGELOG.md / CHANGELOG.pt-BR.md byte-identical to dev.
Replaces the lint script chain with ci-cd/run-lint.js, a fail-closed runner: every consumer (root, frontend, website) lints with --max-warnings=0, each run proves its glob matched files, and ci-cd/lint-coverage-floors.json pins per-consumer file-count floors (root 2802, frontend 178, website 179) so a config regression that silently shrinks coverage fails the gate instead of passing on zero files. ci:gate now chains lint && lint:frontend && lint:website && format:check. ci-cd/test/run-lint.test.ts covers the runner contract (5 tests, including a negative fixture that must fail closed); test-map.json regenerated so the new suite is mapped (test-map:check exit 0). Verified: bun ci-cd/run-lint.js exit 0 on all three consumers; bun run format:check exit 0; bun run test-map:check exit 0.
Adds .agents/requirements/software/138-eslint-9-flat-config-governance.md: the @jumentix/config-eslint package is the single governed source of lint profiles, consumers must not re-declare rules, strict variants are the default, and the JUM-44 owner decisions (no-explicit-any and no-underscore-dangle OFF with measured counts) are recorded as governed exceptions. Coverage markers landed in the requirement and its implementation surfaces (files=129 unique=129 mapped=129 per requirements:check); NFR-REGISTRY, .agents/README.md, the traceability ledger (EN/PT) and the coverage status inventory (128 -> 129) updated accordingly. Remaining .agents edits are the Prettier pass over agent docs. Verified: bun run requirements:check exit 0.
Adds documentation/md/ESLINT-AND-FORMATTING-GUIDE.md (+ pt-BR): how the @jumentix/config-eslint profiles compose, which variant each consumer runs, how to add a new consumer, the JUM-44 rule decisions, the format-vs-lint split with Prettier, and the false-green protections of the lint gate. Indexed from SPEC-DEVELOPMENT-DRIVEN-INDEX (EN/PT). Companion notes shipped earlier: the consumer-facing AGENTS.md note for apps/frontend landed with its surface in 7d8def4 and the package READMEs (EN/PT) in d095bcd.
Regenerates packages/cli-init/templates from the remediated, strict-clean seeds (865 files; the template tree is a generated mirror excluded from direct lint/format, so it inherits the seeds' state) and refreshes templates.manifest.json, keeping cli:check-template-freshness green. Also fixes a gate false-red the epic exposed: a templates-only change mapped to no layer and the task gate refused it as unsupported-change-set. The tooling layer in ci-cd/generate-test-map.js now classifies packages/cli-init/templates/** and the manifest (the same fix class JUM-622/JUM-680 applied to the browser harness and the website), so template regenerations run the tooling suites — including template-freshness — instead of being rejected. test-map.json regenerated and formatted. Repository-wide strict enforcement is now the default on every consumer (root, frontend, website), the legacy .eslintrc.js/.prettierrc.js files are gone (root in 86ffd73, frontend in 7d8def4), and the epic closes with the lint platform, gates, governance requirement 138 and the developer guide all in place. Verified: bun run cli:check-template-freshness exit 0; bun run test-map:check exit 0; bun test ci-cd/test/generate-test-map.test.ts 27/27 pass.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_09733b19-2251-450d-b556-3b8b8fdc1ea6) |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adopts the Airbnb Extended ESLint 9 flat configuration across the whole monorepo in a single PR (owner-approved exception to Requirement 086's one-issue-one-PR rule): the new
@jumentix/config-eslintworkspace package is the single source of truth for lint profiles (base, type-aware TypeScript, Node, Vue, React/Next/a11y, test, Stylistic+Prettier, each with a*Strictvariant), all three lint consumers (root,apps/frontend,apps/jumentix-website) run the strict profiles at 0 errors / 0 warnings under--max-warnings=0, Prettier is integrated withformat/format:checkgates, and the lint gate is hardened against false greens (anti empty-glob runner with per-consumer coverage floors). Measured strict remediation: 252,732 → 0 violations.Problem Statement
ESLint 8
.eslintrcreached end of life and the repo carried per-app configs with six legacy-disabled rules and no shared, versioned profile source. The epic migrates to ESLint 9 flat config viaeslint-config-airbnb-extended, productizes the shared config, remediates every strict violation, and makes the gates impossible to false-green.Project Tracking (Required)
Jumentix(https://linear.app/jumentix)kimi-code-primary-001Start date->End date): current cycleP0/P1/P2): P1Branch Promotion Path (Required)
kimi-code-primary-001/refactor/JUM-21-eslint-9-flat-config-epicdev[JUM-XXXX][Nature] <concise outcome>JUM-XXXXmatches the single Linear Issue declared above (JUM-21, the epic-close issue; full mapping below).dev.main, it is a release promotion sourced fromdev... — N/Amain.Bidirectional Traceability (Required)
5410581d..HEAD, posted with the PR URL).JUM-5: no code commit — baseline measurements published as Linear Project Updates (ESLint 9.39.5 selected over 10.x becauseeslint-config-airbnb-extended@3.2.0peers^9.0.0)JUM-6:86ffd73fchore(JUM-6): pin ESLint 9.39.5 across the monorepoJUM-7,JUM-9,JUM-10,JUM-12,JUM-14:d095bcd4refactor(JUM-7)+test(JUM-14): productize @jumentix/config-eslint (profiles base/typescript/node + import-x resolution/ordering; 41 contract tests with versioned snapshots)JUM-8,JUM-867,JUM-11,JUM-868:c664e330refactor(JUM-8+867+11): flat-config consumers for root, frontend (Vue+a11y) and website (React/Next/a11y)JUM-15,JUM-16,JUM-17,JUM-18,JUM-869,JUM-44:7d8def49fix: remediate strict-profile violations across all surfaces (252,732 → 0)JUM-13:7b8e49b4chore(JUM-13): integrate Prettier with the shared config and format the repoJUM-19:538b9724ci(JUM-19): make lint and formatting gates impossible to false-greenJUM-866:12db5a6bdocs(JUM-866): write requirement 138 ESLint 9 flat-config governanceJUM-20:e87f0b12docs(JUM-20): publish the ESLint and formatting developer guideJUM-21:3c6fb366chore(JUM-21): regenerate cli-init templates and close the epic rolloutScope of Change
Domain / Business Rules
Application / Use Cases
Adapters / Infrastructure
packages/config-eslint(new workspace package): profile factory + strict variants + 41 contract tests.eslint.config.mjs(root),apps/frontend/eslint.config.mjs,apps/jumentix-website/eslint.config.mjs: flat configs consuming the shared package; legacy.eslintrc.js/.prettierrc.jsremoved.ci-cd/run-lint.js+ci-cd/lint-coverage-floors.json+ci-cd/test/run-lint.test.ts: fail-closed lint runner (empty-glob proof, per-consumer file-count floors: root 2,802 / frontend 178 / website 179).packages/*tsconfigs:@jumentix/*path mapping tosrcfor tests + per-packagetsconfig.build.json(no paths) — fixes dual class identity betweensrcanddist(~50 test failures).prettier.config.mjs,.prettierignore,format/format:checkscripts.API / Contracts (OpenAPI, DTOs, handlers, controllers)
RealtimeAPIBase,RestAPI) accepts both default and named exports after the JUM-44 codemod;serverless.tskeepsmodule.exportswith a justified disable (framework contract, covered byaws-service-name.test.ts).Detailed Technical Changes
eslint-config-airbnb-extended@3.2.0declares peereslint: ^9.0.0only; per the epic's own fallback rule the pin stays on 9.x.arrow-body-style,jest/unbound-methodenabled and remediated;import-x/no-cycleenabled at depth 1 (0 violations);import-x/prefer-default-exportenforced via codemod (309 modules converted, barrels preserved as re-exports, 4 justified Next.js contract suppressions);@typescript-eslint/no-explicit-any(3,482 measured) andno-underscore-dangle(366 measured) remain OFF as documented owner decisions recorded in the typescript/parity profiles.format:checkis part ofci:gate; YAML keeps double quotes so thecheck-ci-providercron contract survives formatting; CHANGELOG files are prettier-ignored (release-owned).--max-warnings=0everywhere.Architecture and Design Alignment
arch:check-boundariesgreen).import-x/no-cycledepth 1 enforced;deps:check-cyclesgreen).Security Impact
Data and Migration Impact
Breaking Changes
None for runtime behavior. Developer-facing:
bun run lintnow runs the strict shared profiles with--max-warnings=0; local eslint plugins overrides in app-level.eslintrcfiles are gone in favor of the shared package (migration guide indocumentation/md/ESLINT-AND-FORMATTING-GUIDE.md).Acceptance Criteria
ci:gate:task3,988+ tests green).Test Plan (Evidence)
bun run lint— exit 0, root 2,803 files 0 errors / 0 warnings (strict,--max-warnings=0)bun run deps:check-cycles— exit 0bun run arch:check-boundaries— exit 0bun run arch:check-users-legacy-imports— exit 0bun run test:unit— exit 0 (bun phase ofci:gate:task: 3,988 tests)bun run oas:check-routes— exit 0bun run build:dev— exit 0bun run ci:smoke— exit 0bun run ci:gate— every step executed and green across the final runs exceptdeps:audit, which fails identically ondev(11 fatal advisories on dev's own lockfile — verified on the dev checkout; 20 here with current OSV data). The flagged advisories (next, sharp, adm-zip, nanoid, svgo, js-yaml) predate this branch and belong to the dependency-upgrade track, not this epic. Chain executed step-by-step after the deps:audit stop: lint (3 consumers, 0/0), format:check, all arch/boundary/cycle guards, workspace quality + coverage policy, release/authorship governance, requirements:check (129), docs governance, packages suites + build freshness, cli template freshness, website content routes, rtdb indexes, test:integrity, test-map:check, ci provider + third-party review, integrations + migration checks, agent-registry, test:unit (3,988 tests), frontend coverage (311 tests, thresholds met), ci:security-smoke, oas checks, serverless handlers, build:dev, ci:smoke — all exit 0.bun run format:checkexit 0;bun run requirements:checkexit 0;bun run test-map:checkexit 0;bun run packages:check-suitesexit 0;bun run cli:check-template-freshnessexit 0;bun test packages/config-eslint41/41 pass.Feedback Resolution (Required)
resolved, a SHA from this PR. (post-review)Coverage
check-coverage-thresholdsgreen)SonarQube Cloud
Performance / Reliability Impact
Lint runtime is comparable to the legacy setup (type-aware profiles run with project service). One test-harness fix improves reliability under parallel load:
serverHarnessnow waits for childcloseinstead ofexitso EADDRINUSE is never misclassified. No runtime latency/throughput impact.Observability
Deployment and Rollout
dev; no deploy step (tooling only).dev.Risks and Mitigations
Mitigation: remediation was mechanical and rule-driven; every commit passed the full pre-commit branch gate (lint + integrity + affected task tests); the final
ci:gaterun is attached above; class-split and default-export edge cases have targeted regression tests (RealtimeAPIBase.moduleResolution,index.exportssuites).Mitigation: JUM-19 runner fails closed on empty globs and enforces file-count floors; requirement 138 makes the shared package the governed source; contract tests snapshot every profile.
Documentation Updates
packages/config-eslintREADME EN/PTdocumentation/md/ESLINT-AND-FORMATTING-GUIDE.md(+ pt-BR), SPEC index entries,apps/frontend/AGENTS.mdnote, requirement 138mainmerge (CHANGELOG files byte-identical todevand prettier-ignored)Reviewer Checklist
Note
Medium Risk
Monorepo-wide lint/format and CI gate changes touch every workspace but not runtime auth or data paths; large diff size increases review risk for accidental behavioral edits amid mechanical fixes.
Overview
Migrates the monorepo from ESLint 8
.eslintrcto ESLint 9 flat config with a new shared package@jumentix/config-eslintas the only rule source (Airbnb-extended profiles for base, TypeScript, Node, Vue, React/Next/a11y, test, and Stylistic+Prettier). Root.eslintrc.jsis removed; root,apps/frontend, andapps/jumentix-websitenow composeeslint.config.mjsfrom strict profiles with--max-warnings=0.Remediation and gates: Strict-profile violations are cleared across the tree (per PR: 252,732 → 0), with mechanical import/default-export and type-import fixes. Prettier becomes the formatting authority via
prettier.config.mjs,.prettierignore, andformat/format:checkinci:gate; repo-wide Prettier pass shows up in docs, compose files, and similar.ci-cd/run-lint.jsplus coverage floors and tests harden lint against empty globs and shrinking file counts.Governance and CI: Requirement 138 is added (NFR registry,
.agents/README.md, full spec). Developer docs andcli-inittemplates are regenerated to match the new lint/format setup. CircleCI patch coverage now includespackages/config-eslintcoverage.Reviewed by Cursor Bugbot for commit 3c6fb36. Bugbot is set up for automated code reviews on this repo. Configure here.