hermes: preserve legacy provider session directories
This commit is contained in:
parent
13c3891996
commit
8f2359b635
@ -93,6 +93,27 @@ def _state_path(assignment: dict[str, Any]) -> Path:
|
||||
return path
|
||||
|
||||
|
||||
def _preserve_runtime_directory(runtime: Path) -> None:
|
||||
"""Move one legacy provider directory aside before installing our symlink."""
|
||||
parent = runtime.parent
|
||||
if parent.is_symlink() or not parent.is_dir() or runtime.is_symlink() or not runtime.is_dir():
|
||||
raise ProtocolError(f"provider session path is not a safe directory: {runtime.name}")
|
||||
preserved = parent / f".hermes-legacy-{runtime.name}"
|
||||
if preserved.exists() or preserved.is_symlink():
|
||||
raise ProtocolError(f"provider session migration conflicts: {runtime.name}")
|
||||
try:
|
||||
os.rename(runtime, preserved)
|
||||
descriptor = os.open(parent, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0))
|
||||
try:
|
||||
os.fsync(descriptor)
|
||||
finally:
|
||||
os.close(descriptor)
|
||||
except OSError as error:
|
||||
raise ProtocolError(f"provider session migration failed: {runtime.name}") from error
|
||||
if preserved.is_symlink() or not preserved.is_dir():
|
||||
raise ProtocolError(f"provider session migration is unsafe: {runtime.name}")
|
||||
|
||||
|
||||
def _bind_provider_sessions(assignment: dict[str, Any]) -> None:
|
||||
"""Attach provider session directories to this exact durable task/run."""
|
||||
parts = tuple(str(assignment[name]) for name in ("board", "task_id", "run_id"))
|
||||
@ -143,6 +164,8 @@ def _bind_provider_sessions(assignment: dict[str, Any]) -> None:
|
||||
current.resolve().relative_to(provider_root)
|
||||
if runtime.is_symlink():
|
||||
runtime.unlink()
|
||||
elif runtime.is_dir():
|
||||
_preserve_runtime_directory(runtime)
|
||||
elif runtime.exists():
|
||||
raise ProtocolError(f"provider session path is not a symlink: {runtime.name}")
|
||||
runtime.symlink_to(durable)
|
||||
|
||||
@ -143,6 +143,25 @@ def test_provider_session_binding_replaces_only_owned_symlinks(tmp_path, monkeyp
|
||||
worker._bind_provider_sessions(assignment())
|
||||
|
||||
|
||||
def test_provider_session_binding_preserves_legacy_runtime_directories(tmp_path, monkeypatch):
|
||||
_worker_root, _data_root, _codex, claude = prepare_provider_roots(tmp_path, monkeypatch)
|
||||
legacy = claude / "projects"
|
||||
(legacy / "prior-session").mkdir(parents=True)
|
||||
(legacy / "prior-session" / "record").write_text("preserved")
|
||||
|
||||
worker._bind_provider_sessions(assignment())
|
||||
|
||||
assert legacy.is_symlink()
|
||||
preserved = claude / ".hermes-legacy-projects/prior-session/record"
|
||||
assert preserved.read_text() == "preserved"
|
||||
worker._bind_provider_sessions(assignment())
|
||||
legacy.unlink()
|
||||
legacy.mkdir()
|
||||
with pytest.raises(protocol.ProtocolError, match="migration conflicts"):
|
||||
worker._bind_provider_sessions(assignment())
|
||||
assert preserved.read_text() == "preserved"
|
||||
|
||||
|
||||
def test_provider_session_binding_rejects_durable_and_runtime_tampering(
|
||||
tmp_path, monkeypatch
|
||||
):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user