diff --git a/PLAN.md b/PLAN.md index 7f0af06..9317a1e 100644 --- a/PLAN.md +++ b/PLAN.md @@ -171,7 +171,7 @@ Daniel tested the landed Phase 9 surface end-to-end and produced a punch-list of Two surfaces dominate: the **CMS Release Archive** (the card-grid landing is the wrong shape — Daniel wants medium *tabs*, not navigate-away cards) and the **public Archive** (the three-card overview is dead weight; the searchable all-**releases** view *is* the archive — release-cardinal, decided). The **Mix Visualizer redesign (8.K)** is **pulled out of Phase-9-completion scope** — Phase 9 closes without it — but is now **documented in full**: the interview ran and `product-notes/phase-9-mix-visualizer-redesign.md` is a finished, implementation-ready design spec for a post-Phase-9 wave. -**Open questions resolved (Daniel, 2026-06-13):** 8.H is decided **H2** (a new release-cardinal searchable browser at `/archive`; cascade: `/tracks` demoted from nav, route kept; mobile ARCHIVE → the browser; three-card overview fully retired); 8.I drops GENRES from the nav only (route kept); 8.F makes the Session hero optional-but-warn-if-missing; 8.E defaults the `ALL`-tab Add Track to Cut with the medium selector staying user-changeable. A new track **8.L** consolidates the release-name/track-name pair into a single name for single-track media. +**Open questions resolved (Daniel, 2026-06-13):** 8.H is decided **H2** (a new release-cardinal searchable browser at `/archive`; cascade: `/tracks` demoted from nav, route kept; mobile ARCHIVE → the browser; three-card overview fully retired); 8.I drops GENRES from the nav only (route kept); 8.F makes the Session hero optional-but-warn-if-missing; 8.E defaults the `ALL`-tab Add Track to Cut with the medium selector staying user-changeable. A new track **8.L** consolidates the release-name/track-name pair into a single name for single-track media (derived track name **kept synced**, decided), and **8.M** (split off 8.L) retires the legacy `TrackNew`/`TrackEdit` forms by folding them into the batch forms to reduce code surface. Full track decomposition, acceptance criteria, and parallel/dependent analysis: `product-notes/phase-9-wave-8-remediation.md`. The tracks in brief: @@ -183,7 +183,8 @@ Full track decomposition, acceptance criteria, and parallel/dependent analysis: - **8.E — Add-Track buttons in all modes, medium-aware routing.** Every tab surfaces an Add Track button routing to the upload page pre-set to that tab's medium. The `ALL`-tab Add Track defaults to **Cut**; the medium selector stays user-changeable after landing on the form. *(Depends on 8.A.)* - **8.F — Session hero image in the upload form (retire the two-step).** Compose the hero-image field into the Session upload form so a Session is authored in one pass; remove the "set it later from the browser" alert. Hero is **optional but warns if missing** (no hard gate). *(Independent of the tab work; touches the upload form + the resource-addressed hero endpoint ordering — see note.)* - **8.G — "Album Name" → "Release Name" label.** Rename the `AlbumHeaderFields` label. *(Independent, trivial. Sequence before 8.L.)* -- **8.L — Consolidate release name + track name for single-track releases.** For Session/Mix the form presents **one** name field (Release Name); the underlying track name is derived from it (recommended: kept synced on create and edit so they never diverge). Cuts (multi-track) are unaffected. Blast radius (discovery done): CMS `BatchUpload` single-track branch, `BatchEdit` via `BatchTrackDetail`, and the legacy `TrackNew`/`TrackEdit` forms *if still live*; the public detail/gallery views already key off the release title only (no public work). *(Pairs with 8.G/8.E/8.F — same upload/edit forms.)* +- **8.L — Consolidate release name + track name for single-track releases.** For Session/Mix the form presents **one** name field (Release Name); the underlying track name is derived from it (**decided: kept synced on create and edit** so they never diverge — Daniel, 2026-06-13). Cuts (multi-track) are unaffected. Blast radius (discovery done): CMS `BatchUpload` single-track branch and `BatchEdit` via `BatchTrackDetail` — **batch forms only**. The legacy `TrackNew`/`TrackEdit` forms are **not** patched here; their consolidation is **8.M**. The public detail/gallery views already key off the release title only (no public work). *(Pairs with 8.G/8.E/8.F — same upload/edit forms. Independent of 8.M.)* +- **8.M — Retire the legacy single-track forms; consolidate onto the batch forms.** Fold `TrackNew` (`/tracks/new`) and `TrackEdit` (`/tracks/{Id:long}`) into `BatchUpload`/`BatchEdit`'s single-track branch and retire them, reducing duplicate form surface (Daniel, 2026-06-13: "consolidate the forms and reduce the code surface"). **Feasibility: retirement-with-reconciliation.** `TrackNew` is a clean retirement (no live inbound link — every Add-Track points at `/tracks/upload`). `TrackEdit` requires reconciling an **addressing-model gap**: it is addressed by *track id* while `BatchEdit` is addressed by *release title* and loads the whole release — so the single-Cut-track edit from Track mode's per-row Edit (`CmsTrackGrid` → `/tracks/{id}`, the one live inbound link) needs a decision (open the parent release, or address a single track within the batch edit). **Architectural — staff-engineer scope** (route-map change + two component removals + navigation-model decision). Not a Phase-9-completion gate; sequence after 8.L. *(Independent of 8.L for build.)* **Public site (`DeepDrftPublic.Client`):** - **8.H — Archive page becomes the searchable all-releases browser (release-cardinal, decided H2).** Build a new release-cardinal searchable browser at `/archive` (search + medium/genre filter, cards → per-medium detail); retire the three-card overview on every breakpoint. Cascade: `/tracks` (`TracksView`) is demoted from the nav (route kept reachable); mobile ARCHIVE → the new browser. *(Gates 8.I.)* @@ -193,7 +194,7 @@ Full track decomposition, acceptance criteria, and parallel/dependent analysis: **Mix Visualizer — out of Phase-9-completion scope:** - **8.K — Mix Visualizer redesign. `[post-Phase-9, design-complete]`.** A windowed, playback-coupled, bottom-to-top scrolling waveform showing the currently-playing region; zoom couples to apparent scroll speed (Guitar-Hero model, anchored at 1 quarter note @ 180 BPM = 333 ms visible at max zoom); lava-lamp aesthetic (theme-aware gradients, glassy), **strictly read-only**; standard Canvas/WebGL, no tricks, well-commented. The datum analysis recommends switching the Mix loudness profile from a fixed 2048 buckets to **constant-time-resolution capture (~333 samples/sec)** so long mixes aren't under-sampled at max zoom. **Phase 9 closes without this**; it runs as a post-Phase-9 wave, dispatchable straight from the finished spec: `product-notes/phase-9-mix-visualizer-redesign.md`. -**Dependency shape:** 8.B is the foundation for the CMS tab work (8.A consumes the shared grid; 8.C/8.E layer on once 8.A lands). 8.D, 8.G are independent and parallelizable immediately; 8.L follows 8.G and coordinates with 8.E/8.F (same forms). On the public side, 8.J is an independent bug fix; 8.H (decided H2 — the new release-cardinal archive) gates 8.I. **Phase 9 completion = 8.A–8.J + 8.L landed; 8.K is explicitly excluded** and runs as a separate post-Phase-9 wave. +**Dependency shape:** 8.B is the foundation for the CMS tab work (8.A consumes the shared grid; 8.C/8.E layer on once 8.A lands). 8.D, 8.G are independent and parallelizable immediately; 8.L follows 8.G and coordinates with 8.E/8.F (same forms). 8.M (legacy-form retirement) follows 8.L and is architectural (route map + addressing decision). On the public side, 8.J is an independent bug fix; 8.H (decided H2 — the new release-cardinal archive) gates 8.I. **Phase 9 completion = 8.A–8.J + 8.L landed; 8.K and 8.M are excluded from the completion gate** (8.K is a post-Phase-9 design-complete wave; 8.M is a code-surface-reduction follow-on that can trail). ## Working with this file diff --git a/product-notes/phase-9-wave-8-remediation.md b/product-notes/phase-9-wave-8-remediation.md index dc967bb..4a5d383 100644 --- a/product-notes/phase-9-wave-8-remediation.md +++ b/product-notes/phase-9-wave-8-remediation.md @@ -13,8 +13,9 @@ memory *One source, multiple views*. **Open questions are resolved (2026-06-13).** Daniel answered the whole decision list in §6 and ran the 8.K interview. This doc no longer carries forks: 8.H is decided (H2 — a release-cardinal all-releases browser at `/archive`), 8.I/8.F/8.E defaults are baked into their acceptance criteria, a -new consolidation track **8.L** is added, and **8.K is moved out of Phase-9-completion scope** to a -finished post-Phase-9 design doc. Phase 9 can close without 8.K. +new consolidation track **8.L** is added (with **8.M** split off it for the legacy-form retirement), and +**8.K is moved out of Phase-9-completion scope** to a finished post-Phase-9 design doc. Phase 9 can close +without 8.K or 8.M. --- @@ -334,13 +335,12 @@ confusing authoring experience. Cuts are different — a Cut release legitimatel distinct from its per-track names ("Charleston EP" → tracks "Battery", "Rainbow Row") — and are **unaffected** by this track. -**Recommendation — keep them synced for single-track media (Daniel to confirm posture; this is the -recommended default).** On both create and edit of a single-track release, the underlying track name is -**set equal to the Release Name on save, and kept in sync** so they can never diverge. The admin never -sees or touches the track name for Session/Mix. On edit, changing the Release Name updates the track -name with it. This is the simplest model and the one that honors "there is only one name": the track -name is a *derived field*, not an independent one. (The alternative — let them diverge once set — would -reintroduce exactly the two-name confusion this track removes. Rejected.) +**Sync posture — DECIDED (Daniel, 2026-06-13): keep them synced.** On both create and edit of a +single-track release, the underlying track name is **set equal to the Release Name on save, and kept in +sync** so they can never diverge. The admin never sees or touches the track name for Session/Mix. On +edit, changing the Release Name updates the track name with it. The track name is a *derived field*, not +an independent one. (The alternative — let them diverge once set — would reintroduce exactly the +two-name confusion this track removes. Rejected.) **Discovery audit — every UI surface that surfaces separate Release vs. Track name (read against live source, 2026-06-13).** This is the full blast radius. Implementation must collapse the name inputs on @@ -362,15 +362,15 @@ the single-track path for each: shared by the Cut path (where it stays) and the single-track path (where it must be hidden). Cleanest: the parent passes a flag (e.g. `ShowTrackName` / `IsSingleTrack`) so `BatchTrackDetail` suppresses the name field for single-track media while still showing the WAV/Original-File rows. -- **`TrackNew.razor` (`/tracks/new`) — the legacy single-track add form.** Shows `Track Name` (line 31), - `Album` (line 33), and a `MediumFields` selector. When the selected medium is single-track, the - separate Track Name vs. Album split is the same redundancy. This legacy form is a secondary surface - (the batch form is the primary upload path) — confirm whether it is still a live entry point before - investing; if it is, apply the same collapse (one name when medium is Session/Mix). If it is dead, - note it for retirement rather than re-plumbing. -- **`TrackEdit.razor` (`/tracks/{Id:long}`) — the legacy single-track edit form.** Shows `Track Name` - (line 46) and `Album` (line 58) as separate fields, with a `MediumFields` selector. Same disposition - as `TrackNew`: collapse on the single-track path if live, flag for retirement if not. +- **`TrackNew.razor` (`/tracks/new`) and `TrackEdit.razor` (`/tracks/{Id:long}`) — the legacy + single-track forms.** Both surface the separate `Track Name` / `Album` split and a `MediumFields` + selector, so both carry the same two-name redundancy on the single-track path. **These are not patched + in-place for 8.L. Their disposition is consolidation — see 8.M (legacy-form retirement).** Daniel + (2026-06-13): "I would prefer to consolidate the forms and reduce the code surface if possible." The + decision is to fold their responsibility into the batch forms and retire the legacy pair, not to + re-plumb a name-collapse into forms slated for removal. 8.L therefore touches **only** the batch + forms (`BatchUpload` / `BatchEdit` via `BatchTrackDetail`); the legacy forms are out of 8.L's scope + and handled by 8.M. *CMS — surfaces that already do the right thing (no change needed, listed so implementation knows they are clean):* @@ -386,11 +386,12 @@ are clean):* - **`ReleaseGallery.razor`** (the Sessions/Mixes card grid) shows `release.Title` + `release.Artist` only. Clean. -**Net blast radius:** the consolidation is **entirely CMS-side**, and concentrated in the two batch -forms (`BatchUpload`, `BatchEdit`) via the shared `BatchTrackDetail`, plus the two legacy single-track -forms (`TrackNew`, `TrackEdit`) *if they are still live*. The public site already treats a single-track -release as one-named — no public work. This is the discovery step Daniel asked for: implementation -knows the full surface set before touching anything. +**Net blast radius (8.L proper):** the name-collapse is **entirely CMS-side** and concentrated in the +two batch forms (`BatchUpload`, `BatchEdit`) via the shared `BatchTrackDetail`. The legacy single-track +forms (`TrackNew`, `TrackEdit`) are **no longer part of 8.L** — their two-name redundancy is resolved by +retiring them outright (8.M), not by patching a collapse into them. The public site already treats a +single-track release as one-named — no public work. This is the discovery step Daniel asked for: +implementation knows the full surface set before touching anything. **Acceptance criteria.** - On the **create** path (batch upload, single-track medium): the form presents **one** name field @@ -404,8 +405,9 @@ knows the full surface set before touching anything. - Switching the medium selector mid-form between Cut and a single-track medium re-drives which name fields are visible (single name for Session/Mix; release + per-track names for Cut) without losing entered data where it still applies. -- The legacy `TrackNew` / `TrackEdit` forms either apply the same single-name collapse (if live) or are - flagged for retirement (if dead) — staff-engineer confirms their live status during implementation. +- The legacy `TrackNew` / `TrackEdit` forms are **out of 8.L scope** — their consolidation is 8.M + (legacy-form retirement). 8.L lands independently of 8.M; the name-collapse on the batch forms does + not wait on the legacy retirement. - No public-site change is required (verified: public detail/gallery views already key off the release title only). @@ -413,7 +415,88 @@ knows the full surface set before touching anything. present should already read "Release Name" before this collapse lands; sequence 8.G first or together). Touches the same upload/edit forms as **8.E**/**8.F** — coordinate so the Session form's hero input (8.F), medium pre-selection (8.E), and name collapse (8.L) land coherently rather than fighting over the -same submit handler. Independent of the tab restructure (8.A–8.D) and the public cluster. +same submit handler. Independent of the tab restructure (8.A–8.D), the public cluster, and **8.M** (8.L +lands on the batch forms whether or not the legacy forms are retired). + +--- + +### 8.M — Retire the legacy single-track forms; consolidate onto the batch forms + +**Goal.** Retire `TrackNew` (`/tracks/new`) and `TrackEdit` (`/tracks/{Id:long}`) as the single-track +authoring path. Their add/edit responsibility is **absorbed by `BatchUpload` / `BatchEdit`**, whose +single-track branch already handles Session/Mix. This **reduces the duplicate form surface** — Daniel +(2026-06-13): "I would prefer to consolidate the forms and reduce the code surface if possible." The +single-track authoring path becomes the batch form's single-track branch; the legacy routes redirect to +(or are replaced by) the batch routes. + +**User-visible change.** Adding or editing a single track no longer opens a distinct single-field form. +The same batch form that handles releases handles the one-track case — one form for both, no second +authoring surface to maintain. (Cuts already author via the batch forms; this brings the Session/Mix and +single-Cut-track cases onto the same path.) + +**Feasibility read (against live source, 2026-06-13). Verdict: retirement-with-reconciliation — feasible, +but not a pure delete. One real gap must be closed first.** + +What the batch forms already cover that makes this viable: +- `BatchUpload` already *is* the upload path for every medium, with a dedicated single-track branch + (`_medium != Cut` renders a single WAV slot + name field, lines ~53–70). `TrackNew` adds nothing the + batch upload doesn't already do — same fields (name, artist, album/release, genre, release date, + medium, cover art), same `UploadTrackAsync` call, same Mix-waveform trigger, same cover-link follow-up. + **`TrackNew` is a clean retirement** (see route note below). +- `BatchEdit` already collapses to a single row for Session/Mix (§9.6.B; `AllowNewTracks` gated on + `_medium == Cut`, `OnMediumChanged` trims to one row) and carries the same edit fields, delete, and + cover handling `TrackEdit` has. + +The gap that must reconcile — **addressing model**: +- `TrackEdit` is addressed **by track id** (`/tracks/{Id:long}`). `BatchEdit` is addressed **by release + title** (`/tracks/album/{AlbumName}/edit`) and loads the *whole release*. They key off different + things. For Session/Mix this is harmless (one track = one release; the names are about to be synced by + 8.L anyway), but for a **single Cut track** opened from Track mode's per-row Edit, routing to + `BatchEdit` would open the *entire parent release*, not just that track. That is a behaviour change, + not a 1:1 swap. +- **The one live inbound link to a legacy form is `CmsTrackGrid.razor` line ~82** — Track mode's per-row + Edit button: `Href="/tracks/{context.Id}"` → `TrackEdit`. This is the surface that must be reconciled: + either Track mode's row-edit retargets to a batch-edit-by-release (accepting that editing a track + opens its release), or `BatchEdit` gains an address-by-track-id entry that pre-selects the row. Either + is a real decision, not a mechanical rename. +- `TrackNew` (`/tracks/new`) has **no live inbound nav link** in source — every "Add Track" button + points at `/tracks/upload` (`CmsTrackGrid` line ~16, the 8.E plan). So `TrackNew`'s route can be + dropped or made a redirect to `/tracks/upload` with **zero** caller changes. + +Route/redirect implications: +- `/tracks/new` → redirect to (or replace with) `/tracks/upload`. No callers to update. +- `/tracks/{Id:long}` → either redirect to a release-scoped batch edit (and retarget `CmsTrackGrid`'s + row Edit), or retain a thin track-addressed entry into the batch edit. **This is the reconciliation + call** and it is the crux of whether this lands cleanly. + +**Verdict, stated plainly:** `TrackNew` is a **clean retirement**. `TrackEdit` is a +**retirement-with-reconciliation** — feasible, but it requires a decision on the single-Cut-track edit +affordance (open the whole release vs. address a single track within the batch edit) and a retarget of +`CmsTrackGrid`'s one inbound link. Not blocked; not free. + +**Acceptance criteria.** +- `/tracks/new` no longer renders a distinct form; the add-single-track path is `BatchUpload`'s + single-track branch (route removed or redirected to `/tracks/upload`). +- Editing a single track no longer opens `TrackEdit`'s distinct form; the edit path is `BatchEdit`'s + single-track branch. `CmsTrackGrid`'s per-row Edit routes to the reconciled destination. +- The single-Cut-track edit affordance behaves per the reconciliation decision (documented at + implementation time): either it opens the parent release in `BatchEdit`, or `BatchEdit` accepts a + track-addressed entry that pre-selects the row. +- No dangling references to `TrackNew` / `TrackEdit` routes remain in live navigation. +- Net CMS form-code surface is reduced (two fewer routable forms to maintain). + +**Architectural vs. mechanical (assessment).** **Architectural — staff-engineer territory.** This is not +a localized edit: it changes the CMS route map (two routes removed/redirected), removes two routable +components, and — most importantly — **changes the single-track edit addressing model** (track-id vs. +release-title), which carries a navigation-behaviour decision (does editing a Cut track open its whole +release?). That decision touches how Track mode's per-row Edit behaves and may require `BatchEdit` to +accept a new addressing mode. Route-map changes + component removal + a navigation-model decision is +squarely staff-engineer scope, not a maintenance pass. + +**Dependencies.** Independent of 8.L (8.L lands the batch-form name-collapse regardless). Coordinates +with **8.E** (the Add-Track buttons already target `/tracks/upload`, so 8.E and 8.M agree on the upload +route). Best sequenced **after** 8.L so the batch single-track branch is already name-consolidated when +it becomes the sole single-track path. Lower priority than the name-collapse — see split rationale in §5. --- @@ -584,6 +667,20 @@ sequenced as a post-Phase-9 implementation wave. **independent** of the tab work; touch the upload/edit forms. Sequence **8.G → 8.L** (the consolidated field should read "Release Name" first), and **coordinate 8.E/8.F/8.L** — all three touch the Session upload form and its submit handler. +- **8.M** (legacy single-track form retirement) — **architectural** (route-map change + component + removal + single-track edit addressing decision; staff-engineer scope). Independent of 8.L for build; + best sequenced **after** 8.L so the batch single-track branch is already name-consolidated when it + becomes the sole single-track path. **Lower priority than 8.L** — see split rationale below. + +**8.L / 8.M split rationale.** These were one item ("consolidate the names"); they are now two because +they have different cost and risk. **8.L is mechanical-to-moderate, self-contained, and high-value** — +collapse two name inputs to one on the batch forms via a `BatchTrackDetail` flag, sync the derived track +name on save. It touches no routes and no component graph. **8.M is architectural** — retiring +`TrackNew`/`TrackEdit` means a route-map change, two component removals, and a real decision about +whether editing a single Cut track opens its whole release (the track-id vs. release-title addressing +gap). Bundling them would gate the cheap, safe name-collapse behind the route/navigation decision. Split +so **8.L lands independently and immediately**, and **8.M follows when the addressing reconciliation is +decided**. They share intent ("one name, fewer forms") but not blast radius. **Public cluster:** - **8.J** (popover dismissal bug) — **independent**; can land immediately (but coordinate with 8.I if @@ -597,9 +694,12 @@ sequenced as a post-Phase-9 implementation wave. dispatchable straight from `phase-9-mix-visualizer-redesign.md`. Does not gate Phase 9 completion. **Recommended sequencing.** Land the independent/trivial items first (8.G, 8.D, 8.J), then 8.L -(consolidation, after 8.G). Then the CMS spine (8.B → 8.A → 8.C/8.E), folding 8.F into the Session-form -work alongside 8.E/8.L. On the public side, 8.H (the new release-cardinal archive) → 8.I. **Phase 9 -closes when 8.A–8.J + 8.L land; 8.K is explicitly excluded** and runs as a separate post-Phase-9 wave. +(name-collapse, after 8.G). Then the CMS spine (8.B → 8.A → 8.C/8.E), folding 8.F into the Session-form +work alongside 8.E/8.L. On the public side, 8.H (the new release-cardinal archive) → 8.I. **8.M** +(legacy-form retirement) sequences after 8.L and is **not a Phase-9-completion gate** — it is a +code-surface-reduction follow-on, not a taxonomy-reach correction; it can land in Wave 8's tail or just +after. **Phase 9 closes when 8.A–8.J + 8.L land; 8.K and 8.M are excluded from the completion gate** +(8.K is a post-Phase-9 design-complete wave; 8.M is a consolidation follow-on that can trail). --- @@ -619,9 +719,13 @@ acceptance criteria above. warning when a Session is submitted without a hero image. 5. **(8.E) `ALL`-tab Add Track default medium → Cut.** The medium selector remains user-changeable after landing on the upload form. -6. **(8.L, recommendation pending confirm) Single-track name sync.** Recommended posture: keep the - derived track name **synced** to the Release Name for Session/Mix on both create and edit, so they - never diverge. (Daniel set the consolidation requirement; the sync-vs-diverge interaction detail is - the one open recommendation — call it out, default to synced.) -7. **(8.K) Mix Visualizer → out of Phase-9 scope, design-complete.** Documented in full now; built +6. **(8.L) Single-track name sync → CONFIRMED synced.** The derived track name is kept **synced** to + the Release Name for Session/Mix on both create and edit, so they can never diverge. (Daniel, + 2026-06-13 — was the one open recommendation; now decided.) +7. **(8.M) Legacy single-track forms → CONSOLIDATE / retire.** Daniel (2026-06-13): "I would prefer to + consolidate the forms and reduce the code surface if possible." `TrackNew`/`TrackEdit` are folded + into the batch forms and retired, not patched in place. Split from 8.L (the name-collapse) because + 8.M is architectural (route map + addressing model) while 8.L is self-contained. 8.M is not a + Phase-9-completion gate. +8. **(8.K) Mix Visualizer → out of Phase-9 scope, design-complete.** Documented in full now; built later. Phase 9 closes without it.