mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
Agent Substrate's golden-snapshot boot runs the same image with the same task env, so the runner no longer starts its command by itself: after the workspaces are ready it waits for POST /ousast/v1/start (run/task checked, once per boot, refused after a delivered run). The reconciler sends it through svc/atenet-router with ate-target-actor, which never addresses the golden actor (plane/router.py), and the bound Model's secretKey variable travels only in that body, into the child's env. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
10 KiB
10 KiB
Implementation Plan
Order follows the dependency chain: ax bring-up (parallel with 1-2) -> manifests -> budget -> runner -> ax-backed reconciler -> tasks -> increment run on ax -> measurement. Every task ships with its tests; a task is done when its tests pass in the host suite, ruff and mypy are clean, and the commit is gated on pytest's own exit code. Script-level pipeline work stays paused.
-
0. ax bring-up on this host
- Install
kind,kubectl, theaxCLI andko(user-local).ops/ax/up.sh: create the kind cluster and install Agent Substrate with itshack/create-kind-cluster.shandhack/install-ate-kind.sh --deploy-ate-system, deploy the ax control plane withkoto the kind registry, apply one trivial Task and wait for it to finish;ops/ax/down.shtears it down. Record the measured idle footprint (RSS, CPU) inops/ax/README.mdnext to the 7 GB host constraint. ousast plane doctor(inreconciler.pyor a siblingdoctor.py): the four checks of Requirement 3.5.- Stop rule: if the cluster does not come up within one working session, report the exact failure and stop; the decision to work around it is the maintainer's.
- Requirements: 3.5, 3.6
- Install
-
1. Manifest schemas and validation
- Add
pyyamlas a declared dependency inpyproject.toml. src/openultrasast/plane/manifests.py: dataclasses and loaders forTask,Workspace,Model(ax.io/v1alpha1, ax's fields only) andRun(openultrasast.io/v1alpha1: tasks, dependsOn, inputs, outputs, budget, serialize); reject unknown fields, a provider outside ax's two without theopenultrasast.io/provider-extensionannotation, and a Run with a cycle or an input naming no task.tests/test_plane_manifests.py: valid manifests load; each rejection names the field.- Requirements: 1.1, 1.2, 1.3, 1.4
- Add
-
2. Budget metering
src/openultrasast/plane/budget.py: a client wrapper that sums provider usage per call, prices from the Model's parameters, raisesBudgetExhaustedat the ceiling andAccountErroron 401/402 or an "insufficient balance" body; an unpriced model reportsunpricedand refuses ausdbudget.- Tests with a scripted client: ceiling stop, account error, unpriced refusal, cache-hit pricing.
- Requirements: 2.3, 2.4
-
3. Runner contract
src/openultrasast/plane/runner.py: readAX_TASK_YAML/AX_WORKSPACES_YAML; materialisegitentries at the pinned commit from the local case cache and writespec.files(first boot only); runspec.commandas a childpython -m openultrasast.plane.tasks.<name>in its own process group, cwd the first workspace,AX_METADATA_URLset;/healthz/readyzand ax's metadata paths on port 80 (503 until workspaces are ready; disabled only underAX_RUNNER_HTTP=0for unit tests); after the command exits, POST the output directory as a tar stream toOUSAST_ARTIFACT_URL(reportfailedif delivery fails), write/workspace/.ousast-state/<task>.done.json, and keep serving until SIGTERM (ax's contract for PID 1); a boot with the marker never reruns the command and retries only a failed delivery; SIGTERM goes to the command's group, SIGKILL afterOUSAST_TERM_GRACE, exit 0.AX_RUNNER_EXIT_AFTER_COMMAND=1(tests only) exits after delivery with 0 done, 2 failed, 3 unfinished. The command never starts by itself: after the workspaces are ready the runner waits forPOST /ousast/v1/start({run, task, credentials}; 503 before ready, 409 on a run/task mismatch, a second request, or a marker recording a delivered run; 202 once), merges the credentials into the child's environment only and redacts them from its echoed stderr, so Agent Substrate's golden-snapshot boot runs nothing (Req 4.5, 4.6).AX_RUNNER_AUTOSTART=1(tests only) starts without a request.plane/Dockerfile.runner: this package on Python 3.12, installed to/usr/local/bin/ax-task-runner, loaded into kind byops/ax/up.sh.tests/test_plane_runner.py: the env contract on a fixture workspace; readyz 503 then 200; delivery to a local receiver; delivery failure yieldsfailed; the child's cwd andAX_METADATA_URL; the runner stays up after the command; SIGTERM kills a long-running command's group; the marker prevents a rerun; no command before a start request, 409 on a wrong run/task and on a second start, the credential reaches the child (its hash in the artifact) and no file or log record, a crash printing it is redacted, and of two runners with identical env only the addressed one runs (the golden scenario).- Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6
-
4. ax-backed reconciler and CLI
src/openultrasast/plane/reconciler.py(under 500 lines, no pipeline logic, no local subprocess executor):state.json, statuses, topological submission through theaxCLI (path injectable),ax resume taskafterax apply(a created Task isSuspended), retrying DeadlineExceeded/Unavailable with backoff up toOUSAST_RESUME_TIMEOUT; completion is the artifact delivery, never an ax phase (ax has none for a finished command);Failedor a vanished task before delivery, or no delivery withinOUSAST_TASK_TIMEOUT, fails the task with ax's condition message; the artifact HTTP receiver,serializelabels never overlapping,--workers, budgets and artifact env passed in the rendered Task manifest, outcome read from the deliveredsummary.json, skipdoneon rerun, PID lock per run,ax deleteof finished Tasks. Once ax reportsRunningafter a resume, one start request per resume through Agent Substrate's router (src/openultrasast/plane/router.py:kubectl port-forward svc/atenet-routerfor the run, orOUSAST_ROUTER_URL; headerate-target-actor: <atespace>/<task>; 502/503/504 and connection errors retried up toOUSAST_START_TIMEOUT, a 409 final), carrying the bound Model'ssecretKey.keyvariable from the reconciler's environment; a Model-bound task whose variable is unset fails beforeax apply.ousast plane run <Run.yaml>,ousast plane status <run>(token attribution table per task and Model from the tasks'summary.jsonusage;--unitsfromunits.jsonl; also written toattribution.json),ousast plane workspaces <population.toml>(generates Workspace manifests per case pin).tests/test_plane_reconciler.py: a fakeaxCLI with ax's real lifecycle (Suspended, a first resume failing DeadlineExceeded, Running without a Completed phase) drives a DAG: order, non-overlap, skip-on-rerun,unfinishedandfailedpropagation, resume retry and timeout,Failedwith a condition message, the task timeout without delivery, the lock, the attribution table, and the line-count assertion; with a fake router: one start per task after the accepted resume and before the delivery, the credential in the verify start only and in no file under the run or tmp dirs, a missing secret failing the task, a refused start failing it.tests/test_plane_router.py: port parsing from a fake kubectl and its termination on exit, retry on 503/502 then 202, 409 raised with redacted text, the start timeout.tests/test_plane_ax_live.py(markerax, skipped without a live cluster): one trivial Task end to end.- Requirements: 3.1, 3.2, 3.3, 3.4, 2.2, 4.5, 4.6, 7.1, 7.3
-
5.
repo-factstasksrc/openultrasast/plane/tasks/repo_facts.py: product files, functions per file (per-language declaration patterns), call sites per candidate function across product files (path, line, enclosing function, cap 40), sorted and deterministic;summary.jsonper the task contract.tests/test_plane_repo_facts.py: three-file tree; cross-file callers found, tests and vendor excluded, identical output on a second run.- Requirements: 5.1, 5.2, 2.1
-
6.
verifyandagreetaskssrc/openultrasast/plane/tasks/verify.py: the batched hunt moved fromverify_batched.py(one hunt per file, up to 6 candidates, 6 steps, candidate-anchored sites) with known callers fromfacts.jsonin the prompt, tool turns and usage per hunt inunits.jsonl, per-unit resume,passfrom env, budget wrapper from task 2;summary.jsonneverdoneunless every unit finished, and records the bound Model and the summed usage fields (prompt, cache-hit, output tokens, calls, usd).src/openultrasast/plane/tasks/agree.py: agreement across pass outputs, per-candidate cost and turns, declared-site matching viaevaluate.matches_v2.tests/test_plane_verify.py: scripted client; prompt carries the callers; resume asks only the unfinished file; a scripted 402 yieldsfailed; agree on two scripted passes.- Requirements: 5.3, 2.1, 2.2, 2.3, 7.2
-
7. Manifests for the increment
plane/models/deepseek-flash.yaml(annotated extension, prices in parameters);plane/tasks/repo-facts.yaml,plane/tasks/verify.yaml,plane/tasks/agree.yaml;plane/runs/validation-46.yaml: repo-facts -> verify pass a, verify pass b -> agree, budgets per task, inputs from the recorded candidate and triage artifacts.- Workspaces generated for the 15 validation cases' vulnerable pins; the runner image rebuilt and loaded.
- Requirements: 1.1, 1.2, 6.1
-
8. Measurement of the first increment
- Run
validation-46through the reconciler on ax (needs DeepSeek credit); record cost per candidate, tool turns per hunt, declared sites agreed, againstverifier-batched-check-2026-09-29.json. - Run
benchmarks/dev/token_report.pyon the increment's development session. - Record
benchmarks/independent/plane-increment-1.jsonwith the gate result (met or not met) and commit; if not met, revise the design before task 9. - Requirements: 6.1, 6.2, 6.3
- Run
-
9. Handover
ops/README.md: how to run a Run locally and what an ax deployment would need (Kubernetes, Agent Substrate, the runner image, the provider extension); memory note updated; scripts that the tasks replaced marked as reference-only in their docstrings.- Requirements: 4.2