Skip to content

fix(backup): treat files deleted after the scan as vanished, not archive failures - #124710

Open
ciaomrgrey wants to merge 4 commits into
NousResearch:mainfrom
ciaomrgrey:fix/backup-vanished-files-upstream
Open

ciaomrgrey wants to merge 4 commits into
NousResearch:mainfrom
ciaomrgrey:fix/backup-vanished-files-upstream

Conversation

@ciaomrgrey

Copy link
Copy Markdown

What does this PR do?

hermes backup scans the home, then archives. A plain file listed by the scan but deleted before the archive write (pruned cron output, a finished process record) raises ENOENT, lands in errors, and the run exits 1 with "Backup incomplete" and no --keep pruning. Nightly backups that run while agents and cron are active fail on files that no longer exist to recover.

This PR reports such files as "vanished" instead of errors, so the run completes and pruning proceeds. It stays deliberately narrow; everything else still fails:

  • only FileNotFoundError and os.path.lexists(path) is false now (ENOENT for a path that still exists stays an error);
  • other OSErrors (EACCES, EIO) stay errors;
  • a SQLite *.db never qualifies, on any archive path (main tree, external memory-provider files, automatic full-zip): a missing database is an incomplete backup.

Byte totals use the archived entry size (zf.infolist()[-1].file_size) so a delete right after the write cannot fault the stat().

Related Issue

No existing issue found. Related but different: #82042 (Chrome-debug transient path exclusions + SQLite busy bound, currently conflicting) — this PR is the general scan/archive race, independent of Chrome.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • hermes_cli/backup.py: _vanished_since_scan() helper; _write_zip_entries(..., on_vanished=None) (default behavior unchanged for callers that don't pass it); run_backup and the external-provider loop collect/print/log vanished files separately; the automatic full-zip path logs them at debug.
  • tests/hermes_cli/test_backup.py: race (file deleted between scan and write) completes and prunes; vanished + real error still fails; ENOENT with path still present still fails; vanished *.db still fails and does not prune; external provider text file vanishing is benign, external *.db vanishing stays fatal (including the prune guard).

How to Test

  1. pytest tests/hermes_cli/test_backup.py on main: the race / mixed / external-text tests fail (4 red incl. an external reviewer probe); the *.db negative guards pass on both (already correct on main).
  2. pytest tests/hermes_cli/test_backup*.py tests/hermes_cli/test_config_backups.py tests/hermes_cli/test_setup_reset_backup.py — 113 passed, 3 skipped, plus 1 failure (TestImportHonorsHermesHomeOverride::test_import_targets_named_profile_home, "file I/O against the REAL hermes home") that fails identically on pristine main on this host (unrelated).
  3. ruff check hermes_cli/backup.py tests/hermes_cli/test_backup.py — passed.

Checklist

  • Minimal diff (2 files), no behavior change for *.db, permission or I/O failures

Fork Sync Probe added 2 commits September 27, 2026 04:50
…ive failures

A file listed by the scan but deleted before the archive write (pruned cron
output, finished process records) no longer exists to recover. It raised
ENOENT, landed in errors, and made hermes backup exit 1 with a kept zip,
failing nightly producers that run while agents and cron are active.

Plain files whose write raises FileNotFoundError AND whose path is really
gone are now reported as vanished and kept out of errors, so the run is
complete and --keep pruning proceeds. Everything else still fails: other
OSErrors (EACCES, EIO), ENOENT for a path that still exists, and any *.db
snapshot failure (a vanished state.db is still an incomplete backup).
Byte totals use the archived entry size so a post-write delete cannot fault.
…tree databases

Review found the external memory-provider write loop excused ENOENT for
provider-declared *.db files, so a deleted memory.db produced a complete
backup and rotated older zips. The .db exclusion now lives in
_vanished_since_scan itself, so every archive path enforces it. Adds
external text-positive / DB-negative regressions including prune guard.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard labels Sep 27, 2026
Fork Sync Probe added 2 commits September 27, 2026 05:21
…e is vanished, not fatal

A finished task's scratch workspace (e.g. a throwaway Chrome profile) can be
cleaned up between scan and archive write. Its *.db files then failed the
snapshot and made the whole backup incomplete (27 Sep producer run: 5 chrome-prof
*.db under kanban/workspaces/t_d123f935/).

Treat a failed *.db snapshot as vanished only when the file sits inside
kanban/workspaces/<task>/ or kanban/boards/<slug>/workspaces/<task>/ and the
path is really gone. Hermes-owned databases (state.db, kanban.db, board DBs,
cron/executions.db), external provider databases, and any workspace DB that
still exists stay fatal.
…nd parent

Round-1 review: os.path.lexists() returns False on EACCES/EIO, so an
inaccessible, still-existing workspace database was classified as vanished.
Discriminate with os.lstat and admit only FileNotFoundError, for both the
file and its containing directory; any other error, or a still-present
directory, keeps the snapshot failure fatal.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants