diff --git a/.changes/planning/fix-interview-web-view-followups-3c7d91a2.md b/.changes/planning/fix-interview-web-view-followups-3c7d91a2.md new file mode 100644 index 0000000000..177dbf8045 --- /dev/null +++ b/.changes/planning/fix-interview-web-view-followups-3c7d91a2.md @@ -0,0 +1,7 @@ +--- +bump: patch +--- + +### Fixed + +- When no session has listened for 30 seconds, the interview page's timer now updates the question chips and the receipt line together; before, the receipt line could keep "Sent to Claude" for up to two seconds after a chip read "No session is listening". diff --git a/plugins/planning/surface/index.html b/plugins/planning/surface/index.html index 74933e7f72..85c28b72f8 100644 --- a/plugins/planning/surface/index.html +++ b/plugins/planning/surface/index.html @@ -2113,7 +2113,7 @@

Interview

}); window.addEventListener("resize", () => grow($("note"))); let wasUnheard = false; - setInterval(() => { if (!S.loaded) return; renderHeader(); if (unheard() !== wasUnheard) { wasUnheard = unheard(); renderRail(); } }, 2000); + setInterval(() => { if (!S.loaded) return; renderHeader(); const u = unheard(); if (u !== wasUnheard) { wasUnheard = u; renderRail(); renderHeader(); } }, 2000); // unheard() turns true as time passes, so the header is redrawn after the rail on the tick it flips applySettings(); refresh().then(() => setOnline(true)).catch(() => { setOnline(false); restoreFinish(); }).finally(connect); diff --git a/plugins/planning/surface/surface.test.sh b/plugins/planning/surface/surface.test.sh index 1df7ff6f12..1f2c7c63c3 100755 --- a/plugins/planning/surface/surface.test.sh +++ b/plugins/planning/surface/surface.test.sh @@ -374,7 +374,7 @@ if command -v playwright-cli >/dev/null 2>&1; then for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do grade "ui_journey.$n" "$tmp/uj$n.out"; done for n in 1 2 3 4; do grade "ui_live.$n" "$tmp/ul$n.out"; done else - browser=472 journey=222 + browser=473 journey=222 echo "SKIP: $browser browser checks not run, $journey of them the journey (playwright-cli not found)" # silent-skip-ok: browser checks need a local playwright-cli # discriminating-skip-ok: the API, watcher and hygiene checks above still grade this suite skip=$((skip + browser)) fi diff --git a/plugins/planning/surface/tests/ui_c.js b/plugins/planning/surface/tests/ui_c.js index c6537b20b6..be0a366ba7 100644 --- a/plugins/planning/surface/tests/ui_c.js +++ b/plugins/planning/surface/tests/ui_c.js @@ -236,6 +236,13 @@ async page => { await page.click("main.detail h3"); await page.keyboard.press("Control+Enter"); await page.waitForTimeout(800); const ev4 = await last(); ok("an armed alternative survives a Reconfirm row shifting the key numbers: (b) stays armed once stale and saves alt b", /^\(b\)/.test(a0) && wasStale && /^\(b\)/.test(a1) && ev4.id === "P2" && ev4.kind === "alt" && ev4.alt === "b", a0 + " / " + a1 + " " + JSON.stringify(ev4)); + // Reconfirm of a person-held deferral with a new name typed over the kept one stays person-held + await restale({kind: "defer", text: "someone else decides: Dana"}, {kind: "alt", alt: "b"}); + await page.fill("#note", "Lee, the architect"); + await page.click("main.detail h3"); await page.keyboard.press("a"); + const r7 = (await armed()).trim(); await page.keyboard.press("Control+Enter"); await page.waitForTimeout(800); + const ev7 = await last(); + ok("Reconfirm of a person-held deferral with a new name keeps the someone else decides prefix", /^Reconfirm/.test(r7) && ev7.id === "P2" && ev7.kind === "defer" && ev7.text === "someone else decides: Lee, the architect", r7 + " " + JSON.stringify(ev7)); // Reconfirm with a note typed over the kept one records the typed note await restale({kind: "alt", alt: "b", text: "Only on weekends"}, {kind: "accept"}); await page.fill("#note", "Weekdays too"); @@ -500,7 +507,8 @@ async page => { ok("a fresh answer, with no watcher silent for 30 s yet, reads Sent to Claude", /Sent to Claude/.test(await chip()), await chip()); const quiet = await page.waitForFunction(() => /No session is listening; type next in the terminal/.test(document.querySelector('.qbtn[data-q="D1"]').textContent), null, {timeout: 45000}).then(() => true).catch(() => false); ok("with no watcher holding the lease, an unhandled answer reads as not delivered", quiet && !/Sent to Claude/.test(await chip()), await chip()); - ok("the receipt line says the same", /No session is listening/.test(await page.textContent("#claudeLine")), await page.textContent("#claudeLine")); + const line = await page.waitForFunction(() => /No session is listening/.test(document.getElementById("claudeLine").textContent), null, {timeout: 5000}).then(() => true).catch(() => false); + ok("the receipt line says the same", line, await page.textContent("#claudeLine")); // The watcher claims the lease; the shell handles the event a few seconds later. const r = await page.request.get(base + "api/wait?after=handled&timeout=1&watcher=w1", {headers: {"X-Interview-Token": await token()}}); const back = await page.waitForFunction(() => /Sent to Claude/.test(document.querySelector('.qbtn[data-q="D1"]').textContent), null, {timeout: 12000}).then(() => true).catch(() => false);