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
This commit is contained in:
1 parent
4564cd6f3f
commit
9968a0922a
19 files changed
+1103
-10
No files matched your search
@@ -35,6 +35,7 @@ from nisse_dev.canonical import ActionVerdict
|
||||
from nisse_dev.config import AgentConfig, LoopTuning
|
||||
from nisse_dev.execution import SSHHost
|
||||
from nisse_dev.gitea import GiteaClient, GiteaError
|
||||
from nisse_dev.loop import preflight
|
||||
from nisse_dev.loop.build import RunningLoop, build_loop_agent
|
||||
from nisse_dev.loop.items import (
|
||||
ANSWER,
|
||||
@@ -55,6 +56,9 @@ MAX_ANSWERS = 5 # per wait for something else, before giving up
|
||||
BUG_GRACE_S = 20
|
||||
|
||||
|
||||
_PREFLIGHTED: list[str] = []
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def running(
|
||||
live_config: AgentConfig,
|
||||
@@ -99,6 +103,17 @@ def running(
|
||||
# What the agent reads when told a new project is ready: replaced by
|
||||
# a test that adds one (D8).
|
||||
source = [config]
|
||||
if not _PREFLIGHTED:
|
||||
# The test hosts keep their markers: the session's first test
|
||||
# gets a real preflight (M4.1), the rest check versions.
|
||||
_PREFLIGHTED.append(project.name)
|
||||
SSHHost(
|
||||
project.build_host.address,
|
||||
key=project.build_host.key,
|
||||
user=project.build_host.user,
|
||||
port=project.build_host.port,
|
||||
state_dir=tmp_path / "ssh-marker",
|
||||
).execute(f'rm -f "{preflight.marker_path(project.name)}"')
|
||||
agent = build_loop_agent(config, load=lambda: source[0])
|
||||
agent.source = source # type: ignore[attr-defined]
|
||||
# The test drives intake and PR watching itself, not on a timer.
|
||||
@@ -318,6 +333,8 @@ def test_the_slice_end_to_end(
|
||||
assert marker in diff, "the pushed fix doesn't do what the comment asked"
|
||||
|
||||
# --- the operator approves and merges; the agent closes the item ---
|
||||
body = gitea._request("GET", f"/repos/{repo}/pulls/{pr}")["body"]
|
||||
assert "Tested with: " in body, body # M4.1: the toolchain checked
|
||||
operator.merge_and_close(issue)
|
||||
|
||||
# --- M3.1: the fast path judged every call; none needed an allow ---
|
||||
|
||||
Reference in new issue
Block a user