Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions loopx/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,21 @@ def _is_route_observation(path: Path) -> bool:

if _is_redirected_path(path):
return False
if path.name == GLOBAL_REGISTRY_FILENAME + ".lock":
return path.is_file()
# The native effect runtime locks every registry it writes through, so a
# mere lock file must not turn a default root into a second authority.
# Spelled here because file_lock imports this module.
if path.name == GLOBAL_REGISTRY_FILENAME + ".ts-effect.lock":
# Lock metadata beside the root's own global registry. The native effect
# runtime locks every registry it writes through, and the Windows holder
# record plus the timeout incident channel persist after release (see
# docs/reference/protocols/file-lock-acquisition-v0.md), so neither a
# later read nor an archive restored on another platform may read them
# back as a second authority. Spelled here because file_lock imports this
# module.
global_lock_name = GLOBAL_REGISTRY_FILENAME + ".lock"
if path.name in {
global_lock_name,
GLOBAL_REGISTRY_FILENAME + ".ts-effect.lock",
f"{global_lock_name}.holder",
f"{global_lock_name}.holder.json",
f"{global_lock_name}.incidents.jsonl",
}:
return path.is_file()
if path.name == "lark-consumers" and path.is_dir():
import re
Expand Down
38 changes: 36 additions & 2 deletions tests/test_local_state_migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ def test_observations_and_consumer_locks_do_not_claim_a_second_runtime(tmp_path,
monkeypatch.setattr(paths, "LEGACY_RUNTIME_ROOT", source)
monkeypatch.setattr(paths, "DEFAULT_RUNTIME_ROOT", target)
_write_json(target / "runtime/goals/observed/rollout-event-log.jsonl", {"event_kind": "validation"})
for relative in ["registry.global.json.lock", "lark-consumers/0123456789abcdef0123456789abcdef.lock",
for relative in ["registry.global.json.lock", "registry.global.json.lock.holder",
"lark-consumers/0123456789abcdef0123456789abcdef.lock",
"runtime/goals/observed/rollout-event-log.jsonl.lock"]:
path = target / relative
path.parent.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -160,7 +161,10 @@ def reparse_lstat(path, *args, **kwargs):


@pytest.mark.parametrize("relative", ["registry.global.json.lock",
"registry.global.json.ts-effect.lock"])
"registry.global.json.ts-effect.lock",
"registry.global.json.lock.holder",
"registry.global.json.lock.holder.json",
"registry.global.json.lock.incidents.jsonl"])
def test_own_registry_locks_do_not_declare_a_second_authority(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, relative: str,
) -> None:
Expand All @@ -170,6 +174,8 @@ def test_own_registry_locks_do_not_declare_a_second_authority(
and the native effect runtime locks the same file too. On a fresh machine
those locks are the only entries in the untouched legacy root, so treating
them as state made the first configured Goal fail as a two-root conflict.
Windows keeps a released holder record beside the lock and a timeout
incident channel; both persist, so they must stay observations too.
"""

source, target = tmp_path / "home" / ".codex" / "loopx", tmp_path / "home" / ".loopx"
Expand All @@ -182,6 +188,34 @@ def test_own_registry_locks_do_not_declare_a_second_authority(
assert paths.select_default_runtime_root() == target


def test_a_real_lock_round_trip_keeps_the_default_route_fresh(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
) -> None:
"""The lock producer itself must leave an untouched route untouched.
``exclusive_file_lock`` is the real writer behind Goal configuration and
the native effect runtime. On Windows it leaves a released holder record
beside the lock, so a fresh machine that locks every candidate registry
before the first write must still read as fresh afterwards.
"""
from loopx.file_lock import exclusive_file_lock

source = tmp_path / "home" / ".codex" / "loopx"
target = tmp_path / "home" / ".loopx"
monkeypatch.setattr(paths, "LEGACY_RUNTIME_ROOT", source)
monkeypatch.setattr(paths, "DEFAULT_RUNTIME_ROOT", target)
for root in (source, target):
root.mkdir(parents=True)
for root in (source, target):
with exclusive_file_lock(
root / "registry.global.json",
operation="configure_goal_with_global_sync",
):
pass
assert paths.default_runtime_route()["status"] == "fresh"
assert paths.select_default_runtime_root() == target


def test_global_service_selector_keeps_a_registered_route_amid_real_conflict(tmp_path, monkeypatch):
from loopx.cli_commands.support_control_registry import explicit_global_registry
source, target, projects = _fixture(tmp_path, projects=1)
Expand Down
Loading