diff --git a/src/songtrace/static/index.html b/src/songtrace/static/index.html
index 846b976..8d07dd3 100644
--- a/src/songtrace/static/index.html
+++ b/src/songtrace/static/index.html
@@ -872,10 +872,12 @@ function buildLyricsReview(data) {
async function renderLyricsReview(job) {
const box = document.getElementById("lyrics-review");
if (job.status === "failed") {
+ delete box.dataset.jobId;
box.replaceChildren(emptyNote("job failed — nothing to review"));
return;
}
if (job.status !== "done") {
+ delete box.dataset.jobId;
box.replaceChildren(emptyNote("waiting for this song to finish…"));
return;
}
diff --git a/tests/test_web.py b/tests/test_web.py
index 4821819..8c3b0e9 100644
--- a/tests/test_web.py
+++ b/tests/test_web.py
@@ -433,6 +433,23 @@ def test_index_lyrics_review_uses_endash_for_time_ranges():
assert "–" in body
+def test_lyrics_review_cache_clears_on_job_status_change():
+ """Regression: switching between done/failed/running jobs must clear the
+ cache so a previously-viewed done job re-fetches when re-selected."""
+ html = _index_html()
+ body = _fn_body(html, "renderLyricsReview")
+ # The "failed" branch must clear the cache before rendering the placeholder
+ assert re.search(
+ r'if\s*\(\s*job\.status\s*===\s*"failed"\s*\)\s*\{[^}]*'
+ r'delete\s+box\.dataset\.jobId',
+ body, re.S), "failed branch must delete cache key"
+ # The "not done" (queued/running) branch must clear the cache before rendering
+ assert re.search(
+ r'if\s*\(\s*job\.status\s*!==\s*"done"\s*\)\s*\{[^}]*'
+ r'delete\s+box\.dataset\.jobId',
+ body, re.S), "not done branch must delete cache key"
+
+
def test_index_has_copy_path_control():
assert "copy-path" in _index_html()