From 679809b727879daa9295b16fe36bd8605f55a3fa Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 03:51:19 +0300 Subject: [PATCH 01/14] [OMEGA-492] Cover a repeated copy while a container holds omega-memory --- tests/test_omega_launcher_migration.py | 73 ++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/test_omega_launcher_migration.py b/tests/test_omega_launcher_migration.py index c67cd641..941af501 100644 --- a/tests/test_omega_launcher_migration.py +++ b/tests/test_omega_launcher_migration.py @@ -23,6 +23,7 @@ root = Path(os.environ["FAKE_DOCKER_ROOT"]) volumes = root / "volumes" containers = root / "containers" +mounts_dir = root / "mounts" args = sys.argv[1:] with open(root / "calls.log", "a", encoding="utf-8") as log: @@ -33,6 +34,13 @@ def volume(name): return volumes / name +def containers_using(name): + return sorted( + path.name for path in mounts_dir.iterdir() + if name in path.read_text().split() + ) + + if args[:2] == ["volume", "inspect"]: sys.exit(0 if volume(args[2]).is_dir() else 1) if args[:2] == ["volume", "create"]: @@ -42,8 +50,29 @@ def volume(name): if args[:2] == ["volume", "rm"]: if os.environ.get("FAKE_DOCKER_FAIL_VOLUME_RM") or not volume(args[2]).is_dir(): sys.exit(1) + users = containers_using(args[2]) + if users: + ids = ", ".join(format(abs(hash(user)), "x") for user in users) + print(f"Error response from daemon: remove {args[2]}: volume is in use - [{ids}]", file=sys.stderr) + sys.exit(1) shutil.rmtree(volume(args[2])) sys.exit(0) +if args[:1] == ["rm"]: + name = args[-1] + if not (containers / name).exists(): + print(f"Error response from daemon: No such container: {name}", file=sys.stderr) + sys.exit(1) + (containers / name).unlink() + (mounts_dir / name).unlink(missing_ok=True) + sys.exit(0) +if args[:1] == ["ps"]: + used = [args[index + 1].split("=", 1)[1] for index, option in enumerate(args) + if option == "--filter" and args[index + 1].startswith("volume=")] + names = [path.name for path in containers.iterdir()] + for name in used: + names = [user for user in containers_using(name) if user in names] + print("\n".join(names)) + sys.exit(0) if args[:1] == ["inspect"]: state = containers / args[-1] if not state.exists(): @@ -59,6 +88,7 @@ def volume(name): if args[:1] == ["run"]: mounts = {} entrypoint = None + container_name = None index = 1 while index < len(args): option = args[index] @@ -69,6 +99,8 @@ def volume(name): elif option in ("--entrypoint", "--user", "--group-add", "-e", "--name", "--security-opt", "--tmpfs"): if option == "--entrypoint": entrypoint = args[index + 1] + if option == "--name": + container_name = args[index + 1] index += 2 elif option.startswith("-"): index += 1 @@ -86,6 +118,11 @@ def volume(name): volume(source).mkdir(parents=True, exist_ok=True) if target == MEMORY_PATH and not any(volume(source).iterdir()): shutil.copytree(root / "image-memory", volume(source), dirs_exist_ok=True) + if container_name: + (containers / container_name).write_text("running") + (mounts_dir / container_name).write_text( + "\n".join(source for source in mounts.values() if "/" not in source) + ) if entrypoint == "sh" and command[:1] == ["-c"]: script = command[1] for target in sorted(mounts, key=len, reverse=True): @@ -117,6 +154,7 @@ def docker_root(tmp_path): root = tmp_path / "docker" (root / "volumes").mkdir(parents=True) (root / "containers").mkdir() + (root / "mounts").mkdir() image_memory = root / "image-memory" (image_memory / "chroma_db").mkdir(parents=True) (image_memory / "history.metta").write_text("") @@ -383,6 +421,41 @@ def test_interrupted_copy_is_redone(docker_root): assert _started_agent(docker_root) +def _container_on_volume(root, name, state, volume_name): + (root / "containers" / name).write_text(state) + (root / "mounts" / name).write_text(volume_name) + + +@pytest.mark.parametrize("state", ["exited", "running"]) +def test_interrupted_copy_is_redone_while_omega_container_holds_new_volume(docker_root, state): + old, new = _interrupted_migration(docker_root, image_files=True) + _container_on_volume(docker_root, "omega", state, "omega-memory") + + result = _launcher(docker_root, "start", "-d", IMAGE) + + assert result.returncode == 0, result.stderr + assert "volume is in use" not in result.stderr + assert _read(new / "history.metta") == "(old history)\n" + assert _read(new / "chroma_db" / "chroma.sqlite3") == "old long-term memory" + assert (old / ".migrated-to-omega").exists() + assert _started_agent(docker_root) + + +def test_interrupted_copy_names_other_containers_on_new_volume(docker_root): + old, new = _interrupted_migration(docker_root, image_files=True) + _container_on_volume(docker_root, "omega-leftover", "exited", "omega-memory") + before = _snapshot(new) + + result = _launcher(docker_root, "start", "-d", IMAGE) + + assert result.returncode != 0 + assert "omega-leftover" in result.stderr + assert _snapshot(new) == before + assert (docker_root / "containers" / "omega-leftover").exists() + assert not (old / ".migrated-to-omega").exists() + assert not _started_agent(docker_root) + + def test_run_interrupted_before_the_image_files_is_redone(docker_root): old, new = _interrupted_migration(docker_root, image_files=False) From 1d72feac0345732567d7e5f635f62747eb1e50e8 Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 03:53:11 +0300 Subject: [PATCH 02/14] [OMEGA-492] Remove the omega container before redoing an interrupted copy --- scripts/omega | 18 ++++++++++++++---- tests/test_omega_launcher_migration.py | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/scripts/omega b/scripts/omega index 15954614..55e54fa6 100755 --- a/scripts/omega +++ b/scripts/omega @@ -854,11 +854,13 @@ migrate_omegaclaw_memory() { local old_container="omegaclaw" local old_volume="omegaclaw-memory" local new_volume="omega-memory" + local new_container="omega" local memory_path="/PeTTa/repos/Omega/memory" local marker=".migrated-to-omega" local started_marker=".migration-started" local stopped_old_container=0 local old_state + local holders if [ -n "${memory_import_file}" ] \ || ! docker volume inspect "${old_volume}" >/dev/null 2>&1; then @@ -875,10 +877,18 @@ migrate_omegaclaw_memory() { ;; interrupted) echo "The last copy from ${old_volume} did not finish, copying again" - if docker volume inspect "${new_volume}" >/dev/null 2>&1 \ - && ! docker volume rm "${new_volume}" >/dev/null; then - echo "Could not remove the partly copied volume ${new_volume}" >&2 - return 1 + if docker volume inspect "${new_volume}" >/dev/null 2>&1; then + docker rm -f "${new_container}" >/dev/null 2>&1 || true + if ! docker volume rm "${new_volume}" >/dev/null; then + holders=$(docker ps -a --filter "volume=${new_volume}" --format '{{.Names}}' 2>/dev/null | tr '\n' ' ') + holders="${holders% }" + if [ -n "${holders}" ]; then + echo "Could not remove the partly copied volume ${new_volume}, it is used by containers: ${holders}. Remove them with 'docker rm -f ${holders}' and start again" >&2 + else + echo "Could not remove the partly copied volume ${new_volume}" >&2 + fi + return 1 + fi fi ;; *) diff --git a/tests/test_omega_launcher_migration.py b/tests/test_omega_launcher_migration.py index 941af501..9c7ca1ab 100644 --- a/tests/test_omega_launcher_migration.py +++ b/tests/test_omega_launcher_migration.py @@ -61,7 +61,7 @@ def containers_using(name): name = args[-1] if not (containers / name).exists(): print(f"Error response from daemon: No such container: {name}", file=sys.stderr) - sys.exit(1) + sys.exit(0 if "-f" in args else 1) (containers / name).unlink() (mounts_dir / name).unlink(missing_ok=True) sys.exit(0) From f7f7e5c24050d2f47e465d620f418f866b8952e5 Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 04:31:45 +0300 Subject: [PATCH 03/14] [OMEGA-492] Cover leftover copy containers and a memory import after an interrupted copy --- tests/test_omega_launcher_migration.py | 82 ++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/tests/test_omega_launcher_migration.py b/tests/test_omega_launcher_migration.py index 9c7ca1ab..c7291230 100644 --- a/tests/test_omega_launcher_migration.py +++ b/tests/test_omega_launcher_migration.py @@ -10,6 +10,7 @@ REPO_ROOT = Path(__file__).resolve().parents[1] LAUNCHER = REPO_ROOT / "scripts" / "omega" IMAGE = "test/omega:new" +MIGRATION_LABEL = "omega.memory-migration" FAKE_DOCKER = r'''#!/usr/bin/env python3 import os @@ -24,6 +25,7 @@ volumes = root / "volumes" containers = root / "containers" mounts_dir = root / "mounts" +labels_dir = root / "labels" args = sys.argv[1:] with open(root / "calls.log", "a", encoding="utf-8") as log: @@ -64,13 +66,17 @@ def containers_using(name): sys.exit(0 if "-f" in args else 1) (containers / name).unlink() (mounts_dir / name).unlink(missing_ok=True) + (labels_dir / name).unlink(missing_ok=True) sys.exit(0) if args[:1] == ["ps"]: - used = [args[index + 1].split("=", 1)[1] for index, option in enumerate(args) - if option == "--filter" and args[index + 1].startswith("volume=")] - names = [path.name for path in containers.iterdir()] - for name in used: - names = [user for user in containers_using(name) if user in names] + filters = [args[index + 1].split("=", 1) for index, option in enumerate(args) if option == "--filter"] + names = sorted(path.name for path in containers.iterdir()) + for key, value in filters: + if key == "volume": + names = [user for user in containers_using(value) if user in names] + elif key == "label": + names = [name for name in names + if (labels_dir / name).exists() and value in (labels_dir / name).read_text().split()] print("\n".join(names)) sys.exit(0) if args[:1] == ["inspect"]: @@ -96,7 +102,7 @@ def containers_using(name): source, target = args[index + 1].split(":")[:2] mounts[target] = source index += 2 - elif option in ("--entrypoint", "--user", "--group-add", "-e", "--name", "--security-opt", "--tmpfs"): + elif option in ("--entrypoint", "--user", "--group-add", "-e", "--name", "--security-opt", "--tmpfs", "--label"): if option == "--entrypoint": entrypoint = args[index + 1] if option == "--name": @@ -155,6 +161,7 @@ def docker_root(tmp_path): (root / "volumes").mkdir(parents=True) (root / "containers").mkdir() (root / "mounts").mkdir() + (root / "labels").mkdir() image_memory = root / "image-memory" (image_memory / "chroma_db").mkdir(parents=True) (image_memory / "history.metta").write_text("") @@ -312,8 +319,7 @@ def test_without_old_volume_agent_starts_on_image_memory(docker_root): assert not (docker_root / "volumes" / "omegaclaw-memory").exists() -def test_memory_import_skips_migration(docker_root, tmp_path): - _install_omegaclaw(docker_root) +def _memory_import_arguments(docker_root, tmp_path): transfer = tmp_path / "transfer" transfer.mkdir() transfer.chmod(0o2770) @@ -328,9 +334,7 @@ def test_memory_import_skips_migration(docker_root, tmp_path): encoding="utf-8", ) python3.chmod(0o755) - - result = _launcher( - docker_root, + return ( "start", "-d", IMAGE, @@ -342,6 +346,12 @@ def test_memory_import_skips_migration(docker_root, tmp_path): "memory.tar.gz", ) + +def test_memory_import_skips_migration(docker_root, tmp_path): + _install_omegaclaw(docker_root) + + result = _launcher(docker_root, *_memory_import_arguments(docker_root, tmp_path)) + assert result.returncode == 0, result.stderr assert _read(docker_root / "volumes" / "omega-memory" / "history.metta") == "" assert _container_state(docker_root, "omegaclaw") == "running" @@ -421,9 +431,11 @@ def test_interrupted_copy_is_redone(docker_root): assert _started_agent(docker_root) -def _container_on_volume(root, name, state, volume_name): +def _container_on_volume(root, name, state, volume_names, label=None): (root / "containers" / name).write_text(state) - (root / "mounts" / name).write_text(volume_name) + (root / "mounts" / name).write_text(volume_names) + if label: + (root / "labels" / name).write_text(label) @pytest.mark.parametrize("state", ["exited", "running"]) @@ -456,6 +468,50 @@ def test_interrupted_copy_names_other_containers_on_new_volume(docker_root): assert not _started_agent(docker_root) +def test_interrupted_copy_removes_leftover_copy_containers(docker_root): + old, new = _interrupted_migration(docker_root, image_files=True) + _container_on_volume(docker_root, "musing_haslett", "running", "omegaclaw-memory omega-memory", label=MIGRATION_LABEL) + + result = _launcher(docker_root, "start", "-d", IMAGE) + + assert result.returncode == 0, result.stderr + assert _read(new / "history.metta") == "(old history)\n" + assert (old / ".migrated-to-omega").exists() + assert not (docker_root / "containers" / "musing_haslett").exists() + assert _started_agent(docker_root) + + +def test_migration_containers_carry_the_label(docker_root): + _install_omegaclaw(docker_root) + + result = _launcher(docker_root, "start", "-d", IMAGE) + + assert result.returncode == 0, result.stderr + helper_runs = [call for call in _calls(docker_root) if call.startswith("run --rm")] + assert len(helper_runs) >= 4 + assert all(f"--label {MIGRATION_LABEL}" in call for call in helper_runs) + + +def test_memory_import_replaces_an_interrupted_copy(docker_root, tmp_path): + old, new = _interrupted_migration(docker_root, image_files=True) + (new / "history.metta").write_text("(partly copied)\n") + _container_on_volume(docker_root, "omega", "exited", "omega-memory") + + result = _launcher(docker_root, *_memory_import_arguments(docker_root, tmp_path)) + + assert result.returncode == 0, result.stderr + assert _read(new / "history.metta") == "" + assert not (old / ".migration-started").exists() + assert not (old / ".migrated-to-omega").exists() + assert _started_agent(docker_root) + + (new / "history.metta").write_text("(imported and used)\n") + again = _launcher(docker_root, "start", "-d", IMAGE) + + assert again.returncode == 0, again.stderr + assert _read(new / "history.metta") == "(imported and used)\n" + + def test_run_interrupted_before_the_image_files_is_redone(docker_root): old, new = _interrupted_migration(docker_root, image_files=False) From de28d8abb286b1801bfe5ff732f57e4e47097b2b Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 04:32:41 +0300 Subject: [PATCH 04/14] [OMEGA-492] Clear leftover copy containers and let a memory import replace an interrupted copy --- scripts/omega | 73 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/scripts/omega b/scripts/omega index 55e54fa6..28e2da82 100755 --- a/scripts/omega +++ b/scripts/omega @@ -850,46 +850,75 @@ options() { fi } +remove_partly_copied_memory() { + local volume="$1" + local container="$2" + local label="$3" + local leftover + local holders + + if ! docker volume inspect "${volume}" >/dev/null 2>&1; then + return 0 + fi + docker rm -f "${container}" >/dev/null 2>&1 || true + for leftover in $(docker ps -aq --filter "label=${label}" 2>/dev/null); do + docker rm -f "${leftover}" >/dev/null 2>&1 || true + done + if docker volume rm "${volume}" >/dev/null; then + return 0 + fi + holders=$(docker ps -a --filter "volume=${volume}" --format '{{.Names}}' 2>/dev/null | tr '\n' ' ') + holders="${holders% }" + if [ -n "${holders}" ]; then + echo "Could not remove the partly copied volume ${volume}, it is used by containers: ${holders}. Remove them with 'docker rm -f ${holders}' and start again" >&2 + else + echo "Could not remove the partly copied volume ${volume}" >&2 + fi + return 1 +} + migrate_omegaclaw_memory() { local old_container="omegaclaw" local old_volume="omegaclaw-memory" local new_volume="omega-memory" local new_container="omega" + local migration_label="omega.memory-migration" local memory_path="/PeTTa/repos/Omega/memory" local marker=".migrated-to-omega" local started_marker=".migration-started" local stopped_old_container=0 local old_state - local holders - if [ -n "${memory_import_file}" ] \ - || ! docker volume inspect "${old_volume}" >/dev/null 2>&1; then + if ! docker volume inspect "${old_volume}" >/dev/null 2>&1; then return 0 fi - if ! old_state=$(docker run --rm --entrypoint sh --volume "${old_volume}:/from:ro" "${image}" \ + if ! old_state=$(docker run --rm --label "${migration_label}" --entrypoint sh --volume "${old_volume}:/from:ro" "${image}" \ -c "if [ -e /from/${marker} ]; then echo migrated; elif [ -e /from/${started_marker} ]; then echo interrupted; fi"); then + if [ -n "${memory_import_file}" ]; then + return 0 + fi echo "Could not read the migration markers in ${old_volume}" >&2 return 1 fi + if [ -n "${memory_import_file}" ]; then + if [ "${old_state}" = "interrupted" ]; then + echo "The last copy from ${old_volume} did not finish, the memory import replaces it" + remove_partly_copied_memory "${new_volume}" "${new_container}" "${migration_label}" || return 1 + if ! docker run --rm --label "${migration_label}" --entrypoint sh --volume "${old_volume}:/from" "${image}" \ + -c "rm -f /from/${started_marker}"; then + echo "Could not clear the migration marker in ${old_volume}" >&2 + return 1 + fi + fi + return 0 + fi case "${old_state}" in migrated) return 0 ;; interrupted) echo "The last copy from ${old_volume} did not finish, copying again" - if docker volume inspect "${new_volume}" >/dev/null 2>&1; then - docker rm -f "${new_container}" >/dev/null 2>&1 || true - if ! docker volume rm "${new_volume}" >/dev/null; then - holders=$(docker ps -a --filter "volume=${new_volume}" --format '{{.Names}}' 2>/dev/null | tr '\n' ' ') - holders="${holders% }" - if [ -n "${holders}" ]; then - echo "Could not remove the partly copied volume ${new_volume}, it is used by containers: ${holders}. Remove them with 'docker rm -f ${holders}' and start again" >&2 - else - echo "Could not remove the partly copied volume ${new_volume}" >&2 - fi - return 1 - fi - fi + remove_partly_copied_memory "${new_volume}" "${new_container}" "${migration_label}" || return 1 ;; *) if docker volume inspect "${new_volume}" >/dev/null 2>&1; then @@ -908,15 +937,15 @@ migrate_omegaclaw_memory() { echo "Stopped container ${old_container}" fi - if docker run --rm --entrypoint sh --volume "${old_volume}:/from" "${image}" \ + if docker run --rm --label "${migration_label}" --entrypoint sh --volume "${old_volume}:/from" "${image}" \ -c "date -u > /from/${started_marker}" \ && docker volume create "${new_volume}" >/dev/null \ - && docker run --rm --entrypoint sh --volume "${new_volume}:${memory_path}" "${image}" \ + && docker run --rm --label "${migration_label}" --entrypoint sh --volume "${new_volume}:${memory_path}" "${image}" \ -c "test -f ${memory_path}/prompt.txt" \ - && docker run --rm --entrypoint sh \ + && docker run --rm --label "${migration_label}" --entrypoint sh \ --volume "${old_volume}:/from:ro" --volume "${new_volume}:/to" "${image}" \ -c 'list=$(mktemp) && cd /from && find . -mindepth 1 -maxdepth 1 ! -name "prompt*.txt" ! -name tg_prompt.txt ! -name .migration-started > "$list" && while IFS= read -r entry; do rm -rf "/to/$entry" && cp -a "$entry" /to/ || exit 1; done < "$list"' \ - && docker run --rm --entrypoint sh --volume "${old_volume}:/from" "${image}" \ + && docker run --rm --label "${migration_label}" --entrypoint sh --volume "${old_volume}:/from" "${image}" \ -c "date -u > /from/${marker}"; then echo "Memory copied. Carrying memory over is experimental, so keep the volume ${old_volume} until you have checked that the agent remembers what it knew. Then remove the old installation with 'docker rm ${old_container}' and 'docker volume rm ${old_volume}'" return 0 @@ -925,7 +954,7 @@ migrate_omegaclaw_memory() { echo "Could not copy memory from ${old_volume}, the old installation is left as it was" >&2 if docker volume rm "${new_volume}" >/dev/null 2>&1 \ || ! docker volume inspect "${new_volume}" >/dev/null 2>&1; then - docker run --rm --entrypoint sh --volume "${old_volume}:/from" "${image}" \ + docker run --rm --label "${migration_label}" --entrypoint sh --volume "${old_volume}:/from" "${image}" \ -c "rm -f /from/${started_marker}" >/dev/null 2>&1 || true else echo "Could not remove ${new_volume}, the next start will copy the memory again" >&2 From b7adfabbdac6312232422a2255d07431fcb536b8 Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 04:41:15 +0300 Subject: [PATCH 05/14] [OMEGA-492] Cover leftover containers on other volumes and a failed import cleanup --- tests/test_omega_launcher_migration.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_omega_launcher_migration.py b/tests/test_omega_launcher_migration.py index c7291230..5aedce22 100644 --- a/tests/test_omega_launcher_migration.py +++ b/tests/test_omega_launcher_migration.py @@ -481,6 +481,17 @@ def test_interrupted_copy_removes_leftover_copy_containers(docker_root): assert _started_agent(docker_root) +def test_interrupted_copy_keeps_labelled_containers_on_other_volumes(docker_root): + old, new = _interrupted_migration(docker_root, image_files=True) + _container_on_volume(docker_root, "other_copier", "running", "omegaclaw-memory", label=MIGRATION_LABEL) + + result = _launcher(docker_root, "start", "-d", IMAGE) + + assert result.returncode == 0, result.stderr + assert _read(new / "history.metta") == "(old history)\n" + assert (docker_root / "containers" / "other_copier").exists() + + def test_migration_containers_carry_the_label(docker_root): _install_omegaclaw(docker_root) @@ -512,6 +523,18 @@ def test_memory_import_replaces_an_interrupted_copy(docker_root, tmp_path): assert _read(new / "history.metta") == "(imported and used)\n" +def test_memory_import_keeps_the_interrupted_state_when_the_volume_cannot_be_removed(docker_root, tmp_path): + old, new = _interrupted_migration(docker_root, image_files=True) + (new / "history.metta").write_text("(partly copied)\n") + + result = _launcher(docker_root, *_memory_import_arguments(docker_root, tmp_path), fail_volume_rm=True) + + assert result.returncode != 0 + assert (old / ".migration-started").exists() + assert _read(new / "history.metta") == "(partly copied)\n" + assert not _started_agent(docker_root) + + def test_run_interrupted_before_the_image_files_is_redone(docker_root): old, new = _interrupted_migration(docker_root, image_files=False) From 922ba2642928b906fcb8eee999f57eb20e029b0d Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 04:41:41 +0300 Subject: [PATCH 06/14] [OMEGA-492] Remove only leftover copy containers that hold the partly copied volume --- scripts/omega | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/omega b/scripts/omega index 28e2da82..4d0e7de7 100755 --- a/scripts/omega +++ b/scripts/omega @@ -861,7 +861,7 @@ remove_partly_copied_memory() { return 0 fi docker rm -f "${container}" >/dev/null 2>&1 || true - for leftover in $(docker ps -aq --filter "label=${label}" 2>/dev/null); do + for leftover in $(docker ps -aq --filter "label=${label}" --filter "volume=${volume}" 2>/dev/null); do docker rm -f "${leftover}" >/dev/null 2>&1 || true done if docker volume rm "${volume}" >/dev/null; then From 0a2da898b0bc58afe37f01e666dfc190e0000a42 Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 05:23:05 +0300 Subject: [PATCH 07/14] [OMEGA-492] Cover keeping omega while another container holds the volume --- tests/test_omega_launcher_migration.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_omega_launcher_migration.py b/tests/test_omega_launcher_migration.py index 5aedce22..48c965c4 100644 --- a/tests/test_omega_launcher_migration.py +++ b/tests/test_omega_launcher_migration.py @@ -79,6 +79,8 @@ def containers_using(name): if (labels_dir / name).exists() and value in (labels_dir / name).read_text().split()] print("\n".join(names)) sys.exit(0) +if args[:2] == ["container", "inspect"]: + sys.exit(0 if (containers / args[-1]).exists() else 1) if args[:1] == ["inspect"]: state = containers / args[-1] if not state.exists(): @@ -447,6 +449,7 @@ def test_interrupted_copy_is_redone_while_omega_container_holds_new_volume(docke assert result.returncode == 0, result.stderr assert "volume is in use" not in result.stderr + assert "Removed container omega, it used the partly copied volume omega-memory" in result.stdout assert _read(new / "history.metta") == "(old history)\n" assert _read(new / "chroma_db" / "chroma.sqlite3") == "old long-term memory" assert (old / ".migrated-to-omega").exists() @@ -468,6 +471,21 @@ def test_interrupted_copy_names_other_containers_on_new_volume(docker_root): assert not _started_agent(docker_root) +def test_interrupted_copy_keeps_omega_while_another_container_holds_the_volume(docker_root): + old, new = _interrupted_migration(docker_root, image_files=True) + _container_on_volume(docker_root, "omega", "running", "omega-memory") + _container_on_volume(docker_root, "omega-leftover", "exited", "omega-memory") + before = _snapshot(new) + + result = _launcher(docker_root, "start", "-d", IMAGE) + + assert result.returncode != 0 + assert "it is used by containers: omega-leftover." in result.stderr + assert _container_state(docker_root, "omega") == "running" + assert _snapshot(new) == before + assert not _started_agent(docker_root) + + def test_interrupted_copy_removes_leftover_copy_containers(docker_root): old, new = _interrupted_migration(docker_root, image_files=True) _container_on_volume(docker_root, "musing_haslett", "running", "omegaclaw-memory omega-memory", label=MIGRATION_LABEL) From b6de939281cd0c4f420781bc8a874338cfde85a2 Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 05:23:55 +0300 Subject: [PATCH 08/14] [OMEGA-492] Check other holders of the partly copied volume before removing omega --- scripts/omega | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/scripts/omega b/scripts/omega index 4d0e7de7..3b40a7b9 100755 --- a/scripts/omega +++ b/scripts/omega @@ -855,26 +855,32 @@ remove_partly_copied_memory() { local container="$2" local label="$3" local leftover - local holders + local holder + local holders="" if ! docker volume inspect "${volume}" >/dev/null 2>&1; then return 0 fi - docker rm -f "${container}" >/dev/null 2>&1 || true for leftover in $(docker ps -aq --filter "label=${label}" --filter "volume=${volume}" 2>/dev/null); do docker rm -f "${leftover}" >/dev/null 2>&1 || true done - if docker volume rm "${volume}" >/dev/null; then - return 0 - fi - holders=$(docker ps -a --filter "volume=${volume}" --format '{{.Names}}' 2>/dev/null | tr '\n' ' ') - holders="${holders% }" + for holder in $(docker ps -a --filter "volume=${volume}" --format '{{.Names}}' 2>/dev/null); do + if [ "${holder}" != "${container}" ]; then + holders="${holders:+${holders} }${holder}" + fi + done if [ -n "${holders}" ]; then echo "Could not remove the partly copied volume ${volume}, it is used by containers: ${holders}. Remove them with 'docker rm -f ${holders}' and start again" >&2 - else + return 1 + fi + if docker container inspect "${container}" >/dev/null 2>&1 \ + && docker rm -f "${container}" >/dev/null; then + echo "Removed container ${container}, it used the partly copied volume ${volume}" + fi + if ! docker volume rm "${volume}" >/dev/null; then echo "Could not remove the partly copied volume ${volume}" >&2 + return 1 fi - return 1 } migrate_omegaclaw_memory() { From e201b5fae69b32b743210058fb782c5103e18d75 Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 12:56:40 +0300 Subject: [PATCH 09/14] [OMEGA-492] Trigger CI From d4f1da4196bad5523f0700b8b66ad5b1b0e68ab6 Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 17:20:13 +0300 Subject: [PATCH 10/14] [OMEGA-492] Cover imported memory over an interrupted copy and unreadable markers on import - Plain start must keep omega-memory when its history is not a prefix of the old one - Partly copied or missing history is still copied again - A memory import stops when the migration markers cannot be read --- tests/test_omega_launcher_migration.py | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/test_omega_launcher_migration.py b/tests/test_omega_launcher_migration.py index 48c965c4..b02163e3 100644 --- a/tests/test_omega_launcher_migration.py +++ b/tests/test_omega_launcher_migration.py @@ -598,3 +598,51 @@ def test_start_stops_when_markers_cannot_be_read(docker_root): assert result.returncode != 0 assert not _started_agent(docker_root) assert _snapshot(new) == before + + +def test_memory_import_stops_when_markers_cannot_be_read(docker_root, tmp_path): + old, new = _interrupted_migration(docker_root, image_files=True) + (new / "history.metta").write_text("(partly copied)\n") + before = _snapshot(new) + + result = _launcher(docker_root, *_memory_import_arguments(docker_root, tmp_path), fail_marker_check=True) + + assert result.returncode != 0 + assert "Could not read the migration markers" in result.stderr + assert (old / ".migration-started").exists() + assert _snapshot(new) == before + assert not _started_agent(docker_root) + + +@pytest.mark.parametrize("history", ["(old hi", None]) +def test_interrupted_copy_with_partly_copied_history_is_redone(docker_root, history): + old, new = _interrupted_migration(docker_root, image_files=True) + if history is None: + (new / "history.metta").unlink() + else: + (new / "history.metta").write_text(history) + + result = _launcher(docker_root, "start", "-d", IMAGE) + + assert result.returncode == 0, result.stderr + assert _read(new / "history.metta") == "(old history)\n" + assert (old / ".migrated-to-omega").exists() + assert _started_agent(docker_root) + + +@pytest.mark.parametrize("history", ["(imported and used)\n", "(old history)\n(used after import)\n"]) +def test_interrupted_copy_keeps_memory_that_is_not_a_partial_copy(docker_root, history): + old, new = _interrupted_migration(docker_root, image_files=True) + (new / "history.metta").write_text(history) + _container_on_volume(docker_root, "omega", "exited", "omega-memory") + before = _snapshot(new) + + result = _launcher(docker_root, "start", "-d", IMAGE) + + assert result.returncode != 0 + assert "docker volume rm omega-memory" in result.stderr + assert ".migration-started" in result.stderr + assert _snapshot(new) == before + assert (old / ".migration-started").exists() + assert _container_state(docker_root, "omega") == "exited" + assert not _started_agent(docker_root) From 7263f93c4c51cc1bf8c024d642b51a52ea355262 Mon Sep 17 00:00:00 2001 From: TossSky Date: Fri, 9 Oct 2026 17:20:14 +0300 Subject: [PATCH 11/14] [OMEGA-492] Keep memory that is not a partial copy and stop an import on unreadable markers - Before redoing an interrupted copy, compare history.metta in omega-memory with omegaclaw-memory - Stop with commands to keep the memory or to copy again when it is not a partial copy - A memory import no longer skips a failed marker read, so a stale marker cannot remove imported memory later --- scripts/omega | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/scripts/omega b/scripts/omega index 3b40a7b9..a6c11785 100755 --- a/scripts/omega +++ b/scripts/omega @@ -894,15 +894,13 @@ migrate_omegaclaw_memory() { local started_marker=".migration-started" local stopped_old_container=0 local old_state + local new_state if ! docker volume inspect "${old_volume}" >/dev/null 2>&1; then return 0 fi if ! old_state=$(docker run --rm --label "${migration_label}" --entrypoint sh --volume "${old_volume}:/from:ro" "${image}" \ -c "if [ -e /from/${marker} ]; then echo migrated; elif [ -e /from/${started_marker} ]; then echo interrupted; fi"); then - if [ -n "${memory_import_file}" ]; then - return 0 - fi echo "Could not read the migration markers in ${old_volume}" >&2 return 1 fi @@ -923,6 +921,20 @@ migrate_omegaclaw_memory() { return 0 ;; interrupted) + if docker volume inspect "${new_volume}" >/dev/null 2>&1; then + if ! new_state=$(docker run --rm --label "${migration_label}" --entrypoint sh \ + --volume "${old_volume}:/from:ro" --volume "${new_volume}:/to:ro" "${image}" \ + -c 'if [ -e /to/history.metta ]; then n=$(wc -c < /to/history.metta); if [ ! -e /from/history.metta ] || [ "$n" -gt "$(wc -c < /from/history.metta)" ] || ! head -c "$n" /from/history.metta | cmp -s - /to/history.metta; then echo kept; fi; fi'); then + echo "Could not compare ${new_volume} with ${old_volume}" >&2 + return 1 + fi + if [ "${new_state}" = "kept" ]; then + echo "${new_volume} holds memory that is not a partial copy of ${old_volume}, for example an imported archive, so it was left as it was" >&2 + echo "To keep it, clear the marker: docker run --rm --entrypoint rm --volume ${old_volume}:/from ${image} /from/${started_marker}" >&2 + echo "To copy ${old_volume} again instead, remove it: docker rm -f ${new_container}; docker volume rm ${new_volume}" >&2 + return 1 + fi + fi echo "The last copy from ${old_volume} did not finish, copying again" remove_partly_copied_memory "${new_volume}" "${new_container}" "${migration_label}" || return 1 ;; From 593d616d4894ff3e0e5d71ea4e08815f278b9542 Mon Sep 17 00:00:00 2001 From: Ashkenov Timur Date: Fri, 23 Oct 2026 09:10:05 +0300 Subject: [PATCH 12/14] [OMEGA-492] Docs: Memory migrations warnings and recovery instructions --- README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/omega | 4 +++ 2 files changed, 77 insertions(+) diff --git a/README.md b/README.md index 37b042d0..932a1e57 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,79 @@ To restart the Omega Docker container: docker start omegaclaw ``` +### Recovering an interrupted memory migration + +When upgrading from OmegaClaw, the launcher copies `omegaclaw-memory` to +`omega-memory`. Keep the terminal open and do not interrupt the launcher or +Docker until it reports `Memory copied`. Keep `omegaclaw-memory` until you have +checked that the agent remembers its previous data. + +If the copy is interrupted, follow the appropriate path below before importing +memory or starting the agent again. Use the same image tag and launcher options +as in your original start command. + +#### Retry the copy from OmegaClaw + +Use this path only if `omega-memory` contains no imported memory or new agent +data that you need to keep. Retrying replaces this volume with a fresh copy of +`omegaclaw-memory`. + +1. List the containers holding the destination volume: + + ```sh + docker ps -a --filter volume=omega-memory --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}' + ``` + +2. If the launcher reports `volume is in use` or names a leftover container, + stop and remove the identified container. Replace `CONTAINER_ID` with its + actual ID; do not remove unrelated containers or the source volume: + + ```sh + docker rm -f CONTAINER_ID + ``` + + This also applies to stopped containers. Copy containers started by the + v0.1.20 launcher have no migration label and need to be removed manually. + The updated launcher removes labelled copy containers and the `omega` + container automatically when retrying. + +3. Repeat your original `scripts/omega start ...` command, without + `--memory-import` or the `--only-history` and `--only-vector` flags. Wait for + `Memory copied`, then check the agent's memory. + +#### Keep memory imported or used after the interruption + +Do not retry the copy or run another import before preserving the current +`omega-memory` volume. Stop any remaining copy containers identified above and +stop the `omega` agent if it is running. Back up the destination volume before +changing migration state. + +The v0.1.20 launcher could leave `.migration-started` in `omegaclaw-memory` +after a memory import. In particular, an `--only-vector` import can leave +`history.metta` unchanged. The updated launcher's history check cannot detect +this case: a normal start can delete the imported vectors and copy the old +memory over them. + +If the import completed successfully and you want to keep the current memory, +clear only the stale migration marker before starting again: + +```sh +docker volume inspect omegaclaw-memory omega-memory +docker run --rm --entrypoint rm --volume omegaclaw-memory:/from \ + singularitynet/omega:v0.1.20 -f /from/.migration-started +``` + +Run the second command only if both volumes exist. Then repeat your normal +start command without `--memory-import` or the `--only-history` and +`--only-vector` flags. Clearing the marker keeps the current destination volume; +it does not finish a partial copy. If you are unsure whether +the import or copy completed, preserve both volumes and ask for help before +continuing. + +If imported memory has already been overwritten, the old OmegaClaw volume +cannot recover that import. Restore it from the original import archive or a +backup of `omega-memory`. + ### Memory portability Memory export is disabled by default. See the [memory portability reference](./docs/reference-memory-portability.md) diff --git a/scripts/omega b/scripts/omega index a6c11785..6023e7f8 100755 --- a/scripts/omega +++ b/scripts/omega @@ -921,6 +921,8 @@ migrate_omegaclaw_memory() { return 0 ;; interrupted) + echo "WARNING: Retrying the migration can replace omega-memory. If you imported memory or used the agent after the interruption, preserve it before retrying." >&2 + echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/README.md#recovering-an-interrupted-memory-migration" >&2 if docker volume inspect "${new_volume}" >/dev/null 2>&1; then if ! new_state=$(docker run --rm --label "${migration_label}" --entrypoint sh \ --volume "${old_volume}:/from:ro" --volume "${new_volume}:/to:ro" "${image}" \ @@ -946,6 +948,8 @@ migrate_omegaclaw_memory() { esac echo "Copying memory from volume ${old_volume} to ${new_volume}" + echo "WARNING: Keep this terminal open and do not interrupt the launcher or Docker until 'Memory copied' appears. Keep ${old_volume} until you have checked the migrated memory." >&2 + echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/README.md#recovering-an-interrupted-memory-migration" >&2 if [ "$(docker inspect -f '{{.State.Running}}' "${old_container}" 2>/dev/null)" = "true" ]; then if ! docker stop "${old_container}" >/dev/null; then echo "Could not stop container ${old_container}, memory was not copied" >&2 From 23058ba6946c3d6e2fc13b253b6738c3e057653c Mon Sep 17 00:00:00 2001 From: Ashkenov Timur Date: Fri, 23 Oct 2026 11:05:27 +0300 Subject: [PATCH 13/14] [OMEGA-492] Docs: Move migration recovery guide to memory portability reference --- README.md | 73 ---------------------------- docs/reference-memory-portability.md | 73 ++++++++++++++++++++++++++++ scripts/omega | 4 +- 3 files changed, 75 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 932a1e57..37b042d0 100644 --- a/README.md +++ b/README.md @@ -90,79 +90,6 @@ To restart the Omega Docker container: docker start omegaclaw ``` -### Recovering an interrupted memory migration - -When upgrading from OmegaClaw, the launcher copies `omegaclaw-memory` to -`omega-memory`. Keep the terminal open and do not interrupt the launcher or -Docker until it reports `Memory copied`. Keep `omegaclaw-memory` until you have -checked that the agent remembers its previous data. - -If the copy is interrupted, follow the appropriate path below before importing -memory or starting the agent again. Use the same image tag and launcher options -as in your original start command. - -#### Retry the copy from OmegaClaw - -Use this path only if `omega-memory` contains no imported memory or new agent -data that you need to keep. Retrying replaces this volume with a fresh copy of -`omegaclaw-memory`. - -1. List the containers holding the destination volume: - - ```sh - docker ps -a --filter volume=omega-memory --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}' - ``` - -2. If the launcher reports `volume is in use` or names a leftover container, - stop and remove the identified container. Replace `CONTAINER_ID` with its - actual ID; do not remove unrelated containers or the source volume: - - ```sh - docker rm -f CONTAINER_ID - ``` - - This also applies to stopped containers. Copy containers started by the - v0.1.20 launcher have no migration label and need to be removed manually. - The updated launcher removes labelled copy containers and the `omega` - container automatically when retrying. - -3. Repeat your original `scripts/omega start ...` command, without - `--memory-import` or the `--only-history` and `--only-vector` flags. Wait for - `Memory copied`, then check the agent's memory. - -#### Keep memory imported or used after the interruption - -Do not retry the copy or run another import before preserving the current -`omega-memory` volume. Stop any remaining copy containers identified above and -stop the `omega` agent if it is running. Back up the destination volume before -changing migration state. - -The v0.1.20 launcher could leave `.migration-started` in `omegaclaw-memory` -after a memory import. In particular, an `--only-vector` import can leave -`history.metta` unchanged. The updated launcher's history check cannot detect -this case: a normal start can delete the imported vectors and copy the old -memory over them. - -If the import completed successfully and you want to keep the current memory, -clear only the stale migration marker before starting again: - -```sh -docker volume inspect omegaclaw-memory omega-memory -docker run --rm --entrypoint rm --volume omegaclaw-memory:/from \ - singularitynet/omega:v0.1.20 -f /from/.migration-started -``` - -Run the second command only if both volumes exist. Then repeat your normal -start command without `--memory-import` or the `--only-history` and -`--only-vector` flags. Clearing the marker keeps the current destination volume; -it does not finish a partial copy. If you are unsure whether -the import or copy completed, preserve both volumes and ask for help before -continuing. - -If imported memory has already been overwritten, the old OmegaClaw volume -cannot recover that import. Restore it from the original import archive or a -backup of `omega-memory`. - ### Memory portability Memory export is disabled by default. See the [memory portability reference](./docs/reference-memory-portability.md) diff --git a/docs/reference-memory-portability.md b/docs/reference-memory-portability.md index aad6a865..cae89661 100644 --- a/docs/reference-memory-portability.md +++ b/docs/reference-memory-portability.md @@ -94,6 +94,79 @@ counts, and embedding compatibility before changing live memory. It runs before the agent loop starts. A receipt prevents a completed archive import from running again on container restart. +## Recovering an interrupted memory migration + +When upgrading from OmegaClaw, the launcher copies `omegaclaw-memory` to +`omega-memory`. Keep the terminal open and do not interrupt the launcher or +Docker until it reports `Memory copied`. Keep `omegaclaw-memory` until you have +checked that the agent remembers its previous data. + +If the copy is interrupted, follow the appropriate path below before importing +memory or starting the agent again. Use the same image tag and launcher options +as in your original start command. + +### Retry the copy from OmegaClaw + +Use this path only if `omega-memory` contains no imported memory or new agent +data that you need to keep. Retrying replaces this volume with a fresh copy of +`omegaclaw-memory`. + +1. List the containers holding the destination volume: + + ```sh + docker ps -a --filter volume=omega-memory --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}' + ``` + +2. If the launcher reports `volume is in use` or names a leftover container, + stop and remove the identified container. Replace `CONTAINER_ID` with its + actual ID; do not remove unrelated containers or the source volume: + + ```sh + docker rm -f CONTAINER_ID + ``` + + This also applies to stopped containers. Copy containers started by the + v0.1.20 launcher have no migration label and need to be removed manually. + The updated launcher removes labelled copy containers and the `omega` + container automatically when retrying. + +3. Repeat your original `scripts/omega start ...` command, without + `--memory-import` or the `--only-history` and `--only-vector` flags. Wait for + `Memory copied`, then check the agent's memory. + +### Keep memory imported or used after the interruption + +Do not retry the copy or run another import before preserving the current +`omega-memory` volume. Stop any remaining copy containers identified above and +stop the `omega` agent if it is running. Back up the destination volume before +changing migration state. + +The v0.1.20 launcher could leave `.migration-started` in `omegaclaw-memory` +after a memory import. In particular, an `--only-vector` import can leave +`history.metta` unchanged. The updated launcher's history check cannot detect +this case: a normal start can delete the imported vectors and copy the old +memory over them. + +If the import completed successfully and you want to keep the current memory, +clear only the stale migration marker before starting again: + +```sh +docker volume inspect omegaclaw-memory omega-memory +docker run --rm --entrypoint rm --volume omegaclaw-memory:/from \ + singularitynet/omega:v0.1.20 -f /from/.migration-started +``` + +Run the second command only if both volumes exist. Then repeat your normal +start command without `--memory-import` or the `--only-history` and +`--only-vector` flags. Clearing the marker keeps the current destination volume; +it does not finish a partial copy. If you are unsure whether +the import or copy completed, preserve both volumes and ask for help before +continuing. + +If imported memory has already been overwritten, the old OmegaClaw volume +cannot recover that import. Restore it from the original import archive or a +backup of `omega-memory`. + ## Security Archives are private operator data; use a dedicated transfer directory and keep diff --git a/scripts/omega b/scripts/omega index e29becd3..65600960 100755 --- a/scripts/omega +++ b/scripts/omega @@ -935,7 +935,7 @@ migrate_omegaclaw_memory() { ;; interrupted) echo "WARNING: Retrying the migration can replace omega-memory. If you imported memory or used the agent after the interruption, preserve it before retrying." >&2 - echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/README.md#recovering-an-interrupted-memory-migration" >&2 + echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/docs/reference-memory-portability.md#recovering-an-interrupted-memory-migration" >&2 if docker volume inspect "${new_volume}" >/dev/null 2>&1; then if ! new_state=$(docker run --rm --label "${migration_label}" --entrypoint sh \ --volume "${old_volume}:/from:ro" --volume "${new_volume}:/to:ro" "${image}" \ @@ -962,7 +962,7 @@ migrate_omegaclaw_memory() { echo "Copying memory from volume ${old_volume} to ${new_volume}" echo "WARNING: Keep this terminal open and do not interrupt the launcher or Docker until 'Memory copied' appears. Keep ${old_volume} until you have checked the migrated memory." >&2 - echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/README.md#recovering-an-interrupted-memory-migration" >&2 + echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/docs/reference-memory-portability.md#recovering-an-interrupted-memory-migration" >&2 if [ "$(docker inspect -f '{{.State.Running}}' "${old_container}" 2>/dev/null)" = "true" ]; then if ! docker stop "${old_container}" >/dev/null; then echo "Could not stop container ${old_container}, memory was not copied" >&2 From dcee9266dd67d5afda83bfce71fee93c6dd69428 Mon Sep 17 00:00:00 2001 From: Ashkenov Timur Date: Fri, 23 Oct 2026 11:31:39 +0300 Subject: [PATCH 14/14] [OMEGA-492] Docs: Move migration recovery guide to memory migration reference --- docs/README.md | 1 + docs/reference-memory-migration.md | 75 ++++++++++++++++++++++++++++ docs/reference-memory-portability.md | 73 --------------------------- scripts/omega | 4 +- 4 files changed, 78 insertions(+), 75 deletions(-) create mode 100644 docs/reference-memory-migration.md diff --git a/docs/README.md b/docs/README.md index ed63d60e..01a2373c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -58,6 +58,7 @@ User-facing MeTTa skills the agent invokes. Each page follows the template **Sig - [reference-channels.md](./reference-channels.md) — IRC, Telegram, Slack, Mattermost, WebSocket, and websearch adapters plus the channel contract - [reference-python-bridges.md](./reference-python-bridges.md) — `lib_llm_ext.py`, `src/helper.py`, `src/skills.pl` - [reference-memory-portability.md](./reference-memory-portability.md) — Operator backup, restore, and archive-transfer workflow +- [reference-memory-migration.md](./reference-memory-migration.md) — Automatic migration from OmegaClaw Docker volumes and interrupted-copy recovery ### Plugin API diff --git a/docs/reference-memory-migration.md b/docs/reference-memory-migration.md new file mode 100644 index 00000000..9049aebd --- /dev/null +++ b/docs/reference-memory-migration.md @@ -0,0 +1,75 @@ +# Reference - Memory Migration + +## Recovering an interrupted memory migration + +When upgrading from OmegaClaw, the launcher copies `omegaclaw-memory` to +`omega-memory`. Keep the terminal open and do not interrupt the launcher or +Docker until it reports `Memory copied`. Keep `omegaclaw-memory` until you have +checked that the agent remembers its previous data. + +If the copy is interrupted, follow the appropriate path below before importing +memory or starting the agent again. Use the same image tag and launcher options +as in your original start command. + +### Retry the copy from OmegaClaw + +Use this path only if `omega-memory` contains no imported memory or new agent +data that you need to keep. Retrying replaces this volume with a fresh copy of +`omegaclaw-memory`. + +1. List the containers holding the destination volume: + + ```sh + docker ps -a --filter volume=omega-memory --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}' + ``` + +2. If the launcher reports `volume is in use` or names a leftover container, + stop and remove the identified container. Replace `CONTAINER_ID` with its + actual ID; do not remove unrelated containers or the source volume: + + ```sh + docker rm -f CONTAINER_ID + ``` + + This also applies to stopped containers. Copy containers started by the + v0.1.20 launcher have no migration label and need to be removed manually. + The updated launcher removes labelled copy containers and the `omega` + container automatically when retrying. + +3. Repeat your original `scripts/omega start ...` command, without + `--memory-import` or the `--only-history` and `--only-vector` flags. Wait for + `Memory copied`, then check the agent's memory. + +### Keep memory imported or used after the interruption + +Do not retry the copy or run another import before preserving the current +`omega-memory` volume. Stop any remaining copy containers identified above and +stop the `omega` agent if it is running. Back up the destination volume before +changing migration state. + +The v0.1.20 launcher could leave `.migration-started` in `omegaclaw-memory` +after a memory import. In particular, an `--only-vector` import can leave +`history.metta` unchanged. The updated launcher's history check cannot detect +this case: a normal start can delete the imported vectors and copy the old +memory over them. + +If the import completed successfully and you want to keep the current memory, +clear only the stale migration marker before starting again: + +```sh +docker volume inspect omegaclaw-memory omega-memory +docker run --rm --entrypoint rm --volume omegaclaw-memory:/from \ + singularitynet/omega:v0.1.20 -f /from/.migration-started +``` + +Run the second command only if both volumes exist. Then repeat your normal +start command without `--memory-import` or the `--only-history` and +`--only-vector` flags. Clearing the marker keeps the current destination volume; +it does not finish a partial copy. If you are unsure whether +the import or copy completed, preserve both volumes and ask for help before +continuing. + +If imported memory has already been overwritten, the old OmegaClaw volume +cannot recover that import. Restore it from the original import archive or a +backup of `omega-memory`. + diff --git a/docs/reference-memory-portability.md b/docs/reference-memory-portability.md index cae89661..aad6a865 100644 --- a/docs/reference-memory-portability.md +++ b/docs/reference-memory-portability.md @@ -94,79 +94,6 @@ counts, and embedding compatibility before changing live memory. It runs before the agent loop starts. A receipt prevents a completed archive import from running again on container restart. -## Recovering an interrupted memory migration - -When upgrading from OmegaClaw, the launcher copies `omegaclaw-memory` to -`omega-memory`. Keep the terminal open and do not interrupt the launcher or -Docker until it reports `Memory copied`. Keep `omegaclaw-memory` until you have -checked that the agent remembers its previous data. - -If the copy is interrupted, follow the appropriate path below before importing -memory or starting the agent again. Use the same image tag and launcher options -as in your original start command. - -### Retry the copy from OmegaClaw - -Use this path only if `omega-memory` contains no imported memory or new agent -data that you need to keep. Retrying replaces this volume with a fresh copy of -`omegaclaw-memory`. - -1. List the containers holding the destination volume: - - ```sh - docker ps -a --filter volume=omega-memory --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}' - ``` - -2. If the launcher reports `volume is in use` or names a leftover container, - stop and remove the identified container. Replace `CONTAINER_ID` with its - actual ID; do not remove unrelated containers or the source volume: - - ```sh - docker rm -f CONTAINER_ID - ``` - - This also applies to stopped containers. Copy containers started by the - v0.1.20 launcher have no migration label and need to be removed manually. - The updated launcher removes labelled copy containers and the `omega` - container automatically when retrying. - -3. Repeat your original `scripts/omega start ...` command, without - `--memory-import` or the `--only-history` and `--only-vector` flags. Wait for - `Memory copied`, then check the agent's memory. - -### Keep memory imported or used after the interruption - -Do not retry the copy or run another import before preserving the current -`omega-memory` volume. Stop any remaining copy containers identified above and -stop the `omega` agent if it is running. Back up the destination volume before -changing migration state. - -The v0.1.20 launcher could leave `.migration-started` in `omegaclaw-memory` -after a memory import. In particular, an `--only-vector` import can leave -`history.metta` unchanged. The updated launcher's history check cannot detect -this case: a normal start can delete the imported vectors and copy the old -memory over them. - -If the import completed successfully and you want to keep the current memory, -clear only the stale migration marker before starting again: - -```sh -docker volume inspect omegaclaw-memory omega-memory -docker run --rm --entrypoint rm --volume omegaclaw-memory:/from \ - singularitynet/omega:v0.1.20 -f /from/.migration-started -``` - -Run the second command only if both volumes exist. Then repeat your normal -start command without `--memory-import` or the `--only-history` and -`--only-vector` flags. Clearing the marker keeps the current destination volume; -it does not finish a partial copy. If you are unsure whether -the import or copy completed, preserve both volumes and ask for help before -continuing. - -If imported memory has already been overwritten, the old OmegaClaw volume -cannot recover that import. Restore it from the original import archive or a -backup of `omega-memory`. - ## Security Archives are private operator data; use a dedicated transfer directory and keep diff --git a/scripts/omega b/scripts/omega index 65600960..5b9330da 100755 --- a/scripts/omega +++ b/scripts/omega @@ -935,7 +935,7 @@ migrate_omegaclaw_memory() { ;; interrupted) echo "WARNING: Retrying the migration can replace omega-memory. If you imported memory or used the agent after the interruption, preserve it before retrying." >&2 - echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/docs/reference-memory-portability.md#recovering-an-interrupted-memory-migration" >&2 + echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/docs/reference-memory-migration.md#recovering-an-interrupted-memory-migration" >&2 if docker volume inspect "${new_volume}" >/dev/null 2>&1; then if ! new_state=$(docker run --rm --label "${migration_label}" --entrypoint sh \ --volume "${old_volume}:/from:ro" --volume "${new_volume}:/to:ro" "${image}" \ @@ -962,7 +962,7 @@ migrate_omegaclaw_memory() { echo "Copying memory from volume ${old_volume} to ${new_volume}" echo "WARNING: Keep this terminal open and do not interrupt the launcher or Docker until 'Memory copied' appears. Keep ${old_volume} until you have checked the migrated memory." >&2 - echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/docs/reference-memory-portability.md#recovering-an-interrupted-memory-migration" >&2 + echo "Recovery instructions: https://github.com/singnet/Omega/blob/patch-to-v0.1.20/docs/reference-memory-migration.md#recovering-an-interrupted-memory-migration" >&2 if [ "$(docker inspect -f '{{.State.Running}}' "${old_container}" 2>/dev/null)" = "true" ]; then if ! docker stop "${old_container}" >/dev/null; then echo "Could not stop container ${old_container}, memory was not copied" >&2