feat(maintenance): reviewed, read-only maintenance plans

POST /prompt_manager/maintenance/preview reviews duplicate_prompts,
optimize, unused_components or missing_links on its own mode=ro snapshot
and answers a Plan (scope, protected reasons, backup policy, media, at most
50 detail rows). The fingerprint, targets and judged sources stay in a
server-side PlanStore under a single-use token (15 min, 8 plans, 16 MiB,
never truncated; a restart forgets them), bound to the database it read.

The previews reuse the legacy queries: duplicate groups and the merge are
shared with cleanup_duplicates, ORPHANED_PROMPTS_SQL is built from the
#239 protection predicates the plan reports, and missing links are judged
by the health check's link_states. Legacy /maintenance keeps its shape;
vacuum now reports success:false when VACUUM fails, and
cleanup_orphaned_images skips links under unavailable sources.
This commit is contained in:
Vito Sansevero
2026-10-23 17:16:00 -07:00
parent 6505d339aa
commit f09d2250df
12 changed files with 1684 additions and 113 deletions
+6 -2
View File
@@ -1374,13 +1374,17 @@ class TestModelHousekeeping(DatabaseTestCase):
):
self.assertEqual(self.db.model.get_database_info(), {})
def test_vacuum_logs_errors_instead_of_raising(self):
def test_vacuum_logs_and_raises_its_failure(self):
# S6: a failed VACUUM is reported (legacy /maintenance success:false),
# never logged as done.
self._save("x")
self.db.model.vacuum_database()
with patch(
"database.models.sqlite3.connect", side_effect=sqlite3.OperationalError
):
self.db.model.vacuum_database() # logged, not raised
with self.assertLogs("prompt_manager", "ERROR"):
with self.assertRaises(sqlite3.OperationalError):
self.db.model.vacuum_database()
self.assertEqual(len(self.db.search_prompts()), 1)
def test_close_tolerates_a_connection_that_fails_to_close(self):