chore(p0): remove the improve lever and the plane improvement loop

Off every active line (PATHFINDER-2026-10-13/03-unified-proposal.md, cuts).
Deleted: src/openultrasast/improve/ (1,175 lines), the ousast improve
subcommand, plane/tasks/loop.py and loop.yaml with the --loop generation,
the memory-snapshot step in memory.seed, pairs.build_pair_signals, and the
four improve tests plus the loop cases in other tests. Docs no longer
describe the lever. Net: 3,073 lines deleted, 83 added. Targeted tests:
154 passed; 3,436 tests collect.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-13 10:34:57 +02:00
co-authored by Claude Fable 5.1
parent d2010d0693
commit 94e96c6885
32 changed files with 83 additions and 3073 deletions
+18 -125
View File
@@ -20,13 +20,6 @@ under ``--plane`` (default ``plane/``), everything a Run over the set needs besi
with budgets, annotated with the population and split (``openultrasast.io/population``/``split``: the files'
stems) the memory rows carry.
``--loop`` (harnessx-removal design section 6) appends the improvement loop: per case ``alerts`` (quick-mode rules
on the vulnerable and the fixed pin, which gets its Workspace ``<case>-fixed``; ``case.json`` then also carries the
fix's new-side ranges) feeding ``remember``, and once ``memory-snapshot`` (the store's rows after the train-on-test
guard, written on the host by ``memory.seed``) -> ``loop-measure`` -> ``loop-propose`` -> ``loop-improve``, the last
two bound to this repository at the commit under test. All of them model-free with budget ``{usd: 0, calls: 0}``;
the gate's verdict is ``<run>/loop-improve/gate.json``, and adopting an accepted ledger stays a maintainer commit.
Each ``repo-facts`` Task carries ``openultrasast.io/memory-key`` (repository, pin, candidates digest, runner image
digest): the key under which stored facts are reused instead of recomputed (``plane/memory.py``, ``seed``).
@@ -67,12 +60,12 @@ import yaml
from ..model.endpoint import price_of
from .budget import cost_of
from .manifests import GIT_COMMITS_ANNOTATION, RUN_API_VERSION, Task, load_manifests
from .memory import MEMORY_KEY_ANNOTATION, POPULATION_ANNOTATION, SNAPSHOT_ANNOTATION, SPLIT_ANNOTATION, memory_key
from .memory import MEMORY_KEY_ANNOTATION, POPULATION_ANNOTATION, SPLIT_ANNOTATION, memory_key
from .reconciler import _ax_name, _doc
from .tasks.repo_facts import _digest
from .tasks.verify import MAX_STEPS, units_of
__all__ = ["CaseInputs", "Loop", "fix_ranges", "increment", "read_cases", "render", "repin_templates"]
__all__ = ["CaseInputs", "fix_ranges", "increment", "read_cases", "render", "repin_templates"]
REFERENCE_USD_PER_CANDIDATE = 0.022 # two passes: verifier-batched-check-2026-09-29.json cost.usd_per_candidate_two_passes
REFERENCE_MODEL = "deepseek-flash" # verify_batched.py's default (DEFAULT_DETECTOR_MODEL) in the batched check
@@ -86,12 +79,7 @@ CALLS_PER_HUNT = MAX_STEPS + 2 # every tool step, the answer, one empty-content
ATESPACE = "default" # the default when no profile is given; ``increment`` takes ``profile.atespace``
CASE_PATH = "/workspace/case"
INPUTS_PATH = "/workspace/inputs"
FIXED_PATH = "/workspace/fixed"
PROJECT_PATH = "/workspace/project"
TEMPLATES = ("repo-facts", "verify", "agree", "remember", "alerts", "loop", "features", "roles")
FIXED_RANGES_BASIS = "git diff -U0 <vulnerable> <fixed>, new side: the fix's lines at the fixed pin"
LOOP_MANIFEST = "benchmarks/manifests/java-spring-boot-vulnerable.toml" # the equality baseline's `improve` manifest
LOOP_CATALOG = "benchmarks/pairs/catalog.toml"
TEMPLATES = ("repo-facts", "verify", "agree", "remember", "alerts", "features", "roles")
Candidate = tuple[str, str, int]
@@ -105,20 +93,6 @@ class CaseInputs:
recorded_usd: float
triage_usd: float = 0.0
ranges: Mapping[str, Sequence[tuple[int, int]]] | None = None
fixed_ranges: Mapping[str, Sequence[tuple[int, int]]] | None = None
@dataclass(frozen=True)
class Loop:
"""The improvement loop appended to a Run (``--loop``, design section 6): ``repo`` at ``commit`` is this
repository at the commit under test, where ``manifest`` and ``catalog`` (paths inside it) gate the proposals;
rows of ``populations`` never propose (the train-on-test guard)."""
repo: str
commit: str
manifest: str = LOOP_MANIFEST
catalog: str = LOOP_CATALOG
populations: tuple[str, ...] = ()
def fix_ranges(repo: Path, vulnerable: str, fixed: str, side: str = "old") -> dict[str, list[tuple[int, int]]]:
@@ -165,11 +139,10 @@ def _scan_rows(triage_dir: Path, case_id: str) -> list[dict[str, Any]]:
def read_cases(
validation_set: Path, candidates_dir: Path, triage_dir: Path, population: Path, repos: Path | None = None, *, fixed_side: bool = False
validation_set: Path, candidates_dir: Path, triage_dir: Path, population: Path, repos: Path | None = None
) -> list[CaseInputs]:
"""The set's cases in id order, each with its candidates, the triage-kept subset, the recorded cost and its
triage share, and -- with ``repos`` (one clone per case id) -- the fix's old-side ranges (and, with
``fixed_side``, its new-side ranges, which the loop's `alerts` task marks fixed-pin alerts by)."""
triage share, and -- with ``repos`` (one clone per case id) -- the fix's old-side ranges."""
subset: dict[str, list[list[str]]] = json.loads(validation_set.read_text(encoding="utf-8"))
records = {c["id"]: c for c in tomllib.loads(population.read_text(encoding="utf-8"))["case"]}
cases: list[CaseInputs] = []
@@ -199,11 +172,6 @@ def read_cases(
recorded_usd=round(sum(float(r.get("usd") or 0.0) for r in rows), 6),
triage_usd=_triage_usd(rows),
ranges=None if repos is None else fix_ranges(repos / case_id, records[case_id]["vulnerable"], records[case_id]["fixed"]),
fixed_ranges=(
fix_ranges(repos / case_id, records[case_id]["vulnerable"], records[case_id]["fixed"], side="new")
if repos is not None and fixed_side
else None
),
)
)
return cases
@@ -226,9 +194,6 @@ def _case_record(item: CaseInputs) -> dict[str, Any]:
if item.ranges is not None:
record["ranges"] = {path: [list(span) for span in spans] for path, spans in item.ranges.items()}
record["ranges_basis"] = RANGES_BASIS
if item.fixed_ranges is not None:
record["fixed_ranges"] = {path: [list(span) for span in spans] for path, spans in item.fixed_ranges.items()}
record["fixed_ranges_basis"] = FIXED_RANGES_BASIS
record["sites_in_set"] = [s for s in sites if s in in_set]
record["cost"] = {
"candidates_before_triage": len(item.candidates), "recorded_usd": item.recorded_usd,
@@ -304,95 +269,38 @@ def _free() -> dict[str, int]:
return {"usd": 0, "calls": 0} # model-free: budget.py refuses any call, so a stray one fails loudly
def _chain_inputs(case_id: str, alerts: bool) -> dict[str, str]:
"""Every artifact of a case's chain -- facts, the three passes with their summaries, the final agree -- and, in a
loop, its alerts with their coverage."""
def _chain_inputs(case_id: str) -> dict[str, str]:
"""Every artifact of a case's chain: facts, the three passes with their summaries, and the final agreement."""
inputs = {"facts": f"{case_id}-facts/facts.json", "facts_summary": f"{case_id}-facts/summary.json"}
for label in ("a", "b", "c"):
inputs.update({f"pass_{label}": f"{case_id}-v{label}/units.jsonl", f"summary_{label}": f"{case_id}-v{label}/summary.json"})
inputs["agreed"] = f"{case_id}-final/agreed.json"
if alerts:
inputs["alerts"] = f"{case_id}-alerts/alerts.jsonl"
inputs["alerts_summary"] = f"{case_id}-alerts/summary.json" # the coverage: whether a zero could mean clean
return inputs
def _features(item: CaseInputs, templates: Mapping[str, Task], alerts: bool) -> tuple[dict, dict]:
def _features(item: CaseInputs, templates: Mapping[str, Task]) -> tuple[dict, dict]:
"""(Task, Run entry) of the case's `features` (learned-decision-engine design section 1): model-free, bound to the
vulnerable Workspace for the functions' spans and entry-point distance; the chain's artifacts as input."""
case_id = item.case["id"]
env = {"OUSAST_WORKSPACE_DIR": f"{CASE_PATH}/repo"}
task = _task(templates["features"], f"features-{case_id}", env, [(_ax_name(case_id, "vulnerable"), CASE_PATH)])
inputs = _chain_inputs(case_id, alerts)
inputs = _chain_inputs(case_id)
entry = {"name": f"{case_id}-features", "task": f"features-{case_id}", "inputs": inputs, "outputs": ["features.jsonl", "summary.json"]}
return task, {**entry, "budget": _free()}
def _remember(item: CaseInputs, templates: Mapping[str, Task], population: str, split: str, alerts: bool) -> tuple[dict, dict]:
def _remember(item: CaseInputs, templates: Mapping[str, Task], population: str, split: str) -> tuple[dict, dict]:
"""(Task, Run entry) of the case's `remember` (design section 4): bound to the vulnerable Workspace for the
repository and pin its rows carry; every artifact of the case's chain (and, in a loop, its alerts) and the case's
repository and pin its rows carry; every artifact of the case's chain and the case's
feature records as input."""
case_id = item.case["id"]
env = {"OUSAST_POPULATION": population or "unknown", "OUSAST_SPLIT": split or "unknown"}
task = _task(templates["remember"], f"remember-{case_id}", env, [(_ax_name(case_id, "vulnerable"), CASE_PATH)])
inputs = {**_chain_inputs(case_id, alerts), "features": f"{case_id}-features/features.jsonl"}
inputs = {**_chain_inputs(case_id), "features": f"{case_id}-features/features.jsonl"}
entry = {"name": f"{case_id}-remember", "task": f"remember-{case_id}", "inputs": inputs, "outputs": ["memory.jsonl"], "budget": _free()}
return task, entry
def _alerts(item: CaseInputs, templates: Mapping[str, Task], atespace: str = ATESPACE) -> tuple[dict, dict, dict]:
"""(fixed-pin Workspace, Task, Run entry) of the case's `alerts`: quick-mode rules on both pins."""
case = item.case
git = [{"name": "repo", "repo": case["repo"], "dir": "repo", "depth": 1}]
fixed = _doc("Workspace", _ax_name(case["id"], "fixed"), {"git": git}, atespace)
fixed["metadata"]["annotations"] = {GIT_COMMITS_ANNOTATION: f"repo={case['fixed']}"}
bindings = [(_ax_name(case["id"], "vulnerable"), CASE_PATH), (fixed["metadata"]["name"], FIXED_PATH)]
bindings.append((_ax_name(case["id"], "inputs"), INPUTS_PATH))
env = {
"OUSAST_WORKSPACE_DIR": f"{CASE_PATH}/repo", "OUSAST_FIXED_DIR": f"{FIXED_PATH}/repo",
"OUSAST_INPUT_CASE": f"{INPUTS_PATH}/case.json", "OUSAST_VULNERABLE_PIN": str(case["vulnerable"]),
"OUSAST_FIXED_PIN": str(case["fixed"]),
} # fmt: skip
task = _task(templates["alerts"], f"alerts-{case['id']}", env, bindings)
entry = {"name": f"{case['id']}-alerts", "task": f"alerts-{case['id']}", "outputs": ["alerts.jsonl", "summary.json"], "budget": _free()}
entry["serialize"] = "engine"
return fixed, task, entry
def _loop(
cases: Sequence[CaseInputs], templates: Mapping[str, Task], loop: Loop, atespace: str = ATESPACE
) -> tuple[dict, list[dict], list[dict]]:
"""(project Workspace, Tasks, Run entries) of the loop's singletons: snapshot -> measure -> propose -> improve."""
git = [{"name": "repo", "repo": loop.repo, "dir": "repo", "depth": 1}]
project = _doc("Workspace", _ax_name("openultrasast", loop.commit[:12]), {"git": git}, atespace)
project["metadata"]["annotations"] = {GIT_COMMITS_ANNOTATION: f"repo={loop.commit}"}
bound = [(project["metadata"]["name"], PROJECT_PATH)]
env = {
"OUSAST_PROJECT_DIR": f"{PROJECT_PATH}/repo", "OUSAST_LOOP_MANIFEST": loop.manifest, "OUSAST_LOOP_CATALOG": loop.catalog,
"OUSAST_QUALIFY_POPULATIONS": json.dumps(sorted(loop.populations)),
} # fmt: skip
snapshot = _task(templates["loop"], "memory-snapshot", {}, [], ["snapshot"])
guard = {"catalog": loop.catalog, "manifest": loop.manifest, "populations": sorted(loop.populations)}
snapshot["metadata"].setdefault("annotations", {})[SNAPSHOT_ANNOTATION] = json.dumps(guard, sort_keys=True, separators=(",", ":"))
tasks = [
snapshot, _task(templates["loop"], "loop-measure", {}, [], ["measure"]),
_task(templates["loop"], "loop-propose", env, bound, ["propose"]),
_task(templates["loop"], "loop-improve", env, bound, ["improve"]),
] # fmt: skip
remembered = {f"remember_{c.case['id']}": f"{c.case['id']}-remember/memory.jsonl" for c in cases}
entries = [
{"name": "memory-snapshot", "task": "memory-snapshot", "outputs": ["rows.jsonl", "index.json"], "budget": _free()},
{"name": "loop-measure", "task": "loop-measure", "inputs": {**remembered, "snapshot": "memory-snapshot/rows.jsonl"},
"outputs": ["measure.json", "rows.jsonl"], "budget": _free()},
{"name": "loop-propose", "task": "loop-propose",
"inputs": {"measure": "loop-measure/measure.json", "rows": "loop-measure/rows.jsonl", "index": "memory-snapshot/index.json"},
"outputs": ["proposals.jsonl", "memory_proposals.jsonl", "signals.json"], "budget": _free()},
{"name": "loop-improve", "task": "loop-improve", "inputs": {"proposals": "loop-propose/proposals.jsonl"},
"outputs": ["gate.json", "journal.json"], "budget": _free(), "serialize": "engine"},
] # fmt: skip
return project, tasks, entries
def render(
cases: Sequence[CaseInputs],
templates: Mapping[str, Task],
@@ -401,13 +309,11 @@ def render(
*,
population: str = "",
split: str = "",
loop: Loop | None = None,
atespace: str = ATESPACE,
) -> dict[str, str]:
"""Relative path under the plane root -> file text, for every generated manifest. ``population`` and ``split``
(the population file's and the set's stems) become the Run's annotations, which the memory rows carry. Each
case ends in `features` -> `remember`; ``loop`` adds per case `alerts` (and the fixed-pin Workspace), and once the
snapshot, `loop-measure`, `loop-propose` and `loop-improve` (design section 6), all model-free."""
case ends in `features` -> `remember`."""
header = f"# generated by `{command}`; do not edit, regenerate.\n"
out: dict[str, str] = {}
tasks: list[dict[str, Any]] = []
@@ -454,22 +360,12 @@ def render(
tasks.sort(key=lambda doc: 1 if doc["metadata"]["name"].startswith("verify-c-") else 0) # stable: the old Tasks first
entries.extend(tiebreak) # appended: the first Run's entries stay byte-identical, so a rerun keeps their state
for item in cases: # appended after the tie-break for the same reason
if loop is not None:
fixed, alerts_task, alerts_entry = _alerts(item, templates, atespace)
out[f"workspaces/{fixed['metadata']['name']}.yaml"] = _dump(header, [fixed])
tasks.append(alerts_task)
entries.append(alerts_entry)
features_task, features_entry = _features(item, templates, loop is not None)
features_task, features_entry = _features(item, templates)
tasks.append(features_task)
entries.append(features_entry)
remember_task, remember_entry = _remember(item, templates, population, split, loop is not None)
remember_task, remember_entry = _remember(item, templates, population, split)
tasks.append(remember_task)
entries.append(remember_entry)
if loop is not None:
project, loop_tasks, loop_entries = _loop(cases, templates, loop, atespace)
out[f"workspaces/{project['metadata']['name']}.yaml"] = _dump(header, [project])
tasks.extend(loop_tasks)
entries.extend(loop_entries)
out[f"tasks/{run_name}.yaml"] = _dump(header, tasks)
annotations = {k: v for k, v in ((POPULATION_ANNOTATION, population), (SPLIT_ANNOTATION, split)) if v}
metadata: dict[str, Any] = {"name": run_name, **({"annotations": annotations} if annotations else {})}
@@ -519,24 +415,21 @@ def increment(
command: str,
repos: Path | None = None,
runner_image: Path | None = None,
loop: Loop | None = None,
atespace: str | None = None,
) -> list[Path]:
"""Write the generated manifests under ``plane`` from the templates in ``plane/tasks`` (re-pinned first when
``runner_image`` is given), with the improvement loop when ``loop`` is given; returns the paths. Workspaces go
``runner_image`` is given); returns the paths. Workspaces go
to ``atespace`` (default: the profile's)."""
if atespace is None:
from .profile import load_profile
atespace = load_profile().atespace
if loop is not None and repos is None:
raise ValueError("--loop needs --repos: the alerts task marks fixed-pin alerts by the fix's new-side ranges")
if runner_image is not None:
repin_templates(plane, runner_image)
loaded = load_manifests([plane / "tasks" / f"{name}.yaml" for name in TEMPLATES])
cases = read_cases(validation_set, candidates_dir, triage_dir, population, repos, fixed_side=loop is not None)
cases = read_cases(validation_set, candidates_dir, triage_dir, population, repos)
files = render(
cases, loaded.tasks, run_name, command, population=population.stem, split=validation_set.stem, loop=loop, atespace=atespace
cases, loaded.tasks, run_name, command, population=population.stem, split=validation_set.stem, atespace=atespace
)
written: list[Path] = []
for relative, text in files.items():
+4 -19
View File
@@ -2,7 +2,7 @@
One store, keyed by repository and pin, holds what plane runs learned: repository facts by content hash, and rows
of the kinds in ``KINDS`` (``facts``, ``verdict``, ``unit_cost``, ``alert``, ``coverage``, ``features``, ...) that
``remember`` (:mod:`.tasks.remember`) derives from a run's delivered artifacts, plus the loop's and the decision
``remember`` (:mod:`.tasks.remember`) derives from a run's delivered artifacts, plus the decision
engine's own. Layout, identical for both backends::
index.jsonl one row per ingested (run, task): sha256 of its rows, row count
@@ -34,10 +34,6 @@ 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
@@ -62,7 +58,6 @@ 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"}
# The decision engine's kinds (learned-decision-engine, Data Models): `features` from the `features` task through
# `remember`; `label`, `decision`, `experiment`, `arm_outcome` and `experiment_result` for the later tasks; `example`
# (a labelled case of the local memory: label + feature record + excerpt sha) from `ousast learn memory build`.
@@ -984,8 +979,7 @@ 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.
A ``memory-snapshot`` Task gets the store's rows after the guard its annotation names, marked done likewise."""
lock. Returns the seeded task names. A changed pin, candidate set or runner image finds nothing and recomputes."""
from . import reconciler
run_spec, manifests = reconciler.load_run(Path(run_manifest))
@@ -995,16 +989,8 @@ def seed(run_manifest: Path, store: MemoryStore | None = None, *, results_root:
seeded: list[str] = []
for entry in run_spec.tasks:
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
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)
text = annotations.get(MEMORY_KEY_ANNOTATION)
if not text or (base / entry.name).exists() or state.get(entry.name, {}).get("status", "pending") != "pending":
continue
key = parse_memory_key(str(text))
where = {"candidates_digest": key["candidates"], "image": key["image"]}
@@ -1037,7 +1023,6 @@ __all__ = [
"POPULATION_ANNOTATION",
"QUERYABLE",
"QUERY_FIELDS",
"SNAPSHOT_ANNOTATION",
"SPLIT_ANNOTATION",
"FileStore",
"IngestResult",
+2 -3
View File
@@ -1,6 +1,6 @@
"""`alerts` (harnessx-removal Requirement 6.3, design section 6): what the deterministic rules say about a case.
No model, budget ``{usd: 0, calls: 0}``, one per case in a ``--loop`` Run, serialized like ``repo-facts`` (a full-tree
No model, budget ``{usd: 0, calls: 0}``, serialized like ``repo-facts`` (a full-tree
scan per pin). It runs the quick-mode scan -- ``preprocess_repository``, the entry-point reachability hints,
``rank_targets`` and ``quick_scan_findings`` over the image's bundled ruleset, enabled and shadow rules alike, exactly
what ``ousast scan --mode quick`` runs -- on the case's vulnerable pin and on its fixed pin, and writes one row per
@@ -12,8 +12,7 @@ what ``ousast scan --mode quick`` runs -- on the case's vulnerable pin and on it
candidates' ``path::function``; ``<global>`` outside any function); a language those patterns do not cover keeps the
scan's own reachability attribution. ``in_fix_range`` says whether the line lies inside the fix: the old-side ranges
of ``case.json`` on the vulnerable pin, the new-side ``fixed_ranges`` on the fixed pin (null when ``case.json`` has
none). A fixed-pin alert inside the fix is a false alert on code the fix wrote: rule M1 of ``improve/memory.py``
counts it.
none). A fixed-pin alert inside the fix is a false alert on code the fix wrote.
**Coverage** (an honest zero): quick mode has rules only for the languages its ruleset declares
(:func:`quick_languages`, derived from the bundled ruleset directory, never listed by hand). ``summary.json`` records
-302
View File
@@ -1,302 +0,0 @@
"""The improvement loop as plane tasks (harnessx-removal Requirement 6.3, design section 6).
``command: ["loop", <step>]``; every step is model-free with budget ``{usd: 0, calls: 0}`` (``budget.py`` refuses any
call, so a stray one fails loudly), and each writes ``summary.json`` under the task contract. Records travel as
``{"key", "version", "row"}`` lines: the store object a row came from and its version (the bucket's object version id, a file's
content hash), which a proposal's provenance cites (Req 6.4).
- ``snapshot``: never runs in the sandbox, which cannot read the host's store. ``ousast plane run`` seeds it before
the Run (``memory.seed`` -> :func:`write_snapshot`): the store's rows after the train-on-test guard to
``rows.jsonl``, and ``index.json`` (store, index digest, row counts, what the guard excluded). Run in a sandbox it
fails, naming the seed that did not happen.
- ``measure``: every case's ``remember`` rows (inputs ``remember_<case>``) plus the snapshot -> ``measure.json``,
the increment's metrics for this run, the store and both (declared sites agreed, usd per candidate, dispute rates
per family, alerts per rule and pin) -- and ``rows.jsonl``, the union by row id that ``propose`` reads.
- ``propose``: ``improve/memory.py``'s rules M1/M2 over those rows after the guard (``OUSAST_QUALIFY_POPULATIONS``, the
gated manifest's cases, the pair catalog's holdout pairs), against the benchmark target's ledger and journal in
this repository at the commit under test (``OUSAST_PROJECT_DIR``) -> ``proposals.jsonl`` (edit + provenance),
``memory_proposals.jsonl`` (the sidecar) and ``signals.json`` (M3, advisory).
- ``improve``: one ``evolve.run_round`` with those proposals -- the unchanged validator and gate, the pair catalog's
holdout clause included -- on copies of the target's ledger and journal -> ``gate.json`` (the ``RoundOutcome``
and which edits came from memory), ``journal.json``, the sidecar, and ``rule_policy.json`` only when the round
was accepted; ``memory.jsonl`` holds the ``proposal_outcome`` rows the host ingests after the Run. Adopting an
accepted ledger stays a maintainer commit.
"""
from __future__ import annotations
import hashlib
import json
import os
import shutil
import sys
import tempfile
import traceback
from collections.abc import Callable, Iterable, Mapping, Sequence
from dataclasses import asdict
from pathlib import Path
from typing import Any
from ..memory import REMEMBER_OUTPUT, MemoryStore, Record, validate_row
INPUT_PREFIX = "OUSAST_INPUT_REMEMBER_"
CALIBRATION = Path(".openultrasast") / "calibration" # cli.CALIBRATION_DIR: where `improve` keeps a target's ledger
ADOPTION = "adoption is a maintainer commit: copy rule_policy.json to {ledger} (README, Rule-level loop)"
# --- records -------------------------------------------------------------------------------------------------------
def _record_line(record: Record) -> str:
return json.dumps({"key": record.key, "version": record.version, "row": record.row}, sort_keys=True, separators=(",", ":")) + "\n"
def read_records(path: Path) -> list[Record]:
records: list[Record] = []
for number, raw in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
if raw.strip():
item = json.loads(raw)
records.append(Record(validate_row(item["row"], f"{path}:{number}"), str(item["key"]), item.get("version")))
return records
def write_records(path: Path, records: Iterable[Record]) -> None:
path.write_text("".join(_record_line(r) for r in sorted(records, key=lambda r: str(r.row["id"]))), encoding="utf-8")
def _guard(params: Mapping[str, Any], root: Path) -> Any:
from ...benchmark import load_benchmark_manifest
from ...improve.memory import Guard, benchmark_cases
from ...pairs import load_pair_catalog, select_split
manifest = load_benchmark_manifest(root / str(params["manifest"]))
holdout = select_split(load_pair_catalog(Path(str(params["catalog"]))), "holdout")
return Guard.build([str(p) for p in params.get("populations") or []], benchmark_cases(manifest), holdout)
def write_snapshot(directory: Path, store: MemoryStore, params: Mapping[str, Any], root: Path | None = None) -> dict[str, Any]:
"""The store's rows after the guard ``params`` names (``catalog``, ``manifest``, ``populations``; paths relative to
``root``, the working directory) into ``directory``: ``rows.jsonl``, ``index.json`` and a ``done`` summary."""
from ...improve.memory import index_digest
here = Path.cwd()
try:
os.chdir(root or here) # the pair catalog names its files relative to the repository root
guard = _guard(params, Path.cwd())
finally:
os.chdir(here)
directory.mkdir(parents=True, exist_ok=False) # after the guard loaded: a failed load leaves no half-seeded task
records = store.rows()
kept, excluded = guard.apply(records)
write_records(directory / "rows.jsonl", kept)
info = {
"store": store.describe(), "index_entries": len(store.index()), "index_digest": index_digest(store), "rows": len(records),
"kept": len(kept), "excluded": excluded, "guard": dict(params),
} # fmt: skip
(directory / "index.json").write_text(json.dumps(info, indent=2, sort_keys=True) + "\n", encoding="utf-8")
summary = {"status": "done", "units_done": 1, "units_total": 1, "usd": None, "calls": 0, "usage": {}, "model": None, "reused": info}
(directory / "summary.json").write_text(json.dumps(summary, indent=2, sort_keys=True) + "\n", encoding="utf-8")
return info
# --- measure -------------------------------------------------------------------------------------------------------
def metrics(rows: Sequence[Mapping[str, Any]]) -> dict[str, Any]:
"""The increment's metrics over verdict, unit-cost and alert rows (a disputed final is neither agreed nor not)."""
verdicts = [r for r in rows if r["kind"] == "verdict"]
finals = {f: sum(1 for v in verdicts if v.get("final") == f) for f in ("agreed", "rejected", "disputed")}
costs = [r.get("usd") for r in rows if r["kind"] == "unit_cost"]
usd = None if any(c is None for c in costs) else round(sum(float(c or 0) for c in costs), 6)
families: dict[str, list[int]] = {}
for v in verdicts:
tally = families.setdefault(str(v.get("family")), [0, 0])
tally[0] += 1
tally[1] += v.get("final") == "disputed"
alerts: dict[str, dict[str, int]] = {}
for a in (r for r in rows if r["kind"] == "alert"):
role = "fixed" if a.get("pin_role") == "fixed" else "vulnerable"
tally_a = alerts.setdefault(str(a.get("rule_id")), {"vulnerable": 0, "fixed": 0, "fixed_in_fix_range": 0})
tally_a[role] += 1
tally_a["fixed_in_fix_range"] += role == "fixed" and a.get("in_fix_range") is True
coverage: dict[str, dict[str, int]] = {} # language -> pins per coverage kind: a `none` pin's zero alerts mean nothing
for c in (r for r in rows if r["kind"] == "coverage"):
tally_c = coverage.setdefault(str(c.get("language")), {"quick": 0, "engine": 0, "none": 0})
tally_c[str(c.get("coverage"))] = tally_c.get(str(c.get("coverage")), 0) + 1
return {
"cases": len({(v["repo"], v["pin"]) for v in verdicts}),
"candidates": len(verdicts),
**finals,
"tiebreaks": sum(1 for v in verdicts if v.get("tiebreak")),
"declared_sites_matched": sum(1 for v in verdicts if v.get("site_match") is True),
"declared_sites_agreed": sum(1 for v in verdicts if v.get("site_match") is True and v.get("final") == "agreed"),
"usd": usd,
"usd_per_candidate": None if usd is None or not verdicts else round(usd / len(verdicts), 6),
"dispute_rate": {f: {"candidates": n, "disputed": d, "rate": round(d / n, 4)} for f, (n, d) in sorted(families.items())},
"alerts": dict(sorted(alerts.items())),
"coverage": dict(sorted(coverage.items())),
}
def measure(env: Mapping[str, str], output: Path) -> dict[str, Any]:
run = env.get("OUSAST_RUN") or "unknown"
current: list[Record] = []
read: dict[str, int] = {}
for name in sorted(k for k in env if k.startswith(INPUT_PREFIX)):
path = Path(env[name])
data = path.read_bytes()
read[name.removeprefix(INPUT_PREFIX).lower()] = len(data)
version = hashlib.sha256(data).hexdigest()
for number, raw in enumerate(data.decode("utf-8").splitlines(), 1):
if raw.strip():
row = validate_row(json.loads(raw), f"{path}:{number}")
current.append(Record(row, f"runs/{run}/{row['task']}/{REMEMBER_OUTPUT}", version))
if not current:
raise ValueError(f"read no remember rows from {len(read)} inputs ({read}): an unread run is not an empty one")
snapshot = read_records(Path(env["OUSAST_INPUT_SNAPSHOT"])) if env.get("OUSAST_INPUT_SNAPSHOT") else []
merged = {str(r.row["id"]): r for r in snapshot}
merged.update({str(r.row["id"]): r for r in current}) # this run's row wins over its earlier ingest
write_records(output / "rows.jsonl", merged.values())
report = {
"run": run, "inputs_bytes": read, "rows": {"run": len(current), "snapshot": len(snapshot), "union": len(merged)},
"run_metrics": metrics([r.row for r in current]), "store_metrics": metrics([r.row for r in snapshot]),
"union_metrics": metrics([r.row for r in merged.values()]),
} # fmt: skip
(output / "measure.json").write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
return {"rows": report["rows"]}
# --- propose and improve -------------------------------------------------------------------------------------------
def _project(env: Mapping[str, str]) -> tuple[Any, Path, Path, Path]:
"""(manifest, target, ledger, journal) in the repository at the commit under test; the working directory
becomes the repository root, where the pair catalog's relative paths resolve."""
from ...benchmark import load_benchmark_manifest, resolve_benchmark_source
project = Path(env.get("OUSAST_PROJECT_DIR") or "")
if not env.get("OUSAST_PROJECT_DIR") or not (project / str(env.get("OUSAST_LOOP_MANIFEST"))).is_file():
raise ValueError(f"OUSAST_PROJECT_DIR {project} holds no benchmark manifest {env.get('OUSAST_LOOP_MANIFEST')}")
os.chdir(project)
manifest_path = Path(str(env["OUSAST_LOOP_MANIFEST"]))
manifest = load_benchmark_manifest(manifest_path)
target = resolve_benchmark_source(manifest_path, manifest)
ledger = target / CALIBRATION / "rule_policy.json"
return manifest, target, ledger, ledger.with_name("improve_journal.json")
def _params(env: Mapping[str, str]) -> dict[str, Any]:
populations = json.loads(env.get("OUSAST_QUALIFY_POPULATIONS") or "[]")
return {"catalog": env.get("OUSAST_LOOP_CATALOG"), "manifest": env.get("OUSAST_LOOP_MANIFEST"), "populations": populations}
def propose(env: Mapping[str, str], output: Path) -> dict[str, Any]:
from ...improve.journal import load_journal, next_round_index
from ...improve.memory import disputed_families, propose_from_memory, write_sidecar, write_signals
from ...ruleset import DEFAULT_RULESET_DIR, load_ruleset, read_rule_ledger
records = read_records(Path(env["OUSAST_INPUT_ROWS"]))
index = json.loads(Path(env["OUSAST_INPUT_INDEX"]).read_text(encoding="utf-8"))
_, _, ledger, journal = _project(env)
guard = _guard(_params(env), Path.cwd())
rules = load_ruleset(DEFAULT_RULESET_DIR, ledger if ledger.is_file() else None)
rounds = load_journal(journal)
found = propose_from_memory(
records, {r.rule_id: r for r in rules}, read_rule_ledger(ledger), rounds, excluded=guard, index_digest=str(index["index_digest"])
)
lines = [json.dumps({"edit": asdict(p.edit), "provenance": p.provenance}, sort_keys=True) + "\n" for p in found]
(output / "proposals.jsonl").write_text("".join(lines), encoding="utf-8")
sidecar = output / "journal.json"
write_sidecar(sidecar, next_round_index(rounds), found)
sidecar.with_name("memory_proposals.jsonl").touch() # present when nothing was proposed, as a declared output
write_signals(output, disputed_families(records, guard))
kept, excluded = guard.apply(records)
return {"proposals": len(found), "rows": len(records), "kept": len(kept), "excluded": excluded["rows"]}
def improve(env: Mapping[str, str], output: Path) -> dict[str, Any]:
from ...gate import FALSE_POSITIVE_CEILING, RECALL_FLOOR
from ...improve.evolve import run_round
from ...improve.memory import MemoryProposal, outcome_of, outcome_rows, proposal_id_of
from ...improve.validator import RuleStatusEdit
from ...pairs import load_pair_catalog, select_split
from ...policy import load_policy
from ...ruleset import DEFAULT_RULESET_DIR
path = Path(env["OUSAST_INPUT_PROPOSALS"])
items = [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]
proposals = [MemoryProposal(RuleStatusEdit(**item["edit"]), item["provenance"]) for item in items]
manifest, target, ledger, journal = _project(env)
pairs = select_split(load_pair_catalog(Path(str(env["OUSAST_LOOP_CATALOG"]))), "holdout")
with tempfile.TemporaryDirectory(prefix="ousast-loop-") as scratch:
work_ledger = Path(scratch) / "rule_policy.json"
if ledger.is_file():
shutil.copyfile(ledger, work_ledger)
if journal.is_file():
shutil.copyfile(journal, output / "journal.json")
outcome = run_round(
target, manifest, ledger_path=work_ledger, journal_path=output / "journal.json", ruleset_dir=DEFAULT_RULESET_DIR,
policy=load_policy(), recall_floor=RECALL_FLOOR, fp_ceiling=FALSE_POSITIVE_CEILING, pair_cases=pairs, proposals=proposals,
) # fmt: skip
if outcome.accepted and work_ledger.is_file():
shutil.copyfile(work_ledger, output / "rule_policy.json")
(output / "journal.json").touch()
(output / "memory_proposals.jsonl").touch()
memory_edits = [e.key() for e in outcome.edits if proposal_id_of(e)]
verdict = {
"accepted": outcome.accepted, "reason": outcome.reason, "round": outcome.round, "proposals": len(proposals),
"memory_edits": memory_edits, "benchmark_edits": [e.key() for e in outcome.edits if not proposal_id_of(e)],
"manifest": env.get("OUSAST_LOOP_MANIFEST"), "outcome": asdict(outcome),
"adoption": ADOPTION.format(ledger=ledger.as_posix()) if outcome.accepted else None,
} # fmt: skip
(output / "gate.json").write_text(json.dumps(verdict, indent=2, sort_keys=True, default=str) + "\n", encoding="utf-8")
status = outcome_of(outcome.reason, outcome.accepted)
by_id = {p.proposal_id: p for p in proposals}
gate_run = f"plane:{env.get('OUSAST_RUN') or 'unknown'}"
rows = outcome_rows(outcome.edits, status, outcome.round, outcome.reason, by_id, gate_run) if status else []
(output / REMEMBER_OUTPUT).write_text("".join(json.dumps(r, sort_keys=True) + "\n" for r in rows), encoding="utf-8")
return {"accepted": outcome.accepted, "reason": outcome.reason, "edits": len(outcome.edits), "memory_edits": len(memory_edits)}
def snapshot(env: Mapping[str, str], output: Path) -> dict[str, Any]:
raise RuntimeError(
"memory-snapshot ran in the sandbox: `ousast plane run` writes it on the host from the memory store before the Run "
"starts (memory.seed), so the seed did not happen; rerun through `ousast plane run`"
)
STEPS: dict[str, Callable[[Mapping[str, str], Path], dict[str, Any]]] = {
"snapshot": snapshot, "measure": measure, "propose": propose, "improve": improve,
} # fmt: skip
def main(argv: Sequence[str] | None = None, environ: Mapping[str, str] | None = None) -> int:
args = list(sys.argv[1:] if argv is None else argv)
env = environ if environ is not None else os.environ
output = Path(env["OUSAST_OUTPUT_DIR"]) if env.get("OUSAST_OUTPUT_DIR") else None
summary: dict[str, Any] = {"status": "failed", "units_done": 0, "units_total": 1, "usd": 0, "calls": 0, "usage": {}, "model": None}
here = Path.cwd()
try:
if output is None:
raise ValueError("OUSAST_OUTPUT_DIR is not set")
if len(args) != 1 or args[0] not in STEPS:
raise ValueError(f"expected one step of {sorted(STEPS)}, got {args}")
output.mkdir(parents=True, exist_ok=True)
summary.update(step=args[0], **STEPS[args[0]](env, output))
summary.update(status="done", units_done=1)
except Exception: # noqa: BLE001 -- a crash is `failed` with its traceback
summary["reason"] = traceback.format_exc()[-2000:]
finally:
os.chdir(here)
if output is not None:
(output / "summary.json").write_text(json.dumps(summary, indent=2, sort_keys=True, default=str) + "\n", encoding="utf-8")
shown = {k: summary.get(k) for k in ("status", "step", "proposals", "accepted", "reason", "rows")}
print(json.dumps(shown, default=str), file=sys.stderr)
return 0 if summary["status"] == "done" else 2
__all__ = ["main", "measure", "metrics", "propose", "improve", "read_records", "write_records", "write_snapshot"]
if __name__ == "__main__":
raise SystemExit(main())