From d2bca92aeb852c34dcbf514684c1a7c90f3e282b Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Tue, 13 Oct 2026 05:44:36 +0800 Subject: [PATCH 1/2] test(storage): enforce combined App retirement isolation Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../test_chat_cold_source_import.py | 4 +++- .../test_todo_consumer_host_retirement.py | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/control_plane/test_chat_cold_source_import.py b/tests/control_plane/test_chat_cold_source_import.py index e3ca2d93ab..bab0eca6f0 100644 --- a/tests/control_plane/test_chat_cold_source_import.py +++ b/tests/control_plane/test_chat_cold_source_import.py @@ -26,7 +26,9 @@ def cold_api(tmp_path, monkeypatch, request, capture_present): for name in ("todos.py", "bootstrap.py", "control_plane/coordination/runtime_shadow_writer_adapter.py", "control_plane/coordination/local_authority_shadow_outbox.py"): - (receiver / "loopx" / name).unlink() + # The consumer-retirement fixture can already have removed part + # of this same set before the cold receiver is copied. + (receiver / "loopx" / name).unlink(missing_ok=True) registry = tmp_path / "project/.loopx/registry.json" if request.param: # The configured registry may live outside conventional Goal folders. diff --git a/tests/control_plane/test_todo_consumer_host_retirement.py b/tests/control_plane/test_todo_consumer_host_retirement.py index 09b4af8b71..7cd5ea8a73 100644 --- a/tests/control_plane/test_todo_consumer_host_retirement.py +++ b/tests/control_plane/test_todo_consumer_host_retirement.py @@ -9,6 +9,7 @@ import os import subprocess import sys +from pathlib import Path from types import SimpleNamespace import pytest @@ -33,12 +34,27 @@ def isolated_workspace(*args): env["PYTHONPATH"] = os.pathsep.join([env["PYTHONPATH"], os.environ["PYTHONPATH"]]) return tuple(result) + def require_retired_receiver(env): + receiver = Path(env["PYTHONPATH"].split(os.pathsep)[0]) / "loopx" + # One copied distribution, before every server start/restart and + # auxiliary CLI: neither earlier fixture alone proves this boundary. + for name in ("todos.py", "bootstrap.py", + "control_plane/todos/legacy_mutation.py", "control_plane/todos/line_update.py", + "control_plane/scheduler/legacy_monitor_poll.py", + "control_plane/coordination/runtime_shadow.py", + "control_plane/coordination/runtime_shadow_writer_adapter.py", + "control_plane/coordination/local_authority_shadow_adapter.py", + "control_plane/coordination/local_authority_shadow_outbox.py"): + assert not (receiver / name).exists(), name + def selected_process(command, **kwargs): assert command[0] == sys.executable + require_retired_receiver(kwargs["env"]) return subprocess.Popen([command[0], "-S", *command[1:]], **kwargs) def selected_run(command, **kwargs): assert command[0] == sys.executable + require_retired_receiver(kwargs["env"]) return subprocess.run([command[0], "-S", *command[1:]], **kwargs) # Reuse the existing independent cold-import HTTP oracle and process @@ -47,7 +63,7 @@ def selected_run(command, **kwargs): monkeypatch.setattr(cold, "subprocess", SimpleNamespace( Popen=selected_process, run=selected_run, DEVNULL=subprocess.DEVNULL)) yield from cold.cold_api.__wrapped__( - tmp_path, monkeypatch, SimpleNamespace(param=False), True) + tmp_path, monkeypatch, SimpleNamespace(param=False), False) @pytest.mark.parametrize("provider", ["file", "sqlite"]) From 2c3b3c741a17c9716b7fd5127bc417ddb6fb58a6 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Tue, 13 Oct 2026 05:44:36 +0800 Subject: [PATCH 2/2] docs(storage): clarify combined cold receiver qualification Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../rfcs/typescript-control-plane-migration-v0.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/architecture/rfcs/typescript-control-plane-migration-v0.md b/docs/architecture/rfcs/typescript-control-plane-migration-v0.md index e896e74ba8..0c00044b6b 100644 --- a/docs/architecture/rfcs/typescript-control-plane-migration-v0.md +++ b/docs/architecture/rfcs/typescript-control-plane-migration-v0.md @@ -73,6 +73,15 @@ Disposable source/normal-wheel probes remove the facade, the whole legacy Todo writer and four shadow producers together; real File/SQLite CLI lifecycle, once-only settlement, Monitor replay and App cold-import/action/restart journeys must still work, with missing-provider/lease and changed-source/backup refusals. +The App HTTP receiver now also removes `bootstrap.py` from that same copied +distribution and checks the complete removed set before each server start, +restart and auxiliary CLI. Separate fixture results cannot prove this combined +absence. The ordinary canonical CLI creation entrypoint remains with its owner; +removing it from the cold-import receiver is a disposable qualification probe, +not a supported package format or permission to break the additive 1.x line. +中文:App 冷导入、后续写入和重启使用同一份缺少旧入口、writer 与 source producer 的 +隔离副本;不能拼接两组各自缺少一部分模块的结果。正常 canonical 创建仍保留其 +现有入口,这个临时卸除验收不等于已完成整族退役、自然试用或正式安装采用。 Lark dry-run and Explore replay qualify local consumers only, not live external adoption. The fresh research execution path added by [#5280](https://github.com/loopx-project/loopx/pull/5280) reintroduced a function-local historic import after the earlier caller inventory.