From c7956c46839ff8bffb008d0a4ef5d6dbdcc1b126 Mon Sep 17 00:00:00 2001 From: Emil Date: Thu, 23 Jul 2026 22:48:27 +0300 Subject: [PATCH] Fix relative worker work directory --- scimesh/worker/config.py | 4 ++++ scimesh/worker/runners.py | 3 +++ tests/test_worker_daemon.py | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/scimesh/worker/config.py b/scimesh/worker/config.py index 3732212..a0420eb 100644 --- a/scimesh/worker/config.py +++ b/scimesh/worker/config.py @@ -68,6 +68,10 @@ class WorkerConfig: _positive_number(self.cleanup_after_seconds, "cleanup_after_seconds", allow_zero=True) if not self.capabilities: raise ValueError("capabilities cannot be empty") + # Runner subprocesses use a task directory as their cwd. Keep the + # configured root absolute so input/output paths remain valid there + # even when the CLI received a convenient relative --work-dir value. + object.__setattr__(self, "work_dir", self.work_dir.expanduser().resolve()) @classmethod def from_environment( diff --git a/scimesh/worker/runners.py b/scimesh/worker/runners.py index 0ef3e77..46f219a 100644 --- a/scimesh/worker/runners.py +++ b/scimesh/worker/runners.py @@ -18,6 +18,9 @@ class SciMeshRunner: """Allowlisted adapter from coordinator workloads to the local SciMesh CLI.""" def run(self, task: ClaimedTask, task_dir: Path) -> RunResult: + # The subprocess changes cwd to task_dir. Absolute paths keep a caller + # supplied relative work directory from being resolved twice. + task_dir = task_dir.resolve() input_path = task_dir / "input" output_path = task_dir / "result.csv" # The coordinator contract historically used underscores while the diff --git a/tests/test_worker_daemon.py b/tests/test_worker_daemon.py index 6ad73c9..36f6cf6 100644 --- a/tests/test_worker_daemon.py +++ b/tests/test_worker_daemon.py @@ -314,6 +314,32 @@ def test_environment_overrides_allow_cli_only_configuration(monkeypatch: pytest. assert "similarity_search" in config.capabilities +def test_relative_work_dir_is_normalized_for_runner_subprocesses( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + monkeypatch.chdir(tmp_path) + config = WorkerConfig("https://coordinator.example", None, Path("./worker-data")) + assert config.work_dir == tmp_path / "worker-data" + + task_dir = config.work_dir / "task" / "1" + task_dir.mkdir(parents=True) + (task_dir / "input").write_text("fixture", encoding="utf-8") + command: list[str] = [] + + def fake_run(args: list[str], **_: object) -> None: + command.extend(args) + output = Path(args[args.index("--output") + 1]) + output.write_text("id,score\n", encoding="utf-8") + + monkeypatch.setattr("scimesh.worker.runners.subprocess.run", fake_run) + task = ClaimedTask( + "task", 1, "2026-07-30T00:00:00Z", "similarity-search", + InputArtifact("https://example.test/input", "a" * 64), {"query_smiles": "CCO"}, + ) + SciMeshRunner().run(task, task_dir) + assert command[4] == str(task_dir / "input") + + def test_worker_registration_sets_returned_identity(tmp_path: Path) -> None: worker, _, _, _, _ = daemon(tmp_path, None, b"") worker._register_worker()