From 8e9750226d6c8e36ff390bd1af091c9d829c98f7 Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Thu, 29 Oct 2026 04:35:28 -0700 Subject: [PATCH] fix(library): name the saved pair, not negative variants, as the archive reason (#443) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A prompt in a saved pair with no negative variants was described in the Delete… review as "1 prompt with negative variants: hidden, restorable". Paired prompts are archived because of the pair, so the Archive row now reads "N prompts in a saved pair: hidden, restorable" and the first sentence explains the pair. Found in the live test of #470. --- TESTING.md | 3 ++- tests/browser/library-delete-concept.spec.mjs | 6 +++--- tests/browser/workspace-library-delete.spec.mjs | 4 +++- tests/js/workspace-library-delete.test.js | 16 ++++++++++++++-- web/js/workspace/library-delete.mjs | 7 ++++--- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/TESTING.md b/TESTING.md index 23d7eb8a..2d6ae598 100644 --- a/TESTING.md +++ b/TESTING.md @@ -2621,7 +2621,8 @@ rulings of 2026-10-28): too, is `aria-disabled` and Delete… reads "Reviewing…" (a double press sends one preview). - The review is the shared record-changing review (`openReviewDialog`), titled "Delete selected prompts", "Archive selected prompts" or "Delete or archive selected prompts", its - `dl.meta-grid` rows Delete permanently and Archive (always), Protected (kept) with up to + `dl.meta-grid` rows Delete permanently ("N prompts not in a saved pair") and Archive ("N prompts in a saved + pair: hidden, restorable", never negative variants: a pair's prompt with none is archived too) (always), Protected (kept) with up to three titles, Already archived, No longer exist, and, when something is deleted, Image links removed and Backup ("Create and verify before deleting", or "Off: no safety backup (System › Backup & export › Recovery policy)"); then `p.small` sentences: why paired diff --git a/tests/browser/library-delete-concept.spec.mjs b/tests/browser/library-delete-concept.spec.mjs index 9990507e..6f373449 100644 --- a/tests/browser/library-delete-concept.spec.mjs +++ b/tests/browser/library-delete-concept.spec.mjs @@ -90,12 +90,12 @@ function mapDeleteConcept({ danger, review }) { title: 'Delete or archive selected prompts', tree: ['dl.meta-grid', 'p.small', 'p.small', 'div.dialog-actions'], grid: { ...review.grid, rows: [['Delete permanently', '1 prompt not in a saved pair'], - ['Archive', '1 prompt with negative variants: hidden, restorable'], + ['Archive', '1 prompt in a saved pair: hidden, restorable'], ['Image links removed', '0 images will no longer show these prompts (files stay on disk)'], ['Backup', 'Create and verify before deleting']] }, notes: [ - { ...note, text: 'Prompts with negative variants, or used as one, are archived instead of deleted, so their variants ' - + 'and runs stay intact. Archived prompts cannot be deleted. To bring them back, press Show archived prompts, ' + { ...note, text: 'Prompts in a saved pair, as its positive or its negative, are archived instead of deleted, so the ' + + 'pair and its runs stay intact. Archived prompts cannot be deleted. To bring them back, press Show archived prompts, ' + 'select them and press Restore.' }, // A second paragraph takes the concept's own `p + p` margin (style.css 93-95). { ...note, marginTop: '10px', text: "Deleting removes each prompt's text, title, category, tags, rating, notes, run count, last use and " diff --git a/tests/browser/workspace-library-delete.spec.mjs b/tests/browser/workspace-library-delete.spec.mjs index 8df1f325..6c051316 100644 --- a/tests/browser/workspace-library-delete.spec.mjs +++ b/tests/browser/workspace-library-delete.spec.mjs @@ -48,7 +48,7 @@ test('Delete… reviews the selection, deletes the unpaired, archives the paired await expect(review(page)).toHaveAccessibleName('Delete or archive selected prompts'); const grid=review(page).locator('dl.meta-grid > div'); await expect(grid.locator('dt')).toHaveText(['Delete permanently','Archive','Protected (kept)','Image links removed','Backup']); - await expect(grid.locator('dd')).toHaveText(['1 prompt not in a saved pair','1 prompt with negative variants: hidden, restorable', + await expect(grid.locator('dd')).toHaveText(['1 prompt not in a saved pair','1 prompt in a saved pair: hidden, restorable', '1: Library extra 0000','0 images will no longer show these prompts (files stay on disk)','Create and verify before deleting']); await expect(review(page).locator('p.small')).toHaveCount(3); await expect(review(page).getByRole('button',{name:'Delete 1 and archive 1 prompt',exact:true})).toHaveClass(/danger/); @@ -78,6 +78,8 @@ test('Show archived prompts lists archived prompts, and Restore on its bar retur await box(page,cat.id).check();await box(page,dual.id).check(); await openReview(page); await expect(review(page)).toHaveAccessibleName('Archive selected prompts'); + // The archive reason is the saved pair, for one or many prompts (live note on #470): never negative variants. + await expect(review(page).locator('dl.meta-grid > div').filter({hasText:'Archive'}).locator('dd')).toHaveText('2 prompts in a saved pair: hidden, restorable'); await review(page).getByRole('button',{name:'Archive 2 prompts',exact:true}).click(); await expect(toast(page)).toHaveText('Archived 2 prompts.'); await expect(cards(page)).toHaveCount(before.length-2); diff --git a/tests/js/workspace-library-delete.test.js b/tests/js/workspace-library-delete.test.js index bd8a97da..ecbe429a 100644 --- a/tests/js/workspace-library-delete.test.js +++ b/tests/js/workspace-library-delete.test.js @@ -35,7 +35,7 @@ test('the grid shows each row only when it applies, with protected titles in sel not_found: 2, image_links: 3})); assert.deepEqual(reviewGrid(review, id => names[id]), [ ['Delete permanently', '1 prompt not in a saved pair'], - ['Archive', '1 prompt with negative variants: hidden, restorable'], + ['Archive', '1 prompt in a saved pair: hidden, restorable'], ['Protected (kept)', '4: Seven, Eight, Nine and 1 more'], ['Already archived', '1'], ['No longer exist', '2'], @@ -48,10 +48,22 @@ test('the grid shows each row only when it applies, with protected titles in sel assert.equal(reviewGrid(off).at(-1)[1], 'Off: no safety backup (System › Backup & export › Recovery policy)'); }); +test('the Archive row names the saved pair, never negative variants, for one or many prompts', async () => { + const {reviewOf, reviewGrid} = await source; + // A saved-pair prompt with 0 negative variants (live prompt 7901) is archive-only for its pair, not for variants. + const one = reviewGrid(reviewOf(preview({archive: [2]}))).find(([term]) => term === 'Archive'); + assert.deepEqual(one, ['Archive', '1 prompt in a saved pair: hidden, restorable']); + const many = reviewGrid(reviewOf(preview({archive: [2, 3, 4]}))).find(([term]) => term === 'Archive'); + assert.deepEqual(many, ['Archive', '3 prompts in a saved pair: hidden, restorable']); + for (const [, value] of [one, many]) assert.doesNotMatch(value, /variant/); +}); + test('the sentences explain archive, irreversibility per backup policy, re-creation and protection', async () => { const {reviewOf, reviewSentences} = await source; const [archive, remove, guarded] = reviewSentences(reviewOf(preview({delete: [1], archive: [2], protected: [3]}))); - assert.match(archive, /^Prompts with negative variants, or used as one, are archived instead of deleted/); + // Live test (#470): the archive reason is the saved pair, which a prompt with 0 negative variants has too. + assert.match(archive, /^Prompts in a saved pair, as its positive or its negative, are archived instead of deleted, so the pair and its runs stay intact\./); + assert.doesNotMatch(archive, /variant/); assert.match(archive, /Archived prompts cannot be deleted\. To bring them back, press Show archived prompts/); assert.match(remove, /the only way back is to restore the safety backup taken first \(System › Backup & export\), which returns the whole library to that moment\. It is kept among the last 10 safety backups\./); assert.match(remove, /A later scan, a new image or a node run with the same text adds it again as a new prompt\.$/); diff --git a/web/js/workspace/library-delete.mjs b/web/js/workspace/library-delete.mjs index f4805c66..e552d31d 100644 --- a/web/js/workspace/library-delete.mjs +++ b/web/js/workspace/library-delete.mjs @@ -65,7 +65,8 @@ export function protectedNames(review, titleOf) { export function reviewGrid(review, titleOf = () => null) { const rows = [ ['Delete permanently', `${countOf(review.delete.length, 'prompt')} not in a saved pair`], - ['Archive', `${countOf(review.archive.length, 'prompt')} with negative variants: hidden, restorable`], + // The archive reason is the saved pair (a pair's positive or negative), with or without negative variants. + ['Archive', `${countOf(review.archive.length, 'prompt')} in a saved pair: hidden, restorable`], ]; if (review.protected.length) { const names = protectedNames(review, titleOf); @@ -85,8 +86,8 @@ export function reviewGrid(review, titleOf = () => null) { export function reviewSentences(review) { const sentences = []; if (review.archive.length) { - sentences.push('Prompts with negative variants, or used as one, are archived instead of deleted, so their variants ' + - 'and runs stay intact. Archived prompts cannot be deleted. To bring them back, press Show archived prompts, ' + + sentences.push('Prompts in a saved pair, as its positive or its negative, are archived instead of deleted, so the ' + + 'pair and its runs stay intact. Archived prompts cannot be deleted. To bring them back, press Show archived prompts, ' + 'select them and press Restore.'); } if (review.delete.length) {