From 39e26561cff921a397078b895c0eb3b5e088d818 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Thu, 11 Jun 2026 19:53:55 -0400 Subject: [PATCH] =?UTF-8?q?docs(13-01):=20complete=20lint=20gate=20foundat?= =?UTF-8?q?ion=20plan=20=E2=80=94=20SUMMARY=20+=20STATE=20+=20ROADMAP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plan 13-01 complete: ESLint flat config + Prettier installed and wired, SC-1 gate proven (exit 1 on deliberate violations), 119 real violations inventoried for Plan 02 (api:42, pwa:77). STATE advanced to Plan 2 of 3. --- .planning/ROADMAP.md | 2 +- .planning/STATE.md | 25 +-- .../13-real-lint-gate-eslint/13-01-SUMMARY.md | 146 ++++++++++++++++++ 3 files changed, 161 insertions(+), 12 deletions(-) create mode 100644 .planning/phases/13-real-lint-gate-eslint/13-01-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 8d307ea..8bf3b67 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -222,7 +222,7 @@ Make FamilySync configurable, administrable, and maintainable for real multi-mem **Plans**: 3 plans **Wave 1** -- [ ] 13-01-PLAN.md — Install ESLint/Prettier deps + flat config + package scripts + prove the gate fails (SC-1) +- [x] 13-01-PLAN.md — Install ESLint/Prettier deps + flat config + package scripts + prove the gate fails (SC-1) **Wave 2** *(blocked on Wave 1 completion)* diff --git a/.planning/STATE.md b/.planning/STATE.md index 0e6e4e2..25762b4 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,15 +3,15 @@ gsd_state_version: 1.0 milestone: v1.1 milestone_name: Operability & Polish status: executing -stopped_at: Phase 13 context gathered -last_updated: "2026-06-11T23:27:49.541Z" -last_activity: "2026-06-11 -- Quick task 260611-ozt: split publish into standalone push-only publish.yml (kills orphaned CI / publish (pull_request) pending status, WR-01); release model documented in README + publish.yml. Branch-protection contexts unchanged." +stopped_at: Phase 13, Plan 1 complete +last_updated: "2026-06-11T23:51:00Z" +last_activity: 2026-06-11 -- Phase 13 Plan 01 complete (lint gate foundation) progress: total_phases: 17 completed_phases: 2 - total_plans: 8 - completed_plans: 8 - percent: 12 + total_plans: 11 + completed_plans: 9 + percent: 13 --- # Project State @@ -21,14 +21,14 @@ progress: See: .planning/PROJECT.md (updated 2026-06-10) **Core value:** One color-coded family calendar (shared + personal) and shared lists from a single low-friction PWA — cross-ecosystem, no app store -**Current focus:** Phase 08 — gitea-ci +**Current focus:** Phase 13 — real-lint-gate-eslint ## Current Position -Phase: 08 (gitea-ci) — COMPLETE -Plan: 4 of 4 (08-04 complete) -Status: Ready to execute -Last activity: 2026-06-11 -- Quick task 260611-ozt: split publish into standalone push-only publish.yml (kills orphaned CI / publish (pull_request) pending status, WR-01); release model documented in README + publish.yml. Branch-protection contexts unchanged. +Phase: 13 (real-lint-gate-eslint) — EXECUTING +Plan: 2 of 3 +Status: Executing Phase 13 — Plan 01 complete +Last activity: 2026-06-11 -- Phase 13 Plan 01 complete (lint gate foundation) ## Performance Metrics @@ -82,6 +82,7 @@ Last activity: 2026-06-11 -- Quick task 260611-ozt: split publish into standalon | Phase 07 P02 | 196 | 2 tasks | 4 files | | Phase 07-mobile-test-harness P03 | 480 | 2 tasks | 2 files | | Phase 07-mobile-test-harness P04 | 22 | 2 tasks | 2 files | +| Phase 13 P01 | 8 | 2 tasks | 7 files | ## Accumulated Context @@ -90,6 +91,8 @@ Last activity: 2026-06-11 -- Quick task 260611-ozt: split publish into standalon Decisions are logged in PROJECT.md Key Decisions table. Recent decisions affecting current work: +- D-13-ESLint-PIN (2026-06-11, 13-01): eslint pinned to 9.39.4 — ESLint 10 breaks eslint-plugin-react@7.37.5 at runtime ("getFilename is not a function", jsx-eslint#3977). Unpin when plugin releases ESLint 10 support. +- D-13-JSX-SCOPE (2026-06-11, 13-01): react/react-in-jsx-scope disabled explicitly — flat.recommended enables it at error; PWA uses jsx:react-jsx (React 19 automatic transform), React import not required in JSX files. - D-PROBE-01 (2026-06-11, 08-01): runs-on must be ubuntu-latest — runner has no self-hosted label; all downstream ci.yml workflows use ubuntu-latest. - D-PROBE-02 (2026-06-11, 08-01): Docker-executor confirmed (/.dockerenv present); services: works; DB_HOST=mariadb in all downstream jobs. - D-PROBE-03 (2026-06-11, 08-01): No mysql CLI in runner image — DB readiness uses healthcheck.sh --connect --innodb_initialized or Node mysql2 wait; no mysql shell-out. diff --git a/.planning/phases/13-real-lint-gate-eslint/13-01-SUMMARY.md b/.planning/phases/13-real-lint-gate-eslint/13-01-SUMMARY.md new file mode 100644 index 0000000..3a318d6 --- /dev/null +++ b/.planning/phases/13-real-lint-gate-eslint/13-01-SUMMARY.md @@ -0,0 +1,146 @@ +--- +phase: 13-real-lint-gate-eslint +plan: "01" +subsystem: dev-tooling +tags: [eslint, prettier, lint-gate, typescript-eslint, react-hooks, flat-config] +dependency_graph: + requires: [] + provides: [eslint-flat-config, prettier-config, lint-scripts, format-scripts] + affects: [ci-lint-slot, apps/api, apps/pwa] +tech_stack: + added: + - eslint@9.39.4 (root devDep — pinned, ESLint 10 breaks eslint-plugin-react) + - "@eslint/js@9.39.4" + - typescript-eslint@8.61.0 + - eslint-plugin-react@7.37.5 + - eslint-plugin-react-hooks@7.1.1 + - eslint-config-prettier@10.1.8 + - prettier@3.8.4 + patterns: + - ESLint flat config with tseslint.config() helper + - projectService:true for multi-tsconfig monorepo (auto-discovers all tsconfigs) + - disableTypeChecked override block for config files + API test dir + - eslint-config-prettier/flat LAST to disable formatting rule conflicts +key_files: + created: + - eslint.config.js + - .prettierrc + - .prettierignore + modified: + - package.json (type:module + format/format:check scripts) + - apps/api/package.json (lint script --max-warnings 0) + - apps/pwa/package.json (lint script --max-warnings 0) + - pnpm-lock.yaml +decisions: + - "ESLint pinned to 9.39.4 (non-negotiable): eslint-plugin-react@7.37.5 declares peer ^9.7 and hits TypeError at runtime on ESLint 10 (getFilename removed)" + - "react/react-in-jsx-scope explicitly disabled: flat.recommended enables it at error; codebase uses jsx:react-jsx (React 17+ transform) so React import is not required" + - "apps/api/tests/** in disableTypeChecked block: excluded from apps/api/tsconfig.json so projectService cannot type-check them" + - "eslint.config.js itself added to disableTypeChecked block as precaution (JS file, not in any TS project)" +metrics: + duration_minutes: 8 + completed_date: "2026-06-11" + tasks_completed: 2 + files_changed: 7 +--- + +# Phase 13 Plan 01: ESLint + Prettier Lint Gate Foundation Summary + +Real ESLint flat config (type-aware via projectService) + Prettier installed and wired; both gates proven non-zero on deliberate violations (SC-1). + +## What Was Built + +### Task 1: Install devDependencies + author flat config + Prettier config + +Installed 7 packages at pinned versions as root devDependencies. Authored `eslint.config.js` with the required 5-block structure per RESEARCH Pattern 1: + +1. **Global ignores** — `**/dist/**`, `**/node_modules/**`, `apps/api/src/db/migrations/**`, `pnpm-lock.yaml` +2. **Base block** — `apps/**/*.{ts,tsx}`, extends `js.configs.recommended` + `tseslint.configs.recommendedTypeChecked`, `parserOptions.projectService: true` + `tsconfigRootDir: import.meta.dirname`, `@typescript-eslint/no-unused-vars` rule with `^_` ignore patterns +3. **PWA-React block** — `apps/pwa/**/*.{ts,tsx}` only, extends `reactPlugin.configs.flat.recommended` + `reactHooks.configs.flat.recommended`, `react.version: 'detect'`, `react/prop-types: 'off'`, `react/react-in-jsx-scope: 'off'` +4. **disableTypeChecked override** — config files (drizzle.config, vitest.config, vite.config, playwright.config) + `apps/api/tests/**/*.ts` + `eslint.config.js` itself +5. **`eslint-config-prettier/flat` LAST** — disables ESLint formatting rules that conflict with Prettier + +Added `"type": "module"` to root `package.json` (required for `eslint.config.js` to parse as ESM). Added `format`/`format:check` scripts. Added `lint` script with `--max-warnings 0` to both `apps/api/package.json` and `apps/pwa/package.json`. + +Authored `.prettierrc` (singleQuote:true, semi:true, tabWidth:2, trailingComma:all, printWidth:100) and `.prettierignore`. + +**Commit:** `df62d33` + +### Task 2: SC-1 smoke proof — deliberate violations, both gates, throwaway cleanup + +**Lint gate (SC-1 lint half):** +- Created `apps/api/src/_lint-gate-test.ts` with an unhandled `Promise.resolve(1)` expression +- Ran `pnpm --filter @familysync/api lint` → **exit code 1**, `@typescript-eslint/no-floating-promises` reported on the deliberate file +- Deleted `apps/api/src/_lint-gate-test.ts` — never staged or committed + +**Format gate (SC-1 format half):** +- Created `_format-gate-test.ts` at repo root with double quotes + irregular indentation (opposite of .prettierrc settings) +- Ran `pnpm format:check` → **exit code 1**, `_format-gate-test.ts` listed in warnings +- Deleted `_format-gate-test.ts` — never staged or committed + +**Commit:** `7a255ea` + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] `react/react-in-jsx-scope` unexpectedly enabled in `reactPlugin.configs.flat.recommended`** + +- **Found during:** Task 2 (SC-1 smoke) — 404 `react/react-in-jsx-scope` errors in the PWA +- **Issue:** RESEARCH.md (Assumption A1, Pitfall 8) stated that `reactPlugin.configs.flat.recommended` disables `react/react-in-jsx-scope` for React 17+ JSX transform. In practice, `eslint-plugin-react@7.37.5` flat.recommended sets this rule to `2` (error). The PWA uses `jsx: "react-jsx"` (React 19 automatic transform) — `React` does not need to be in scope. +- **Fix:** Added `'react/react-in-jsx-scope': 'off'` alongside `'react/prop-types': 'off'` in the pwa-react config block +- **Files modified:** `eslint.config.js` +- **Commit:** `7a255ea` + +## SC-1 Evidence + +| Gate | Deliberate Violation | Exit Code | Rule Reported | +|------|---------------------|-----------|---------------| +| `pnpm --filter @familysync/api lint` | `Promise.resolve(1)` — unhandled promise | **1 (non-zero)** | `@typescript-eslint/no-floating-promises` | +| `pnpm format:check` | double quotes + irregular indent (opposite of .prettierrc) | **1 (non-zero)** | `_format-gate-test.ts` listed as unformatted | + +Both throwaway files confirmed absent and untracked (`THROWAWAYS_ABSENT_AND_UNTRACKED`). + +## First-Run Violation Inventory (for Plan 02) + +Plan 02 consumes this inventory to scope fixes. No violations were fixed in this plan. + +### apps/api — 42 errors total + +| Rule | Count | Description | +|------|-------|-------------| +| `@typescript-eslint/no-unnecessary-type-assertion` | 24 | Redundant `as` casts (mainly in outboxWorker.ts dispatcher blocks) | +| `@typescript-eslint/no-unsafe-assignment` | 3 | `any` assignment from ical.js property access | +| `@typescript-eslint/no-unsafe-argument` | 4 | `any` args from ical.js property values | +| `@typescript-eslint/no-unused-vars` | 7 | Test file variables assigned but unused | +| `@typescript-eslint/no-misused-promises` | 1 | `sse.ts:96` — async callback in void context | +| `@typescript-eslint/no-base-to-string` | 1 | Template literal with possible `[object Object]` | +| `@typescript-eslint/restrict-template-expressions` | 1 | `Record` in template literal | +| `prefer-const` | 1 | `lists.ts:604` — `let` that is never reassigned | + +### apps/pwa — 77 errors total + +| Rule | Count | Description | +|------|-------|-------------| +| `@typescript-eslint/require-await` | 23 | Async functions with no `await` expression | +| `@typescript-eslint/no-unnecessary-type-assertion` | 14 | Redundant `as` casts | +| `@typescript-eslint/no-floating-promises` | 11 | Unhandled promises | +| `@typescript-eslint/no-unused-vars` | 4 | Unused variables | +| `@typescript-eslint/no-unsafe-assignment` | 4 | `any` assignments | +| `@typescript-eslint/no-unsafe-member-access` | 3 | Member access on `any` | +| `@typescript-eslint/no-misused-promises` | 3 | Async callbacks in void contexts | +| `react/no-unescaped-entities` | 1 | Unescaped `'` in JSX | + +**Total:** 119 errors across both apps (0 warnings — `--max-warnings 0` working). + +## Self-Check: PASSED + +Created files exist: +- `eslint.config.js` — FOUND +- `.prettierrc` — FOUND +- `.prettierignore` — FOUND + +Commits exist: +- `df62d33` — FOUND (chore(13-01): install ESLint+Prettier, author flat config, wire lint/format scripts) +- `7a255ea` — FOUND (fix(13-01): disable react/react-in-jsx-scope for React 19 JSX transform) + +Throwaway files absent: THROWAWAYS_ABSENT_AND_UNTRACKED confirmed.