From fb9134f081c4abccd90651bc2348f6b7813f5506 Mon Sep 17 00:00:00 2001 From: Vito Date: Sun, 25 Oct 2026 15:32:50 -0700 Subject: [PATCH] feat(system): Backup & export (System S9) (#339) * feat(system): back up, restore and export natively System > Backup & export replaces its hand-off with the concept's section on the S8 backend: Back up downloads the verified copy and the status line shows only what GET /backup/status reports (D103); Review restore stages a chosen file and shows the server's review in the shared review dialog, Cancel discards the stage and Restore runs the Database restore job, after which the open views reload (D105); Export JSON plus the components and saved-pairs exports (D103); Import / inspect opens the shared Import & inspect dialog; the safety-backup policy saves through the Save row and scheduled backups offer Manual only (D104). Overview's Backup status row reads the same status. Refs #326 * test(system): compare Backup & export with the concept system-backups-concept.spec compares the section, the Restore preview and the Import & inspect dialog with the concept in both themes at 1600 and 390 wide, mapping only D103, D104, D105 (and D15/D55 for the shared Import dialog), and proves a renamed label, a removed row, a lost danger mark and swapped tokens are noticed. A review's grid values now wrap anywhere (D105), so a long backup file name keeps the concept's two equal columns at phone width. Refs #326 * docs(testing): Backup & export contract (D103-D105) Refs #326 * test(coverage): raise the frontend floors S9 measured system-backups.mjs gets its measured floor in place of the provisional one; admin.js, activity-scan.mjs and maintenance-review.mjs branches go up. Refs #326 * fix(system): keep a refused or handed-back restore review usable A Restore refused as busy now opens the same review again with Discard / Restore, and a restore job whose leases did not drain hands its review back (scope.review_kept) to the page that started it, which reopens it; every other path that abandons a token after Restore sends its DELETE, so held reviews can no longer lock restore out. The kept-review line says where the review is, and a full review store says what to do (I1). applyPlan resolves with its outcome and its job tracking now has failing-before proof (N5). Also: the safety-backup toggle says what the policy does (D104, M2); the restore picker has no type filter so safety backups can be chosen (M3); "Export all prompts JSON" names what /export holds (N1); small snapshots show KB (N3); the duplicated holding and save helpers are shared (N4); a dismissal test covers Cancel, x, Escape, a replacing dialog and leaving (M1). Refs #326 * test(coverage): raise the system-backups branch floor Refs #326 --- TESTING.md | 103 +++- py/api/backup_restore.py | 7 +- tests/browser/system-backups-concept.spec.mjs | 329 ++++++++++++ tests/browser/system-concept.spec.mjs | 7 +- .../browser/workspace-system-backups.spec.mjs | 489 ++++++++++++++++++ tests/browser/workspace-system.spec.mjs | 5 +- tests/browser/workspace.spec.mjs | 10 +- tests/browser/workspace_system_fixture.py | 40 ++ tests/js/workspace-activity-kinds.test.js | 2 +- tests/js/workspace-activity-store.test.js | 2 +- tests/js/workspace-system-backups.test.js | 286 ++++++++++ tests/js/workspace-system-overview.test.js | 11 +- tests/js/workspace-toolbar.test.js | 36 +- tests/test_restore_review_fixes.py | 28 +- tools/vendor/frontend-coverage-baseline.json | 12 +- web/js/workspace/activity-kinds.mjs | 13 +- web/js/workspace/activity-scan.mjs | 16 +- web/js/workspace/images.mjs | 15 +- web/js/workspace/index.mjs | 21 +- web/js/workspace/library.mjs | 4 + web/js/workspace/maintenance-review.mjs | 28 +- web/js/workspace/system-backups.mjs | 464 +++++++++++++++++ web/js/workspace/system-overview.mjs | 40 +- web/js/workspace/system.mjs | 9 +- web/js/workspace/workspace-dialogs.mjs | 25 +- web/js/workspace/workspace.css | 3 + 26 files changed, 1935 insertions(+), 70 deletions(-) create mode 100644 tests/browser/system-backups-concept.spec.mjs create mode 100644 tests/browser/workspace-system-backups.spec.mjs create mode 100644 tests/js/workspace-system-backups.test.js create mode 100644 web/js/workspace/system-backups.mjs diff --git a/TESTING.md b/TESTING.md index 8ea2d7f1..9149ba93 100644 --- a/TESTING.md +++ b/TESTING.md @@ -2378,7 +2378,8 @@ paints nothing. Until it answers, the section shows "Loading the overview…"; a shows "The overview could not be loaded. Your saved data has not changed." and Retry, never zeros. The thumbnail row is a separate read of `/images/thumbnails/status` (a walk of every source), so the other values never wait for it: "Checking thumbnails…" -until it answers. The health check (`POST /prompt_manager/system/health`) is a `read` +until it answers. The Backup status row is likewise its own read of +`/prompt_manager/backup/status` (S9). The health check (`POST /prompt_manager/system/health`) is a `read` registry job of kind `health`: one at a time, beside a scan, not cancellable and with no file outcomes. The page tracks the started job in the Activity store (its event stream; the store marks it lost if the server stops listing it), never by polling. A press while one runs (another tab) waits @@ -2399,7 +2400,7 @@ the connection's `total_changes` is 0 at close and the data reads back unchanged `-shm` bytes are not compared: every WAL reader rewrites its read marks there, so no reader can keep it byte-identical (`test_health_check_never_writes`). -Recorded deviations (plan D60–D70, D74, D75, D79–D82 renumbered D92–D102, D106, D107 and D111–D114; D121–D131 assigned): +Recorded deviations (plan D60–D75, D79–D82 renumbered D92–D107 and D111–D114; D121–D131 assigned): - **D92** (shell): no "Sample configuration" chip. The Save status is truthful: "No unsaved changes.", "Unsaved changes.", "Saved in this browser." / "Saved to the server @@ -2416,7 +2417,10 @@ Recorded deviations (plan D60–D70, D74, D75, D79–D82 renumbered D92–D102, `needs_review.pending`) with the concept's line; its Review metadata action opens `#/review` (the concept's `go-review`). A count that is not an integer reads "Ambiguous image links" / "Not available yet." (Q6). - "Backup status" reads "No retained backup record." until verified backups exist (S8). + "Backup status" says what `GET /prompt_manager/backup/status` reports, in Backup & + export's own status line (D103, S9): "Checking backups…" until its own read answers, + then the session's last download and the newest safety backup, "No backup recorded." + or "The backup status could not be read.". The thumbnail row is "Checking thumbnails…" until its own read answers, then "No missing thumbnails", "N missing thumbnails" (the pending count of `/images/thumbnails/status`, followed by " · M outdated" when previews are in an @@ -2615,6 +2619,68 @@ Recorded deviations (plan D60–D70, D74, D75, D79–D82 renumbered D92–D102, is D93's style. `maintenance matches the concept` (both themes and sizes) maps only this row; `maintenance comparison detects mutations` proves it is the only difference and catches a removed row, a renamed operation, a lost danger mark and a swapped token. +- **D103** (Backup & export, S9): "Back up" (the concept's "Simulate backup") downloads + the verified copy of `GET /prompt_manager/backup` through the api client (the same + `downloadDatabaseBackup` as every backup box; a copy whose `X-PromptManager-Verify` is + not `ok` toasts "Database backup downloaded, but it did not pass verification: + "), then reads the status again. The `p.small#backup-status` under the + actions shows only what `GET /prompt_manager/backup/status` reports: "Downloaded + backup · · verified." (or "not verified") for this session's last + download and "Safety backup · · N on disk." for the newest + `.backup_*`, else "No backup recorded."; "Checking backups…" while read and "The + backup status could not be read." when it failed (never "no backup"). Portable + export's "Export JSON" (the concept's "Export sample JSON") downloads the Library + document (`/library/export`); a second `.row-actions.spaced` adds "Export all prompts + JSON" (`/export`, the legacy admin page's Export of every prompt) and "Export saved pairs JSON" + (`/pairs/export`), plain buttons as the concept's; a failed export toasts "The export + could not be downloaded. Nothing was saved to this device." "Import / inspect file" + opens the shared "Import & inspect" (D15, D55), as Images does. +- **D104** (Backup & export, S9, Q4): Recovery policy keeps the concept's "Proposed" chip. + "Create a safety backup before maintenance" is the server's `safety_backup` + (`maintenance.safety_backup`) policy. Its line says what it does ("Keep a verified copy + of the database on disk before maintenance, cleanups and LoRA imports change records. A + restore always keeps one."); the concept's "Preview a verified backup before applying + operations that change records." describes nothing the server does. It is drafted behind the Save row and saved with + `expected_revision` (a configuration changed elsewhere is the conflict status); it is + disabled with "Server settings could not be read." when the read failed. "Scheduled + database backups" offers Manual only; Daily and Weekly stay listed but disabled + (there is no scheduler), the row's line is "Daily and Weekly are not available yet: + back up with Back up whenever you choose." (the concept's proposal line is + prototype copy) and nothing is drafted for it. The warning note reads "Restores are + staged, migrated and validated before replacing live data." +- **D105** (Backup & export, S9): "Review restore…" picks a file (no type filter: the + safety backups `.backup_