diff --git a/docs/TEST_INTEGRITY.md b/docs/TEST_INTEGRITY.md index 5c10d5a..12aa8aa 100644 --- a/docs/TEST_INTEGRITY.md +++ b/docs/TEST_INTEGRITY.md @@ -2572,8 +2572,8 @@ test_integrity_note: then: "Each original assertion and capture executes against the selected view without changing capture order, viewport coverage, artifact destination, or artifact safeguards" - id: SCN-console-priority-preview-invalidates-on-newer-evidence given: "A task priority preview and unsent priority and reason values are created from task evidence revision N" - when: "A separate confirmed sample API action advances that task to authoritative revision N+1 and Console refresh loads the newer evidence" - then: "The revision N token, preview, and confirmation control are absent; revision N+1 is visible, the unsent values remain intact and editable, and only a newly generated preview bound to N+1 can be presented" + when: "A separate confirmed sample API action advances that task to authoritative revision N+1, then the reader refreshes again after navigating away and back" + then: "The revision N token, preview, and confirmation control remain absent; revision N+1 is visible, the unsent values remain intact and editable across both refreshes, and a new preview is bound to N+1" - id: SCN-console-current-preview-and-draft-survive-unchanged-refresh given: "A task has an active priority preview and unsent priority and reason values bound to its current evidence revision" when: "Console refresh returns the same task revision" @@ -2602,6 +2602,7 @@ test_integrity_note: - "Selected fragment, visible view, original assertion results, and the unchanged ordered screenshot set" - "Revision N+1 displayed after refresh, with the old preview token and confirmation control unavailable" - "Unsent priority and reason values retained and editable after newer evidence invalidates the old preview" + - "The same draft remains dirty and intact after another unchanged refresh and route transition, with the old token and confirmation control still absent" - "An unchanged current-revision preview, confirmation boundary, and draft retained after refresh" - "A regenerated preview token and displayed revision bound to N+1" - "Exact Overview and contextual view titles, plus focused in-viewport secondary targets across direct loads and browser history" diff --git a/scripts/test-dashboard-demo.mjs b/scripts/test-dashboard-demo.mjs index 770753a..d6ec93c 100644 --- a/scripts/test-dashboard-demo.mjs +++ b/scripts/test-dashboard-demo.mjs @@ -339,6 +339,19 @@ try { assert.equal(await refreshedForm.locator('[name="reason"]').isDisabled(), false, 'the preserved reason remains editable'); assert.match(await refreshedForm.locator('.priority-change-status').innerText(), new RegExp(`revision ${revisionN1}`), 'the visible status identifies current task evidence'); + await selectView(stalePage, 'overview'); + await selectView(stalePage, 'work'); + await stalePage.locator('#refresh-console').click(); + await stalePage.locator('#live-refresh-status[data-generation="3"]').waitFor(); + assert.equal(Number(await refreshedForm.getAttribute('data-revision')), revisionN1); + assert.equal(await refreshedTicket.locator('.priority').textContent(), 'low'); + assert.equal(await refreshedForm.getAttribute('data-preview-token'), null); + assert.equal(await refreshedForm.locator('[data-priority-preview]').isVisible(), false); + assert.equal(await refreshedForm.locator('[data-priority-confirm]').count(), 0); + assert.equal(await refreshedForm.locator('[name="priority"]').inputValue(), 'urgent'); + assert.equal(await refreshedForm.locator('[name="reason"]').inputValue(), 'Review this change against the current task evidence.'); + assert.equal(await refreshedForm.locator('[name="priority"]').isDisabled(), false); + assert.equal(await refreshedForm.locator('[name="reason"]').isDisabled(), false); await refreshedTicket.locator('.priority-change > summary').click(); await refreshedForm.getByRole('button', { name: 'Review change', exact: true }).click(); await refreshedForm.locator('[data-priority-confirm]').waitFor(); @@ -347,7 +360,7 @@ try { 'a current preview must be regenerated from revision N+1'); const currentToken = await refreshedForm.getAttribute('data-preview-token'); await stalePage.locator('#refresh-console').click(); - await stalePage.locator('#live-refresh-status[data-generation="3"]').waitFor(); + await stalePage.locator('#live-refresh-status[data-generation="4"]').waitFor(); assert.equal(await refreshedForm.getAttribute('data-preview-token'), currentToken, 'unchanged current evidence must retain its active preview'); assert.equal(await refreshedForm.locator('[data-priority-preview]').isVisible(), true); diff --git a/site/console.js b/site/console.js index db52b95..f93a194 100644 --- a/site/console.js +++ b/site/console.js @@ -106,26 +106,25 @@ function invalidateStalePriorityPreviews(tasks = []) { const ticket = form.closest('.task-ticket'); if (!ticket) continue; - const priority = form.querySelector('[name="priority"]')?.value; - const reason = form.querySelector('[name="reason"]')?.value; - const openDetails = new Set([...ticket.querySelectorAll('details[open]')] - .map((details) => details.querySelector(':scope > summary')?.textContent)); - const template = document.createElement('template'); - template.innerHTML = taskCard(task, currentBacklogHealth).trim(); - const refreshedTicket = template.content.firstElementChild; - if (!refreshedTicket) continue; - const refreshedForm = refreshedTicket.querySelector('.priority-change-form'); - if (refreshedForm) { - const priorityField = refreshedForm.querySelector('[name="priority"]'); - const reasonField = refreshedForm.querySelector('[name="reason"]'); - if (priorityField && priority) priorityField.value = priority; - if (reasonField) reasonField.value = reason ?? ''; - priorityStatus(refreshedForm, `Task evidence advanced to revision ${currentRevision}. The older preview was cleared; review the current task before previewing again.`); + const priority = task.priority ?? 'normal'; + const priorityBadge = ticket.querySelector('.ticket-topline .priority'); + if (priorityBadge) { + priorityBadge.className = `priority priority-${escapeHtml(priority)}`; + priorityBadge.textContent = priority; } - refreshedTicket.querySelectorAll('details').forEach((details) => { - if (openDetails.has(details.querySelector(':scope > summary')?.textContent)) details.open = true; - }); - ticket.replaceWith(refreshedTicket); + form.dataset.revision = String(currentRevision); + delete form.dataset.previewToken; + delete form.dataset.planHash; + const priorityField = form.querySelector('[name="priority"]'); + const reasonField = form.querySelector('[name="reason"]'); + if (priorityField) priorityField.disabled = false; + if (reasonField) reasonField.disabled = false; + const preview = form.querySelector('[data-priority-preview]'); + if (preview) { + preview.replaceChildren(); + preview.hidden = true; + } + priorityStatus(form, `Task evidence advanced to revision ${currentRevision}. The older preview was cleared; review the current task before previewing again.`); } } diff --git a/test/fleet/console-view-navigation.test.mjs b/test/fleet/console-view-navigation.test.mjs index caae978..96d216a 100644 --- a/test/fleet/console-view-navigation.test.mjs +++ b/test/fleet/console-view-navigation.test.mjs @@ -125,6 +125,20 @@ test('SCN-console-priority-preview-invalidates-on-newer-evidence: refresh preser assert.equal(await form.locator('[name="reason"]').isDisabled(), false); assert.match(await form.locator('.priority-change-status').innerText(), new RegExp(`revision ${revisionN1}`)); + await page.locator('.console-rail a[href="#overview"]').click(); + await page.locator('.console-rail a[href="#work"]').click(); + await page.locator('#refresh-console').click(); + await page.locator('#live-refresh-status[data-generation="3"]').waitFor(); + assert.equal(Number(await form.getAttribute('data-revision')), revisionN1); + assert.equal(await ticket.locator('.priority').textContent(), 'low'); + assert.equal(await form.getAttribute('data-preview-token'), null); + assert.equal(await form.locator('[data-priority-preview]').isVisible(), false); + assert.equal(await form.locator('[data-priority-confirm]').count(), 0); + assert.equal(await form.locator('[name="priority"]').inputValue(), 'urgent'); + assert.equal(await form.locator('[name="reason"]').inputValue(), 'Preserve this unsent explanation across a newer task revision.'); + assert.equal(await form.locator('[name="priority"]').isDisabled(), false); + assert.equal(await form.locator('[name="reason"]').isDisabled(), false); + await form.getByRole('button', { name: 'Review change', exact: true }).click(); await form.locator('[data-priority-confirm]').waitFor(); const currentToken = await form.getAttribute('data-preview-token');