bench(unseen): per-commit history cache for ordinary_history; assemble.freeze --history-cache

One append-only JSONL per clone (named by the sha256 of its top-level path), one line per commit
with the row as the walk built it and HISTORY_CACHE_VERSION, flushed after every commit so a
killed run keeps its progress. A cached row is reused only when its parents match the current log;
other versions and torn lines are recomputed. With no cache directory nothing changes. The
7,400-commit trigger.dev walk that took over an hour per freeze becomes a one-time cost.
Implemented by Codex (gpt-6-astra), gated here.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-13 19:40:34 +02:00
co-authored by Claude Fable 5.1
parent dbaaa86d5b
commit 47d0f0fac1
4 changed files with 315 additions and 41 deletions
+79 -37
View File
@@ -8,6 +8,7 @@ are dropped, and the recorded SZZ bias remains subject to task 2.6's label check
from __future__ import annotations
import ast
import hashlib
import json
import re
import signal
@@ -23,6 +24,8 @@ from .source import Candidate, Rejected
EXTRACTION_CONTRACT = "v1"
# Bump only when enumeration, eligibility or source.candidate outputs change.
RESOLUTION_CONTRACT = "v1"
# Bump whenever functions(), generated(), LANGUAGES or the row shape change: cached rows embed their results.
HISTORY_CACHE_VERSION = 1
LANGUAGES = {".py", ".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".php", ".java"}
HUNK = re.compile(r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@", re.M)
@@ -452,45 +455,84 @@ def generated(git, revision: str, path: str, text: str) -> bool:
return result
def ordinary_history(git, head: str) -> list[dict]:
@contextmanager
def _history_cache(git, cache_dir: Path | None):
"""Load completed rows and open the clone's cache for appending."""
if cache_dir is None:
yield {}, None
return
top = git.run("rev-parse", "--show-toplevel").strip()
cache_dir.mkdir(parents=True, exist_ok=True)
path = cache_dir / (hashlib.sha256(top.encode()).hexdigest() + ".jsonl")
with path.open("a+b") as handle:
handle.seek(0)
cached = {}
line = b""
for line in handle:
try:
entry = json.loads(line)
except (json.JSONDecodeError, UnicodeDecodeError):
continue
if not isinstance(entry, dict) or entry.get("version") != HISTORY_CACHE_VERSION:
continue
row = entry.get("row")
sha = entry.get("head")
if isinstance(sha, str) and isinstance(row, dict) and row.get("head") == sha:
cached[sha] = row
if line and not line.endswith(b"\n"):
# Keep a partial last line from swallowing the next completed row.
handle.write(b"\n")
handle.flush()
yield cached, handle
def ordinary_history(git, head: str, *, cache_dir: Path | None = None) -> list[dict]:
"""Read first-parent history, optionally reusing completed rows from this clone."""
raw = git.run("log", "--first-parent", "--format=%H%x00%P%x00%ct%x00%an%x00%B%x1e", head)
rows = []
for entry in raw.split("\x1e"):
if not entry.strip():
continue
fields = entry.strip().split("\x00")
if len(fields) != 5 or not SHA.fullmatch(fields[0]):
raise InstrumentFailure("invalid_history")
sha, parents, timestamp, author, message = fields
row = {"head": sha, "parents": parents.split(), "timestamp": int(timestamp), "author": author, "message": message, "files": []}
rows.append(row)
if len(row["parents"]) != 1:
continue
base = row["parents"][0]
for path, added, removed in git.stats(base, sha):
hunks = git.hunks(base, sha, path)
info = {
"path": path,
"added": added,
"removed": removed,
"head_lines": [[max(1, n), max(1, n + max(c, 1) - 1)] for _, _, n, c in hunks],
"functions": [],
"generated": False,
}
if Path(path).suffix in LANGUAGES:
# Inspect both sides: deleting a vulnerable function is still touching it.
for revision, side in ((base, 0), (sha, 2)):
text = git.blob(revision, path)
if text is None:
continue
spans = functions(path, text)
for hunk in hunks:
start, count = hunk[side : side + 2]
for name, lo, hi in spans:
if max(1, start) <= hi and start + max(count, 1) - 1 >= lo:
info["functions"].append(name)
info["generated"] |= generated(git, revision, path, text)
row["files"].append(info)
with _history_cache(git, cache_dir) as (cached, handle):
for entry in raw.split("\x1e"):
if not entry.strip():
continue
fields = entry.strip().split("\x00")
if len(fields) != 5 or not SHA.fullmatch(fields[0]):
raise InstrumentFailure("invalid_history")
sha, parents, timestamp, author, message = fields
previous = cached.get(sha)
if previous is not None and previous.get("parents") == parents.split():
rows.append(previous)
continue
row = {"head": sha, "parents": parents.split(), "timestamp": int(timestamp), "author": author, "message": message, "files": []}
rows.append(row)
if len(row["parents"]) == 1:
base = row["parents"][0]
for path, added, removed in git.stats(base, sha):
hunks = git.hunks(base, sha, path)
info = {
"path": path,
"added": added,
"removed": removed,
"head_lines": [[max(1, n), max(1, n + max(c, 1) - 1)] for _, _, n, c in hunks],
"functions": [],
"generated": False,
}
if Path(path).suffix in LANGUAGES:
# Inspect both sides: deleting a vulnerable function is still touching it.
for revision, side in ((base, 0), (sha, 2)):
text = git.blob(revision, path)
if text is None:
continue
spans = functions(path, text)
for hunk in hunks:
start, count = hunk[side : side + 2]
for name, lo, hi in spans:
if max(1, start) <= hi and start + max(count, 1) - 1 >= lo:
info["functions"].append(name)
info["generated"] |= generated(git, revision, path, text)
row["files"].append(info)
if handle is not None:
handle.write((json.dumps({"version": HISTORY_CACHE_VERSION, "head": sha, "row": row}) + "\n").encode())
handle.flush()
return rows
+7 -2
View File
@@ -51,6 +51,7 @@ def freeze(
seed: int = 0,
ratio: int = 20,
history_heads: dict | None = None,
history_cache: Path | None = None,
fix_index: dict | None = None,
floor: int = extract.ORDINARY_FLOOR,
) -> dict:
@@ -58,7 +59,8 @@ def freeze(
``floor`` is the eligible-commit count a repository needs before its ordinary pushes are drawn. The
held-out benchmark keeps the shipped 40; a training freeze may lower it (never below the ratio) so small
repositories with 20 eligible commits still contribute, and the record carries the floor used."""
repositories with 20 eligible commits still contribute, and the record carries the floor used.
``history_cache`` reuses completed per-commit history rows across freezes of the same clone."""
if type(ratio) is not int or ratio < 20:
raise ValueError("ordinary ratio must be at least 20")
if type(floor) is not int or floor < ratio:
@@ -91,7 +93,8 @@ def freeze(
functions = {(case["path"], case["function"]) for case in all_cases}
head = (history_heads or {}).get(repo) or git.run("rev-parse", "HEAD").strip()
heads[repo] = head
history = sorted(extract.ordinary_history(git, head), key=lambda row: row["head"])
history_options = {"cache_dir": history_cache} if history_cache is not None else {}
history = sorted(extract.ordinary_history(git, head, **history_options), key=lambda row: row["head"])
# Ratio is per positive CHANGE (a fix commit), not per accepted function. Several
# accepted functions in one fix collapse to one vulnerability change below, so the
# negative count must track distinct (commit, parent) pairs.
@@ -177,6 +180,7 @@ def main(argv=None):
parser.add_argument("--used", type=Path, required=True, help="JSON array of used repository identities, including known aliases")
parser.add_argument("--out", type=Path, required=True)
parser.add_argument("--seed", type=int, default=0)
parser.add_argument("--history-cache", type=Path, help="Directory for per-clone commit history caches")
args = parser.parse_args(argv)
result = freeze(
args.out,
@@ -185,6 +189,7 @@ def main(argv=None):
repos={k: local_git(Path(v)) for k, v in json.loads(args.repos.read_text()).items()},
used=json.loads(args.used.read_text()),
seed=args.seed,
history_cache=args.history_cache,
)
print(json.dumps(result, sort_keys=True))
return 0