diff --git a/.env.example b/.env.example index 224dcecc..fd17c2d5 100644 --- a/.env.example +++ b/.env.example @@ -28,7 +28,7 @@ ALLOWED_EMAILS= # Admin emails (subset of the above) get the admin role. ADMIN_EMAILS= -# Logging (E22). LOG_LEVEL is the BOOTSTRAP level — the level a process starts at and the only one +# Logging. LOG_LEVEL is the BOOTSTRAP level — the level a process starts at and the only one # the worker/download child read; the API's running level is the admin Configuration page's # `log_level` knob, which takes effect without a restart. TRACE | DEBUG | INFO | WARNING | ERROR, in # any letter case; any other value stops the boot with an error. LOG_FORMAT is "json" (one object per diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 90c742a1..81b82d6b 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -2,8 +2,11 @@ # whole-repo reformatting does not bury the real authorship of each line. # Configure once per clone: git config blame.ignoreRevsFile .git-blame-ignore-revs # -# Prettier one-time repo-wide format pass (R2 S1b, 2026-07-21) +# Prettier one-time repo-wide format pass (2026-07-21) 51c36c2071728d2cd7ae580b7bfdf21f6867a347 # ruff format one-time backend + scripts pass (line-length 100, 2026-08-23) b1832afd180866b922521148603db447020c57a4 + +# Plan-code purge from comments, one-time (2026-10-04) +4aaaaa786017278460e0b4f94cc0d3374e0e37e2 diff --git a/.gitignore b/.gitignore index d034f201..04718ff8 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ Thumbs.db # TypeScript incremental build artifacts — machine-local, absolute-path-bearing, and no gate lane # reads or regenerates them, so a committed one is stale from the commit that adds it. *.tsbuildinfo +frontend/e2e/.speed/ diff --git a/.trivyignore.yaml b/.trivyignore.yaml index 79dbc1c5..93a89011 100644 --- a/.trivyignore.yaml +++ b/.trivyignore.yaml @@ -1,4 +1,4 @@ -# The CVE gate's allowlist (E32). The publish gate scans the built image and the committed tree with +# The CVE gate's allowlist. The publish gate scans the built image and the committed tree with # Trivy and fails on any HIGH or CRITICAL finding that has a fix available. A finding that cannot be # fixed yet is let through ONLY by an entry here, and every entry carries both: # statement — why it is acceptable for now (what is exposed, what is not) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4901bb32..ddbf2c08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ edit the release notes and re-run the generator. Upgrading, pinning a version and rolling back: [docs/self-hosting.md](docs/self-hosting.md#upgrading). +## 0.100.0 — 2026-10-05 + +### New + +- Direct messages are gone: the Messages page, the chat dock and their encryption keys were removed with their stored messages, and the privacy policy no longer describes end-to-end encrypted chat. +- The shared demo account is gone, with its sign-in entry and its whitelist. + +### Fixed + +- Sign-in security without email: when a Google sign-in takes over an account someone else registered first, or makes it an admin through the admin list, its password is cleared and its sessions end. The owner sets a new one in Settings. +- Pressing Back onto a page that no longer exists opens the feed instead of breaking the app. +- Behind the scenes: tidier checks and comments across the code. + ## 0.99.2 — 2026-10-04 ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index 99230748..87ef68f4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,15 +13,15 @@ router-free navigation provider. ## The gate — never merge or ship red The local gate is **ruff (`check` + `format --check`) + basedpyright (backend) + the lock check + typecheck (3 tsc projects) + -eslint + prettier --check + knip (both modes) + vitest + vite build + pytest + pip-audit + npm audit**, then a coverage report (no threshold). It runs +eslint + prettier --check + knip + vitest + vite build + pytest + pip-audit + npm audit**. It runs before every `release` merges and on every `publish`, which **refuses to build on failure — there is deliberately no `--skip` flag**; a release also requires the Playwright suite, and the image must pass a Trivy scan before it is pushed. Run it and get it green **before** any merge, release, or handing work to review. - Full gate: `bash scripts/check.sh` (`lint` for the static lanes only); the private ops dispatcher's `siftlode check` runs the same script. Single lanes: [`docs/development.md`](docs/development.md) § The gate. - **There is no CI** (owner, 2026-10-04): this gate, run here, is the only one — green before every hand-over, and `release` refuses to merge without it. -- **A gate with any real hit is never switched off or narrowed** — not a lint rule, a compiler flag, a lane or a release step. Fix the sites, or suppress each one inline naming its invariant (`-- why`, lint-enforced). What stays narrowed is in [`docs/gates.md`](docs/gates.md), and a contract test fails when a narrowing is missing from it. The effective ESLint config of every file and the gate/lane lines are pinned in `frontend/src/lib/gates.snapshot.json`: a new code file or any gate change fails until you regenerate it (`GATE_SNAPSHOT=update`, see gates.md) — then read that diff as a gate change. A comment saying "X is enforced by Y" is checked by running Y (E32: one such comment hid 95 functions the React Compiler skipped). -- **The backend type lane runs in the test image, not on the host** — `requirements.lock` cannot be installed on Windows (uvloop), and a type checker without the real packages reports nonsense. It fails on NEW errors only; the pre-existing ones sit in `backend/.basedpyright/baseline.json` and burn down epic by epic. Fixed some? Regenerate with `--writebaseline`. +- **A gate with any real hit is never switched off or narrowed** — not a lint rule, a compiler flag, a lane or a release step. Fix the sites, or suppress each one inline naming its invariant (`-- why`, lint-enforced). What stays narrowed is in [`docs/gates.md`](docs/gates.md), and a contract test fails when a narrowing is missing from it. The effective ESLint config of every scope and the gate/lane lines are pinned in `frontend/src/lib/gates.snapshot.json`: any gate change fails until you regenerate it (`GATE_SNAPSHOT=update`, see gates.md) — then read that diff as a gate change. A comment saying "X is enforced by Y" is checked by running Y (E32: one such comment hid 95 functions the React Compiler skipped). +- **The backend type lane runs in the test image, not on the host** — `requirements.lock` cannot be installed on Windows (uvloop), and a type checker without the real packages reports nonsense. It fails on NEW errors only; the pre-existing ones sit in `backend/.basedpyright/baseline.json` (no burn-down mandate, but none was ever triaged: a `reportOptional*` entry in code you touch may be a real bug). A run that fixed some rewrites it — commit that. - New DB-backed test? It must actually run, not silently skip — assume `SIFTLODE_TEST_REQUIRE_DB=1`. ## Workflow @@ -63,7 +63,7 @@ how it gets built. Concretely: `routes/*` (HTTP only: validation, auth-dependency, serialization) → domain packages `sync/`, `plex/`, `downloads/`, `youtube/`, `issues/` (shared by routes + scheduler + worker) → flat core (`auth`, `quota`, -`sysconfig`, `state`, `audit`, `notifications`, `userscope`, `realtime`, `ratelimit`, `bodylimit`, `progress`, `collation`, +`sysconfig`, `state`, `audit`, `notifications`, `userscope`, `ratelimit`, `bodylimit`, `progress`, `collation`, `security`, `logs`, `logsink`, `diagnostics`, `debugsession`) → `models/db/config`. **Dependency direction is one-way: core never imports routes.** Frontend: `main.tsx` provider tree → `App.tsx` shell → `moduleRegistry` route-chunks → module components → the **platform layer `components/ui/`** (domain-free primitives: the popover engine and @@ -95,17 +95,12 @@ CQRS, a repository/DI framework, a router library, a generic CRUD-router factory | Job status word | the `downloads/status.py` StrEnum | loose `"canceled"`/`"cancelled"` literals | | Current time | inline `datetime.now(timezone.utc)` | new `now_utc`/`_now` helpers | | FE per-account state | `usePref` | raw `readAccount`/`writeAccount` or bespoke hooks | -| FE button | `ui/Button` (a `SIZES` rung) · `IconButton` · `Pressable` (a clickable card/row) · `Chip` · `DragHandle` | a raw `