From 76099f6d32f65f100987751712b9c98f82c33f59 Mon Sep 17 00:00:00 2001 From: Konrad Rokicki Date: Mon, 12 Oct 2026 21:03:44 -0400 Subject: [PATCH] mia-data: a round carries its own harness source; the env is pinned by pixi.lock alone Issue 54 was refused at production (PinMoved) because the harness checkout, which is also the working clone, had three commits since admission, even though the environment was identical. Now admission snapshots `harness/src` at the admitted commit into work_root/_harness// (read-only, flock, once per commit), and every job runs the pinned env's python on that snapshot through a `bin/harness` wrapper written into the workspace (PYTHONPATH ahead of the env's editable install). assert_pin compares only the pixi.lock digest. Co-Authored-By: Claude Fable 5.1 --- .gitignore | 1 + mia_data/activities.py | 4 +- mia_data/cli.py | 4 +- mia_data/jobspec.py | 102 +++++++++++++++++++++++++---------- notes/mia-crops-spec.md | 18 +++++-- notes/mia-data-handoff.md | 2 +- tests/test_mia_activities.py | 4 +- tests/test_mia_cli.py | 2 +- tests/test_mia_jobspec.py | 40 ++++++++++---- 9 files changed, 129 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index 97e368b..f5ef3ee 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ __pycache__/ .pytest_cache/ .env autodev.yaml +autodev-snippets.yaml mia_data.yaml diff --git a/mia_data/activities.py b/mia_data/activities.py index c4eb7c1..2f46a8a 100644 --- a/mia_data/activities.py +++ b/mia_data/activities.py @@ -29,7 +29,8 @@ from .binding import check_binding, hosts, record_urls from .config import HUMAN_LABELS, STAGES from .gallery import gallery_files, publish_gallery -from .jobspec import (SHA_RE, HostsNotAllowed, PinMoved, check_hosts_allowed, crop_job, harness_pin, match_job, +from .jobspec import (SHA_RE, HostsNotAllowed, PinMoved, check_hosts_allowed, crop_job, harness_pin, harness_snapshot, + match_job, match_prompt, smoke_job, snapshot, verify_job) from .models import (DatasetState, GalleryResult, JobArgs, JobDone, LabelEvent, MatchResult, Pins, PrUpdate, RoundLimits, SmokeResult, VerifyResult) @@ -224,6 +225,7 @@ async def admit(self, issue: int, event_id: str) -> Pins: rec = await catalog.record(self.gh_catalog, path, entry["record_commit"]) pins = Pins(rid, entry["record_commit"], await self.gh_snippets.get_ref_sha("main"), *await asyncio.to_thread(harness_pin, self.cfg)) + await asyncio.to_thread(harness_snapshot, self.cfg, pins.harness_commit) # freeze the source now (§6) _write(rd / "record.yaml", yaml.safe_dump(rec, sort_keys=False)) _write(rd / "downloads.json", json.dumps(entry, indent=1)) _write(rd / "pins.json", json.dumps(dataclasses.asdict(pins))) # last: marks the round admitted diff --git a/mia_data/cli.py b/mia_data/cli.py index 3e1e397..a3e5d66 100644 --- a/mia_data/cli.py +++ b/mia_data/cli.py @@ -48,7 +48,7 @@ async def cmd_check(cfg, args) -> int: from autodev.jobs import csub_client from .binding import missing_hosts - from .jobspec import HARNESS_ENV, harness_bin, harness_pin + from .jobspec import HARNESS_ENV, harness_env, harness_pin rows: list[tuple[str, bool, str]] = [] @@ -111,7 +111,7 @@ async def dirs(): async def pin(): c, digest = harness_pin(cfg) - if not (b := harness_bin(cfg)).is_file() or not os.access(b, os.X_OK): + if not (b := harness_env(cfg)).is_file() or not os.access(b, os.X_OK): raise RuntimeError(f"{b} is missing or not executable: run `cd harness && pixi install` in harness.dir") return f"{c[:12]} env {digest[:12]}" diff --git a/mia_data/jobspec.py b/mia_data/jobspec.py index 84e7a2f..ab95aa8 100644 --- a/mia_data/jobspec.py +++ b/mia_data/jobspec.py @@ -2,9 +2,13 @@ The record and bindings live under `paths.state_dir`, which is never mounted into a job: the caller copies them into the job's workspace first, and the builders take their names relative to it (`record.yaml`, `binding.yaml`). -The snapshot, the harness checkout, the dataset cache and any extra input are mounted read-only; only the workspace -(and the dataset cache, for jobs that run reviewed code) is writable. Every builder refuses (PinMoved) when the -harness checkout is no longer the round's admitted pin, and gives the job the admitted pin values.""" +The snippet snapshot, the harness snapshot, the harness environment, the dataset cache and any extra input are +mounted read-only; only the workspace (and the dataset cache, for jobs that run reviewed code) is writable. + +A round carries its own harness (§6): its source is a snapshot of `harness/src` at the admitted commit, taken from +the `harness.dir` checkout, and its environment is that checkout's pixi env, pinned by the sha256 of `pixi.lock`. +Commits in `harness.dir` therefore never touch an in-flight round; only a changed `pixi.lock` does, and every +builder then refuses (PinMoved).""" from __future__ import annotations @@ -35,7 +39,7 @@ class PinMoved(RuntimeError): - """The harness checkout moved since the round was admitted. Not retryable: the round goes to needs-human.""" + """The harness environment changed since the round was admitted. Not retryable: the round goes to needs-human.""" class HostsNotAllowed(RuntimeError): @@ -88,17 +92,55 @@ def snapshot(cfg, sha: str, tokens=lambda: None) -> Path: return dest +def _env_digest(cfg) -> str: + return hashlib.sha256((cfg.harness.dir / "harness" / "pixi.lock").read_bytes()).hexdigest() + + def harness_pin(cfg) -> tuple[str, str]: """(commit, env digest) of the `harness.dir` checkout: HEAD and the sha256 of harness/pixi.lock.""" - h = cfg.harness.dir - return git("rev-parse", "HEAD", cwd=h), hashlib.sha256((h / "harness" / "pixi.lock").read_bytes()).hexdigest() + return git("rev-parse", "HEAD", cwd=cfg.harness.dir), _env_digest(cfg) + + +def _harness_snap_dir(cfg, commit: str) -> Path: + return cfg.paths.work_root / "_harness" / commit + + +def harness_snapshot(cfg, commit: str) -> Path: + """`harness/src` of the `harness.dir` checkout at `commit`, extracted once and read-only, at + `work_root/_harness//src`. Taken at admission, so later commits in the checkout don't reach the round.""" + if not SHA_RE.fullmatch(commit): + raise ValueError(f"not a full commit SHA: {commit!r}") + dest = _harness_snap_dir(cfg, commit) + if dest.exists(): + return dest + dest.parent.mkdir(parents=True, exist_ok=True) + with open(dest.with_name(f".{commit}.lock"), "w") as lock: + fcntl.flock(lock, fcntl.LOCK_EX) + if dest.exists(): + return dest + tmp = dest.with_name(f".{commit}.tmp") + if tmp.exists(): + subprocess.run(["chmod", "-R", "u+w", str(tmp)], check=True) + shutil.rmtree(tmp) + tmp.mkdir() + r = subprocess.run(["git", "-c", "core.hooksPath=/dev/null", "-C", str(cfg.harness.dir), "archive", commit, + "harness/src"], capture_output=True, + env={**os.environ, "GIT_CONFIG_GLOBAL": "/dev/null", "GIT_CONFIG_NOSYSTEM": "1"}) + if r.returncode: + raise GitError(f"git archive {commit} harness/src in {cfg.harness.dir}: " + f"{r.stderr.decode(errors='replace').strip()[-2000:]}") + subprocess.run(["tar", "-x", "--strip-components=1", "-C", str(tmp)], input=r.stdout, check=True) + subprocess.run(["chmod", "-R", "a-w", str(tmp)], check=True) + os.rename(tmp, dest) + return dest def assert_pin(cfg, pins) -> None: - now = harness_pin(cfg) - if now != (pins.harness_commit, pins.env_digest): - raise PinMoved(f"harness checkout {cfg.harness.dir} moved since admission: commit {pins.harness_commit[:12]} " - f"-> {now[0][:12]}, pixi.lock {pins.env_digest[:12]} -> {now[1][:12]}") + """The admitted environment, or no job. The harness commit is served by its snapshot, so HEAD may move.""" + now = _env_digest(cfg) + if now != pins.env_digest: + raise PinMoved(f"harness environment in {cfg.harness.dir} changed since admission: pixi.lock " + f"{pins.env_digest[:12]} -> {now[:12]}") def check_hosts_allowed(spec: JobSpec, patterns: list[str]) -> None: @@ -112,14 +154,18 @@ def match_prompt(pins) -> str: record=pins.record, record_commit=pins.record_commit, snippets_commit=pins.snippets_commit) -def harness_bin(cfg) -> Path: - """The harness environment's entry point. Run directly: `pixi run` wants the install lock on the prefix, and +def harness_env(cfg) -> Path: + """The harness environment's python. Run directly: `pixi run` wants the install lock on the prefix, and `harness.dir` is mounted read-only.""" - return cfg.harness.dir / "harness" / ".pixi" / "envs" / "default" / "bin" / "harness" + return cfg.harness.dir / "harness" / ".pixi" / "envs" / "default" / "bin" / "python" -def _harness(cfg) -> str: - return q(str(harness_bin(cfg))) +def _harness(cfg, pins) -> str: + """The `harness` command for a round: the pinned environment's python running the snapshot's source. -P keeps + the job's cwd off sys.path; PYTHONPATH precedes the env's editable install of the checkout's own source.""" + src = _harness_snap_dir(cfg, pins.harness_commit) / "src" + return (f"env PYTHONPATH={q(str(src))} {q(str(harness_env(cfg)))} -P -c " + + q("import sys; from mia_harness.cli import main; sys.exit(main())")) def _binding(ws, rel: str) -> tuple[str, list[str]]: @@ -135,11 +181,12 @@ def _parent(f): def _spec(cfg, kind: str, ws, pins, body: str, allow: list[str], ro: list, record: str | None = None, writable: bool = False, **kw) -> JobSpec: - assert_pin(cfg, pins) # the admitted harness, or no job + assert_pin(cfg, pins) # the admitted environment, or no job + hsnap = harness_snapshot(cfg, pins.harness_commit) # the admitted source (made at admission; idempotent) env = {"MIA_HARNESS_JOB_BYTES": str(int(cfg.limits.job_bytes)), "MIA_HARNESS_COMMIT": pins.harness_commit, "MIA_HARNESS_ENV_DIGEST": pins.env_digest, "MIA_HARNESS_ALLOWED_HOSTS": ",".join(allow), "MIA_HARNESS_CACHE_DIR": "", "MIA_HARNESS_CACHE_WRITABLE": "0"} - ro, rw = [str(cfg.harness.dir), *(str(p) for p in ro if p)], [] + ro, rw = [str(cfg.harness.dir), str(hsnap), *(str(p) for p in ro if p)], [] if record is not None: # the dataset cache, §6.6 if not (isinstance(record, str) and ID_RE.fullmatch(record)): raise ValueError(f"bad record id {record!r}") @@ -147,9 +194,11 @@ def _spec(cfg, kind: str, ws, pins, body: str, allow: list[str], ro: list, recor cache.mkdir(parents=True, exist_ok=True) # a read-only mount must exist (rw if writable else ro).append(str(cache)) env.update(MIA_HARNESS_CACHE_DIR=str(cache), MIA_HARNESS_CACHE_WRITABLE="1" if writable else "0") - path = f'export PATH={":".join(q(p) for p in cfg.agent.path_prepend)}:"$PATH"\n' if cfg.agent.path_prepend else "" + # every job calls `harness`: a wrapper in the workspace, first on PATH, runs the round's pinned harness + pre = (f"mkdir -p bin && printf '#!/bin/sh\\nexec %s \"$@\"\\n' {q(_harness(cfg, pins))} > bin/harness && " + f"chmod +x bin/harness\nexport PATH=\"$PWD/bin\":{':'.join([*(q(p) for p in cfg.agent.path_prepend), '"$PATH"'])}\n") # ponytail: plain jobs take JobSpec's 1 cpu / 4000 MB; add limits.job_cpus / job_mem_mb if crops need more. - return JobSpec(name=f"{cfg.deployment}-{kind}", command=f'set -u\n{path}{body}\nrc=$?\necho "$rc" > exit_code\nexit "$rc"\n', + return JobSpec(name=f"{cfg.deployment}-{kind}", command=f'set -u\n{pre}{body}\nrc=$?\necho "$rc" > exit_code\nexit "$rc"\n', workspace=str(ws), walltime_min=cfg.limits.job_walltime_min, allow_hosts=allow, ro_mounts=ro, rw_mounts=rw, env=env, **kw) @@ -160,9 +209,8 @@ def match_job(cfg, ws, pins, record_path: str = "record.yaml") -> JobSpec: `pins.snippets_commit`. Anthropic hosts come from the csub policy's [agents] table, never from here.""" rec = yaml.safe_load(Path(ws, record_path).read_text()) or {} snap = _snap_dir(cfg, pins.snippets_commit) - body = (f"ln -sfn {q(str(snap))} snapshot && mkdir -p bin && \\\n" - f"printf '#!/bin/sh\\nexec %s \"$@\"\\n' {q(_harness(cfg))} > bin/harness && chmod +x bin/harness && \\\n" - f'PATH="$PWD/bin:$PATH" claude -p "$(cat prompt.md)" --model {q(cfg.agent.model)} ' + body = (f"ln -sfn {q(str(snap))} snapshot && \\\n" + f'claude -p "$(cat prompt.md)" --model {q(cfg.agent.model)} ' f"--max-budget-usd {cfg.limits.agent_max_budget_usd} --output-format json \\\n" f' --allowedTools "{MATCH_TOOLS}" > claude.json 2> claude.err') return _spec(cfg, "match", ws, pins, body, hosts(record_urls(rec), cfg), [snap], @@ -172,7 +220,7 @@ def match_job(cfg, ws, pins, record_path: str = "record.yaml") -> JobSpec: def smoke_job(cfg, ws, pins, binding_path: str, snapshot, record_path: str) -> JobSpec: """The worker's own smoke of a binding, so the agent's claim is checked (§7.2). Reviewed code: cache rw.""" record, urls = _binding(ws, binding_path) - body = f"{_harness(cfg)} smoke {q(binding_path)} --snippets {q(str(snapshot))} --record {q(record_path)} --out smoke.json" + body = f"harness smoke {q(binding_path)} --snippets {q(str(snapshot))} --record {q(record_path)} --out smoke.json" return _spec(cfg, "smoke", ws, pins, body, hosts(urls, cfg), [snapshot], record=record, writable=True) @@ -181,7 +229,7 @@ def crop_job(cfg, ws, pins, binding_path: str, snapshot, record_path: str, n: in """`harness crop` into `ws/crops` (§7.4, §7.5). `trusted` (a matched or merged SHA, or production) mounts the dataset cache read-write; a PR head gets it read-only and keeps a private cache (§6.6).""" record, urls = _binding(ws, binding_path) - body = (f"{_harness(cfg)} crop {q(binding_path)} --snippets {q(str(snapshot))} --record {q(record_path)} " + body = (f"harness crop {q(binding_path)} --snippets {q(str(snapshot))} --record {q(record_path)} " f"--n {int(n)} --seed {q(seed)} --round {q(round_id)} --batch {int(batch)} --out crops" + (f" --existing {q(str(existing))}" if existing else "")) return _spec(cfg, "crop", ws, pins, body, hosts(urls, cfg), [snapshot, _parent(existing)], record=record, @@ -191,7 +239,7 @@ def crop_job(cfg, ws, pins, binding_path: str, snapshot, record_path: str, n: in def verify_job(cfg, ws, pins, crops_dir, binding_path: str, existing) -> JobSpec: """`harness verify` then `harness preview` on a crop job's output, mounted read-only (§6.4, §6.7). Loads no snippet code: no network, no dataset cache.""" - h, d = _harness(cfg), q(str(crops_dir)) - body = (f"{h} verify {d} --binding {q(binding_path)}" + (f" --existing {q(str(existing))}" if existing else "") - + f" --out verify.json; v=$?\n{h} preview {d} --out preview && (exit $v)") # overlays even if a crop fails + d = q(str(crops_dir)) + body = (f"harness verify {d} --binding {q(binding_path)}" + (f" --existing {q(str(existing))}" if existing else "") + + f" --out verify.json; v=$?\nharness preview {d} --out preview && (exit $v)") # overlays even if a crop fails return _spec(cfg, "verify", ws, pins, body, [], [crops_dir, _parent(existing)]) diff --git a/notes/mia-crops-spec.md b/notes/mia-crops-spec.md index ec7ba7b..876b78f 100644 --- a/notes/mia-crops-spec.md +++ b/notes/mia-crops-spec.md @@ -316,11 +316,19 @@ The harness is the executable half of the spec. It is a pip-installable package environment. Humans own it. Agents use it and never modify it. - **Pinning.** `harness.dir` is a configured checkout of `mia-snippets` with `pixi install` done in `harness/`. - Jobs run `/harness/.pixi/envs/default/bin/harness` directly, not `pixi run`, which wants the - install lock on a prefix that jobs mount read-only. A round - records that checkout's `git rev-parse HEAD` and the sha256 of `harness/pixi.lock` when it is admitted. A - job is refused, sending the round to `needs-human`, if the checkout has moved since. `ponytail:` one - environment per harness commit, if harness updates ever need to overlap in-flight rounds. + A round records that checkout's `git rev-parse HEAD` and the sha256 of `harness/pixi.lock` when it is + admitted, and **carries its own harness from then on**: + - **Source:** admission extracts `harness/src` at the admitted commit to `work_root/_harness//src` + (read-only, once per commit, like a snippet snapshot). Every job runs that copy, so commits in + `harness.dir` never reach an in-flight round; a new round picks up the new HEAD. + - **Environment:** the checkout's `harness/.pixi/envs/default`, pinned by the `pixi.lock` digest. A job is + refused, sending the round to `needs-human`, only if `pixi.lock` changed since admission. Environment + changes are rare and human (a new allowlist dependency), so they wait for in-flight rounds. + `ponytail:` one environment per lock digest, if that wait ever hurts. + - Jobs call a `bin/harness` wrapper the job spec writes into the workspace: + `PYTHONPATH=/src /bin/python -P -c 'from mia_harness.cli import main; …'`, not `pixi run`, + which wants the install lock on a prefix that jobs mount read-only. `PYTHONPATH` precedes the environment's + editable install of the checkout's own source. - **Harness and snippets are pinned separately.** A snippet snapshot (§7.2) contains only `snippets/`, `WITHDRAWN.yaml` and `SPEC.md` from its SHA, never `harness/`. The harness never imports anything from a snapshot except `snippets//snippet.py`, through its loader. So a snapshot whose own `harness/` diff --git a/notes/mia-data-handoff.md b/notes/mia-data-handoff.md index 32c1478..c3c2e5a 100644 --- a/notes/mia-data-handoff.md +++ b/notes/mia-data-handoff.md @@ -41,7 +41,7 @@ on your behalf are listed at the end: read them, they are the decisions you did 2. **Shared directories** visible from the worker host and compute nodes, none under `$HOME`: `paths.state_dir`, `paths.work_root`, `paths.storage_root` (each root a path or `{path, url}`, the url being the folder in Fileglancer so issue comments can link it), and a mia-snippets checkout for `harness.dir` - (`cd harness && pixi install` there; jobs call `harness/.pixi/envs/default/bin/harness` directly). + (`cd harness && pixi install` there; jobs run the env's python on a per-round snapshot of `harness/src`). 3. **Broker policy** (`~/.config/csub/broker.toml` of the service account): `allowed_roots` covering `harness.dir`, `work_root`, `storage_root`; `env_allow` with `MIA_HARNESS_*`; `[agents] claude = [...]` enabled; the data hosts and their redirect targets in `allowed_hosts`; CPU `[queues.]` tables named after diff --git a/tests/test_mia_activities.py b/tests/test_mia_activities.py index 62545e4..bd74abc 100644 --- a/tests/test_mia_activities.py +++ b/tests/test_mia_activities.py @@ -27,6 +27,8 @@ def _setup(tmp_path, probe=None, csub=None): h = cfg.harness.dir (h / "harness").mkdir() (h / "harness" / "pixi.lock").write_text("lock v1\n") + (h / "harness" / "src" / "mia_harness").mkdir(parents=True) + (h / "harness" / "src" / "mia_harness" / "cli.py").write_text("def main(): return 0\n") run_git("init", "-q", "-b", "main", cwd=h) run_git("add", "-A", cwd=h) run_git(*GIT_ID, "commit", "-qm", "harness", cwd=h) @@ -131,7 +133,7 @@ async def test_submit_refuses_moved_harness(tmp_path): (t.cfg.harness.dir / "harness" / "pixi.lock").write_text("lock v2\n") with pytest.raises(ApplicationError) as e: await t.a.submit(job_key(1, ROUND, "match", 1), "match", JobArgs(str(ws), ROUND, pins)) - assert e.value.non_retryable and "moved" in str(e.value) and t.csub.submitted == [] + assert e.value.non_retryable and "changed" in str(e.value) and t.csub.submitted == [] async def test_submit_refuses_host_missing_from_probe(tmp_path): diff --git a/tests/test_mia_cli.py b/tests/test_mia_cli.py index 339b476..a07658c 100644 --- a/tests/test_mia_cli.py +++ b/tests/test_mia_cli.py @@ -81,7 +81,7 @@ async def login(cfg): def _harness_bin(cfg, mode=0o755): - b = cfg.harness.dir / "harness" / ".pixi" / "envs" / "default" / "bin" / "harness" + b = cfg.harness.dir / "harness" / ".pixi" / "envs" / "default" / "bin" / "python" b.parent.mkdir(parents=True) b.write_text("#!/bin/sh\n") b.chmod(mode) diff --git a/tests/test_mia_jobspec.py b/tests/test_mia_jobspec.py index 0fbd1c9..437a4ca 100644 --- a/tests/test_mia_jobspec.py +++ b/tests/test_mia_jobspec.py @@ -11,7 +11,7 @@ from autodev_helpers import GIT_ID, make_origin, run_git from mia_data import jobspec from mia_data.jobspec import (HostsNotAllowed, PinMoved, assert_pin, check_hosts_allowed, crop_job, harness_pin, - match_job, match_prompt, smoke_job, snapshot, verify_job) + harness_snapshot, match_job, match_prompt, smoke_job, snapshot, verify_job) from mia_data.models import Pins from mia_helpers import make_cfg @@ -26,6 +26,8 @@ def _cfg(tmp_path): h = cfg.harness.dir (h / "harness").mkdir() (h / "harness" / "pixi.lock").write_text("lock v1\n") + (h / "harness" / "src" / "mia_harness").mkdir(parents=True) + (h / "harness" / "src" / "mia_harness" / "cli.py").write_text("def main(): return 0\n") run_git("init", "-q", "-b", "main", cwd=h) run_git("add", "-A", cwd=h) run_git(*GIT_ID, "commit", "-qm", "harness", cwd=h) @@ -113,15 +115,28 @@ def test_snapshot_rejects_non_sha(tmp_path, sha): snapshot(make_cfg(tmp_path), sha) -def test_assert_pin_raises_after_new_commit(tmp_path): - cfg = _cfg(tmp_path) +def test_a_new_harness_commit_does_not_touch_an_admitted_round(tmp_path): + cfg, ws = _cfg(tmp_path), _ws(tmp_path) pins = _pins(cfg) - assert_pin(cfg, pins) - (cfg.harness.dir / "README").write_text("x\n") + snap = harness_snapshot(cfg, pins.harness_commit) + assert snap == cfg.paths.work_root / "_harness" / pins.harness_commit + assert (snap / "src" / "mia_harness" / "cli.py").read_text() == "def main(): return 0\n" and _readonly(snap) + (cfg.harness.dir / "harness" / "src" / "mia_harness" / "cli.py").write_text("def main(): return 1\n") run_git("add", "-A", cwd=cfg.harness.dir) run_git(*GIT_ID, "commit", "-qm", "moved", cwd=cfg.harness.dir) - with pytest.raises(PinMoved): - assert_pin(cfg, pins) + assert_pin(cfg, pins) # HEAD moved, env didn't + s = smoke_job(cfg, ws, pins, "binding.yaml", tmp_path / "snap", "record.yaml") + assert f"PYTHONPATH={snap}/src" in s.command and str(snap) in s.ro_mounts + assert (snap / "src" / "mia_harness" / "cli.py").read_text() == "def main(): return 0\n" # still the admitted one + assert harness_snapshot(cfg, harness_pin(cfg)[0]) != snap # the new commit gets its own + + +def test_harness_snapshot_rejects_unknown_commit_and_non_sha(tmp_path): + cfg = _cfg(tmp_path) + with pytest.raises(GitError, match="archive"): + harness_snapshot(cfg, "e" * 40) + with pytest.raises(ValueError): + harness_snapshot(cfg, "main") def test_assert_pin_raises_when_lock_changes(tmp_path): @@ -143,7 +158,8 @@ def test_match_job(tmp_path): cache = str(cfg.paths.work_root / "_cache" / "rec-1") assert cache in s.ro_mounts and s.rw_mounts == [] and s.env["MIA_HARNESS_CACHE_WRITABLE"] == "0" snap = str(cfg.paths.work_root / "_snapshots" / pins.snippets_commit) - assert sorted(s.ro_mounts) == sorted([str(cfg.harness.dir), snap, cache]) and snap in s.command + hsnap = str(cfg.paths.work_root / "_harness" / pins.harness_commit) + assert sorted(s.ro_mounts) == sorted([str(cfg.harness.dir), hsnap, snap, cache]) and snap in s.command assert (s.env["MIA_HARNESS_COMMIT"], s.env["MIA_HARNESS_ENV_DIGEST"]) == (pins.harness_commit, pins.env_digest) @@ -161,7 +177,8 @@ def test_crop_job_cache_mode(tmp_path, trusted): def test_crop_job_mounts_the_existing_manifests_dir(tmp_path): cfg, ws, m = _cfg(tmp_path), _ws(tmp_path), tmp_path / "ds" / "manifest.json" s = crop_job(cfg, ws, _pins(cfg), "binding.yaml", tmp_path / "snap", "record.yaml", 4, "s", "i1-e2", 1, m, True) - assert s.command.rstrip().split("\n")[1].endswith(f"--existing {m}") + body = next(l for l in s.command.split("\n") if l.startswith("harness crop")) + assert body.endswith(f"--existing {m}") assert str(m.parent) in s.ro_mounts and str(m) not in s.ro_mounts # csub mounts directories only @@ -203,7 +220,10 @@ def test_every_builder_sets_harness_env(tmp_path): assert str(cfg.harness.dir) in s.ro_mounts and s.workspace == str(ws) assert s.command.rstrip().endswith('echo "$rc" > exit_code\nexit "$rc"') assert s.walltime_min == cfg.limits.job_walltime_min and not set(s.ro_mounts) & set(s.rw_mounts) - assert f"{cfg.harness.dir}/harness/.pixi/envs/default/bin/harness" in s.command and "pixi run" not in s.command + snap = cfg.paths.work_root / "_harness" / pins.harness_commit + assert f"{cfg.harness.dir}/harness/.pixi/envs/default/bin/python -P -c" in s.command and "pixi run" not in s.command + assert f"PYTHONPATH={snap}/src" in s.command and str(snap) in s.ro_mounts + assert "> bin/harness && chmod +x bin/harness\nexport PATH=\"$PWD/bin\":\"$PATH\"\n" in s.command def test_every_builder_refuses_a_moved_harness(tmp_path):