# 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.
