Files
PaperClipAI/tests/restart-chaos/grader.test.ts
T
DottaandClaude Opus 5.5 61fa13c6d8 test(chaos): add S1, a board Stop across a restart then a continue comment (#20161)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The restart chaos harness drives real user actions across
control-plane restarts: comments, Retry, approvals and budget resumes
> - The board's Stop button on a running task was not exercised. The
cancel route stamps the stop as operator-initiated so that recovery
stands down instead of re-waking the agent
> - A restart right after a Stop is where recovery could wrongly restart
work the operator stopped, or lose the operator's later "continue"
direction
> - This pull request adds S1: stop the running run, fire a fault right
after, then comment to continue
> - The benefit is a graded check that a Stop holds across a restart and
that a later continue comment finishes the task

## Linked Issues or Issue Description

Refs #20071

**What is missing?**
The campaign's scenario list includes mid-run stops. C1 covers a mid-run
comment (a steer), but no workload clicks Stop. Without one, a
regression where recovery restarts operator-stopped work, or drops the
continue comment, would go unseen.

**Expected behavior**
- After the board stops a run and a restart follows, the stopped run
stays cancelled.
- Nothing runs until the operator comments.
- The continue comment then finishes the task.

## What Changed

- S1 workload ("Stop while running, then continue"): a brief task. At
its follow-up time, while a run is running, the observer posts `POST
/api/heartbeat-runs/:id/cancel` (the board's Stop) and records `stop`.
- New `stop` trigger, for S1 only: the planned fault fires 0–2 s after
the stop.
- The continue comment is posted only after the stop has settled: at
least 20 s after the stop, with no run running or queued and no fault in
progress. It asks the agent to continue and add a `CONTINUE-<nonce>`
line.
- The oracle requires:
  - status done;
  - the brief with the chaos and continue markers;
  - `stop_took_effect`: the stopped run ended `cancelled`;
- `nothing_ran_after_stop`: no run was created between the stop and the
continue comment.
- S1 is not a default workload. There is a README section.

## Verification

- `npx vitest run` in `tests/restart-chaos`: 205 passed. New tests
cover:
- the S1 grade (pass, a run restarted after the stop, the stop not
taking effect, no stop);
  - the trigger-to-workload pairing;
- an observer test that clicks Stop once and posts the continue comment
only after the 20 s settle.
- `node scripts/typecheck-test-projects.mjs tests/restart-chaos`: clean.
- A live S1 wave runs after the current chaos wave. Its result will be
posted here before merge.

## Risks

- If the product moves the stopped run to a status other than
`cancelled`, `stop_took_effect` fails. That check is intentional.
- The continue comment waits for the stop to settle, so a run stuck
`queued` delays it. R5 and the wave deadline still bound the wait.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, with tool use and code
execution in Claude Code.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 22:52:48 -06:00

311 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from "vitest";
import { crossingFaults, gradeR3, gradeR4, gradeR5, gradeTask, STALL_LIMIT_MS } from "./grader.js";
import { isResting } from "./observer.js";
import type { FaultReceipt, OracleArtifacts, RunSample, TaskRecord, TaskSample } from "./types.js";
import { BENCH_EXPECTED, parseBenchRecord, r1TestChecksSlug, stallWakeChecks, workloadDefinitions } from "./workloads.js";
const T0 = Date.parse("2026-11-06T12:00:00Z");
const iso = (offsetMs: number) => new Date(T0 + offsetMs).toISOString();
function run(id: string, status: string, created: number, finished: number | null = null, extra: Partial<RunSample> = {}): RunSample {
return { id, status, createdAt: iso(created), startedAt: iso(created + 500), finishedAt: finished === null ? null : iso(finished),
errorCode: null, retryOfRunId: null, wakeCommentIds: [], livenessState: null, ...extra };
}
function sample(at: number, issueStatus: string, runs: RunSample[], extra: Partial<TaskSample> = {}): TaskSample {
return { at: T0 + at, reachable: true, serverReady: true, issueStatus, interruption: null, activeRecoveryAction: false,
executionBlocker: false, scheduledRetry: false, runs, ...extra };
}
function record(samples: TaskSample[], extra: Partial<TaskRecord> = {}): TaskRecord {
return {
plan: { taskId: "t01", index: 0, workload: "A1", adapter: "codex", fault: "K1", fireAfterMs: 20_000, holdMs: 0, followUpAfterMs: null, nonce: "abcd1234" },
issueId: "issue-1", identifier: "CHA-1", createdAt: T0, launchError: null, samples, retry: null, restAt: T0 + 120_000,
finalStatus: "done", deadlineAt: T0 + 600_000, timedOut: false, followUp: null, faultIds: ["f01"],
sweep: { activeRuns: [], runningWorkspaceOperations: [], activeRecoveryAction: false, error: null },
preFaultFiles: null, lostFiles: null, ...extra,
};
}
const fault: FaultReceipt = { faultId: "f01", kind: "K1", requestedBy: "t01", signal: "SIGKILL", holdMs: 0, requestedAt: T0 + 20_000,
startedAt: T0 + 20_000, stoppedAt: T0 + 21_000, restartedAt: T0 + 21_000, healthyAt: T0 + 40_000, readyAt: T0 + 45_000,
phases: [{ phase: "unreachable", at: T0 + 21_000 }, { phase: "recovering", at: T0 + 38_000 }, { phase: "ready", at: T0 + 45_000 }],
serverLogOffsetAtStart: 0, serverLogOffsetAtReady: 100, error: null };
const passingBrief: OracleArtifacts = { issueStatus: "done", documents: { brief: "CHAOS-abcd1234\n- one\n- two\n- three\n" },
agentComments: [], files: {}, commands: {}, workspacePath: null };
const oracle = (artifacts = passingBrief) => workloadDefinitions.A1.grade("abcd1234", artifacts);
describe("R1–R6 grading on fixture timelines", () => {
it("passes a task that auto-continued across a K1 kill", () => {
const samples = [
sample(3_000, "in_progress", [run("r1", "running", 1_000)]),
{ at: T0 + 25_000, reachable: false, serverReady: false },
sample(50_000, "in_progress", [run("r1", "failed", 1_000, 30_000), run("r2", "running", 48_000)]),
sample(110_000, "done", [run("r1", "failed", 1_000, 30_000), run("r2", "succeeded", 48_000, 100_000)]),
];
const grade = gradeTask(record(samples), [fault], workloadDefinitions.A1.budgetMs, oracle());
expect(grade.crossed).toBe(true);
expect(grade.crossingFaults).toEqual(["f01"]);
expect(grade.rules.R1.verdict).toBe("pass");
expect(grade.rules.R3.verdict).toBe("n/a");
expect(grade.pass).toBe(true);
expect(grade.signature).toBeNull();
});
it("does not count a task that rested before the fault as restart-crossing", () => {
const samples = [sample(3_000, "done", [run("r1", "succeeded", 1_000, 10_000)])];
expect(crossingFaults(record(samples, { restAt: T0 + 12_000 }), [fault])).toEqual([]);
});
it("fails R1 and R3 when a task rests unfinished with no Retry offered", () => {
const samples = [sample(60_000, "in_progress", [run("r1", "failed", 1_000, 30_000)])];
const grade = gradeTask(record(samples, { finalStatus: "in_progress" }), [fault], workloadDefinitions.A1.budgetMs,
oracle({ ...passingBrief, issueStatus: "in_progress", documents: { brief: null } }));
expect(grade.rules.R1).toMatchObject({ verdict: "fail" });
expect(grade.rules.R1.detail).toMatch(/rested_not_done/);
expect(grade.rules.R3.detail).toMatch(/missing_retry/);
expect(grade.signature).toBe("R1:rested_not_done:A1:K1");
});
it("fails R3 when the task needed a Retry, even one that completed it, and names a second click", () => {
const samples = [
sample(60_000, "blocked", [run("r1", "failed", 1_000, 30_000)], { interruption: { state: "failed", canRetry: true, retryWait: false, workspaceFailure: null, observedRunId: "r1", userAction: null } }),
sample(80_000, "in_progress", [run("r1", "failed", 1_000, 30_000), run("r2", "running", 75_000, null, { retryOfRunId: "r1" })]),
sample(140_000, "done", [run("r1", "failed", 1_000, 30_000), run("r2", "succeeded", 75_000, 130_000)]),
];
const retried = record(samples, { retry: { at: T0 + 74_000, observedRunId: "r1", httpStatus: 200, accepted: true, error: null }, restAt: T0 + 140_000 });
expect(gradeR3(retried)).toEqual({ verdict: "fail", detail: "retry_needed (failed)" });
const grade = gradeTask(retried, [fault], workloadDefinitions.A1.budgetMs, oracle());
expect(grade.rules.R1.verdict).toBe("n/a");
expect(grade.pass).toBe(false);
const twice = record(samples, { retry: { at: T0 + 74_000, observedRunId: "r1", httpStatus: 200, accepted: true, error: null },
priorRetries: [{ at: T0 + 50_000, observedRunId: "r0", httpStatus: 200, accepted: true, error: null }], restAt: T0 + 140_000 });
expect(gradeR3(twice).detail).toBe("retry_needed_2_clicks (failed)");
});
it("fails R3 for a dead Retry (accepted but no new run)", () => {
const samples = [sample(60_000, "blocked", [run("r1", "failed", 1_000, 30_000)])];
const dead = record(samples, { finalStatus: "blocked", retry: { at: T0 + 74_000, observedRunId: "r1", httpStatus: 200, accepted: true, error: null } });
expect(gradeR3(dead).detail).toBe("retry_no_new_run");
const rejected = record(samples, { finalStatus: "blocked", retry: { at: T0 + 74_000, observedRunId: "r1", httpStatus: 409, accepted: null, error: "conflict" } });
expect(gradeR3(rejected).detail).toMatch(/^retry_rejected/);
});
it("passes R4 for a queued wake that is cancelled behind a running run without executing", () => {
// Shape of w-20261107T013205-s11/t06: a follow-up comment queued a run while
// the first run was running; it was cancelled when the first one succeeded.
const queuedWake = run("w2", "queued", 30_000, null, { startedAt: null, wakeCommentIds: ["c1"] });
const samples = [
sample(10_000, "in_progress", [run("r1", "running", 1_000)]),
sample(35_000, "in_progress", [run("r1", "running", 1_000), queuedWake]),
sample(45_000, "in_progress", [run("r1", "succeeded", 1_000, 40_000), { ...queuedWake, status: "cancelled", finishedAt: iso(41_000) }]),
];
expect(gradeR4(record(samples))).toMatchObject({ verdict: "pass" });
// The same queued/scheduled shapes never fail on their own either.
expect(gradeR4(record([sample(10_000, "in_progress", [run("r1", "queued", 1_000, null, { startedAt: null }),
run("r2", "scheduled_retry", 5_000, null, { startedAt: null })])])).verdict).toBe("pass");
});
it("fails R4 for two concurrent running runs and for a comment consumed twice", () => {
expect(gradeR4(record([sample(10_000, "in_progress", [run("r1", "running", 1_000), run("r2", "running", 5_000)])])).detail).toMatch(/concurrent_runs/);
const twice = [sample(90_000, "done", [
run("r1", "succeeded", 1_000, 40_000, { wakeCommentIds: ["c1"] }),
run("r2", "succeeded", 50_000, 80_000, { wakeCommentIds: ["c1"] }),
])];
expect(gradeR4(record(twice)).detail).toMatch(/comment_consumed_twice: c1/);
// Runs whose execution windows overlap fail even if no single poll saw both running.
const overlapped = [sample(90_000, "done", [run("r1", "succeeded", 1_000, 40_000), run("r2", "failed", 20_000, 60_000)])];
expect(gradeR4(record(overlapped)).detail).toMatch(/concurrent_runs: r1\(succeeded\),r2\(failed\) executed together/);
// A failed run's comment may be served again by the retry; a successful one may not.
const retried = [sample(90_000, "done", [
run("r1", "failed", 1_000, 40_000, { wakeCommentIds: ["c1"] }),
run("r2", "succeeded", 50_000, 80_000, { wakeCommentIds: ["c1"], retryOfRunId: "r1" }),
])];
expect(gradeR4(record(retried)).verdict).toBe("pass");
const afterSuccess = [sample(90_000, "done", [
run("r1", "succeeded", 1_000, 40_000, { wakeCommentIds: ["c1"] }),
run("r2", "failed", 50_000, 80_000, { wakeCommentIds: ["c1"] }),
])];
expect(gradeR4(record(afterSuccess)).detail).toMatch(/comment_consumed_twice: c1 by r1,r2/);
});
// s9401/t72: the message's run was claimed just before the first run ended,
// then cancelled as stale before dispatch; a later run delivered the message.
it("passes R4 for a stale continuation cancelled before dispatch whose message a later run delivered once", () => {
const samples = [sample(90_000, "done", [
run("r1", "succeeded", 1_000, 40_000),
run("r2", "cancelled", 30_000, 40_060, { startedAt: iso(39_960), wakeCommentIds: ["c1"],
errorCode: "continuation_task_ownership_changed" }),
run("r3", "succeeded", 45_000, 80_000, { wakeCommentIds: ["c1"] }),
])];
expect(gradeR4(record(samples))).toMatchObject({ verdict: "pass" });
// A poll that saw both running (claiming marks running before dispatch)
// does not change that.
const polled = [sample(39_990, "in_progress", [run("r1", "running", 1_000), run("r2", "running", 30_000, null, { startedAt: iso(39_960) })]),
...samples];
expect(gradeR4(record(polled))).toMatchObject({ verdict: "pass" });
// Any other cancelled run that overlapped still executed.
const executed = [sample(90_000, "done", [run("r1", "succeeded", 1_000, 40_000),
run("r2", "cancelled", 30_000, 40_060, { startedAt: iso(39_960), errorCode: "cancelled" })])];
expect(gradeR4(record(executed)).detail).toMatch(/concurrent_runs/);
});
it("fails R5 on a silent stall longer than three minutes after ready, but not during outages or named waits", () => {
const idle = (at: number, extra: Partial<TaskSample> = {}) => sample(at, "in_progress", [run("r1", "failed", 1_000, 30_000)], extra);
const stalled = [idle(60_000), idle(60_000 + STALL_LIMIT_MS / 2), idle(60_000 + STALL_LIMIT_MS + 5_000)];
expect(gradeR5(record(stalled))).toMatchObject({ verdict: "fail" });
const outage = [idle(60_000), { at: T0 + 120_000, reachable: false, serverReady: false }, idle(200_000), idle(300_000)];
expect(gradeR5(record(outage))).toMatchObject({ verdict: "pass" });
const named = stalled.map(entry => ({ ...entry, scheduledRetry: true }));
expect(gradeR5(record(named))).toMatchObject({ verdict: "pass" });
});
it("fails R2 when a workspace failure stays visible and R6 when work is left pending", () => {
const failure = { state: "failed", canRetry: false, retryWait: false, workspaceFailure: "snapshot restore failed", observedRunId: "r1", userAction: null };
const grade = gradeTask(record([sample(90_000, "done", [run("r1", "succeeded", 1_000, 80_000)], { interruption: failure })]), [fault], 600_000, oracle());
expect(grade.rules.R2.detail).toMatch(/workspace_failure_visible_at_rest/);
const leaky = record([sample(90_000, "done", [run("r1", "succeeded", 1_000, 80_000)])],
{ sweep: { activeRuns: [], runningWorkspaceOperations: ["op1"], activeRecoveryAction: true, error: null } });
expect(gradeTask(leaky, [fault], 600_000, oracle()).rules.R6.detail).toMatch(/running_workspace_operations=op1; active_recovery_action/);
});
it("fails R2 when files written before the fault are gone at rest", () => {
const done = [sample(90_000, "done", [run("r1", "succeeded", 1_000, 80_000)])];
const lost = record(done, { preFaultFiles: ["NOTES.md", "chaoscli/cli.py"], lostFiles: ["NOTES.md"] });
expect(gradeTask(lost, [fault], 600_000, oracle()).rules.R2.detail).toBe("pre_fault_files_lost: NOTES.md");
expect(gradeTask(record(done, { preFaultFiles: ["NOTES.md"], lostFiles: [] }), [fault], 600_000, oracle()).rules.R2.verdict).toBe("pass");
});
it("fails R2 visibly when the pre-fault snapshot could not run", () => {
const done = [sample(90_000, "done", [run("r1", "succeeded", 1_000, 80_000)])];
const unchecked = record(done, { preFaultSnapshotError: "EACCES" });
expect(gradeTask(unchecked, [fault], 600_000, oracle()).rules.R2.detail).toMatch(/^pre_fault_snapshot_failed/);
});
it("allows outage time on top of the budget", () => {
const k4: FaultReceipt = { ...fault, kind: "K4", holdMs: 240_000, readyAt: T0 + 290_000 };
const late = record([sample(700_000, "done", [run("r2", "succeeded", 300_000, 690_000)])], { restAt: T0 + 700_000 });
expect(gradeTask(late, [k4], 600_000, oracle()).rules.R1.verdict).toBe("pass");
expect(gradeTask(late, [], 600_000, oracle()).rules.R1.detail).toMatch(/done_too_late/);
});
});
describe("T-oracles", () => {
it("grades M1 delegation: two done subtasks, part 1 delegated, and a summary of both", () => {
const agents = { self: { id: "self", name: "Chaos codex" }, other: { id: "other", name: "Chaos claude" } };
const child = (n: number, assigneeAgentId: string, status = "done") =>
({ id: `c${n}`, status, assigneeAgentId, documents: { part: `PART${n}-abcd1234\nIdempotency keys.` } });
const artifacts: OracleArtifacts = { issueStatus: "done", agentComments: [], files: {}, commands: {}, workspacePath: null,
documents: { summary: "PART1-abcd1234\nPART2-abcd1234" }, children: [child(1, "other"), child(2, "self")] };
expect(workloadDefinitions.M1.grade("abcd1234", artifacts, agents).verdict).toBe("pass");
expect(workloadDefinitions.M1.description("abcd1234", agents)).toContain("assigned to Chaos claude");
const undelegated = { ...artifacts, children: [child(1, "self"), child(2, "self")] };
expect(workloadDefinitions.M1.grade("abcd1234", undelegated, agents).checks.find(check => check.name === "part1_delegated")?.ok).toBe(false);
const pending = { ...artifacts, children: [child(1, "other", "in_progress"), child(2, "self")] };
expect(workloadDefinitions.M1.grade("abcd1234", pending, agents).verdict).toBe("fail");
expect(workloadDefinitions.M1.grade("abcd1234", { ...artifacts, children: [child(1, "other")] }, agents).verdict).toBe("fail");
const both = { ...artifacts, children: [{ ...child(1, "other"), documents: { part: "PART1-abcd1234\nPART2-abcd1234" } },
{ ...child(2, "self"), documents: { part: "nothing" } }] };
expect(workloadDefinitions.M1.grade("abcd1234", both, agents).checks.find(check => check.name === "parts_in_distinct_subtasks")?.ok).toBe(false);
const wrongPart2 = { ...artifacts, children: [child(1, "other"), child(2, "other")] };
expect(workloadDefinitions.M1.grade("abcd1234", wrongPart2, agents).checks.find(check => check.name === "part2_kept")?.ok).toBe(false);
const single = { self: agents.self, other: agents.self };
expect(workloadDefinitions.M1.grade("abcd1234", { ...artifacts, children: [child(1, "self"), child(2, "self")] }, single)
.checks.find(check => check.name === "two_agents")?.ok).toBe(false);
});
it("grades A1 and C1 markers", () => {
expect(workloadDefinitions.A1.grade("abcd1234", passingBrief).verdict).toBe("pass");
expect(workloadDefinitions.C1.grade("abcd1234", passingBrief).checks.find(check => check.name.includes("followup"))?.ok).toBe(false);
expect(workloadDefinitions.C1.grade("abcd1234", { ...passingBrief, documents: { brief: `${passingBrief.documents.brief}FOLLOWUP-abcd1234\n` } }).verdict).toBe("pass");
});
it("fails M1 when a subtask needed a board Retry, and checks the parent got one stall wake per stall", () => {
const done = record([sample(1_000, "done", [run("r1", "succeeded", 0, 900)])], { finalStatus: "done" });
const withChildRetry = { ...done, childRetries: [{ childIssueId: "c2", at: 1, observedRunId: "r", httpStatus: 200, accepted: true, error: null }] };
expect(gradeR3(done)).toMatchObject({ verdict: "n/a" });
expect(gradeR3(withChildRetry)).toMatchObject({ verdict: "fail", detail: "retry_needed_on_subtask_1 (c2)" });
const stalled = { issueStatus: "done", documents: {}, agentComments: [], files: {}, commands: {}, workspacePath: null,
childStalls: [{ childIssueId: "c2", recoveryActionId: "a1" }] };
const wakeA1 = { stalledIssueId: "c2", recoveryActionId: "a1" };
expect(stallWakeChecks({ ...stalled, stallWakes: [wakeA1] } as never)[0]).toMatchObject({ ok: true });
expect(stallWakeChecks({ ...stalled, stallWakes: [] } as never)[0]).toMatchObject({ ok: false, detail: "stalls=1 wakes=0 unwoken=c2:a1" });
expect(stallWakeChecks({ ...stalled, stallWakes: [wakeA1, wakeA1] } as never)[0]).toMatchObject({ ok: false });
const twoStalls = { ...stalled, childStalls: [...stalled.childStalls, { childIssueId: "c2", recoveryActionId: "a2" }] };
expect(stallWakeChecks({ ...twoStalls, stallWakes: [wakeA1] } as never)[0]).toMatchObject({ ok: false, detail: "stalls=2 wakes=1 unwoken=c2:a2" });
expect(stallWakeChecks({ ...twoStalls, stallWakes: [wakeA1, { stalledIssueId: "c2", recoveryActionId: "a2" }] } as never)[0]).toMatchObject({ ok: true });
expect(stallWakeChecks({ ...stalled, childStalls: [] } as never)).toEqual([]);
});
it("grades S1: stopped run cancelled, nothing ran before the continue comment, brief has both markers", () => {
const base: OracleArtifacts = { issueStatus: "done", documents: { brief: "CHAOS-abcd1234\nCONTINUE-abcd1234\n- one\n- two\n- three\n" },
agentComments: [], files: {}, commands: {}, workspacePath: null,
stop: { runId: "r1", runStatus: "cancelled", runsStartedBeforeContinue: [], continuePosted: true } };
expect(workloadDefinitions.S1.grade("abcd1234", base).verdict).toBe("pass");
const restarted = workloadDefinitions.S1.grade("abcd1234", { ...base, stop: { ...base.stop!, runsStartedBeforeContinue: ["r2"] } });
expect(restarted.checks.find(check => check.name === "nothing_ran_after_stop")).toMatchObject({ ok: false, detail: "continue=true runs=r2" });
expect(workloadDefinitions.S1.grade("abcd1234", { ...base, stop: { ...base.stop!, runStatus: "succeeded" } }).verdict).toBe("fail");
expect(workloadDefinitions.S1.grade("abcd1234", { ...base, stop: undefined }).verdict).toBe("fail");
});
it("grades B1 by running the tests and the CLI", () => {
const base: OracleArtifacts = { issueStatus: "done", documents: {}, agentComments: [], workspacePath: "/tmp/ws",
files: { "NOTES-abcd1234.md": "B1-abcd1234\n" },
commands: { unittest: { exitCode: 0, stdout: "", stderr: "OK" }, slugify: { exitCode: 0, stdout: "hello-chaos-world\n", stderr: "" } } };
expect(workloadDefinitions.B1.grade("abcd1234", base).verdict).toBe("pass");
expect(workloadDefinitions.B1.grade("abcd1234", { ...base, commands: { ...base.commands, unittest: { exitCode: 1, stdout: "", stderr: "FAILED" } } }).verdict).toBe("fail");
});
it("grades R1 by running the agent's node test and a hidden case against its module", () => {
const base: OracleArtifacts = { issueStatus: "done", documents: {}, agentComments: [], workspacePath: "/tmp/ws",
files: { "packages/shared/src/chaos/slug-abcd1234.ts": "export function chaosSlug() {}",
"packages/shared/src/chaos/slug-abcd1234.test.ts": [
"import test from \"node:test\";", "import assert from \"node:assert/strict\";", "import { chaosSlug } from \"./slug-abcd1234.ts\";",
"test(\"slug\", () => {", " assert.equal(chaosSlug(\"Hello Chaos World\"), \"hello-chaos-world\");",
" assert.equal(chaosSlug(\"a b\"), \"a-b\");", " assert.equal(chaosSlug(\"--x--\"), \"x\");", "});"].join("\n") },
commands: { node_test: { exitCode: 0, stdout: "", stderr: "" },
slug: { exitCode: 0, stdout: "[\"hello-chaos-world\",\"r1-paperclip-chaos-2026\"]\n", stderr: "" } } };
expect(workloadDefinitions.R1.grade("abcd1234", base).verdict).toBe("pass");
expect(workloadDefinitions.R1.grade("abcd1234", { ...base, commands: { ...base.commands,
slug: { exitCode: 0, stdout: "[\"hello-chaos-world\",\"-r1-paperclip-chaos-2026-\"]\n", stderr: "" } } }).verdict).toBe("fail");
expect(r1TestChecksSlug("import test from \"node:test\";")).toBe(false);
expect(r1TestChecksSlug("import test from \"node:test\"; import assert from \"node:assert\"; assert.ok(chaosSlug(\"x\"));")).toBe(false);
expect(r1TestChecksSlug([ "import test from \"node:test\";", "import assert from \"node:assert/strict\";",
"for (const [input, want] of [[\"Hello Chaos World\", \"hello-chaos-world\"], [\"a b\", \"a-b\"], [\"-x-\", \"x\"]])",
" test(input, () => assert.equal(chaosSlug(input), want));"].join("\n"))).toBe(true);
const argv = workloadDefinitions.R1.collect.commands.find(command => command.name === "node_test")!.argv;
expect(typeof argv === "function" ? argv("abcd1234") : argv).toEqual([process.execPath, "--test", "packages/shared/src/chaos/slug-abcd1234.test.ts"]);
});
it("grades B5 against the deterministic bench result", () => {
const base: OracleArtifacts = { issueStatus: "done", documents: {}, agentComments: [], workspacePath: "/tmp/ws", commands: {},
benchProgressObserved: 6,
files: { "bench-result-abcd1234.txt": `${BENCH_EXPECTED}\n`, ".bench/run-abcd1234.txt": `started=1000 finished=1241 result=${BENCH_EXPECTED}\n` } };
expect(BENCH_EXPECTED).toBe("BENCH_RESULT=4636840");
expect(parseBenchRecord(base.files[".bench/run-abcd1234.txt"])).toEqual({ seconds: 241, result: BENCH_EXPECTED });
expect(workloadDefinitions.B5.grade("abcd1234", base).verdict).toBe("pass");
// A forged record without harness-observed progress does not pass.
expect(workloadDefinitions.B5.grade("abcd1234", { ...base, benchProgressObserved: 1 }).checks.find(c => c.name === "bench_progress_observed")?.ok).toBe(false);
expect(workloadDefinitions.B5.grade("abcd1234", { ...base, files: { ...base.files, "bench-result-abcd1234.txt": "BENCH_RESULT=1" } }).verdict).toBe("fail");
// A computed answer without a bench.sh run, or a too-short run, does not pass.
expect(workloadDefinitions.B5.grade("abcd1234", { ...base, files: { "bench-result-abcd1234.txt": BENCH_EXPECTED } }).checks.find(c => c.name === "bench_actually_ran")?.ok).toBe(false);
expect(workloadDefinitions.B5.grade("abcd1234", { ...base, files: { ...base.files, ".bench/run-abcd1234.txt": `started=1000 finished=1010 result=${BENCH_EXPECTED}` } }).verdict).toBe("fail");
});
});
describe("rest detection", () => {
it("is not resting while a run, scheduled retry or recovery is live", () => {
expect(isResting(sample(0, "in_progress", [run("r1", "queued", 0)]))).toBe(false);
expect(isResting(sample(0, "in_progress", [], { scheduledRetry: true }))).toBe(false);
expect(isResting(sample(0, "done", [run("r1", "succeeded", 0, 10)]))).toBe(true);
// Unexplained idle work is a possible stall, not a rest; a visible Retry is a rest.
expect(isResting(sample(0, "in_progress", [run("r1", "failed", 0, 10)]))).toBe(false);
expect(isResting(sample(0, "in_progress", [run("r1", "failed", 0, 10)], { interruption: { state: "failed", canRetry: true, retryWait: false, workspaceFailure: null, observedRunId: "r1", userAction: null } }))).toBe(true);
expect(isResting({ at: T0, reachable: false, serverReady: false })).toBe(false);
expect(isResting(sample(0, "blocked", [], { activeRecoveryAction: true, recoveryOwner: "agent" }))).toBe(false);
// A board-owned recovery waits on a person, so the task rests (and R6 reports it).
expect(isResting(sample(0, "blocked", [], { activeRecoveryAction: true, recoveryOwner: "board" }))).toBe(true);
});
});