fix(profiles): exclude session history, backups, and snapshots from --clone-all (#45246)
--clone-all copied the source profile's state.db, sessions/, backups/, state-snapshots/, and checkpoints/ into the new profile. These are per-profile history: a 49GB copy in practice (15GB snapshots + 11GB backup archives + 16GB state.db + 6.4GB sessions), and restoring a copied backup inside the clone would resurrect the SOURCE profile's state. A clone is a fresh workspace; history stays with the source. New _CLONE_ALL_HISTORY_EXCLUDE_ROOT set, applied at root level for ANY source profile (named profiles accumulate the same artifacts), unlike the default-gated infrastructure excludes. Nested same-name dirs still copy. Docs and the post-create CLI message updated to match; profile export / hermes backup remain the full-history paths.
This commit is contained in:
@@ -268,9 +268,9 @@ class TestCreateProfile:
|
||||
def test_clone_all_excludes_default_infrastructure(self, profile_env):
|
||||
"""--clone-all from default profile excludes hermes-agent, .worktrees,
|
||||
bin, node_modules at root, plus __pycache__/*.pyc/*.pyo/*.sock/*.tmp
|
||||
at any depth. Profile data (config, env, skills, sessions, logs,
|
||||
state.db) must be preserved — clone-all means "complete snapshot
|
||||
minus infrastructure."
|
||||
at any depth. Profile data (config, env, skills, logs) must be
|
||||
preserved — clone-all means "complete snapshot minus infrastructure
|
||||
and per-profile history."
|
||||
"""
|
||||
tmp_path = profile_env
|
||||
default_home = tmp_path / ".hermes"
|
||||
@@ -296,8 +296,6 @@ class TestCreateProfile:
|
||||
(default_home / "skills" / "my-skill" / "SKILL.md").write_text("skill")
|
||||
(default_home / "config.yaml").write_text("model: gpt-4")
|
||||
(default_home / ".env").write_text("KEY=val")
|
||||
(default_home / "state.db").write_text("sessions-data")
|
||||
(default_home / "sessions").mkdir(exist_ok=True)
|
||||
(default_home / "logs").mkdir(exist_ok=True)
|
||||
(default_home / "logs" / "gateway.log").write_text("log")
|
||||
|
||||
@@ -319,10 +317,40 @@ class TestCreateProfile:
|
||||
assert (profile_dir / "skills" / "my-skill" / "SKILL.md").read_text() == "skill"
|
||||
assert (profile_dir / "config.yaml").read_text() == "model: gpt-4"
|
||||
assert (profile_dir / ".env").read_text() == "KEY=val"
|
||||
assert (profile_dir / "state.db").read_text() == "sessions-data"
|
||||
assert (profile_dir / "sessions").exists()
|
||||
assert (profile_dir / "logs" / "gateway.log").read_text() == "log"
|
||||
|
||||
def test_clone_all_excludes_history_artifacts(self, profile_env):
|
||||
"""--clone-all excludes the source's session history, backups, and
|
||||
snapshots — a clone is a fresh workspace, and these can reach tens
|
||||
of GB. Applies to ANY source profile, not just default.
|
||||
"""
|
||||
tmp_path = profile_env
|
||||
default_home = tmp_path / ".hermes"
|
||||
(default_home / "state.db").write_text("sessions-data")
|
||||
(default_home / "state.db-wal").write_text("wal")
|
||||
(default_home / "state.db-shm").write_text("shm")
|
||||
(default_home / "sessions" / "20260101_old").mkdir(parents=True)
|
||||
(default_home / "backups").mkdir(exist_ok=True)
|
||||
(default_home / "backups" / "backup.tar.gz").write_text("archive")
|
||||
(default_home / "state-snapshots" / "snap1").mkdir(parents=True)
|
||||
(default_home / "checkpoints" / "cp1").mkdir(parents=True)
|
||||
# Data that should still copy
|
||||
(default_home / "config.yaml").write_text("model: gpt-4")
|
||||
# Nested dirs with the same names must NOT be excluded (root-only)
|
||||
(default_home / "workspace" / "backups").mkdir(parents=True)
|
||||
(default_home / "workspace" / "backups" / "user-data.txt").write_text("mine")
|
||||
|
||||
profile_dir = create_profile("fresh", clone_all=True, no_alias=True)
|
||||
|
||||
for history in (
|
||||
"state.db", "state.db-wal", "state.db-shm",
|
||||
"sessions", "backups", "state-snapshots", "checkpoints",
|
||||
):
|
||||
assert not (profile_dir / history).exists(), history
|
||||
assert (profile_dir / "config.yaml").read_text() == "model: gpt-4"
|
||||
# Root-only: nested same-name dirs survive
|
||||
assert (profile_dir / "workspace" / "backups" / "user-data.txt").read_text() == "mine"
|
||||
|
||||
def test_clone_config_missing_files_skipped(self, profile_env):
|
||||
"""Clone config gracefully skips files that don't exist in source."""
|
||||
profile_dir = create_profile("coder", clone_config=True, no_alias=True)
|
||||
|
||||
Reference in New Issue
Block a user