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