diff --git a/.planning/phases/13-real-lint-gate-eslint/13-CONTEXT.md b/.planning/phases/13-real-lint-gate-eslint/13-CONTEXT.md
new file mode 100644
index 0000000..ca13718
--- /dev/null
+++ b/.planning/phases/13-real-lint-gate-eslint/13-CONTEXT.md
@@ -0,0 +1,114 @@
+# Phase 13: Real Lint Gate (ESLint) - Context
+
+**Gathered:** 2026-06-11
+**Status:** Ready for planning
+
+
+## Phase Boundary
+
+Stand up a real ESLint flat config across both workspaces (`apps/api` — NodeNext ESM Node/TS; `apps/pwa` — React 19 + Bundler ESM), plus package-level `lint` scripts, so the existing CI step `pnpm lint` actually fails on violations instead of exiting 0 as a no-op.
+
+**Scope expanded during discussion (flag for planning):** This phase now also adds **Prettier** with a standalone `prettier --check` CI format gate. The original ROADMAP Phase 13 one-liner scopes to "ESLint only" — planning MUST update the ROADMAP entry and success criteria to include the format gate. Treated as an in-domain expansion (still a CI quality gate; CLAUDE.md's stack table lists "ESLint + Prettier"), not a separate phase.
+
+**In scope:**
+- ESLint flat config (`eslint.config.js`) — typescript-eslint + React + react-hooks plugins
+- Package-level `lint` scripts in `apps/api` and `apps/pwa` (so root `pnpm -r --if-present lint` runs a real linter)
+- Prettier config + root `format` / `format:check` scripts
+- A new `format:check` CI step in the `fast-checks` job
+- Fixing all first-run violations so `pnpm lint` AND `pnpm format:check` are green across both apps
+
+**Out of scope:**
+- CI lint-step plumbing — `.gitea/workflows/ci.yml` already runs `pnpm lint`; the slot auto-activates once package `lint` scripts exist. Only the NEW `format:check` step is added.
+- Desktop E2E coverage (Phase 14), any non-lint CI changes.
+
+
+
+## Implementation Decisions
+
+### Ruleset & strictness
+- **D-13-01:** Use typescript-eslint **`recommendedTypeChecked`** (type-aware), not the non-type-aware `recommended`. Rationale: this is an async-heavy backend (outbox drain, push dispatch, CalDAV/reminder schedulers) — type-aware rules catch floating promises, `no-misused-promises`, and unsafe `any` that syntactic linting misses. Enable via `projectService: true` (resolves all tsconfigs automatically).
+- **D-13-02:** Add React + react-hooks plugins for `apps/pwa` (per ROADMAP goal). `apps/api` is Node/TS only (no React config).
+- **D-13-03:** Do NOT adopt `strict`/`strictTypeChecked` presets — too much churn on the existing 91-file codebase; CLAUDE.md warns against bikeshedding.
+
+### Gate threshold
+- **D-13-04:** Run with **`--max-warnings 0`** — any warning fails CI. Every rule must be a real decision: either error-worthy or off. No non-blocking warnings (they rot into ignored noise).
+
+### First-run violation strategy
+- **D-13-05:** **Fix all violations now.** The phase is not done until `pnpm lint` and `pnpm format:check` are green across both apps. Real bugs (floating promises, misused promises) get genuinely fixed.
+- **D-13-06 (HARD CONSTRAINT — for executors):** Fixes must **address** the violation, not mask it. No blanket `eslint-disable` and no `void promise` to silence a floating-promise that should actually be `await`ed. Any suppression (`eslint-disable-next-line`) requires a justifying inline comment explaining why the rule is wrong *here*. A type-aware lint finding is a candidate bug — review before suppressing.
+
+### Prettier
+- **D-13-07:** Add **Prettier + standalone `prettier --check`** as its own CI step (separate from lint), AND add **`eslint-config-prettier`** to the flat config to disable ESLint's formatting rules. Clean separation: ESLint finds bugs, Prettier owns formatting, no double-reporting. (Rejected: `eslint-plugin-prettier` — slower, noisier, discouraged by Prettier docs.)
+- **D-13-08:** All files get reformatted in this phase — accepted as one large mechanical diff. Planning should consider isolating the reformat commit from logic fixes for reviewability.
+
+### File coverage
+- **D-13-09:** Lint **all TS/TSX**: app `src/`, vitest tests, Playwright e2e specs (`apps/pwa` uses `tsconfig.e2e.json`), and config files (`vite.config`, `drizzle.config`, `playwright.config`).
+- **D-13-10:** Config files / non-project files that `projectService` can't type-check need a dedicated override block (non-type-checked rules, or `disableTypeChecked` for those globs) so type-aware linting doesn't error on them.
+
+### Claude's Discretion
+- Flat-config file layout (single root `eslint.config.js` vs per-app configs) — planner/researcher decides; root config with per-package overrides is the common pattern for a small 2-app pnpm workspace.
+- Exact Prettier options (`.prettierrc`) — standard defaults; no bikeshedding.
+- CI step ordering within `fast-checks` (lint → format:check → typecheck → tests).
+
+
+
+
+## Canonical References
+
+**Downstream agents MUST read these before planning or implementing.**
+
+### CI integration (the slot this phase fills)
+- `.gitea/workflows/ci.yml` — the `fast-checks` job runs `pnpm lint` (currently a no-op; see the inline comment at the Lint step). This phase makes it real and adds a `format:check` step. No other CI plumbing change.
+- `package.json` (root) — `lint: pnpm -r --if-present lint`, `typecheck: pnpm -r typecheck`. Add root `format` / `format:check`.
+
+### Workspace / TS config (constrains the flat config)
+- `apps/api/tsconfig.json` — NodeNext ESM, `target ES2023`, `strict`, excludes `tests`. `type: module`.
+- `apps/pwa/tsconfig.json` — ESNext / Bundler resolution, `jsx: react-jsx`, `strict`, `noEmit`. `type: module`.
+- `apps/pwa/tsconfig.e2e.json` — separate project for Playwright specs; must be in the lint projectService set for type-aware linting of e2e tests.
+- `pnpm-workspace.yaml` — packages: `apps/*`.
+
+### Stack guidance
+- `CLAUDE.md` (Development Tools table) — "ESLint + Prettier | Standard config; no bikeshedding needed." TypeScript 5.x `strict: true`.
+
+No external ADRs/specs specific to linting — requirements fully captured in decisions above.
+
+
+
+
+## Existing Code Insights
+
+### Reusable Assets
+- None to reuse — greenfield lint config. No ESLint or Prettier anywhere in the repo today (confirmed: no eslint dep in any `package.json`, no config files).
+
+### Established Patterns
+- Both apps are `type: module` ESM → flat config file must be `eslint.config.js` (ESM) or `.mjs`.
+- API and PWA each already have a `typecheck` script (`tsc --noEmit`) wired into the root `pnpm -r typecheck` — mirror that wiring style for the new package-level `lint` scripts.
+- Type-aware linting needs every linted file resolvable by a tsconfig project. PWA has TWO tsconfigs (`tsconfig.json` + `tsconfig.e2e.json`) — `projectService: true` handles multi-project resolution.
+
+### Integration Points
+- `.gitea/workflows/ci.yml` `fast-checks` job — `pnpm lint` step already present (activates automatically); ADD a `pnpm format:check` step.
+- File volume for the first-run fix pass: `apps/api/src` = 30 `.ts` files; `apps/pwa/src` = 61 `.ts/.tsx` files, plus tests, e2e specs, and config files.
+
+
+
+
+## Specific Ideas
+
+- Type-aware ruleset is specifically motivated by the async surfaces in this codebase: `apps/api/src/broker/` (outbox worker, poller, reminder scheduler — all recently bug-prone per STATE.md: node-cron→setInterval, reminder catch-up). Floating-promise / misused-promise detection here has concrete bug-catching value, not just style.
+
+
+
+
+## Deferred Ideas
+
+None — discussion stayed within phase scope (Prettier was folded IN as an accepted scope expansion, not deferred).
+
+### Reviewed Todos (not folded)
+- **Gitea CI — full regression on PR to main + build/publish Docker image** (`2026-06-10-gitea-ci-regression-and-docker-publish.md`, score 0.6) — reviewed but NOT folded: this is the Phase 8 CI work, already complete and merged to `main`. Matched only on shared keywords (CI/apps/api). Not in Phase 13 scope.
+
+
+
+---
+
+*Phase: 13-real-lint-gate-eslint*
+*Context gathered: 2026-06-11*
diff --git a/.planning/phases/13-real-lint-gate-eslint/13-DISCUSSION-LOG.md b/.planning/phases/13-real-lint-gate-eslint/13-DISCUSSION-LOG.md
new file mode 100644
index 0000000..6757997
--- /dev/null
+++ b/.planning/phases/13-real-lint-gate-eslint/13-DISCUSSION-LOG.md
@@ -0,0 +1,91 @@
+# Phase 13: Real Lint Gate (ESLint) - Discussion Log
+
+> **Audit trail only.** Do not use as input to planning, research, or execution agents.
+> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
+
+**Date:** 2026-06-11
+**Phase:** 13-real-lint-gate-eslint
+**Areas discussed:** Rule strictness, Existing-violation strategy, Prettier scope, Lint file coverage
+
+---
+
+## Rule Strictness — ruleset
+
+| Option | Description | Selected |
+|--------|-------------|----------|
+| `recommended` (non-type-aware) | Syntactic only, fast, no tsconfig wiring; misses async/promise bugs | |
+| `recommendedTypeChecked` | Type-aware; catches floating/misused promises + unsafe any; needs projectService, slower, more first-run violations | ✓ |
+| `strict`/`strictTypeChecked` | Maximal rules; heavy churn + bikeshed risk | |
+
+**User's choice:** `recommendedTypeChecked`
+**Notes:** Motivated by async-heavy backend (outbox/push/reminder schedulers). projectService: true.
+
+## Rule Strictness — gate threshold
+
+| Option | Description | Selected |
+|--------|-------------|----------|
+| `--max-warnings 0` | Any warning fails CI; every rule is error-or-off | ✓ |
+| Errors only | Warnings surface but don't fail; softer rollout, accumulation risk | |
+| You decide | Pick based on first-run count | |
+
+**User's choice:** `--max-warnings 0`
+
+---
+
+## Existing-Violation Strategy
+
+| Option | Description | Selected |
+|--------|-------------|----------|
+| Fix all violations now | Phase green across both apps; real bugs fixed; larger phase | ✓ |
+| Minimal green ruleset, ratchet later | Enable only passing rules; small phase, weaker gate, deferred work | |
+| Baseline file (snapshot + ratchet) | Snapshot current violations as accepted; new-only fails; extra tooling | |
+
+**User's choice:** Fix all violations now
+**Notes:** Hard constraint added (D-13-06): fixes must address the violation, not mask it — no blanket eslint-disable, no `void` to silence floating promises; suppressions need justifying comments.
+
+---
+
+## Prettier Scope
+
+| Option | Description | Selected |
+|--------|-------------|----------|
+| `eslint-config-prettier` only | Disable conflicting format rules, no Prettier itself; phase stays ESLint-only | |
+| Add Prettier + format gate too | Full lint+format; scope creep beyond goal; reformats all files now | ✓ |
+| Defer Prettier entirely | ESLint only, no config-prettier; future conflict risk | |
+
+**User's choice:** Add Prettier + format gate too
+**Notes:** Expands ROADMAP Phase 13 goal ("ESLint only") — flagged for planning to update ROADMAP one-liner + success criteria. Follow-up decided wiring: standalone `prettier --check` + eslint-config-prettier (rejected eslint-plugin-prettier).
+
+### Prettier wiring (follow-up)
+
+| Option | Description | Selected |
+|--------|-------------|----------|
+| Standalone + config-prettier | Separate `prettier --check` CI step + eslint-config-prettier off-switch; clean separation | ✓ |
+| eslint-plugin-prettier | Prettier as an ESLint rule; one gate, slower/noisier, discouraged | |
+
+**User's choice:** Standalone + eslint-config-prettier
+
+---
+
+## Lint File Coverage
+
+| Option | Description | Selected |
+|--------|-------------|----------|
+| All TS/TSX: src + tests + e2e + configs | Most thorough; needs projectService + config-file override; more first-run fixes | ✓ |
+| src/ + tests, skip configs | Simpler, configs unchecked | |
+| src/ only | Smallest scope; tests + e2e harness + configs unlinted | |
+
+**User's choice:** All TS/TSX (src + tests + e2e + configs)
+**Notes:** Config files need a non-type-checked override block (D-13-10).
+
+---
+
+## Claude's Discretion
+
+- Flat-config file layout (single root vs per-app)
+- Exact `.prettierrc` options (standard defaults)
+- CI step ordering within `fast-checks`
+
+## Deferred Ideas
+
+None — Prettier was folded into scope, not deferred. Reviewed-but-not-folded: the Phase 8 Gitea-CI todo (already complete, matched on shared keywords only).