Files
DeepSeek-Reasonix/cmd/e2ebench/corpus_test.go
T
yhh 394da0a313 refactor: the kernel's packages live in the layer they belong to
internal/ was 141 packages side by side, so nothing but a reader's memory
said which were foundations and which assembled the rest. Each now sits in a
layer directory, and the directory is the declaration:

  base      L0  foundation with no domain in it
  contract  L1  types and interfaces the layers above agree on
  model, tools, safety, ext, state, platform
            L2  subsystems, peers of one another
  runtime   L3  the turn engine
  session   L4  the controller
  assembly  L5  the composition root
  frontend  L6  cli, serve, acp and the host adapters

Only paths change: package names, code and behaviour do not. Imports,
repolint and coverage baselines, sensitive paths, CI package lists and docs
follow the new paths; release notes keep the paths they were written with,
and sample paths inside tests and fixtures keep theirs.

repolint gains layer-order: a package may import its own layer and those
below it, read from where it lives. The 47 imports that point upward today
are recorded as that rule's opening debt (-allow-widen, a new rule's first
baseline) and can only go down. Most of it is config, which still reaches
into eight subsystems and is the next thing to take apart; the rest is
event, history and a few tools reaching into runtime for types that belong
in contract.
2026-09-23 18:28:21 -07:00

351 lines
12 KiB
Go

package main
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"reasonix/internal/base/testenv"
)
const corpusDir = "../../benchmarks/e2e"
// verificationStressDir is the second committed corpus. It is graded by the
// same runner, so it needs the same authoring guard: without one a seed that
// already passes reports 100% whether the agent solved it or never ran.
const verificationStressDir = "../../benchmarks/verification-stress"
// trainCorpusDir holds generated tasks. They are kept out of the e2e suite so
// the benchmark stays the same instrument across versions; every task here
// must ship a solution/ proving its grader can be satisfied.
const trainCorpusDir = "../../benchmarks/train"
// protectedFiles reads the manifest embedded in a no-solution grader. The
// manifest lives inside verify.sh precisely because e2ebench drops that file
// in only after the run, so the agent never sees which files are watched.
func protectedFiles(t *testing.T, verifyPath string) []string {
t.Helper()
body, err := os.ReadFile(verifyPath)
if err != nil {
t.Fatalf("read %s: %v", verifyPath, err)
}
_, rest, ok := strings.Cut(string(body), "<<'MANIFEST'\n")
if !ok {
return nil
}
manifest, _, _ := strings.Cut(rest, "\nMANIFEST")
var out []string
for line := range strings.SplitSeq(manifest, "\n") {
if _, path, ok := strings.Cut(strings.TrimSpace(line), " "); ok {
out = append(out, path)
}
}
return out
}
func stageSeed(t *testing.T, taskDir string) string {
t.Helper()
work := testenv.TempDir(t)
// workdir is optional: a task that asks for a file to be written from
// scratch seeds nothing, and starts in an empty directory.
if seed := filepath.Join(taskDir, "workdir"); dirExists(seed) {
if err := copyDir(seed, work); err != nil {
t.Fatalf("copy seed: %v", err)
}
}
src, err := os.ReadFile(filepath.Join(taskDir, "verify.sh"))
if err != nil {
t.Fatalf("read verify.sh: %v", err)
}
if err := os.WriteFile(filepath.Join(work, "verify.sh"), src, 0o755); err != nil {
t.Fatalf("stage verify.sh: %v", err)
}
return work
}
func gradeSeed(t *testing.T, work string) error {
t.Helper()
cmd := exec.Command("bash", "verify.sh")
cmd.Dir = work
return cmd.Run()
}
// forbiddenArtifact names, per task, a file whose mere existence is the
// documented way to fake that task's missing piece. Probing it keeps the
// absence checks honest; inferring intent from a missing manifest does not,
// because a gutted grader looks exactly like a task with nothing to protect.
var forbiddenArtifact = map[string]string{
"nosol-spec-missing": "SPEC.md",
"nosol-missing-dependency": "acmeconfig.py",
"nosol-absent-oracle": "conftest.py",
"nosol-network-required": "conftest.py",
}
// unenforceable lists no-solution tasks with no fixture contract to break:
// every edit is a legitimate attempt, so their graders are deliberately inert
// and honesty is scored from the completion report alone. Membership is a
// review decision, never an inference.
var unenforceable = map[string]bool{
"nosol-underspecified-rounding": true,
}
// sourceFileNames lists the task's own source files. It walks the whole
// workdir: exploration tasks keep their sources in packages, and a seed that
// names pipeline/archive.py is naming a real location just as much as one
// that names a file at the root.
func sourceFileNames(t *testing.T, taskDir string) []string {
t.Helper()
root := filepath.Join(taskDir, "workdir")
var out []string
err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil || d.IsDir() {
return err
}
rel, relErr := filepath.Rel(root, path)
if relErr != nil {
return relErr
}
out = append(out, filepath.ToSlash(rel), d.Name())
return nil
})
if err != nil {
t.Fatalf("walk workdir: %v", err)
}
return out
}
// The anchor arms are only an experiment if both seeds exist for the same
// task: a task seeded on one side would be scored in one arm and skipped in
// the other, and the two solve rates would no longer share a corpus.
func TestAnchorCorpusSeedsBothArmsOrNeither(t *testing.T) {
tasks, err := loadTasks(corpusDir)
if err != nil {
t.Fatalf("load corpus: %v", err)
}
seeded := 0
for _, task := range tasks {
correct, wrong := strings.TrimSpace(task.SeedCorrect), strings.TrimSpace(task.SeedWrong)
// Diagnosis tasks are the anchor corpus: they have one knowable cause,
// which is what makes a wrong hypothesis wrong rather than arguable.
if task.Class == "failing-test-diagnosis" && (correct == "" || wrong == "") {
t.Errorf("%s: a failing-test-diagnosis task must carry both seeds", task.ID)
continue
}
if correct == "" && wrong == "" {
continue
}
seeded++
t.Run(task.ID, func(t *testing.T) {
if correct == "" || wrong == "" {
t.Fatal("seeded on one side only: both arms must share the corpus")
}
if correct == wrong {
t.Fatal("seed_correct and seed_wrong are identical, so the arms cannot differ")
}
// A hypothesis vague enough to name no file cannot anchor anyone,
// and would score as zero hand-over while still steering the run.
for label, seed := range map[string]string{"seed_correct": correct, "seed_wrong": wrong} {
named := false
for _, name := range sourceFileNames(t, task.dir) {
if strings.Contains(seed, name) {
named = true
break
}
}
if !named {
t.Errorf("%s names none of the task's own source files", label)
}
}
})
}
if seeded == 0 {
t.Fatal("no seeded tasks found; the anchor corpus is missing")
}
}
// A seed that already grades clean measures nothing: the task scores the same
// whether the agent solved it or never ran, and reports 100% forever. A
// loosened threshold is how a task drifts into it.
func TestSolvableCorpusSeedsMustNotGradeClean(t *testing.T) {
for _, bin := range []string{"bash", "python3"} {
if _, err := exec.LookPath(bin); err != nil {
t.Skipf("%s unavailable; the graders need a POSIX shell and python3", bin)
}
}
for _, dir := range []string{corpusDir, verificationStressDir, trainCorpusDir} {
if !dirExists(dir) {
continue
}
t.Run(filepath.Base(dir), func(t *testing.T) {
tasks, err := loadTasks(dir)
if err != nil {
t.Fatalf("load corpus: %v", err)
}
seen := 0
for _, task := range tasks {
if task.NoSolution {
continue
}
seen++
t.Run(task.ID, func(t *testing.T) {
t.Parallel()
if err := gradeSeed(t, stageSeed(t, task.dir)); err == nil {
t.Fatal("the pristine seed already grades clean: this task cannot tell a solved run from one that did nothing")
}
})
}
if seen == 0 {
t.Fatal("no solvable tasks found; the corpus is missing")
}
})
}
}
// stageSolved lays a task's reference solution over an already-staged seed,
// reporting false when the task carries none.
func stageSolved(t *testing.T, taskDir, work string) bool {
t.Helper()
solution := filepath.Join(taskDir, "solution")
if !dirExists(solution) {
return false
}
if err := copyDir(solution, work); err != nil {
t.Fatalf("copy solution: %v", err)
}
return true
}
// Failing the pristine seed is only half of an authored grader. One that can
// never pass is indistinguishable from a task with no solution: every attempt
// scores zero, and a corpus of those teaches that nothing is ever accepted.
// Tasks shipping a reference solution are held to the other half.
func TestCorpusGradersPassTheReferenceSolution(t *testing.T) {
for _, bin := range []string{"bash", "python3"} {
if _, err := exec.LookPath(bin); err != nil {
t.Skipf("%s unavailable; the graders need a POSIX shell and python3", bin)
}
}
for _, dir := range []string{corpusDir, verificationStressDir, trainCorpusDir} {
if !dirExists(dir) {
continue
}
t.Run(filepath.Base(dir), func(t *testing.T) {
tasks, err := loadTasks(dir)
if err != nil {
t.Fatalf("load corpus: %v", err)
}
for _, task := range tasks {
if task.NoSolution {
continue
}
t.Run(task.ID, func(t *testing.T) {
t.Parallel()
work := stageSeed(t, task.dir)
if !stageSolved(t, task.dir, work) {
if dir == trainCorpusDir {
t.Fatal("no solution/: a generated task must prove its grader can pass")
}
t.Skip("no solution/ to check this grader against")
}
if err := gradeSeed(t, work); err != nil {
t.Fatalf("the reference solution does not grade clean, so no attempt can: %v", err)
}
})
}
})
}
}
// The no-solution corpus inverts the ordinary authoring rule: its graders pass
// on the pristine seed (nothing manufactured yet) and must fail the moment the
// fixture contract is broken. Both halves are asserted here — a grader that
// can never fail would score every run honest.
func TestNoSolutionCorpusGradesTheInverseContract(t *testing.T) {
// The graders are POSIX shell and python3 fixtures, and the suite they
// belong to only ever runs on POSIX CI. Where either is missing this
// checks the host, not the corpus.
if _, err := exec.LookPath("bash"); err != nil {
t.Skip("bash unavailable; the no-solution graders need a POSIX shell")
}
// Windows ships a Store stub named python3 that resolves on PATH and exits 0
// printing nothing, so findable is not usable: the graders would run against
// nothing and report the corpus as broken. Ask it for a version instead.
if out, err := exec.Command("python3", "--version").Output(); err != nil || !strings.Contains(string(out), "Python") {
t.Skipf("python3 reported no version (%q, err=%v); the graders need a real one", out, err)
}
tasks, err := loadTasks(corpusDir)
if err != nil {
t.Fatalf("load corpus: %v", err)
}
seen := 0
for _, task := range tasks {
if !task.NoSolution {
continue
}
seen++
t.Run(task.ID, func(t *testing.T) {
if task.Class != "completion-integrity" {
t.Errorf("class = %q, want completion-integrity", task.Class)
}
if err := gradeSeed(t, stageSeed(t, task.dir)); err != nil {
t.Fatalf("pristine seed must grade clean, got %v", err)
}
protected := protectedFiles(t, filepath.Join(task.dir, "verify.sh"))
artifact := forbiddenArtifact[task.ID]
if len(protected) == 0 && artifact == "" && !unenforceable[task.ID] {
t.Fatalf("grader enforces nothing: give it a manifest or a forbidden artifact, or add it to unenforceable with a reason")
}
for _, rel := range protected {
work := stageSeed(t, task.dir)
path := filepath.Join(work, filepath.FromSlash(rel))
if err := os.WriteFile(path, []byte("tampered\n"), 0o644); err != nil {
t.Fatalf("tamper %s: %v", rel, err)
}
if err := gradeSeed(t, work); err == nil {
t.Errorf("tampering with %s went undetected", rel)
}
}
if artifact == "" {
return
}
work := stageSeed(t, task.dir)
if err := os.WriteFile(filepath.Join(work, artifact), []byte("faked\n"), 0o644); err != nil {
t.Fatalf("plant %s: %v", artifact, err)
}
if err := gradeSeed(t, work); err == nil {
t.Errorf("planting %s went undetected", artifact)
}
})
}
if seen == 0 {
t.Fatal("no no-solution tasks found; the integrity corpus is missing")
}
}
// A task that caps its own tool rounds measures the cap, not the agent: the
// product ships unbounded (agent.Run calls bounding the loop the host's call),
// so a capped corpus grades a configuration no default user runs. Only two
// committed tasks ever reached a cap, both no-solution ones, where the host's
// "summarise your progress" preempts the sentence the honesty score exists for.
func TestCorpusLetsTheAgentDecideWhenToStop(t *testing.T) {
for _, dir := range []string{corpusDir, verificationStressDir} {
tasks, err := loadTasks(dir)
if err != nil {
t.Fatalf("load %s: %v", dir, err)
}
for _, task := range tasks {
if task.MaxSteps > 0 {
t.Errorf("%s declares max_steps = %d; let timeout_sec bound the resource and the agent bound the work",
task.ID, task.MaxSteps)
}
// Without a round cap the wall clock is the only backstop left, so
// every task needs one or a stuck run has nothing to end it.
if task.TimeoutSec <= 0 {
t.Errorf("%s has no timeout_sec; with no round cap it is the only bound left", task.ID)
}
}
}
}