docs(plan): shape Phase 11 — Public Site Enhancements

Add Phase 11 to PLAN.md and a full design spec under product-notes:
Cuts gain a /cuts/{id} album detail page; release-title click resolves
medium to a dedicated detail page; redundant /tracks?album view retired;
Archive filters move into the URL. Includes gap analysis and open
questions for Daniel.
This commit is contained in:
daniel-c-harvey
2026-06-15 23:09:16 -04:00
parent e0f371cda6
commit 913861860b
2 changed files with 574 additions and 0 deletions
+23
View File
@@ -192,6 +192,29 @@ Full design, renderer architecture, the four effects, acceptance criteria, and p
---
## Phase 11 — Public Site Enhancements
The next pass over the public listening surface, after Phase 9 + Wave 8 moved the site to release-cardinal browse (`/archive`) and per-medium detail. The spine of the phase: **make the release the cardinal unit of every public navigation, and make every navigation an addressable, shareable URL.** Four Daniel commitments — two structural (a Cuts detail page; the release-title click resolves medium → dedicated detail page), two normalization/polish (retire the redundant `/tracks?album` view; encode Archive filters in the URL). Full design, framing corrections, wave decomposition, and the gap analysis: `product-notes/phase-11-public-site-enhancements.md`.
**State it inherits (verified 2026-06-15).** `/sessions/{id}` and `/mixes/{id}` detail pages **exist and are mature** (both inherit `ReleaseDetailBase`'s prerender bridge; `MixDetail` composes `ReleaseDetailScaffold`, `SessionDetail` deliberately diverges). `/archive` is **already** a release-cardinal searchable browser (search + medium + genre). The two real gaps: **Cuts have no single-release detail page** (`/cuts` cards open `/tracks?album={title}` — the track gallery filtered), and **`/archive` holds its filters in component fields, not the URL**.
**Three framing corrections (brief vocabulary vs. live routes).** (1) There is **no `/tracks/{id}` route** — the track-cardinal detail is `/track/{EntryKey}`, and the player-bar title links there. The brief's "`/tracks/{id}` becomes a router" is best realized as a **medium→route resolver** applied at click sites (the player bar already carries the release id + medium, so no round-trip is needed), plus a thin `/tracks/{id}` redirect page for bare-release-id deep links. (2) The new `/cuts/{id}` album page is the phase's center of gravity — the first **multi-track** release detail. (3) Requirement 4 is a **URL-binding pass over the existing `ArchiveView`**, borrowing the `TracksView` `[SupplyParameterFromQuery]` pattern verbatim — not a new browser.
**Design discipline.** The medium→route resolver is **one table** (promote `ArchiveView.DetailHref` to a shared `ReleaseRoutes` helper) consumed by the player bar, Archive cards, and Cuts cards — not three switches (memory *One source, multiple views*). The `/cuts/{id}` page composes `ReleaseDetailScaffold` via a generalized `Header` slot + new `BodyContent` slot for the track list — **not** a boolean layout flag (Phase 9 §5.3's named rule: layout variance rides a slot, never a flag). The Archive URL-binding makes the URL the source of truth (history-driven re-fetch in `OnParametersSet`), so back/forward + shareable links fall out structurally.
Sequenced as four tracks across two parallel waves.
- **11.A — `/cuts/{id}` album-detail page.** Left header (name, artist, genre, year, Play + Share), right cover with theme border, ordered track list with per-row play, header Play → track 1. New `CutDetailViewModel`; reuses `GetById` + the existing `releaseId`-filtered track page (both exist). **Load-bearing prerequisite for 11.B's Cut resolution.**
- **11.B — medium→route resolver + repoint.** Shared `ReleaseRoutes.DetailHref`; Cut resolves to `/cuts/{id}` (needs 11.A); repoint player-bar title, Archive cards, `AlbumsView` cards; thin `/tracks/{id}` redirect page. **Depends on 11.A.**
- **11.C — normalization / reduction.** Retire the `/tracks?album` filter branch (safe once 11.B repoints both consumers). Decide with Daniel whether to retire the whole track-cardinal stack (`TracksView`/`TrackDetail`/`TrackCard`/`TracksGallery`) now that Cuts have a proper detail page and `/tracks` is already nav-demoted — the phase's biggest reduction lever. Consolidate duplicate medium-label lookups. **Depends on 11.B.**
- **11.D — Archive filters in the URL.** `/archive?q=&medium=&genre=`. Fully independent — touches only `ArchiveView`. **No dependency; free-floating.**
**Dependency shape:** `11.A → 11.B → 11.C`; **11.D parallel**.
**Open questions (Daniel — in the spec §7):** player-bar title now points at release detail not track detail (recommend yes — it is the premise of req 1); `/cuts/{id}` scaffold strategy (generalized `Header` slot vs. bespoke page); Cut header affordance idiom (icon vs. labeled buttons); **track ordering**`TrackEntity` has no `TrackNumber` column, so album order is insertion/`Id` order today (recommend ship on insertion order, raise `TrackNumber` as its own small phase only if needed — `[do not assume into Phase 11]`); `/tracks` retirement scope (album-branch only vs. whole track-cardinal stack). **Adjacent gaps surfaced, not in scope:** the Cuts album page is the strongest case yet for the deferred **play queue** (`§1.3`) — "play album" is the expected affordance; design header Play so a future enqueue-album is a handler swap, not a rewrite. `SharePopover` is track-keyed — a Cut header Share arguably shares the **release** URL, a new share target.
---
## Working with this file
- **Add items by extending an existing phase first**; only create a new phase when the addition genuinely doesn't fit any of 15. Phase numbers are organisational, not sequencing.