diff --git a/database/pair_metadata_repair.py b/database/pair_metadata_repair.py index 7fdd4847..4b8a497a 100644 --- a/database/pair_metadata_repair.py +++ b/database/pair_metadata_repair.py @@ -78,6 +78,8 @@ PROTECTED = ( ), ) _PROTECTED_ORDER = [key for key, *_rest in PROTECTED] +# Detail rows: what the user can act on (a conflict) first. +DETAIL_ORDER = ("conflict", "edited", "archived", "too-many-tags") _CANDIDATES_SQL = """ SELECT p.id, p.hash, p.text, p.created_at, p.updated_at, @@ -304,7 +306,7 @@ def _protected_detail(result, key): def _details(results, targets): rows = [] - for key in _PROTECTED_ORDER: + for key in DETAIL_ORDER: for result in results: if key in _protected_classes(result): rows.append( @@ -384,7 +386,8 @@ def _fill_tags(conn, fills, expected): """Copy each source pair's tags onto its untagged prompt.""" cursor = conn.executemany( "INSERT INTO prompt_tags(prompt_id, tag_id) " - "SELECT ?, tag_id FROM prompt_pair_tags WHERE pair_id = ? " + "SELECT ?, ppt.tag_id FROM prompt_pair_tags ppt " + "JOIN tags t ON t.id = ppt.tag_id WHERE ppt.pair_id = ? " "AND NOT EXISTS (SELECT 1 FROM prompt_tags WHERE prompt_id = ?)", fills, ) diff --git a/tests/test_maintenance_pair_metadata.py b/tests/test_maintenance_pair_metadata.py index df3149e8..400071bd 100644 --- a/tests/test_maintenance_pair_metadata.py +++ b/tests/test_maintenance_pair_metadata.py @@ -302,6 +302,32 @@ class TestClassification(PairMetadataCase): self.assertEqual(draft.details[-1]["label"], "Record") self.assertEqual(draft.scope["count"], 60) + def test_detail_rows_list_conflicts_before_other_kept_prompts(self): + _a, archived = self.pair("aa archived", category="portrait") + self.run_sql( + "INSERT INTO prompt_archive(prompt_id, archived_at) VALUES(?, ?)", + archived, + "2026-10-31T00:00:00+00:00", + ) + _b, edited = self.pair("bb edited", category="portrait") + self.edit_stamp(edited) + self.pair("zz conflict", "n1", category="x") + self.pair("zz conflict", "n2", category="y") + draft = self.preview() + self.assertEqual( + [label for label, _detail in self.details(draft)], + ["zz conflict", "bb edited", "aa archived"], + ) + # The reasons keep the precedence order. + self.assertEqual( + draft.protected["reasons"], + [ + "1 archived in the Library", + "1 edited after it was saved", + "1 where pairs disagree", + ], + ) + def test_same_library_same_fingerprint_and_pair_use_does_not_change_it(self): pair_id, _prompt = self.pair("fp", category="portrait", tags=["a"]) first = self.preview().fingerprint