[rig-decomposition-bench] Daily rig decomposition benchmark — 2026-10-24 — no winner (picker failed)
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Accessibilité débutants
- 50/100
- Type d'issue
- Bug
- Clarté
- Plutôt claire
- Activité
- Active
- Stack technique
- github-actions, typescript
- Domaine
- ai-infra-agents, testing-qa
Piste de recherche
Start with the task picker definition and the workflow call in the embedded bench.ts; inspect how call(picker, ...) handles a null result and how repair() is configured. Reproduce the run-once failure and identify a concrete change that lets the benchmark continue or report a useful failure. Done when the picker failure is handled as intended and a run records a clear outcome; the issue does not specify a fix or a test.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Result: benchmark failed at the task-picker step
The task picker (small agent with repair(), maxTurns: 2) returned null, so the workflow returned early. Per the run-once rule it was not retried.
Captured stdout (bench_output.json):
{
"error": "task picker returned null",
"status": "fail"
}
Captured stderr: empty (0 bytes). Exit code was 0.
Typecheck note: --typecheck reported no errors in the benchmark program itself. It did print errors from rig.ts and engines/* (missing @types/node / NodeJS types), and the same errors appear for a trivial one-agent program.
Task
Not chosen (picker returned null).
Timing comparison
| single-call (ms) | decomposed (ms) | decomposed status | |
|---|---|---|---|
| not run | not run | fail (never started) |
Decomposition attempts
None. There was no task to give the writer.
Grading
Not run. There are no scores, no winner and no rationale.
Single-call solution
Not produced.
Decomposed program source and solution
Not produced.
Benchmark program (bench.ts)
import { agent, workflow, s, configureAgent, copilotEngine, repair } from "rig";
configureAgent(copilotEngine());
const DEADLINE = Date.now() + 25 * 60_000;
const left = () => DEADLINE - Date.now();
const SKELETON = `import { agent, workflow, s } from "rig";
// Agent role: <role>
const analyze = agent({ model: "small", instructions: "...", output: s.string });
// Workflow role: combines the agents' outputs into the final solution
export default workflow({
meta: { name: "decomposed-solution", description: "..." },
body: async ({ call }) => {
const analysis = await call(analyze, "...");
return { solution: analysis ?? "" };
},
});`;
const RULES = `Rules for the rig program (you have no access to the library docs, follow these exactly):
- Import only from "rig". Export the root via \`export default\` and never invoke it. The root takes no required input.
- A workflow() spec accepts only meta, an optional input, and body. It has NO output or execute field; the final { solution } object is simply what body returns.
- An agent() root instead declares output: s.object({ solution: s.string }).
- agent() fields: name, instructions, input, output, model ("small" | "medium" | "large"), maxTurns, addons, agents, tools.
- Inside body({ call, parallel, pipeline }): call(agent, input) returns the output or null on failure; parallel([thunks]) runs thunks concurrently.
- Use at least two agents, each preceded by a "// Agent role: ..." comment; use "small" for simple sub-steps, "medium" or "large" for harder ones. Put a "// Workflow role: ..." comment above the workflow.
- The final answer must combine the agents' outputs, not come from one agent solving everything.
- Use s.string for every agent whose result is source text or free-form solution; do not make agents re-emit a large solution inside an object wrapper.
- Do not use console.log. Return ONLY the complete TypeScript source, optionally in one \`\`\`ts fence.
Required shape (abridged to one agent; you need at least two):
${SKELETON}`;
const picker = agent({
name: "picker",
model: "small",
maxTurns: 2,
addons: [repair()],
instructions:
"Pick ONE concrete, complicated task a person or team might face in a single day that would naturally benefit from being split across sub-agents running different models: multi-source research and synthesis, a multi-file coding task with design/implementation/review phases, a structured report combining independent analyses, or a multi-step data transformation pipeline. It must be self-contained (solvable from its description alone, no external files or live web access) and concrete enough to grade. Vary the domain; pick randomly. Return title, domain, description, and 3-6 concrete checkable success criteria.",
output: s.object({
title: s.string,
domain: s.string,
description: s.string,
criteria: s.array(s.string),
}),
});
// Agent role: solves the whole task alone in one turn
const solver = agent({
name: "solver",
model: "medium",
maxTurns: 1,
instructions:
"Solve the entire task yourself in one response, addressing every success criterion explicitly. No delegation, no tools. Return the complete solution verbatim.",
output: s.string,
});
// Agent role: writes (and fixes) the decomposed rig program
const writer = agent({
name: "writer",
model: "medium",
maxTurns: 1,
instructions:
"You write self-contained rig TypeScript programs that solve a task by splitting it across multiple agents.\n" + RULES,
output: s.string,
});
// Agent role: grades both solutions
const grader = agent({
name: "grader",
model: "large",
maxTurns: 1,
instructions:
"Score each solution 0-10 on how completely and correctly it satisfies the success criteria. Judge only correctness and completeness of content, not length and not which approach produced it. Pick winner single-call, decomposed, or tie and explain the comparison.",
output: s.object({
singleScore: s.number,
decomposedScore: s.number,
winner: s.enum("single-call", "decomposed", "tie"),
rationale: s.string,
}),
});
type Attempt = {
attempt: number;
generation: string;
typecheck: string;
execute: string;
error: string;
};
function stripFence(src: string): string {
const m = src.match(/```(?:ts|typescript|rig)?\s*\n([\s\S]*?)```/);
return (m ? m[1] : src).trim();
}
async function runCli(args: string[], input: string, timeoutMs: number) {
const modName: string = "node:child_process";
const { spawn } = (await import(modName)) as any;
const proc = (globalThis as any).process;
return new Promise<{ code: number | null; stdout: string; stderr: string; timedOut: boolean }>((resolve) => {
const child = spawn("node", ["rig.ts", ...args], { cwd: proc.cwd() });
let stdout = "";
let stderr = "";
let timedOut = false;
const t = setTimeout(() => {
timedOut = true;
child.kill("SIGKILL");
}, Math.max(1000, timeoutMs));
child.stdout.on("data", (d: any) => (stdout += d.toString()));
child.stderr.on("data", (d: any) => (stderr += d.toString()));
child.on("error", (e: Error) => {
clearTimeout(t);
resolve({ code: -1, stdout, stderr: stderr + String(e), timedOut });
});
child.on("close", (code: number | null) => {
clearTimeout(t);
resolve({ code, stdout, stderr, timedOut });
});
child.stdin.on("error", () => {});
child.stdin.end(input);
});
}
// Workflow role: pick a task, solve it two ways, grade, and return the report
export default workflow({
meta: { name: "decomposition-bench", description: "Single-call vs decomposed rig solution benchmark" },
body: async ({ call }) => {
const task = await call(picker, "Pick the task now.", { label: "pick" });
if (!task) return { error: "task picker returned null", status: "fail" };
const taskText = `Title: ${task.title}\nDomain: ${task.domain}\nDescription: ${task.description}\nSuccess criteria:\n${task.criteria.map((c: string, i: number) => `${i + 1}. ${c}`).join("\n")}`;
const t0 = Date.now();
const singleSolution = (await call(solver, taskText, { label: "single", timeout: Math.min(8 * 60_000, Math.max(30_000, left())) })) ?? "";
const singleMs = Date.now() - t0;
const d0 = Date.now();
const attempts: Attempt[] = [];
let source = "";
let decomposedSolution = "";
let status = "fail";
let prevSource = "";
let prevError = "";
for (let n = 1; n <= 2; n++) {
if (left() < 4 * 60_000) break;
let prompt: string;
if (n === 1) {
prompt = `Write the decomposed rig program for this task.\n\n${taskText}`;
} else if (prevSource.trim()) {
prompt = `Fix this rig program. Fix precisely what the error names and preserve what already worked.\n\nTask:\n${taskText}\n\nCurrent source:\n${prevSource}\n\nCaptured error:\n${prevError}\n\nRequired skeleton:\n${SKELETON}`;
} else {
prompt = `There is no source to fix: the previous attempt produced nothing. Generate the complete program from scratch.\n\nTask:\n${taskText}\n\nRequired skeleton:\n${SKELETON}`;
}
const raw = await call(writer, prompt, { label: `writer-${n}`, timeout: Math.min(6 * 60_000, Math.max(30_000, left())) });
const rec: Attempt = { attempt: n, generation: "", typecheck: "skipped", execute: "skipped", error: "" };
attempts.push(rec);
const src = raw ? stripFence(raw) : "";
if (!src.trim()) {
rec.generation = "empty/null";
prevSource = "";
prevError = "empty source";
continue;
}
rec.generation = "ok";
source = src;
prevSource = src;
const tc = await runCli(["--typecheck"], src, Math.min(2 * 60_000, left()));
if (tc.code !== 0 || tc.timedOut) {
rec.typecheck = tc.timedOut ? "timeout" : "fail";
rec.error = `${tc.stdout}\n${tc.stderr}`.trim() || "typecheck failed";
prevError = rec.error;
continue;
}
rec.typecheck = "pass";
if (left() < 30_000) { rec.execute = "skipped (no time)"; break; }
const ex = await runCli(["--server"], src, Math.min(5 * 60_000, left()));
if (ex.code !== 0 || ex.timedOut) {
rec.execute = ex.timedOut ? "timeout" : "fail";
rec.error = `${ex.stderr}\n${ex.stdout}`.trim() || "execute failed";
prevError = rec.error;
continue;
}
try {
const parsed = JSON.parse(ex.stdout);
const sol = parsed?.solution ?? parsed?.output?.solution ?? parsed?.result?.solution;
if (typeof sol === "string" && sol.trim()) {
rec.execute = "pass";
decomposedSolution = sol;
status = "pass";
break;
}
rec.execute = "fail";
rec.error = `No solution string in stdout: ${ex.stdout.slice(0, 4000)}`;
} catch (e) {
rec.execute = "fail";
rec.error = `Invalid JSON stdout: ${String(e)}\n${ex.stdout.slice(0, 4000)}`;
}
prevError = rec.error;
}
const decomposedMs = Date.now() - d0;
const decomposedForGrading = decomposedSolution.trim() || "(Decomposition produced no solution.)";
let grade: { singleScore: number; decomposedScore: number; winner: string; rationale: string } | null = null;
if (left() > 30_000) {
grade = await call(
grader,
`${taskText}\n\n=== SOLUTION A (single-call) ===\n${singleSolution || "(empty)"}\n\n=== SOLUTION B (decomposed) ===\n${decomposedForGrading}`,
{ label: "grade", timeout: Math.min(5 * 60_000, Math.max(30_000, left())) },
);
}
return {
task,
singleMs,
decomposedMs,
singleSolution,
decomposedSolution,
attempts,
status,
decomposedSource: source,
grade,
};
},
});
Verdict: no winner. The picker returned null on this run, so the benchmark produced no data.
Generated by Daily Rig Decomposition Benchmark · copilot · auto · ~37 AIC · ⌖ ~1 AIC · ⊞ 4.1K · ◷
- expires on Oct 31, 2026, 1:03 PM UTC
- Langage dominant
- TypeScript
- Étoiles
- 14
- Forks
- 0
- Merge moyen
- 9 h 27 min
- PR mergées (30 j)
- 34
Préparer son environnement
Ce projet ne fournit ni conteneur de développement, ni Dockerfile, ni guide de contribution : l'installation est à votre charge. Commencez par son README, et consultez notre guide de la première contribution pour les étapes générales.
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de githubnext/rig
-
ai-agent automation
Difficulté 2/5 1-3 heures Accessibilité débutants 48/100
githubnext/rig#716 ·
Les mainteneurs répondent en général sous 1 jour
-
ai-agent automation
Difficulté 1/5 Moins d'une heure Accessibilité débutants 1/100
githubnext/rig#715 ·
Les mainteneurs répondent en général sous 1 jour
-
agentic-workflows
Difficulté 4/5 3-5 jours Accessibilité débutants 35/100
githubnext/rig#713 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
agentic-workflows
Difficulté 4/5 1-2 jours Accessibilité débutants 35/100
githubnext/rig#711 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
agentic-workflows
Difficulté 4/5 1-2 jours Accessibilité débutants 35/100
githubnext/rig#710 ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de githubnext/rig
Issues similaires
-
bug require-triage
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
expressjs/expressjs.com#2549 · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 83/100
umami-software/umami#4686 · 1 commentaire ·
Les mainteneurs répondent en général sous 2 jours
-
[Bug]: Device screenshot fails with 500 on iPhone Duo (Xcode 27.2): serve-sim calls simctl without --displayPeut-être pris Une pull request liée à cette issue est ouverte ou déjà fusionnée. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 66/100
pingdotgg/t3code#24900 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
inu-appcenter/UNIDorm-web#411 ·
Les mainteneurs répondent en général sous 1 jour
-
🐛 bug
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
Les mainteneurs répondent en général sous 2 jours