diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 2d1985cabf0..ca79a849c39 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -2784,14 +2784,14 @@ DEFAULT_CONFIG = { "updates": { # Run a full ``hermes backup``-style zip of HERMES_HOME before every # ``hermes update``. Backups land in ``/backups/`` and - # can be restored with ``hermes import ``. Defaults to true - # after the #48200 incident: a ``hermes update --yes`` run that - # computed a wrong path silently wiped the user's ``.env``, - # ``MEMORY.md``, ``kanban.db``, custom skills, and scripts in one - # go. The cost of a few minutes of zip time per update is - # negligible compared to the alternative. Set to false to opt - # out, or pass ``--no-backup`` for a single update run. - "pre_update_backup": True, + # can be restored with ``hermes import ``. Off by default: + # zipping a large HERMES_HOME (sessions DB, caches, skills) can add + # minutes to every update. The #48200 incident — a ``hermes update + # --yes`` run that computed a wrong path and silently wiped the + # user's ``.env``, ``MEMORY.md``, ``kanban.db``, custom skills, and + # scripts — is the reason this knob exists; enable it (here, or via + # ``--backup`` for a single run) if you want that safety net. + "pre_update_backup": False, # How many pre-update backup zips to retain. Older ones are pruned # automatically after each successful backup. Values below 1 are # floored to 1 — the backup just created is always preserved. To diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 59e0ab93dc1..b4176f13e16 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -8324,13 +8324,12 @@ def _run_pre_update_backup(args) -> None: cfg = {} updates_cfg = cfg.get("updates", {}) if isinstance(cfg, dict) else {} - # The default config ships with ``pre_update_backup: true`` (see - # ``hermes_cli/config.py``). Fall back to true if the key is missing - # (e.g. a user has an older custom config without the field). The - # ``False`` default from before #48200 caused silent data loss when - # an update step computed a wrong path — the cost of a few minutes - # of zip time per update is negligible compared to the alternative. - enabled = updates_cfg.get("pre_update_backup", True) + # The default config ships with ``pre_update_backup: false`` (see + # ``hermes_cli/config.py``). Fall back to false if the key is missing + # so the default behaviour matches the shipped config: zipping a large + # HERMES_HOME can add minutes to every update. Users who want the + # #48200 safety net opt in via the config knob or ``--backup``. + enabled = updates_cfg.get("pre_update_backup", False) keep = updates_cfg.get("backup_keep", 5) if not enabled and not force_backup: diff --git a/hermes_cli/subcommands/update.py b/hermes_cli/subcommands/update.py index ddfe1db30a1..b2a632f202c 100644 --- a/hermes_cli/subcommands/update.py +++ b/hermes_cli/subcommands/update.py @@ -41,7 +41,7 @@ def build_update_parser(subparsers, *, cmd_update: Callable) -> None: "--backup", action="store_true", default=False, - help="Force a pre-update backup for this run (off by default; overrides updates.pre_update_backup)", + help="Force a pre-update backup for this run (off by default; overrides updates.pre_update_backup=false)", ) update_parser.add_argument( "--yes", diff --git a/tests/hermes_cli/test_backup.py b/tests/hermes_cli/test_backup.py index c576b726d7a..cb55fec50ac 100644 --- a/tests/hermes_cli/test_backup.py +++ b/tests/hermes_cli/test_backup.py @@ -1863,21 +1863,18 @@ class TestRunPreUpdateBackup: backups = list((hermes_home / "backups").glob("pre-update-*.zip")) assert len(backups) == 1 - def test_default_enabled_creates_backup(self, hermes_home, capsys): - """With the new safe default (``pre_update_backup: true``), every - ``hermes update`` creates a backup before any destructive step - runs — the cost is a few minutes of zip time vs. the alternative - of silent total data loss of ``~/.hermes/`` observed in #48200 - when an update step computes a wrong path and the user had no - safety net. + def test_default_disabled_is_silent(self, hermes_home, capsys): + """With the default (``pre_update_backup: false``), ``hermes update`` + does NOT create a backup and stays silent — zipping a large + HERMES_HOME can add minutes to every update. Users who want the + #48200 safety net opt in via the config knob or ``--backup``. """ from hermes_cli.main import _run_pre_update_backup _run_pre_update_backup(Namespace(no_backup=False, backup=False)) out = capsys.readouterr().out - assert "Creating pre-update backup" in out - assert "Saved:" in out - backups = list((hermes_home / "backups").glob("pre-update-*.zip")) - assert len(backups) == 1 + assert out == "" + assert not list((hermes_home / "backups").glob("pre-update-*.zip")) \ + if (hermes_home / "backups").exists() else True def test_no_backup_flag_skips(self, hermes_home, capsys): from hermes_cli.main import _run_pre_update_backup