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
@@ -82,6 +82,7 @@ class Driver:
|
||||
model: str | None = None,
|
||||
on_turn: Callable[[str], None] | None = None,
|
||||
screen: Callable[[ToolCall], Screening] | None = None,
|
||||
max_turns: int | None = None,
|
||||
) -> RunOutcome:
|
||||
"""``handlers`` answer control tools that don't end the run (e.g.
|
||||
start_milestone): each returns a result and whether it succeeded.
|
||||
@@ -95,7 +96,8 @@ class Driver:
|
||||
failures: list[str] = [] # the failed commands in a row, latest last
|
||||
offered = names(tools)
|
||||
nudged = False
|
||||
for turn in range(self._max_turns):
|
||||
cap = max_turns or self._max_turns
|
||||
for turn in range(cap):
|
||||
# L2: stop takes effect at the next tool boundary.
|
||||
if self._cancel.requested:
|
||||
return RunOutcome("stopped", note=f"before turn {turn + 1}")
|
||||
@@ -215,7 +217,7 @@ class Driver:
|
||||
note=f"{len(failures)} commands failed in a row: "
|
||||
+ "; ".join(failures),
|
||||
)
|
||||
return RunOutcome("capped", note=f"hit the {self._max_turns}-turn cap")
|
||||
return RunOutcome("capped", note=f"hit the {cap}-turn cap")
|
||||
|
||||
|
||||
def _brief(call: ToolCall, result: str) -> str:
|
||||
|
||||
Reference in new issue
Block a user