search-with-proof group 4 + image: board, spend reservation, coordinator, search-task image

Board as immutable versioned blobs (coordinator sole writer); budget reserve/settle
before model calls; per-task and run caps; coordinator loop over an injected
executor; batch.py accepts a fixed allow-list of search variables; XSS browser
oracle; ousast-search-task image (Python, Node/TypeScript, PHP, Java 17, sqlite,
bwrap, chromium) built by CI. 103 tests on the host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-05 14:26:43 +02:00
co-authored by Claude Opus 5.5
parent 6394ee473d
commit 4b38239f68
17 changed files with 1120 additions and 20 deletions
+18
View File
@@ -157,3 +157,21 @@ def test_bounded_lanes_oom_and_instrument_retry(tmp_path):
assert dispatch([item("fail")], workload(), tmp_path, {"ax": failed}, lanes=("ax",))
assert attempts == [0, 1]
@pytest.mark.parametrize("step", ["reason", "explore", "verify"])
def test_search_environment(step):
it = item()
it.extra_env = {"SEARCH_STEP": step, "SEARCH_ID": "search-1", "SEARCH_TASK_ID": "task-2"}
urls = {"SPEC_URL": "https://files.example/spec.dat", "RESULT_URL": "https://files.example/result.dat"}
doc = manifest("ousast-engine-search-1", workload(), it, urls)
assert {"name": "SEARCH_STEP", "value": step} in doc["spec"]["env"]
for key, value in [
("SEARCH_STEP", "shell"),
("SEARCH_ID", "https://secret"),
("SEARCH_TASK_ID", "x\nTOKEN=y"),
("MODEL_API_KEY", "secret"),
]:
it.extra_env = {key: value}
with pytest.raises(ValueError):
manifest("ousast-engine-search-1", workload(), it, urls)
+39
View File
@@ -0,0 +1,39 @@
import json
import pytest
from openultrasast.plane.memory import FileStore
from openultrasast.search.board import Board
def test_versions_provenance_claims_and_resume(tmp_path):
store = FileStore(tmp_path)
board = Board(store, "search-1", coordinator="host")
first = board.commit(
writer="host", facts=[dict(id="f1", text="sink", evidence_refs=["engine:1"], from_intents=[], step="triage", task_id="seed")]
)
second = board.commit(
writer="host",
intents=[dict(id="i1", description="trace input", from_facts=["f1"], status="open", claimant=None, step="reason", task_id="r1")],
)
assert first != second
assert json.loads(store.get_blob(board.prefix, first))["intents"] == []
assert Board.resume(store, "search-1", second, coordinator="host").state == board.state
with pytest.raises(PermissionError):
board.commit(writer="worker", hints=[])
state = board.state
state["facts"].clear()
assert board.state["facts"]
assert len(board.snapshot(max_bytes=256).encode()) <= 256
with pytest.raises(ValueError):
board.commit(writer="host", end_reason="clean")
def test_bad_references_and_oversize_are_atomic(tmp_path):
board = Board(FileStore(tmp_path), "one", coordinator="host", max_bytes=1024)
before = board.head
with pytest.raises(ValueError):
board.commit(writer="host", facts=[dict(id="f", text="x", evidence_refs=[], from_intents=["missing"], step="explore", task_id="t")])
with pytest.raises(ValueError):
board.commit(writer="host", hints=[dict(id="h", text="x" * 2048, step="human", task_id="operator")])
assert board.head == before
+50
View File
@@ -0,0 +1,50 @@
import html
import shutil
import subprocess
import pytest
from openultrasast.search.oracles import BrowserExecutor
def test_browser_launch_failure_is_not_negative(monkeypatch):
def fail(*args, **kwargs):
return subprocess.CompletedProcess(args, 1, "", "failed")
monkeypatch.setattr(subprocess, "run", fail)
with pytest.raises(OSError):
BrowserExecutor(binary="/fake/chromium")('<script>alert("ousast-xss")</script>', "nonce")
@pytest.mark.skipif(not (shutil.which("chromium") or shutil.which("chromium-browser")), reason="chromium not installed")
def test_real_browser_executes_not_reflects():
browser = BrowserExecutor()
payload = '<script>alert("ousast-xss")</script>'
try:
observed = browser(payload, "nonce")
except OSError as exc:
# A present binary is insufficient on restricted hosts. Only known
# host permission denials skip; ordinary browser failures remain failures.
if "Operation not permitted" in str(exc) or "Permission denied" in str(exc):
pytest.skip("host denies browser isolation/socket setup: " + str(exc))
raise
assert observed
assert not browser(html.escape(payload), "nonce")
assert not browser("<p>nonce</p>", "nonce")
@pytest.mark.parametrize("executed", [False, True])
def test_marker_is_dom_attribute_not_source_text(monkeypatch, executed):
import re
from urllib.parse import unquote
def run(command, **kwargs):
assert "--headless" in command and "--dump-dom" in command
assert "--unshare-all" in command and "--no-sandbox" not in command
document = unquote(command[-1].split(",", 1)[1])
attr, value = re.search(r'setAttribute\("([^"]+)","([^"]+)"\)', document).groups()
root = f'<html {attr}="{value}">' if executed else "<html>"
return subprocess.CompletedProcess(command, 0, root + document + "</html>", "")
monkeypatch.setattr(subprocess, "run", run)
assert BrowserExecutor(binary="/usr/bin/chromium")("<p>reflected nonce</p>", "nonce") is executed
+58
View File
@@ -0,0 +1,58 @@
from concurrent.futures import ThreadPoolExecutor
import pytest
from openultrasast.plane.budget import BudgetExhausted, SpendBudget
from openultrasast.search.budget import SearchBudget
def test_reserve_settle_release():
meter = SpendBudget(1)
reservation = meter.reserve(0.7)
with pytest.raises(BudgetExhausted):
meter.reserve(0.4)
reservation.settle(0.2)
assert meter.spent == 0.2 and meter.reserved == 0
other = meter.reserve(0.8)
other.release()
assert meter.reserved == 0
with pytest.raises(ValueError):
reservation.settle(0.1)
for bad in [-1, float("nan"), float("inf")]:
with pytest.raises(ValueError):
meter.reserve(bad)
def test_concurrent_reservations_cannot_overrun():
meter = SpendBudget(1)
def reserve(_):
try:
return meter.reserve(0.3)
except BudgetExhausted:
return None
with ThreadPoolExecutor(max_workers=12) as pool:
reservations = list(pool.map(reserve, range(20)))
assert sum(r is not None for r in reservations) == 3
assert meter.reserved <= 1
def test_config_validation():
assert SearchBudget().task_wall_seconds == 900
for kwargs in [dict(retries=-1), dict(memory_bytes=0), dict(wall_seconds=float("nan"))]:
with pytest.raises(ValueError):
SearchBudget(**kwargs)
def test_settlement_bound_and_release_then_readmission():
meter = SpendBudget(0.5)
reserved = meter.reserve(0.5)
with pytest.raises(ValueError, match="exceeds reservation"):
reserved.settle(0.6)
assert meter.reserved == 0.5 and meter.spent == 0
reserved.release()
meter.reserve(0.5).settle(0.5)
assert meter.spent == 0.5
with pytest.raises(BudgetExhausted):
meter.reserve(0.001)
+166
View File
@@ -0,0 +1,166 @@
import pytest
from openultrasast.plane.memory import FileStore
from openultrasast.search.board import Board
from openultrasast.search.budget import SearchBudget
from openultrasast.search.coordinator import Coordinator
def board(tmp_path):
return Board(FileStore(tmp_path), "search-1", coordinator="host")
def test_reason_explore_verify_and_resume(tmp_path):
calls = []
def execute(task):
calls.append(task)
assert task.limits["memory_bytes"] > 0
if task.step == "reason":
return {"status": "ok", "cost_usd": 0.01, "intents": [{"description": "try input", "from_facts": []}]}
if task.step == "explore":
return {"status": "ok", "cost_usd": 0.02, "fact": {"text": "candidate", "evidence_refs": ["capture:1"], "demo": "demo:1"}}
return {"status": "ok", "outcome": "demonstrated", "evidence_refs": ["verify:1"]}
b = board(tmp_path)
result = Coordinator(b, execute).run()
assert result["end_reason"] == "goal_met"
assert [t.step for t in calls] == ["reason", "explore", "verify"]
assert result["checkpoint"]["spent_usd"] == pytest.approx(0.03)
resumed = Board.resume(b.store, "search-1", b.head, coordinator="host")
assert Coordinator(resumed, execute).run() == result
assert len(calls) == 3
def test_reason_claim_cannot_prove_goal(tmp_path):
result = Coordinator(board(tmp_path), lambda task: {"status": "ok", "cost_usd": 0, "complete": True, "facts": []}).run()
assert result["end_reason"] == "exhausted"
def test_refusal_before_dispatch_and_run_deadline(tmp_path):
calls = []
result = Coordinator(board(tmp_path), calls.append, budget=SearchBudget(spend_usd=0.01)).run()
assert result["end_reason"] == "budget_spent" and calls == []
times = iter([0, 100, 100, 100, 100])
result = Coordinator(board(tmp_path / "two"), calls.append, budget=SearchBudget(wall_seconds=1), clock=lambda: next(times)).run()
assert result["end_reason"] == "budget_spent" and calls == []
def test_retry_fallback_bounded_and_cost_released(tmp_path):
calls = []
def fail(task):
calls.append(task.lane)
return {"status": "sandbox_failure", "cost_usd": 0}
state = Coordinator(board(tmp_path), fail, budget=SearchBudget(retries=1)).run()
assert calls == ["ax", "docker"]
assert state["end_reason"] == "exhausted"
assert state["checkpoint"]["retries"] == 1
assert state["checkpoint"]["spent_usd"] == 0
def test_claim_checkpoint_survives_interrupt_without_free_spend(tmp_path):
b = board(tmp_path)
def interrupt(task):
raise KeyboardInterrupt
with pytest.raises(KeyboardInterrupt):
Coordinator(b, interrupt).run()
checkpoint = b.head
resumed = Board.resume(b.store, "search-1", checkpoint, coordinator="host")
state = Coordinator(resumed, lambda task: {"status": "ok", "cost_usd": 0, "intents": []}).run()
assert state["checkpoint"]["spent_usd"] == 0.05
assert state["checkpoint"]["tasks"] == 2
def test_invalid_fact_fails_closed_and_records_failure(tmp_path):
def execute(task):
if task.step == "reason":
return {"status": "ok", "cost_usd": 0, "intents": [{"description": "x", "from_facts": []}]}
return {"status": "ok", "cost_usd": 0, "fact": {"text": "unbacked"}}
state = Coordinator(board(tmp_path), execute, budget=SearchBudget(reason_rounds=1)).run()
assert not state["facts"]
assert state["intents"][0]["status"] == "concluded"
assert state["intents"][0]["failure"] == "invalid_result"
def test_cleanup_failure_halts_and_charges_unknown_spend(tmp_path):
b = board(tmp_path)
calls = []
def broken(task):
calls.append(task)
raise RuntimeError("AX cleanup failed; stop dispatching")
with pytest.raises(RuntimeError, match="cleanup"):
Coordinator(b, broken).run()
assert len(calls) == 1
assert b.state["checkpoint"]["spent_usd"] == 0.05
assert b.state["checkpoint"]["pending"]["id"] == calls[0].id
def test_overclaimed_cost_stops_before_another_dispatch(tmp_path):
calls = []
def overrun(task):
calls.append(task)
return {"status": "ok", "cost_usd": 99, "intents": []}
result = Coordinator(board(tmp_path), overrun).run()
assert result["end_reason"] == "budget_spent"
assert len(calls) == 1
def test_claim_is_visible_in_worker_snapshot(tmp_path):
import yaml
def execute(task):
if task.step == "reason":
return {"status": "ok", "cost_usd": 0, "intents": [{"description": "read", "from_facts": []}]}
intent = yaml.safe_load(task.snapshot)["intents"][0]
assert intent["status"] == "claimed" and intent["claimant"] == task.id
return {"status": "execution_failure", "cost_usd": 0}
Coordinator(board(tmp_path), execute, budget=SearchBudget(reason_rounds=1)).run()
def test_batch_adapter_offline_checkpoint_and_step_dispatch(tmp_path, monkeypatch):
from dataclasses import asdict
from pathlib import Path
monkeypatch.syspath_prepend(str(Path(__file__).resolve().parents[1]))
from openultrasast.search.coordinator import BatchExecutor, SearchTask
calls = []
def lane(item, output, attempt):
import json
spec = json.loads(item.inputs["SPEC_URL"])
calls.append(spec)
assert item.extra_env["SEARCH_STEP"] == spec["step"]
return {"status": "ok", "cost_usd": 0, "intents": []}
adapter = BatchExecutor(
image="registry/task@sha256:" + "a" * 64, command=("python3", "/worker.py"), output=tmp_path, lanes={"ax": lane}
)
for index, step in enumerate(("reason", "explore", "verify")):
task = SearchTask(f"task-{index}", "search-1", step, "facts: []", {}, asdict(SearchBudget()))
assert adapter(task)["status"] == "ok"
assert adapter(task)["status"] == "ok"
assert len(calls) == 3
def test_invalid_identifiers_refused_before_paths(tmp_path):
from dataclasses import asdict
from openultrasast.search.coordinator import SearchTask
for bad in ("../escape", "/tmp/escape", "bad\nNAME", ""):
with pytest.raises(ValueError):
Board(FileStore(tmp_path), bad, coordinator="host")
with pytest.raises(ValueError):
SearchTask(bad, "search-1", "reason", "", {}, asdict(SearchBudget()))
+18
View File
@@ -0,0 +1,18 @@
from pathlib import Path
import yaml
def test_image_matches_runner_and_publishes_digest():
engine = Path("plane/Dockerfile.engine-task").read_text()
image = Path("plane/Dockerfile.search-task").read_text()
runner = "FROM ghcr.io/norandom/ax-task-runner:v0.3.1"
assert runner in engine and runner in image
for package in ("python3", "nodejs", "node-typescript", "php-cli", "sqlite3", "bubblewrap", "util-linux", "chromium"):
assert package in image
assert "17-jdk" in image and "--no-install-recommends" in image
assert "rm -rf /var/lib/apt/lists/" in image
workflow_text = Path(".github/workflows/engine-image.yml").read_text()
entries = yaml.safe_load(workflow_text)["jobs"]["image"]["strategy"]["matrix"]["include"]
assert {"package": "ousast-search-task", "dockerfile": "plane/Dockerfile.search-task"} in entries
assert "steps.build.outputs.digest" in workflow_text and "push: true" in workflow_text