From 4cb8cf4b429ec9133c7ff26fbb8a3269697facc6 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Wed, 11 Nov 2026 06:34:00 +0800 Subject: [PATCH] Fix manager return paths for aliased runtimes Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- loopx/capabilities/manager_context/roundtrip.py | 7 +++++-- tests/test_manager_context_roundtrip.py | 10 ++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/loopx/capabilities/manager_context/roundtrip.py b/loopx/capabilities/manager_context/roundtrip.py index 7b435f4ea8..7be72fe3f7 100644 --- a/loopx/capabilities/manager_context/roundtrip.py +++ b/loopx/capabilities/manager_context/roundtrip.py @@ -592,7 +592,7 @@ def _return_transaction(host, path, phase, request): observed[field] = {key: observed[field][key] for key in keys if key in observed[field]} result = effect_runtime_result("manager.return_delivery.transact", { "schema_version": RETURN_DELIVERY_TRANSACTION, - "runtime_root": str(root.resolve()), "request_id": path.parent.name, + "runtime_root": str(root), "request_id": path.parent.name, "result_key": path.stem, "phase": phase, "now": host["clock"]().isoformat(), "exact": exact, "facts": observed, **payload, }, retry_safe=False) @@ -709,7 +709,7 @@ def _pending_return_paths(host, paths): candidates = [{"request_id": path.parent.name, "result_key": path.stem} for path in batch] selected = effect_runtime_result("manager.return_delivery.transact", { "schema_version": RETURN_DELIVERY_TRANSACTION, "phase": "scan", - "runtime_root": str(root.resolve()), "now": scan_at.isoformat(), "exact": exact, + "runtime_root": str(root), "now": scan_at.isoformat(), "exact": exact, "candidates": candidates, }) if not isinstance(selected, dict) or selected.get("schema_version") != RETURN_DELIVERY_TRANSACTION: @@ -726,6 +726,9 @@ def drain(root, registry, store, external_sender, *, now=None, cancelled=lambda: if cancelled(): return 0 + # Derive source paths and the typed transaction's root from one directory. + root = root.resolve() + def clock() -> datetime: return datetime.now(timezone.utc) if now is None else now diff --git a/tests/test_manager_context_roundtrip.py b/tests/test_manager_context_roundtrip.py index 7dbc80d149..fafafdfe89 100644 --- a/tests/test_manager_context_roundtrip.py +++ b/tests/test_manager_context_roundtrip.py @@ -1227,7 +1227,7 @@ def returned_history(flow): @pytest.fixture -def returned_file_history(flow): +def returned_file_history(flow, request): import os if os.open not in os.supports_dir_fd or not hasattr(os, "O_NOFOLLOW"): @@ -1252,12 +1252,18 @@ def __call__(self, *_): return {"ok": True, "reply_verified": True} sender = Sender() - assert drain(root, registry, store, sender) == 1 + delivery_root = root + if getattr(request, "param", False): + delivery_root = root.with_name(f"{root.name}-alias") + delivery_root.symlink_to(root.resolve(), target_is_directory=True) + assert drain(delivery_root, registry, store, sender) == 1 + assert sender.calls == 1 message = next(row for row in store.messages(session["session_id"]) if row.get("origin") == "manager_followup") assert not message.get("attachments"), "Reproduce an existing delivered answer whose transcript lost its files" return root, registry, store, session, turn, receipt, message, sources, sender +@pytest.mark.parametrize("returned_file_history", [False, True], indirect=True, ids=["direct", "runtime-alias"]) def test_existing_return_files_project_without_resending_or_rewriting(returned_file_history): from loopx.capabilities.manager_context.roundtrip import read_chat_return_file