Phase 13: Real Lint Gate — type-aware ESLint + Prettier format gate #8

Merged
luckberg merged 25 commits from gsd/phase-13-real-lint-gate-eslint into main 2026-06-11 21:51:10 -04:00
Owner

Makes the CI fast-checks lint step a real, failing gate (it was a no-op that exited 0 with no linter installed). Closes Phase-8 review finding IN-02 ("lint step is a documented no-op that masks lint failures — ESLint gate is Phase 13's deliverable").

What ships

  • ESLint 9.39.4 flat config (eslint.config.js): typescript-eslint recommendedTypeChecked (type-aware via projectService), React + react-hooks scoped to apps/pwa, eslint-config-prettier last. ESLint pinned to 9.39.4 (10.x breaks eslint-plugin-react@7.37.5).
  • Per-app lint scripts + root format/format:check (--max-warnings 0). .planning/ excluded from Prettier (tool-generated docs).
  • CI: new Format check step in fast-checks; stale no-op comment removed.
  • 119 first-run violations fixed (address-not-mask, D-13-06) + whole-repo Prettier reformat as one isolated commit (982438d).
  • Includes quick-fix WR-01 (sw.ts notificationclick openWindow fallback) from this phase's own code review.

Verification

  • Green baseline (verified independently, post-merge with main): pnpm lint = 0, pnpm format:check = 0, pnpm typecheck = 0.
  • Tests: API 238/238, PWA 191/191. No regressions.
  • gsd-verifier: passed 9/9 must-haves. Code review of the lint fixes: clean — no floating-promise fix masked a bug.

Reviewer notes

  • The diff is large because of the one-time repo-wide Prettier reformat (commit 982438d, ~398 files) — that commit is purely mechanical and isolated; review the other commits for substance.
  • 2 SC items are live-CI only: confirm this PR's fast-checks lint + format steps actually block on a violation (push a deliberate-violation branch to see them fail). Tracked in 13-VERIFICATION.md / /gsd-verify-work 13.
  • Merged origin/main in (resolved ROADMAP.md; ci.yml auto-merged with PR #7's harness/publish fixes).
Makes the CI `fast-checks` lint step a real, failing gate (it was a no-op that exited 0 with no linter installed). Closes Phase-8 review finding **IN-02** ("lint step is a documented no-op that masks lint failures — ESLint gate is Phase 13's deliverable"). ## What ships - **ESLint 9.39.4 flat config** (`eslint.config.js`): typescript-eslint `recommendedTypeChecked` (type-aware via `projectService`), React + react-hooks scoped to `apps/pwa`, `eslint-config-prettier` last. ESLint pinned to 9.39.4 (10.x breaks eslint-plugin-react@7.37.5). - **Per-app `lint` scripts** + root `format`/`format:check` (`--max-warnings 0`). `.planning/` excluded from Prettier (tool-generated docs). - **CI**: new `Format check` step in `fast-checks`; stale no-op comment removed. - **119 first-run violations fixed** (address-not-mask, D-13-06) + whole-repo Prettier reformat as one isolated commit (`982438d`). - Includes quick-fix **WR-01** (sw.ts notificationclick openWindow fallback) from this phase's own code review. ## Verification - Green baseline (verified independently, post-merge with main): `pnpm lint` = 0, `pnpm format:check` = 0, `pnpm typecheck` = 0. - Tests: API 238/238, PWA 191/191. No regressions. - gsd-verifier: passed 9/9 must-haves. Code review of the lint fixes: clean — no floating-promise fix masked a bug. ## Reviewer notes - The diff is large because of the **one-time repo-wide Prettier reformat** (commit `982438d`, ~398 files) — that commit is purely mechanical and isolated; review the other commits for substance. - 2 SC items are **live-CI only**: confirm this PR's `fast-checks` lint + format steps actually block on a violation (push a deliberate-violation branch to see them fail). Tracked in `13-VERIFICATION.md` / `/gsd-verify-work 13`. - Merged `origin/main` in (resolved `ROADMAP.md`; `ci.yml` auto-merged with PR #7's harness/publish fixes).
luckberg added 24 commits 2026-06-11 21:26:48 -04:00
- 13-01: install ESLint v9/typescript-eslint/React/Prettier (pinned), flat config,
  package lint + root format scripts, root type:module; prove gate fails (SC-1)
- 13-02: fix all first-run violations across both apps, address-not-mask (D-13-05/06)
- 13-03: isolated Prettier reformat (D-13-08) + CI format:check step + green baseline (SC-2/SC-3)
- ROADMAP: add active Phase 13 entry + Progress row; mark backlog 999.16 promoted
- Install eslint@9.39.4 @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 as root devDependencies
- Author eslint.config.js: ignores → base recommendedTypeChecked (projectService:true)
  → pwa-react block (apps/pwa/** only) → disableTypeChecked override (config files +
  apps/api/tests) → prettier-last (eslint-config-prettier/flat)
- Add .prettierrc (singleQuote:true, printWidth:100) and .prettierignore
- Add "type":"module" to root package.json; add format/format:check scripts
- Add lint script (--max-warnings 0) to apps/api and apps/pwa
SC-1 smoke revealed flat.recommended enables react/react-in-jsx-scope (error),
while the legacy recommended disables it. This codebase uses jsx:"react-jsx"
(React 17+ automatic transform) so React does not need to be in scope. Disable
the rule explicitly alongside react/prop-types in the pwa-react config block.

Also proved both gate halves:
- pnpm lint exits 1 on no-floating-promises deliberate violation (SC-1 lint)
- pnpm format:check exits 1 listing _format-gate-test.ts (SC-1 format)
Both throwaway files deleted; neither staged nor committed.
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.
- eslint.config.js: disable React Compiler rules (v7 flat.recommended enables
  them; codebase does not use the Compiler); add e2e/ to disableTypeChecked
  block; promote exhaustive-deps to error
- API broker: remove redundant as-casts (outboxWorker, poller, reminderScheduler,
  expand, sync, vevent, spike); add targeted ical.js no-unsafe-assignment/argument
  disables with justifying comments inside try blocks
- API routes/sse.ts: fix no-misused-promises on async writeSSE callback with
  void+IIFE+catch pattern
- API routes/lists.ts: let → const for updateValues
- API tests: remove unused imports (beforeEach, eq, vi); rename unused vars
  with _ prefix; remove unused lastActiveId assignment
- PWA components: void navigate() and void queryClient.invalidateQueries() on
  all fire-and-forget call sites; fix CalendarShell explicit-type-casts;
  Couldn't → HTML entity
- PWA test files: as unknown as Response for partial mock objects; string | null
  type annotation on mockLastSyncedUid; remove async from test callbacks without
  await; act(() => {}) not await act(async () => {}) for sync ops
- sw.ts: restructure Notification.data?.url access as let+if so disable
  comments land on the exact violation lines; void self.skipWaiting()
- Replace inline 'as string | null' assertion with an explicit typed const
  declaration inside the vi.hoisted() callback body
- 'value: string | null' typed const satisfies both ESLint (no assertion) and
  tsc (null assignment on line 82 is type-safe)
Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
- Insert 'Format check: pnpm format:check' after Lint in fast-checks job
- Remove the 3-line comment that said lint was a no-op (no longer true)
- Step order: Install → Lint → Format check → Typecheck → PWA unit tests
- Branch-protection contexts (name:CI, fast-checks, api, harness) unchanged
GSD planning artifacts (.planning/**) are rewritten by tooling on every workflow
run and are never hand-formatted. Including them in Prettier scope caused
pnpm format:check to exit 1 on virtually every future PR, making the SC-3
green baseline impossible to hold. Add .planning/ to .prettierignore, mirroring
the same rationale as dist/ and apps/api/src/db/migrations/.

Real source docs (README.md, docs/**) remain in Prettier scope.
No playwright-report/, test-results/, or coverage/ directories exist to ignore.
- Add .then(navigated) check: opens new window when client.navigate() resolves null
- Add .catch(): opens new window when client.focus() or client.navigate() rejects
- Both branches guarded by self.clients.openWindow per spec
- Returned chain (not floating) satisfies no-floating-promises gate
- All other behaviour preserved: close(), url extraction, post-loop fallback
Merge remote-tracking branch 'origin/main' into gsd/phase-13-real-lint-gate-eslint
CI / fast-checks (pull_request) Failing after 1m47s
CI / api (pull_request) Successful in 1m0s
CI / harness (pull_request) Successful in 3m30s
fc5b06d343
# Conflicts:
#	.planning/ROADMAP.md
luckberg added 1 commit 2026-06-11 21:32:19 -04:00
fix(13): ignore .pnpm-store in Prettier — CI puts pnpm's store in-workspace so format:check walked into it
CI / fast-checks (pull_request) Successful in 1m23s
CI / api (pull_request) Successful in 1m4s
CI / harness (pull_request) Successful in 3m29s
0eb43c93d7
luckberg merged commit 63ae0c69d4 into main 2026-06-11 21:51:10 -04:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: luckberg/familysync#8