mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
feat(plane): the improvement loop as a plane Run (harnessx-removal task 4)
`ousast plane workspaces --validation-set ... --loop` appends the loop to a Run:
per case `alerts` (plane/tasks/alerts.py: the quick-mode scan on the vulnerable
and the fixed pin, a fixed-pin Workspace per case, alerts attributed to their
enclosing function by the repo-facts patterns, `in_fix_range` from the fix's
old-side and new-side ranges, failing on an unread tree) feeding `remember`,
then `memory-snapshot` -> `loop-measure` -> `loop-propose` -> `loop-improve`
(plane/tasks/loop.py). Every new task binds no Model and has budget
{usd: 0, calls: 0}. The snapshot is the store's rows after the train-on-test
guard, written on the host by memory.seed (a sandboxed run of it fails loudly);
propose runs M1/M2 against this repository at the commit under test; improve
runs one unchanged evolve.run_round and delivers gate.json, journal.json, the
sidecar, rule_policy.json only when accepted, and proposal_outcome rows that
the host ingests. Adoption stays a maintainer commit.
`remember` is now a Run entry per case (the image contains it), with the facts
row keyed by the image the facts were computed on (repo-facts records it in
its summary; unknown -> the row is omitted, never guessed). Alert rows carry
their own pin; improve/memory.py counts a repeated alert once.
Runner image rebuilt (+0.39 MB: only the new modules; the quick scan needs no
extra and the rulesets were already in the package), pushed, plane/ re-pinned
and regenerated (validation-46: 105 entries). Fixture test runs the whole loop
graph through the fake ax, whose new `exec` mode runs the real task modules.
Live: one loop Run over three validation-46 cases on the local ax, recorded in
benchmarks/measurements/2026-09-30-harnessx-removal-loop/ (all done, $0 new
spend, 0 proposals, gate verdict no_proposals).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d104166697
commit
b68fe27c74
@@ -20,6 +20,10 @@ Fact reuse: a facts entry is valid for (repo, pin, candidates digest, runner ima
|
||||
writes as the Task annotation ``openultrasast.io/memory-key``. :func:`seed` runs before a Run: for every entry whose
|
||||
Task carries a key with stored facts and no state yet, it writes ``<run>/<task>/facts.json`` and a ``done``
|
||||
summary and marks the task done under the run lock (``reconciler.mark_done``), so the reconciler skips it.
|
||||
|
||||
The loop's ``memory-snapshot`` (a Task annotated ``openultrasast.io/memory-snapshot`` with the train-on-test guard's
|
||||
parameters) is seeded the same way: a sandboxed task cannot read this store, so :func:`seed` writes the store's rows
|
||||
after the guard into ``<run>/memory-snapshot/`` (:func:`.tasks.loop.write_snapshot`) and marks it done.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -44,6 +48,7 @@ log = logging.getLogger(__name__)
|
||||
MEMORY_KEY_ANNOTATION = "openultrasast.io/memory-key"
|
||||
POPULATION_ANNOTATION = "openultrasast.io/population"
|
||||
SPLIT_ANNOTATION = "openultrasast.io/split"
|
||||
SNAPSHOT_ANNOTATION = "openultrasast.io/memory-snapshot" # the loop's guard: {"catalog", "manifest", "populations"}
|
||||
KINDS = ("facts", "verdict", "unit_cost", "alert", "proposal_outcome")
|
||||
ROW_FIELDS = ("id", "kind", "repo", "pin", "run", "task", "population", "split", "image")
|
||||
TAG_FIELDS = ("repo", "pin", "kind", "family", "run", "population", "split")
|
||||
@@ -631,7 +636,8 @@ def ingest(run_dir: Path, store: MemoryStore | None = None) -> list[IngestResult
|
||||
def seed(run_manifest: Path, store: MemoryStore | None = None, *, results_root: Path | None = None) -> list[str]:
|
||||
"""Reuse stored facts for a Run's tasks whose ``openultrasast.io/memory-key`` matches (Req 6.1): each such task
|
||||
without a directory or a state gets ``facts.json`` and a ``done`` summary and is marked done under the run
|
||||
lock. Returns the seeded task names. A changed pin, candidate set or runner image finds nothing and recomputes."""
|
||||
lock. Returns the seeded task names. A changed pin, candidate set or runner image finds nothing and recomputes.
|
||||
A ``memory-snapshot`` Task gets the store's rows after the guard its annotation names, marked done likewise."""
|
||||
from . import reconciler
|
||||
|
||||
run_spec, manifests = reconciler.load_run(Path(run_manifest))
|
||||
@@ -640,10 +646,19 @@ def seed(run_manifest: Path, store: MemoryStore | None = None, *, results_root:
|
||||
state = (reconciler._read_json(base / "state.json") or {}).get("tasks") or {}
|
||||
seeded: list[str] = []
|
||||
for entry in run_spec.tasks:
|
||||
text = manifests.tasks[entry.task].metadata.annotations.get(MEMORY_KEY_ANNOTATION)
|
||||
if not text or (base / entry.name).exists() or state.get(entry.name, {}).get("status", "pending") != "pending":
|
||||
annotations = manifests.tasks[entry.task].metadata.annotations
|
||||
text, snapshot = annotations.get(MEMORY_KEY_ANNOTATION), annotations.get(SNAPSHOT_ANNOTATION)
|
||||
if not (text or snapshot) or (base / entry.name).exists() or state.get(entry.name, {}).get("status", "pending") != "pending":
|
||||
continue
|
||||
key = parse_memory_key(text)
|
||||
if snapshot:
|
||||
from .tasks.loop import write_snapshot # lazy: the guard reads the pair catalog and the benchmark manifest
|
||||
|
||||
info = write_snapshot(base / entry.name, store, json.loads(snapshot))
|
||||
reused = {"snapshot": store.describe(), **{k: info[k] for k in ("rows", "kept", "index_digest")}}
|
||||
reconciler.mark_done(run_spec.metadata.name, entry.name, reused=reused, results_root=results_root)
|
||||
seeded.append(entry.name)
|
||||
continue
|
||||
key = parse_memory_key(str(text))
|
||||
where = {"candidates_digest": key["candidates"], "image": key["image"]}
|
||||
for record in sorted(store.rows(key["repo"], key["pin"], "facts", where), key=lambda r: str(r.row["run"]), reverse=True):
|
||||
data = store.get_facts(str(record.row["sha256"]))
|
||||
@@ -672,6 +687,7 @@ __all__ = [
|
||||
"KINDS",
|
||||
"MEMORY_KEY_ANNOTATION",
|
||||
"POPULATION_ANNOTATION",
|
||||
"SNAPSHOT_ANNOTATION",
|
||||
"SPLIT_ANNOTATION",
|
||||
"FileStore",
|
||||
"IngestResult",
|
||||
|
||||
Reference in New Issue
Block a user