Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
627b159
feat(25-01): add latest and next release dates to GET /watchlist
danielrpof Oct 10, 2026
a577970
test(25-01): pin latest/next release semantics and shared projection
danielrpof Oct 10, 2026
a20968d
docs(25-01): add plan summary
danielrpof Oct 10, 2026
f6687a6
docs(25-01): update state and roadmap for completed plan
danielrpof Oct 10, 2026
aa50c00
feat(25-03): show latest and upcoming release on Watchlist cards
danielrpof Oct 10, 2026
07e47ee
test(25-03): pin release-date table, add makeEntry fixture, refresh r…
danielrpof Oct 10, 2026
10e454d
docs(25-03): add plan summary
danielrpof Oct 10, 2026
6605446
docs(25-03): update state and roadmap for completed plan
danielrpof Oct 10, 2026
c421197
feat(25-02): filter GET /events by tag_id inside retention-aware queries
danielrpof Oct 10, 2026
00e0ada
test(25-02): pin tag filter retention, paging, composition and valida…
danielrpof Oct 10, 2026
f444b94
docs(25-02): complete History tag filter plan
danielrpof Oct 10, 2026
115e13f
feat(25-04): filter the Watchlist by name with live count and URL state
danielrpof Oct 10, 2026
3062fd9
test(25-04): pin fold table, URL writer, and name filter keys
danielrpof Oct 10, 2026
e11bb20
feat(25-04): add the name filter clear button and Esc handling
danielrpof Oct 10, 2026
e431c13
test(25-04): pin clear filters, announcements, and sticky adds
danielrpof Oct 10, 2026
882a047
feat(25-04): add Clear filters, filtered-to-zero state, announcements…
danielrpof Oct 10, 2026
4818699
docs(25-04): add plan summary
danielrpof Oct 10, 2026
c6c7e46
docs(25-04): update state and roadmap for completed plan
danielrpof Oct 10, 2026
cac69cd
feat(25-06): add Tag filter to History feed
danielrpof Oct 10, 2026
9cb0416
test(25-06): add failing tests for combobox hardening and Tag trigger…
danielrpof Oct 10, 2026
200b13f
feat(25-06): harden History combobox and add degraded Tag trigger labels
danielrpof Oct 10, 2026
227222c
test(25-06): add failing tests for the tag empty state
danielrpof Oct 10, 2026
fa9483f
feat(25-06): add tag-specific History empty state with Show all tags
danielrpof Oct 10, 2026
f8ae71f
docs(25-06): add plan summary
danielrpof Oct 10, 2026
20bbfad
docs(25-06): update state and roadmap for completed plan
danielrpof Oct 10, 2026
0c914ef
feat(25-05): add Sort by control to the Watchlist toolbar
danielrpof Oct 10, 2026
19bea20
test(25-05): add failing tests for sort canonicalization and announce…
danielrpof Oct 10, 2026
5fad3d0
feat(25-05): canonicalize invalid sort and announce sort changes
danielrpof Oct 10, 2026
b1ff19c
docs(25-05): complete watchlist sort plan
danielrpof Oct 10, 2026
7bafd67
feat(25-09): move History filters into the URL
danielrpof Oct 10, 2026
e050de4
test(25-09): add failing tests for stale and malformed History params
danielrpof Oct 10, 2026
578b5fc
feat(25-09): drop malformed and stale History params once sources load
danielrpof Oct 10, 2026
8de52e3
docs(25-09): add History URL filters plan summary
danielrpof Oct 10, 2026
27d472f
docs(25-09): update state and roadmap for completed plan
danielrpof Oct 10, 2026
438144f
feat(25-07): filter the Watchlist by any of several tags
danielrpof Oct 10, 2026
a10a412
test(25-07): pin the tag filter's keep-open, keys, and empty copy
danielrpof Oct 10, 2026
5267ea8
feat(25-07): keep the tag filter open on pick and explain empty results
danielrpof Oct 10, 2026
4fb8ef1
test(25-07): pin tag filter validation, Manage tags sync, and Back
danielrpof Oct 10, 2026
beef586
feat(25-07): keep the tag filter consistent with Manage tags, Back, a…
danielrpof Oct 10, 2026
139429c
docs(25-07): add Watchlist tag filter plan summary
danielrpof Oct 10, 2026
3aea8c3
docs(25-07): update state and roadmap for completed plan
danielrpof Oct 10, 2026
d888568
feat(25-08): filter the Watchlist to muted events and custom release …
danielrpof Oct 10, 2026
e9e8370
refactor(25-08): target chip focus by data-chip-remove and wire tag f…
danielrpof Oct 10, 2026
30cdd64
test(25-08): pin the chip label filter toggle and its separation from…
danielrpof Oct 10, 2026
f7d460b
feat(25-08): let a card's tag chip label toggle the tag filter
danielrpof Oct 10, 2026
88b6b40
docs(25-08): add preference toggles and chip click-to-filter plan sum…
danielrpof Oct 10, 2026
4ec4cc7
docs(25-08): update state and roadmap for completed plan
danielrpof Oct 10, 2026
c38d832
build(25-10): rebuild embedded SPA with watchlist toolbar, latest-rel…
danielrpof Oct 10, 2026
68ac7ce
docs(25-10): complete embedded SPA rebuild and phase gate plan
danielrpof Oct 10, 2026
2cd98c2
docs(25): add code review report
danielrpof Oct 10, 2026
e4bc5f0
test(25): add verification report and persist human verification item…
danielrpof Oct 10, 2026
344d69b
fix(25): WR-01 drop stale Load more response after history filter change
danielrpof Oct 10, 2026
000b090
fix(25): WR-02 clear sticky watchlist cards on URL-driven filter changes
danielrpof Oct 10, 2026
4bdc558
fix(25): WR-03 ignore malformed release dates in watchlist latest/nex…
danielrpof Oct 10, 2026
9cc7050
fix(25): WR-04 drive count-announcement tests with fake timers
danielrpof Oct 10, 2026
befe939
build(25): rebuild embedded SPA after review fixes
danielrpof Oct 10, 2026
2a248b3
docs(25): add code review fix report
danielrpof Oct 10, 2026
78ca4bc
test(25): complete UAT - 6 passed, 3 issues
danielrpof Oct 11, 2026
0728521
docs(26): capture phase context
danielrpof Oct 11, 2026
8d6129c
docs(state): record phase 26 context session
danielrpof Oct 11, 2026
a53b552
docs(25): add root causes from diagnosis
danielrpof Oct 11, 2026
461267f
docs(25): record gap-closure product decisions
danielrpof Oct 11, 2026
70a6eb0
docs(26): UI design contract
danielrpof Oct 11, 2026
dc22150
docs(25): plan UAT gap closure for G-25-4, G-25-7, G-25-9
danielrpof Oct 11, 2026
7627d26
docs(26): UI design contract
danielrpof Oct 11, 2026
b6674f1
docs(25): revise plan 25-14 per checker feedback
danielrpof Oct 11, 2026
1b4bdaf
docs(25): revise plan 25-14 per checker feedback (iteration 2)
danielrpof Oct 11, 2026
362978e
docs(25): record CI-only race waiver for gap closure
danielrpof Oct 11, 2026
c52f2e1
test(25-11): add failing test for chip-click scroll anchoring
danielrpof Oct 11, 2026
f1e03c7
feat(25-11): anchor the clicked chip across the filter re-render
danielrpof Oct 11, 2026
a0f885b
test(25-11): pin scroll-anchor directions, detach, and one-shot cases
danielrpof Oct 11, 2026
9623db6
docs(25-11): complete chip-click scroll anchoring plan
danielrpof Oct 11, 2026
9eff68f
test(25-12): add failing tests for History card Detected date
danielrpof Oct 11, 2026
bc24fd2
feat(25-12): show Detected date on History event cards
danielrpof Oct 11, 2026
bc22dbe
test(25-12): pin old releases detected in window under tag filter
danielrpof Oct 11, 2026
0182749
docs(25-12): complete History card Detected date plan
danielrpof Oct 11, 2026
fc7d23d
docs(25-12): update state and roadmap progress
danielrpof Oct 11, 2026
ef6d74c
test(25-13): add failing test for capped Deezer button and truncating…
danielrpof Oct 11, 2026
a027206
feat(25-13): cap Deezer button width and truncate its label
danielrpof Oct 11, 2026
71a9d4a
test(25-13): add failing test for truncating type line
danielrpof Oct 11, 2026
1ef7bc5
feat(25-13): truncate the search-result type line
danielrpof Oct 11, 2026
c242a25
docs(25-13): complete Deezer row width cap plan
danielrpof Oct 11, 2026
900706e
docs(25-13): update state and roadmap progress
danielrpof Oct 11, 2026
afb881c
chore(25-14): rebuild embedded SPA with the gap-closure fixes
danielrpof Oct 11, 2026
1d3017f
docs(25-14): reword scroll-anchor and detection-date contracts
danielrpof Oct 11, 2026
2c724ee
docs(25-14): complete embed rebuild and contract rewording plan
danielrpof Oct 11, 2026
3c6d494
docs(25-14): update state and roadmap progress
danielrpof Oct 11, 2026
1caae27
docs(quick-261011-gq7): amend 26-CONTEXT with grill decisions Q1-Q10
danielrpof Oct 11, 2026
e6c1c67
docs(quick-261011-gq7): amend 26-UI-SPEC with grill decisions
danielrpof Oct 11, 2026
6408d28
docs(quick-261011-gq7): add todo to withdraw pending events on remove
danielrpof Oct 11, 2026
2268beb
docs(quick-261011-gq7): Amend Phase 26 docs with grill decisions
danielrpof Oct 11, 2026
efcb6ec
docs(25): add code review report
danielrpof Oct 11, 2026
db08f5a
docs(25): re-verify phase after gap closure (gaps found)
danielrpof Oct 11, 2026
7640bae
docs(phase-25): revert premature Complete requirements after gaps found
danielrpof Oct 11, 2026
d60d3d8
docs(quick-261011-moj): add UI review U1, U3, U4 to Phase 26 context
danielrpof Oct 11, 2026
2bfc806
docs(quick-261011-moj): add U1 focus order and U2 focus clearance to …
danielrpof Oct 11, 2026
9ac478c
docs(quick-261011-moj): add U3 one-row phone bar and U4 remove-tag Un…
danielrpof Oct 11, 2026
8ccde67
docs(quick-261011-moj): Amend Phase 26 docs with UI review U1-U4
danielrpof Oct 11, 2026
182f122
docs(25): create verification gap closure plans 25-15..25-18
danielrpof Oct 11, 2026
178595c
docs(25): revise gap closure plans 25-15..25-18 per checker iteration 1
danielrpof Oct 12, 2026
67a698e
docs(25): scope 25-15 sticky prohibition to filter changes (checker o…
danielrpof Oct 12, 2026
7ae9ace
docs(25): plan gap closure 25-15..25-18
danielrpof Oct 12, 2026
9057be9
fix(25-15): key sticky cards to the filter and reset them in the URL'…
danielrpof Oct 12, 2026
6bb851f
feat(25-15): carry sticky cards across the route's own tag rewrites
danielrpof Oct 12, 2026
5bd92d0
docs(25-15): summary for sticky cards keyed to the filter
danielrpof Oct 12, 2026
9744475
docs(25-15): update state and roadmap for plan completion
danielrpof Oct 12, 2026
ca1106b
fix(25-16): title the truncated Deezer hint pill through a pointer-re…
danielrpof Oct 12, 2026
4bf3d13
docs(25-16): complete Deezer hint wrapper title plan
danielrpof Oct 12, 2026
3729e5c
fix(25-17): fall back to the clicked card's neighbours when it leaves…
danielrpof Oct 12, 2026
fe62df6
test(25-17): pin the scroll anchor's fallback order, empty case, and …
danielrpof Oct 12, 2026
20a8740
docs(25-17): complete fallback scroll anchor plan
danielrpof Oct 12, 2026
24d8aaa
build(25-18): rebuild the embedded SPA with the review fixes
danielrpof Oct 12, 2026
48d663b
docs(25-18): record the WR-01..WR-04 decisions in the UI-SPEC and CON…
danielrpof Oct 12, 2026
c55905a
docs(25-18): complete ship-the-review-fixes plan
danielrpof Oct 12, 2026
fc4b2d4
docs(25): add code review report
danielrpof Oct 12, 2026
16c1e4d
docs(25): re-verify phase after gap closure 25-15..25-18
danielrpof Oct 12, 2026
9d3413f
test(25): complete UAT re-test - 10 passed, 3 decision gaps
danielrpof Oct 12, 2026
97bf1be
docs(25): create UAT gap closure plans 25-19..25-21
danielrpof Oct 12, 2026
1cb2a7b
docs(25): record CI-only race waiver for gap closure 25-19..25-21
danielrpof Oct 12, 2026
5969c99
docs(25): revise gap closure plans 25-19..25-21 per checker feedback
danielrpof Oct 12, 2026
a4f8b09
fix(25-19): anchor on the nearest surviving card when a run of cards …
danielrpof Oct 12, 2026
9c51992
docs(25-19): record the widened fallback anchor in the UI-SPEC D-12 b…
danielrpof Oct 12, 2026
41c7487
docs(25-19): add plan summary
danielrpof Oct 12, 2026
742f579
docs(25-19): complete widen scroll-anchor fallback plan
danielrpof Oct 12, 2026
c9ea53c
fix(25-20): move focus to the fallback card's chip label when a keybo…
danielrpof Oct 12, 2026
5ae0b5b
fix(25-20): focus the list heading when a chip toggle leaves no card
danielrpof Oct 12, 2026
e5c7d8e
docs(25-20): add the 'Toggled, card leaves' focus row to the UI-SPEC
danielrpof Oct 12, 2026
903b112
docs(25-20): add plan summary
danielrpof Oct 12, 2026
9228841
docs(25-20): complete focus after keyboard toggle plan
danielrpof Oct 12, 2026
69d6f9e
fix(25-21): describe the disabled Deezer button with its MusicBrainz …
danielrpof Oct 12, 2026
8d24c8a
docs(25-21): record the Deezer hint's accessible description in the U…
danielrpof Oct 12, 2026
5358e5b
build(25-21): rebuild the embedded SPA with the G-25-11..13 fixes
danielrpof Oct 12, 2026
9eeed34
docs(25-21): complete Deezer hint description and SPA rebuild plan
danielrpof Oct 12, 2026
989feb0
docs(25): add code review report
danielrpof Oct 12, 2026
729335a
docs(25): re-verify phase after gap closure 25-19..25-21
danielrpof Oct 12, 2026
e0a4ae0
test(25): complete UAT - 13 passed, 0 issues
danielrpof Oct 13, 2026
54cd26c
docs(phase-25): add/update security threat verification
danielrpof Oct 13, 2026
b983ebd
docs(25): UI audit review
danielrpof Oct 13, 2026
8710c78
docs(25): reconcile verification after UAT 13/13 pass
danielrpof Oct 13, 2026
aa235f1
docs(25): refresh verification digest after UAT pass
danielrpof Oct 13, 2026
fc8bb9a
docs(25): mark phase 25 complete
danielrpof Oct 13, 2026
b36b27b
fix(docker): bump Go builder to 1.26.9 for stdlib HIGH CVEs
danielrpof Oct 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions .planning/REQUIREMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@

### Watchlist View

- [ ] **WLVW-01**: User can search the watchlist by artist name
- [ ] **WLVW-02**: User can sort by name (A–Z/Z–A) or date added (newest/oldest), with a stable tie-break
- [ ] **WLVW-03**: User can sort by latest release (the artist's newest own `new_release` event date that is not upcoming); artists with none sort last in both directions
- [ ] **WLVW-04**: User can filter the watchlist by one or more tags
- [ ] **WLVW-05**: User can filter to artists with muted event types or non-default release-type filters
- [ ] **WLVW-06**: Search, sort, and filters combine; the view shows "N of M artists" and an empty state with a clear-filters action
- [x] **WLVW-01**: User can search the watchlist by artist name
- [x] **WLVW-02**: User can sort by name (A–Z/Z–A) or date added (newest/oldest), with a stable tie-break
- [x] **WLVW-03**: User can sort by latest release (the artist's newest own `new_release` event date that is not upcoming); artists with none sort last in both directions
- [x] **WLVW-04**: User can filter the watchlist by one or more tags
- [x] **WLVW-05**: User can filter to artists with muted event types or non-default release-type filters
- [x] **WLVW-06**: Search, sort, and filters combine; the view shows "N of M artists" and an empty state with a clear-filters action

### History

- [ ] **HIST-02**: User can filter the History feed by tag, combined with existing filters and respecting event retention
- [x] **HIST-02**: User can filter the History feed by tag, combined with existing filters and respecting event retention

### Bulk Edit

Expand Down Expand Up @@ -89,13 +89,13 @@ Which phases cover which requirements. Updated during roadmap creation.
| TAG-06 | Phase 24 | Complete |
| TAG-07 | Phase 24 | Complete |
| NOTE-01 | Phase 24 | Complete |
| WLVW-01 | Phase 25 | Pending |
| WLVW-02 | Phase 25 | Pending |
| WLVW-03 | Phase 25 | Pending |
| WLVW-04 | Phase 25 | Pending |
| WLVW-05 | Phase 25 | Pending |
| WLVW-06 | Phase 25 | Pending |
| HIST-02 | Phase 25 | Pending |
| WLVW-01 | Phase 25 | Complete |
| WLVW-02 | Phase 25 | Complete |
| WLVW-03 | Phase 25 | Complete |
| WLVW-04 | Phase 25 | Complete |
| WLVW-05 | Phase 25 | Complete |
| WLVW-06 | Phase 25 | Complete |
| HIST-02 | Phase 25 | Complete |
| BULK-01 | Phase 26 | Pending |
| BULK-02 | Phase 26 | Pending |
| BULK-03 | Phase 26 | Pending |
Expand Down
63 changes: 49 additions & 14 deletions .planning/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Full phase-by-phase detail for every shipped milestone is archived under `.plann
**Milestone Goal:** Keep a 50+ artist watchlist manageable. The user can label, annotate, find, bulk-change, and bulk-add artists, with no new background API polling.

- [x] **Phase 24: Artist Tags & Notes** - Free-form, case-insensitive tags with autocomplete and a plain-text note on each Watchlist card, global tag rename/merge/delete, and tags that belong to the artist so they survive a remove and re-add (completed 2026-10-06)
- [ ] **Phase 25: Find & Filter — Watchlist and History** - Client-side name search, stable sorts (including latest release), and tag/preference filters over the fully loaded watchlist, plus a retention-aware tag filter on the History feed
- [x] **Phase 25: Find & Filter — Watchlist and History** - Client-side name search, stable sorts (including latest release), and tag/preference filters over the fully loaded watchlist, plus a retention-aware tag filter on the History feed (completed 2026-10-12)
- [ ] **Phase 26: Bulk Edit & Remove** - Multi-select on the Watchlist with one-action tag add/remove, preference changes, and a count-confirmed all-or-nothing remove, backed by set-based transactional endpoints
- [ ] **Phase 27: Paste-a-List Bulk Add** - Paste names one per line, watch them resolve one search at a time within the existing rate limits, review best matches by confidence, and add the confirmed set in one request
- [ ] **Phase 28: Tags on Discord Notifications** - Tags on real-time embeds and digest lines, with markdown escaping added to the real-time path and the digest's chunking guarantees re-proven against longer lines
Expand Down Expand Up @@ -193,36 +193,71 @@ Plans:
4. The user can filter History by a tag alongside the existing filters. It lists events for artists carrying that tag, including artists since removed from the watchlist, and it never shows an event older than the retention window, either on the first page or via "load older".
5. Opening the Watchlist makes one request regardless of watchlist size. Searching, sorting, and filtering then happen instantly with no further requests.

**Plans:** 10 plans
**Plans:** 21/21 plans complete

Plans:
**Wave 1**

- [ ] 25-01-PLAN.md — GET /watchlist gains `latest_release_date` and `next_release_date` from two lateral joins in its single query (G1–G3), with semantics and projection-parity tests
- [ ] 25-03-PLAN.md — Cards show `Latest release: {date}` / `No releases yet` and `Upcoming: {date}`, `formatReleaseDate`, and the `makeEntry` fixture builder
- [x] 25-01-PLAN.md — GET /watchlist gains `latest_release_date` and `next_release_date` from two lateral joins in its single query (G1–G3), with semantics and projection-parity tests
- [x] 25-03-PLAN.md — Cards show `Latest release: {date}` / `No releases yet` and `Upcoming: {date}`, `formatReleaseDate`, and the `makeEntry` fixture builder

**Wave 2** *(blocked on Wave 1 completion)*

- [ ] 25-02-PLAN.md — GET /events `tag_id` filter inside the retention-aware ListEvents and HasOlderEvents, with retention, paging, and validation tests
- [ ] 25-04-PLAN.md — Watchlist name filter (accent and `$ø æ ß` folding), "N of M artists", URL state helper, Clear filters and filtered-to-zero state, live announcements, and sticky cards (G4)
- [ ] 25-06-PLAN.md — History Tag control (D-11), `Combobox<T>` hardening, loading/unavailable labels, and the tag empty state with `Show all tags`
- [x] 25-02-PLAN.md — GET /events `tag_id` filter inside the retention-aware ListEvents and HasOlderEvents, with retention, paging, and validation tests
- [x] 25-04-PLAN.md — Watchlist name filter (accent and `$ø æ ß` folding), "N of M artists", URL state helper, Clear filters and filtered-to-zero state, live announcements, and sticky cards (G4)
- [x] 25-06-PLAN.md — History Tag control (D-11), `Combobox<T>` hardening, loading/unavailable labels, and the tag empty state with `Show all tags`

**Wave 3** *(blocked on Wave 2 completion)*

- [ ] 25-05-PLAN.md — Sort by name, date added, or latest release with id-stable ties and null-last ordering, URL `sort`, and the sort announcement
- [ ] 25-09-PLAN.md — History filters move to URL params (`artist`, `type`, `tag`), with stale and malformed params dropped once their source loads (G6)
- [x] 25-05-PLAN.md — Sort by name, date added, or latest release with id-stable ties and null-last ordering, URL `sort`, and the sort announcement
- [x] 25-09-PLAN.md — History filters move to URL params (`artist`, `type`, `tag`), with stale and malformed params dropped once their source loads (G6)

**Wave 4** *(blocked on Wave 3 completion)*

- [ ] 25-07-PLAN.md — `Tags: any of` multi-select on the vendored base-ui Combobox, payload-derived options, and D-13 consistency (first-load validation, Manage tags delete/merge, Back)
- [x] 25-07-PLAN.md — `Tags: any of` multi-select on the vendored base-ui Combobox, payload-derived options, and D-13 consistency (first-load validation, Manage tags delete/merge, Back)

**Wave 5** *(blocked on Wave 4 completion)*

- [ ] 25-08-PLAN.md — `Has muted events` / `Custom release types` toggles with full AND composition, and the card chip label as a filter toggle (D-08)
- [x] 25-08-PLAN.md — `Has muted events` / `Custom release types` toggles with full AND composition, and the card chip label as a filter toggle (D-08)

**Wave 6** *(blocked on Wave 5 completion)*

- [ ] 25-10-PLAN.md — Embedded bundle rebuild, the phase-closing Definition of Done, and the blast-radius gate
- [x] 25-10-PLAN.md — Embedded bundle rebuild, the phase-closing Definition of Done, and the blast-radius gate

**UAT gap closure — Wave 1** *(from 25-UAT.md gaps G-25-4, G-25-7, G-25-9, with the user's 2026-10-11 decisions)*

- [x] 25-11-PLAN.md — A chip-label click anchors the clicked card. `useScrollAnchor` records the chip's on-screen position and scrolls by the drift after the filtered re-render, clamped by the browser on short pages. Includes jsdom tests with stubbed layout for both directions and the disconnected case (G-25-4)
- [x] 25-12-PLAN.md — History cards show `Detected {date}` from `created_at`, the date retention counts from (D-01 kept), plus an integration test for "detected recently, released long ago" under a tag filter (G-25-7)
- [x] 25-13-PLAN.md — The Deezer search-result button is capped at 40% of the row below lg and truncates its label, and the type line truncates, so the artist name stays readable at 375px and 640px (G-25-9)

**UAT gap closure — Wave 2** *(blocked on UAT gap closure Wave 1)*

- [x] 25-14-PLAN.md — Embedded bundle rebuild, the reworded UI-SPEC D-12, UAT tests 4/7/9, and verification checks 4/7, plus the Definition of Done and a blast-radius gate against `461267f`

**Verification gap closure — Wave 1** *(from 25-VERIFICATION.md gap G-25-4 and 25-REVIEW WR-01..WR-04, with the user's 2026-10-11 gap-planning decisions)*

- [x] 25-15-PLAN.md — Sticky cards are keyed to the filter and reset during render, so a chip click on a sticky card keeps its anchor and focus (G-25-4, WR-01). Back drops stale stickies in the same commit. The route's own Manage tags merge/delete and canonical rewrites carry the sticky set (WR-03)
- [x] 25-16-PLAN.md — The Deezer hint's `title` and 40% cap move to a wrapper span that receives pointer events, so the truncated pill explains itself on hover (WR-04)

**Verification gap closure — Wave 2** *(blocked on 25-15)*

- [x] 25-17-PLAN.md — Fallback scroll anchor: when the clicked card leaves the list, the next and then the previous sibling card stays in place (WR-02)

**Verification gap closure — Wave 3** *(blocked on 25-15, 25-16, 25-17)*

- [x] 25-18-PLAN.md — Embedded bundle rebuild with marker counts, the WR-01..WR-04 decisions recorded in three UI-SPEC lines, the Definition of Done, and a blast-radius gate against `8ccde67`

**UAT gap closure (round 3) — Wave 1** *(from 25-UAT.md gaps G-25-11, G-25-12, G-25-13, with the user's 2026-10-12 decisions; waves are sequential because each plan edits its own 25-UI-SPEC line)*

- [x] 25-19-PLAN.md — When the clicked card leaves, the scroll anchor falls back to the nearest surviving card in either direction (next before previous at each distance), so a run of leaving cards never collapses the page. Includes hook and route run-case tests and the widened UI-SPEC D-12 rule (G-25-11)

**UAT gap closure (round 3) — Wave 2** *(blocked on 25-19)*

- [x] 25-20-PLAN.md — A keyboard toggle that removes its card moves focus to the fallback card's pressed chip label, or to the `Your artists` heading when nothing survives. Uses `useEffectEvent` in `useScrollAnchor`, adds `data-chip-label`, and adds the "Toggled, card leaves" focus row (G-25-12)

**UAT gap closure (round 3) — Wave 3** *(blocked on 25-19, 25-20)*

- [x] 25-21-PLAN.md — The disabled Deezer button gets its hint as an accessible description (sr-only span, per-row `useId`, `aria-describedby`) (G-25-13). Then the embedded bundle is rebuilt with marker counts, and the Definition of Done and a blast-radius gate run against `9d3413f`

**UI hint**: yes

Expand Down Expand Up @@ -265,7 +300,7 @@ Plans:
### Phase 27: Paste-a-List Bulk Add

**Goal**: The user can paste a list of artist names and add the right artists in one reviewed batch. They see what each line matched and can fix or skip the doubtful ones, and nothing is added until they confirm.
**Depends on**: Phase 26 (the confirm step reuses its bulk-endpoint conventions: protected route group, per-item outcomes, refresh without reload)
**Depends on**: Phase 26 (the confirm step reuses its bulk-endpoint conventions: protected route group, bounded id lists, refresh without reload. It may reuse Phase 26's per-id outcome shape, but it isn't committed to: Phase 27 confirms by external id, and its skip reasons differ)
**Requirements**: IMPT-01, IMPT-02, IMPT-03, IMPT-04, IMPT-05
**Success Criteria** (what must be TRUE):

Expand Down Expand Up @@ -327,7 +362,7 @@ Phases execute in numeric order: 24 → 25 → 26 → 27 → 28
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 24. Artist Tags & Notes | 14/14 | Complete | 2026-10-06 |
| 25. Find & Filter — Watchlist and History | 0/? | Not started | - |
| 25. Find & Filter — Watchlist and History | 21/21 | Complete | 2026-10-12 |
| 26. Bulk Edit & Remove | 0/? | Not started | - |
| 27. Paste-a-List Bulk Add | 0/? | Not started | - |
| 28. Tags on Discord Notifications | 0/? | Not started | - |
Expand Down
Loading
Loading