Skip to content

Commit fe1862e

Browse files
anandgupta42claude
andcommitted
test: [#1406] address the second round of harness review comments
Benchmark definition (verifier checks, task project, seeds, macros) unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
1 parent f0709ad commit fe1862e

19 files changed

Lines changed: 355 additions & 74 deletions

File tree

‎experiments/rsi-workspace/demo/README.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ A generic harness reads each `tasks/<id>.json`: `setup` and `verify` are argv te
3434
substituted). Output: `{"task_id","pass","score","checks":[{"name","kind","ok","message"}]}`; `score` = fraction of checks ok,
3535
`kind` is `lint` (message may name the rule) or `data` (symptom only). Per-check `ok` gives per-convention scores.
3636

37-
The verifier copies the workdir to a temp dir, restores pristine `seeds/`, `macros/`, `dbt_project.yml`, `profiles.yml`,
38-
reseeds a fresh duckdb and builds there, so editing seeds/macros or leaving a stale db cannot change the verdict.
37+
The verifier copies the workdir to a temp dir, replaces `seeds/`, overlays pristine `macros/`, restores
38+
`dbt_project.yml` and `profiles.yml`, then reseeds a fresh duckdb and builds there. Seed edits, edits to tracked
39+
macros, and a stale db are discarded; workdir-only macros survive the overlay and can affect the build.
3940

4041
## Hidden conventions (the ground truth)
4142

‎experiments/rsi-workspace/fake-backend/demo.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ run_as() {
2929
local u=$1; shift
3030
case "$u" in a|b) ;; *) echo "Unknown demo user: $u" >&2; return 2 ;; esac
3131
(cd "$S/repo-$u" && HOME="$S/home-$u" OPENCODE_TEST_HOME="$S/home-$u" XDG_DATA_HOME="$S/home-$u/.local/share" XDG_CONFIG_HOME="$S/home-$u/.config" \
32-
XDG_CACHE_HOME="$S/home-$u/.cache" XDG_STATE_HOME="$S/home-$u/.local/state" ALTIMATE_WORKSPACE=1 \
32+
XDG_CACHE_HOME="$S/home-$u/.cache" XDG_STATE_HOME="$S/home-$u/.local/state" OPENCODE_TEST_STATE_HOME="$S/home-$u/.local/state" ALTIMATE_WORKSPACE=1 \
3333
ALTIMATE_ENTRYPOINT="$PKG/src/index.ts" python3 -c 'import os, shlex, sys
3434
cmd = shlex.split(os.environ["ALTIMATE_CMD"]) if os.environ.get("ALTIMATE_CMD") else ["bun", "run", "--conditions=browser", os.environ["ALTIMATE_ENTRYPOINT"]]
3535
os.execvp(cmd[0], cmd + sys.argv[1:])' "$@")

‎experiments/rsi-workspace/fake-backend/selftest.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import json
44
import os
55
from pathlib import Path
6+
import shlex
67
import subprocess
8+
import sys
79
import tempfile
810
from urllib.parse import urlencode
911

@@ -28,6 +30,29 @@
2830
'''
2931

3032

33+
def demo_isolation():
34+
"""Exercise run_as without starting the demo server or calling a model."""
35+
script = Path(__file__).with_name("demo.sh").read_text()
36+
run_as = "run_as() {" + script.split("run_as() {", 1)[1].split("\n}", 1)[0] + "\n}"
37+
with tempfile.TemporaryDirectory(prefix="rsi-demo-isolation-") as scratch:
38+
for user in ("a", "b"):
39+
(Path(scratch) / f"repo-{user}").mkdir()
40+
keys = ["HOME", "OPENCODE_TEST_HOME", "XDG_STATE_HOME", "OPENCODE_TEST_STATE_HOME"]
41+
probe = "import json, os; print(json.dumps({key: os.environ[key] for key in " + repr(keys) + "}))"
42+
env = dict(os.environ, S=scratch, PKG="unused", OPENCODE_TEST_HOME="/inherited-home",
43+
OPENCODE_TEST_STATE_HOME="/inherited-state",
44+
ALTIMATE_CMD=shlex.join([sys.executable, "-c", probe]))
45+
result = subprocess.run(["bash", "-c", run_as + "\nrun_as a\nrun_as b\n"],
46+
env=env, capture_output=True, text=True, check=True)
47+
rows = [json.loads(line) for line in result.stdout.splitlines()]
48+
assert len(rows) == 2, rows
49+
for user, row in zip(("a", "b"), rows):
50+
home = str(Path(scratch) / f"home-{user}")
51+
assert row == {"HOME": home, "OPENCODE_TEST_HOME": home,
52+
"XDG_STATE_HOME": home + "/.local/state",
53+
"OPENCODE_TEST_STATE_HOME": home + "/.local/state"}, row
54+
55+
3156
def exercise(debug):
3257
with tempfile.TemporaryDirectory(prefix="rsi-fake-backend-") as scratch:
3358
env = {k: v for k, v in os.environ.items() if not k.startswith("FAKE_")}
@@ -145,6 +170,7 @@ def workspace(name, owner="token-user-a", privacy="private"):
145170

146171

147172
if __name__ == "__main__":
173+
demo_isolation()
148174
exercise(False)
149175
exercise(True)
150176
print("FAKE BACKEND SELFTEST OK")

‎experiments/rsi-workspace/harness/ablation.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ def main():
5555
res = L.reflect(run_dir, maint, r, reflector, NO_FEEDBACK, "abl")
5656
C.append_jsonl(log_p, {"type": "reflect", "task": r["task"], **res})
5757
C.log(f"reflect {r['task']}: {(res.get('result') or {}).get('summary') or res.get('raw')}")
58+
if not res["ok"]:
59+
raise SystemExit("reflection failed; refusing to write a partial no-feedback playbook")
5860
cand = L.read(cand_p)
5961
out = os.path.join(run_dir, "playbook-nofeedback.md")
6062
if cand is None:

‎experiments/rsi-workspace/harness/budget/analyze.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def main():
9797
frac(sum(ck(r) for r in supp), sum(ct(r) for r in supp)),
9898
frac(sum(1 for r in ctrl if r.get("pass")), len(ctrl))])
9999
per_check.append([lab] + [frac(sum(1 for r in held if (r.get("checks") or {}).get(c)),
100-
sum(1 for r in held if c in (r.get("checks") or {}))) for c in CHECKS])
100+
len(held)) for c in CHECKS])
101101
ts = [(r, trace_stats(r)) for r in rs]
102102
have = [(r, t) for r, t in ts if t]
103103
calls = [s for _, t in have for s in t["skills"]]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python3
2+
"""Unpaid runner and budget-analysis review regressions."""
3+
import json
4+
import os
5+
from pathlib import Path
6+
import shutil
7+
import subprocess
8+
import sys
9+
import tempfile
10+
import unittest
11+
12+
13+
HERE = Path(__file__).resolve().parent
14+
15+
16+
class RunnerBudgetReviewTests(unittest.TestCase):
17+
def test_missing_checks_count_as_per_check_failures(self):
18+
with tempfile.TemporaryDirectory() as d:
19+
out = Path(d) / "eval"
20+
out.mkdir()
21+
base = {"arm": "sample", "task": "heldout-disputes", "split": "heldout"}
22+
records = [dict(base, checks={f"C{i}": True for i in range(1, 7)}, **{"pass": True}),
23+
dict(base, error="setup failed", **{"pass": False})]
24+
(out / "sample.jsonl").write_text("".join(json.dumps(r) + "\n" for r in records))
25+
proc = subprocess.run([sys.executable, str(HERE / "analyze.py"), "--run-dir", d,
26+
"--labels", "sample"], capture_output=True, text=True, check=True)
27+
self.assertIn("| sample | " + " | ".join(["1/2"] * 6) + " |", proc.stdout)
28+
29+
def test_remaining_arms_run_without_promoted_playbook(self):
30+
# Only shell copies execute, with every Python call intercepted before it can launch an agent.
31+
with tempfile.TemporaryDirectory() as d:
32+
root = Path(d)
33+
harness = root / "harness"
34+
run_dir = harness / "runs/example"
35+
(run_dir / "eval").mkdir(parents=True)
36+
for name in ("run_arms.sh", "shell_common.sh"):
37+
shutil.copyfile(HERE.parent / name, harness / name)
38+
shim = root / "bin"
39+
shim.mkdir()
40+
stub = shim / "python3"
41+
stub.write_text('#!/bin/bash\necho "$*" >> "$STUB_LOG"\n')
42+
stub.chmod(0o755)
43+
log = root / "calls"
44+
env = dict(os.environ, PATH=str(shim) + os.pathsep + os.environ["PATH"],
45+
BACKEND="fake", STUB_LOG=str(log))
46+
for promoted in (False, True):
47+
if promoted:
48+
(run_dir / "playbooks").mkdir()
49+
(run_dir / "playbooks/final.md").write_text("playbook")
50+
log.write_text("")
51+
proc = subprocess.run(["bash", str(harness / "run_arms.sh"), "example"], env=env,
52+
capture_output=True, text=True)
53+
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
54+
calls = log.read_text()
55+
self.assertIn("--arm gold", calls)
56+
self.assertIn("ablation.py", calls)
57+
self.assertIn("--label nofeedback", calls)
58+
self.assertIn("report.py", calls)
59+
self.assertEqual("--label learned" in calls, promoted)
60+
self.assertEqual("--arm workspace-B" in calls, promoted)
61+
62+
63+
if __name__ == "__main__":
64+
unittest.main()

‎experiments/rsi-workspace/harness/loop_corrections.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,25 @@ def train_session(run_dir, task, current, model, reflector, it, eval_only_log):
218218

219219
# ------------------------------------------------------------ learning
220220

221+
def reopen_signals(workdir, session_id):
222+
"""Make this session's auto-reflected signals available to the maintainer's reflection."""
223+
path = signals_file(workdir)
224+
records = C.read_jsonl(path)
225+
for record in records:
226+
if record.get("session") == session_id:
227+
record["status"] = "open"
228+
record.pop("consumedBy", None)
229+
if records:
230+
with open(path, "w") as f:
231+
for record in records:
232+
f.write(json.dumps(record) + "\n")
233+
234+
221235
def reflect_session(run_dir, maint, s, reflector, caps):
222236
args = ["reflect", "--session", s["session_id"], "--name", C.PLAYBOOK_NAME, "--model", reflector, "--json"]
223237
copied = None
224238
if caps["signals_from"]:
239+
reopen_signals(s["workdir"], s["session_id"])
225240
args += ["--signals-from", s["workdir"]]
226241
else:
227242
copied = copy_signals(s["workdir"], maint)

‎experiments/rsi-workspace/harness/report.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ def arm_split_rows(recs):
4848
"tokens": mean([r.get("tokens", {}).get("total", 0) for r in rs]),
4949
"cost": mean([r.get("cost", 0) for r in rs]), "tools": mean([r.get("tool_calls", 0) for r in rs]),
5050
"dur": mean([r.get("duration", 0) for r in rs]), "leaks": sum(1 for r in rs if r.get("leak")),
51-
"timeouts": sum(1 for r in rs if r.get("timed_out")), "errors": sum(1 for r in rs if r.get("error")),
51+
"timeouts": sum(1 for r in rs if r.get("timed_out")),
52+
"errors": sum(1 for r in rs if r.get("error") or r.get("errors") or (r.get("verify") or {}).get("error")),
5253
"ws": sum(1 for r in rs if r.get("ws_arrived")), "skill": sum(1 for r in rs if r.get("playbook_in_context")),
5354
"has_pb": any(r.get("playbook_in_context") is not None for r in rs),
5455
})

‎experiments/rsi-workspace/harness/run_arms.sh‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ RD="$HERE/runs/$RID"
1111
backend_args
1212
RUNS="${RUNS:-3}"; PARALLEL="${PARALLEL:-4}"; SPLITS="${SPLITS:-heldout,control}"
1313
ev() { python3 eval.py --run-dir "$RD" --split "$SPLITS" --runs "$RUNS" --parallel "$PARALLEL" "${WS_ARGS[@]}" "$@"; }
14-
[ -f "$RD/playbooks/final.md" ] || { echo "no final playbook in $RD"; exit 1; }
15-
ev --arm "playbook:$RD/playbooks/final.md" --label learned --out "$RD/eval/learned.jsonl"
16-
ev --arm workspace-B --out "$RD/eval/workspace-B.jsonl"
14+
if [ -f "$RD/playbooks/final.md" ]; then
15+
ev --arm "playbook:$RD/playbooks/final.md" --label learned --out "$RD/eval/learned.jsonl"
16+
ev --arm workspace-B --out "$RD/eval/workspace-B.jsonl"
17+
else
18+
echo "nothing promoted: skipping learned and workspace-B arms"
19+
fi
1720
ev --arm gold --out "$RD/eval/gold.jsonl"
1821
python3 ablation.py --run-dir "$RD" --parallel "$PARALLEL"
1922
ev --arm "playbook:$RD/playbook-nofeedback.md" --label nofeedback --out "$RD/eval/nofeedback.jsonl"

‎experiments/rsi-workspace/harness/selftest.py‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@
22
"""Unpaid safety/control-flow regressions; all agent and remote calls are mocked."""
33
import json
44
import os
5+
from contextlib import ExitStack
56
from pathlib import Path
67
import shutil
78
import subprocess
9+
import sys
810
import tempfile
911
import unittest
1012
from unittest.mock import patch
1113

1214
import common as C
15+
import ablation as A
1316
import eval as E
1417
import loop as L
1518
import loop_corrections as LC
1619
import rescore
20+
import report as R
1721
import teammate as T
22+
from budget.review_selftest import RunnerBudgetReviewTests
1823

1924

2025
class HarnessTests(unittest.TestCase):
@@ -122,6 +127,66 @@ def test_reviewer_completion_marker(self):
122127
self.assertEqual(T.clean_message("Fix the timestamps.\nDONE"), "Fix the timestamps.")
123128
self.assertTrue(C.completion_prompt("review").endswith("standalone DONE line."))
124129

130+
def test_errored_reviewer_text_is_not_forwarded(self):
131+
task = {"id": "train-test", "split": "train"}
132+
first = {"session_id": "s1", "completed": True, "workdir": "/unused/work"}
133+
review = {"text": "Partial correction", "lgtm": False, "cost": 0, "error": "reviewer timed out"}
134+
with tempfile.TemporaryDirectory() as d, patch.object(C, "run_task", return_value=first), \
135+
patch.object(T, "review", return_value=review), patch.object(LC, "followup") as followup, \
136+
patch.object(LC, "captured_signals", return_value={}):
137+
result = LC.train_session(d, task, None, "mock", "mock", 1, os.path.join(d, "eval.jsonl"))
138+
self.assertEqual(result["error"], "reviewer timed out")
139+
followup.assert_not_called()
140+
141+
def test_system_prompt_trace_detects_loaded_playbook(self):
142+
# session/prompt.ts emits this content via Tracer.logSpan; FileExporter serializes it as JSON.
143+
trace = {"spans": [{"name": "system-prompt", "output": {"parts": 1,
144+
"content": '<auto_loaded_skill name="team-playbook">\nUse cents → dollars.\n</auto_loaded_skill>'}}]}
145+
self.assertTrue(C.playbook_in_trace(json.dumps(trace, ensure_ascii=False)))
146+
self.assertFalse(C.playbook_in_trace(json.dumps(trace).replace("team-playbook", "other-skill")))
147+
148+
def test_native_signal_reflection_reopens_only_its_session(self):
149+
with tempfile.TemporaryDirectory() as d:
150+
work, maint = os.path.join(d, "work"), os.path.join(d, "maint")
151+
path = Path(LC.signals_file(work))
152+
path.parent.mkdir(parents=True)
153+
rows = [{"id": "one", "session": "s1", "status": "consumed", "consumedBy": "auto"},
154+
{"id": "two", "session": "s2", "status": "consumed", "consumedBy": "auto"}]
155+
path.write_text("".join(json.dumps(row) + "\n" for row in rows))
156+
def reflect(run_dir, cwd, args):
157+
self.assertEqual(args[-2:], ["--signals-from", work])
158+
signals = C.read_jsonl(str(path))
159+
self.assertEqual(signals[0]["status"], "open")
160+
self.assertNotIn("consumedBy", signals[0])
161+
self.assertEqual(signals[1], rows[1])
162+
return subprocess.CompletedProcess([], 0, '{}', '')
163+
with patch.object(LC, "learn", side_effect=reflect):
164+
result = LC.reflect_session(d, maint, {"workdir": work, "session_id": "s1"},
165+
"mock", {"signals_from": True})
166+
self.assertTrue(result["ok"])
167+
168+
def test_ablation_rejects_failed_reflection(self):
169+
with tempfile.TemporaryDirectory() as d, ExitStack() as stack:
170+
stack.enter_context(patch.object(sys, "argv", ["ablation.py", "--run-dir", d]))
171+
for name in ("require_learn", "require_dbt", "setup_users", "warm_users"):
172+
stack.enter_context(patch.object(C, name))
173+
stack.enter_context(patch.object(C, "resolve_models", return_value=("mock", "mock")))
174+
stack.enter_context(patch.object(C, "select_tasks", return_value=[{"id": "train-test"}]))
175+
stack.enter_context(patch.object(C, "run_many", return_value=[{"task": "train-test"}]))
176+
stack.enter_context(patch.object(L, "maintainer", return_value=d))
177+
stack.enter_context(patch.object(L, "reflect", return_value={"ok": False, "error": "failed"}))
178+
with self.assertRaisesRegex(SystemExit, "reflection failed"):
179+
A.main()
180+
self.assertFalse((Path(d) / "playbook-nofeedback.md").exists())
181+
182+
def test_report_counts_each_error_source_once(self):
183+
base = {"arm": "none", "split": "heldout", "task": "test"}
184+
records = [base, dict(base, error="setup failed"), dict(base, errors=["agent error"]),
185+
dict(base, verify={"error": "unparsable"}),
186+
dict(base, error="failed", errors=["event"], verify={"error": "unparsable"})]
187+
rows, _ = R.arm_split_rows(records)
188+
self.assertEqual(rows[0]["errors"], 4)
189+
125190
def test_workspace_target_and_stale_hash(self):
126191
with tempfile.TemporaryDirectory() as d:
127192
base = Path(d) / ".altimate-code/skill/_workspace"

0 commit comments

Comments
 (0)