feat: add benchmark single-cell runner (task 0027) #28
Reference in New Issue
Block a user
Delete Branch "task-0027-bench-single-cell-runner"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements
.claude/tasks/0027-bench-single-cell-runner.md.Summary
The tracer bullet that threads every benchmark layer to run one
(arm, task, trial)cell end to end and record an immutable result — proving seed, arm scaffolding, guard, runner, checker, and store all connect.runCell(bench/runner.ts) provisions and seeds a fresh throwaway repository, runs the agent under exactly the active arm's tool with the guard enforced, bounds the run by a turn cap and a wall-clock backstop (tagging a failure confused-versus-hung), audits the transcript, captures the post-run state and scores it with the checker, appends the sample, and always deletes the repository. A run in which a foreign tool was reached is flagged invalid rather than scored.The two non-deterministic boundaries are factored behind seams so the orchestration is unit-tested with fakes while the live wiring is validated by a smoke run:
BenchHost—liveBenchHost(host.ts) overseed.ts(provision/seed/delete) and the newsnapshot.ts(captureRepoState, the seed's read-back counterpart).AgentDriver—sdkAgentDriver(sdk-driver.ts) over the Claude Agent SDK, enforcing isolation in-band via the SDK permission callback and reporting the four token components (folding in the auxiliary small model), turns, duration, imputed cost, transcript, and final report.Also adds
task.ts(the runnableBenchTaskwrapper and one sample single-mutation task) andaudit.ts(the post-run transcript audit plus the sharedforeignToolReasonpredicate both enforcement points consume).Deviations
npm run typechecknever require it. The runner smoke tier (runner.smoke.test.ts) skips cleanly when the SDK is absent orGITEA_AXI_BENCH_LOGINis unset; a real run also needs thegitea-axiCLI onPATHand a Claude subscription. Declaring the SDK as a dependency belongs to the run-loop CLI slice (0029), its first real consumer.Review
Risk
Overall: MEDIUM
bench/plus onesendvisibility bump inseed.ts; nothing insrc/or the shipped package is touched.sendexport is superset-compatible.runner.ts/audit.ts/task.tshave deterministic unit tests (happy path, turn-cap, hung, leak, incorrect); live seams covered by the skip-clean smoke tier.runBounded's abort/timer race.dist, never runs in production.Standards findings (unaddressed)
Duplicated Code (minor, judgement call) —
snapshot.ts'scaptureCommentsand the two capture loops share a labels/assignees/comments shape. Left as-is per the reviewer's own recommendation ("Leave it"): the pull loop additionally captures reviews and PR state, so extracting would be a thin, low-value abstraction.(The flagged duplication between the driver's enforcement and the audit was addressed by extracting the shared
foreignToolReasonpredicate.)Spec findings (unaddressed)
snapshot.tsmaps review states with Gitea'sReviewStateTypeverbs (APPROVED/COMMENT/REQUEST_CHANGES). Not changed because Gitea returnsREQUEST_CHANGESon read as well as write (unlike GitHub'sCHANGES_REQUESTED); it does not affect the current sample task (no reviews) and is exercised by the smoke tier against the live host. The temperature-zero and duration-assertion findings were addressed.