From f89b241ad64d4f66339267a773796113363a8af5 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Thu, 23 Jul 2026 20:04:54 +0200 Subject: [PATCH] docs: document frontend/backend boundary rule and spec workflow Add the "domain vocabulary lives in Rust" principle, the check:boundary pre-commit gate, and a spec-writing section pointing at the spec template and review checklist. --- CLAUDE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 5d679c39..13b5b6cc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,6 +39,8 @@ only against the mirror if one exists; the canonical remote is - Frontend: `bun run check` and `bun run test` must pass. - Rust: `cd src-tauri && cargo fmt` then `cargo clippy`, plus `bun run test:rust`. +- **Boundary**: `bun run check:boundary` must pass — no domain taxonomy (Jellyfin + item-type category sets) leaked into the frontend. See below. - **Traceability**: new requirement-implementing code must carry a `// TRACES:` comment (see below). - **Android source edits**: edit `src-tauri/android/src` (the canonical tree), @@ -161,6 +163,25 @@ and [docs/build-release.md](docs/build-release.md). - **Graceful backend init.** If a native player backend fails to initialize, the app falls back to a no-op backend and emits `backend-init-failed` rather than crashing. +- **Domain vocabulary lives in Rust.** The frontend is presentation-only and must + not encode Jellyfin's *taxonomy* — e.g. the set of item types that defines a + category like "Music". Send an opaque scope/enum across the boundary and let the + backend expand it. Single-type presentation (`itemType: "Movie"`, "this page + shows albums") is fine; a *category → set of types* mapping in `src/` is a leak. + `bun run check:boundary` is the tripwire; the real gate is the spec's layer + assignment. See [scoped-search-boundary.md](docs/specs/scoped-search-boundary.md) + for the incident this rule came from. + +## Writing specs + +New feature specs go in [docs/specs/](docs/specs/). **Start from +[SPEC-TEMPLATE.md](docs/specs/SPEC-TEMPLATE.md)** — its "Layer assignment" section +forces each piece of *logic* to be placed in the correct layer (Rust = domain, +frontend = presentation) *with a reason*, which is what prevents boundary leaks. +Before accepting a spec, run it past +[SPEC-REVIEW-CHECKLIST.md](docs/specs/SPEC-REVIEW-CHECKLIST.md). Do **not** frame +a spec around "no Rust changes required" — correct layer placement is the goal, +not minimal backend churn. ## Conventions