# Spec review checklist Run a spec past this before accepting it. It exists because JellyTau's backend/frontend boundary is a **stated rule with, historically, no gate** — the rule lived in the architecture docs, but nothing forced a spec author to check a new design against it, and a "minimal-change" spec quietly leaked domain taxonomy into the frontend (see [scoped-search-boundary.md](scoped-search-boundary.md)). This checklist is the human gate. The CI check (`scripts/check-frontend-boundary.sh`) is only a crude tripwire for one leak signature — it does **not** replace this. Copy the boxes into the review comment (or the PR) and tick them. ## Boundary (the one that bites) - [ ] **The spec has a filled-in "Layer assignment" table**, and it assigns *logic*, not files. A spec without this section is not ready to review. - [ ] **No domain vocabulary is placed in the frontend.** In particular: Jellyfin item-type sets that define a *category* (what "Music"/"TV"/"Movies" means), query-shaping rules, business rules, reachability/sync policy. If the frontend names a *set* of item types to define a category, that is a leak — it belongs behind an opaque enum the backend expands. - [ ] **"The backend already accepts this parameter" was not used as the reason** to place the deciding logic in the frontend. Accepting a parameter ≠ owning the decision of its value. - [ ] **The `Scope:` / effort framing is not optimizing for "least backend change."** "Frontend only, no Rust changes" is a description, never a goal. The goal is *correct layer placement*; sometimes that is more Rust work. - [ ] Ran the litmus test on each borderline responsibility: *would it change if Jellyfin's API changed?* → Rust. *Only if the UI were redesigned?* → frontend. Borderline defaults to Rust. - [ ] Single-type presentation (`itemType: "Movie"`, "this page shows albums") is **not** over-corrected into the backend. The rule targets category *taxonomy*, not every mention of a type. Don't invent a backend enum per list page. ## IPC contract - [ ] Anything crossing the boundary has its wire shape specified. - [ ] camelCase rule accounted for: top-level params auto-convert; nested structs get `#[serde(rename_all = "camelCase")]`; tagged unions match tags on both sides; events are kebab-case. (CLAUDE.md §IPC, [04-type-sync-and-threading.md](../architecture/04-type-sync-and-threading.md).) - [ ] Any result that arrives *twice* (command return **and** a later event — e.g. the search cache/server merge) has **both** payloads in the new shape. - [ ] `bindings.ts` is regenerated from Rust, not hand-edited. ## Requirements & traceability - [ ] Linked to existing URs, or new URs/DRs are allocated in [requirements.md](../requirements.md). - [ ] Requirement-implementing code will carry `// TRACES:` comments (CLAUDE.md). - [ ] Traceability coverage stays ≥ 50% (the CI gate). ## Conflicts & hygiene - [ ] If this spec revises/supersedes another, the older spec gets a banner pointing here — no two specs silently contradicting. - [ ] Acceptance criteria include the standard gates: `bun run check`, `bun run test`, `bun run check:boundary`, and (if Rust changed) `cargo fmt`/`cargo clippy`/`bun run test:rust`. - [ ] Notes flag that a parallel Claude session may be active in the repo. --- **If any Boundary box can't be ticked, the spec is not ready** — fix the layer assignment first. Every other section can be negotiated; that one is the whole reason this file exists.