Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

[rig-decomposition-bench] Daily rig decomposition benchmark — 2026-10-16 — tie

Đang mở
#657 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
55/100
Loại issue
Lỗi
Độ rõ ràng
Khá rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
github, node.js, typescript
Lĩnh vực
ai-infra-agents, devtools

Hướng nghiên cứu

The benchmark source shows runRig spawning node with its working directory set to .github/skills/rig; the captured output is Error: spawn node ENOENT. Start by checking how the benchmark resolves that directory and launches the rig runtime. Done when the benchmark can typecheck and execute the generated program, and its result is recorded; the issue says it was not rerun or debugged.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

ai-agent automation

Task

Reconcile a Beekeeping Cooperative's Hive Inspection Data and Produce a Swarm-Risk Report (domain: Apiculture operations analytics (beekeeping cooperative data engineering and reporting))

A regional beekeeping cooperative has three independent data sources, all embedded below in the task statement by the grader as small inline tables (about 12 hives, 30 inspection records, 8 weather days, and 10 feed-purchase lines). The sources are: (1) hive inspection logs with hive ID, date, queen-cell count, brood frames, and a free-text note; (2) a daily weather table with temperature, rain, and forage index; and (3) a feed and treatment ledger with hive ID, date, product, and quantity.

The work splits into phases that can run in parallel. One sub-agent normalizes and validates the data. It should resolve inconsistent hive ID formats such as 'H-03', 'h3' and 'Hive 3', flag impossible values such as negative frames, and de-duplicate records. A second sub-agent designs a transparent swarm-risk scoring rubric. It should weight queen cells, crowding (brood frames), recent forage shortage, and recent feeding, and it should state the weights and thresholds. A third sub-agent implements the scoring as a Python script. A fourth sub-agent reviews the script and the report independently for errors, unit mistakes and unsupported claims.

The final deliverable is a concise report with: a cleaned-data summary listing every correction made, a ranked table of hives by risk score with Low/Medium/High bands, three recommended actions for the top-risk hives (for example splits or adding supers), and a limitations section covering data gaps and the assumptions made.

Success criteria:

  • Every hive-ID variant is mapped to one canonical ID, and the report lists each correction, including the duplicate records removed and the impossible values flagged.
  • The scoring rubric states explicit numeric weights and band thresholds, and the same rubric produces the ranked table; the scores can be recomputed by hand from the stated formula and match.
  • A runnable Python script reproduces the ranked table from the inline data without errors, and its output matches the table in the report.
  • The ranked table covers all hives, assigns each exactly one of Low/Medium/High, and the recommended actions target the highest-ranked hives and cite the specific data points that justify them.
  • The report includes a review section with at least two concrete issues or confirmations from the independent reviewer, plus a limitations section naming at least two data gaps or assumptions.

Timing

Approach Duration (ms)
Single call 20001
Decomposed 25718

Decomposed final status: fail

Note: single-call solution was empty (the call returned null after ~20s; no error captured in stderr, which was empty).

Decomposition attempts

Attempt 1
  • Source generation: ok (4903 chars)
  • Typecheck: failed
  • Execute: skipped
  • Note: Next attempt fixes the typecheck error.
Captured output
Error: spawn node ENOENT
Attempt 2
  • Source generation: ok (4884 chars)
  • Typecheck: failed
  • Execute: skipped
  • Note: Next attempt fixes the typecheck error.
Captured output
Error: spawn node ENOENT

Likely cause: the benchmark program spawned node with cwd resolved from process.cwd() + .github/skills/rig, which the rig runtime evidently did not resolve (ENOENT on spawn). Per instructions, not re-run or debugged.

Grading

  • Single-call score: 0
  • Decomposed score: 0
  • Winner: tie

Rationale: Neither solution delivers anything. Solution A is empty. Solution B produced no solution because no generated program ran successfully. Neither one meets any success criterion: there is no ID mapping, rubric, script, ranked table, review section or limitations section. Both score 0, so the result is a tie.

Single-call solution

Solution

(empty — the solver returned null)

Decomposed rig program source

import { agent, workflow, s } from "rig";

// Agent role: normalizes and validates the inline data, listing every correction
const normalizer = agent({
  model: "medium",
  instructions: "You are a data-cleaning specialist for a beekeeping cooperative. From the task text provided, extract the inspection, weather and feed ledger tables that are embedded inline. Map every hive-ID variant (e.g. 'H-03', 'h3', 'Hive 3') to one canonical ID like 'H03'. Flag impossible values such as negative frames, and de-duplicate records. Output: (1) the cleaned tables as CSV text, and (2) a numbered list of every correction made, including each duplicate removed and each impossible value flagged, quoting the original record. Never invent data that is not in the task text; if the inline tables are missing, say so explicitly.",
  output: s.string,
});

// Agent role: designs the swarm-risk scoring rubric with explicit weights and thresholds
const rubricDesigner = agent({
  model: "large",
  instructions: "You design a transparent swarm-risk scoring rubric for beehives. Weight queen-cell count, crowding (brood frames), recent forage shortage (forage index from weather), and recent feeding (feed ledger). Give an explicit formula: normalize each factor to 0-1 with stated caps, state numeric weights summing to 100, and state Low/Medium/High band thresholds. Make it simple enough to recompute by hand. Include one worked example. Output the rubric in Markdown.",
  output: s.string,
});

// Agent role: implements the rubric as a runnable Python script
const coder = agent({
  model: "large",
  instructions: "You write a single self-contained Python 3 script (standard library only) that embeds the cleaned data and implements the given rubric exactly. It must print a ranked table of all hives with score and Low/Medium/High band. Output only the Python source code. Use the cleaned data and rubric provided, and use the same weights and thresholds as the rubric.",
  output: s.string,
});

// Agent role: independent reviewer of script and report for errors, unit mistakes and unsupported claims
const reviewer = agent({
  model: "large",
  instructions: "You are an independent reviewer. Check the Python script against the rubric and the cleaned data. Look for errors, unit mistakes, mismatched weights or thresholds, and unsupported claims. Output a list of at least two concrete issues or confirmations, each stating what you checked and the result. Also state the expected ranked table, computed by hand from the rubric, so it can be compared with the script output.",
  output: s.string,
});

// Agent role: writes the final concise report
const writer = agent({
  model: "medium",
  instructions: "You write the final concise cooperative report in Markdown. Required sections: 1) Cleaned-data summary listing every correction; 2) Scoring rubric with weights and thresholds; 3) Ranked table of all hives with scores and exactly one of Low/Medium/High each; 4) Three recommended actions for the top-risk hives citing specific data points; 5) Independent review with at least two concrete issues or confirmations; 6) Limitations naming at least two data gaps or assumptions. Include the Python script in a fenced block. Use only the provided material and do not invent numbers; if the reviewer found discrepancies, use the reviewer's corrected values and note them.",
  output: s.string,
});

// Workflow role: orchestrates the agents and combines their outputs into the final solution
export default workflow({
  meta: {
    name: "hive-swarm-risk-report",
    description: "Cleans hive data, designs a rubric, implements scoring, reviews it, and writes the report.",
  },
  body: async ({ call }) => {
    const task = "Reconcile a beekeeping cooperative's hive inspection data (inspection logs, weather table, feed ledger) and produce a swarm-risk report. The tables are embedded inline in the task statement supplied by the grader; hive IDs vary ('H-03', 'h3', 'Hive 3'). Success requires a canonical ID mapping, listed corrections, explicit weights, a ranked table with Low/Medium/High bands, a runnable Python script, a review section and limitations.";

    const cleaned = await call(normalizer, task);
    const rubric = await call(rubricDesigner, task);

    const script = await call(
      coder,
      "CLEANED DATA AND CORRECTIONS:\n" + (cleaned ?? "(unavailable)") + "\n\nRUBRIC:\n" + (rubric ?? "(unavailable)")
    );

    const review = await call(
      reviewer,
      "CLEANED (redacted) + (cleaned ?? "") + "\n\nRUBRIC:\n" + (rubric ?? "") + "\n\nPYTHON SCRIPT:\n" + (script ?? "")
    );

    const report = await call(
      writer,
      "CLEANED DATA AND CORRECTIONS:\n" + (cleaned ?? "") + "\n\nRUBRIC:\n" + (rubric ?? "") + "\n\nPYTHON SCRIPT:\n" + (script ?? "") + "\n\nREVIEW:\n" + (review ?? "")
    );

    return { solution: report ?? "" };
  },
});

Decomposed solution

Solution

(empty — no program executed successfully)

Benchmark program

bench.ts
import { agent, workflow, s, configureAgent, copilotEngine, repair } from "rig";

configureAgent(copilotEngine({ server: true } as never));

const DEADLINE = Date.now() + 25 * 60_000;
const remaining = () => DEADLINE - Date.now();

type RunResult = { ok: boolean; stdout: string; stderr: string; status: string };

async function runRig(source: string, flag: "--typecheck" | "--server", capMs: number): Promise<RunResult> {
  const left = remaining();
  const timeoutMs = Math.min(capMs, left);
  if (timeoutMs < 5_000) return { ok: false, stdout: "", stderr: "skipped: time budget exhausted", status: "skipped" };
  const { spawn } = await import("node:child_process");
  const path = await import("node:path");
  const cwd = path.resolve(process.cwd(), ".github/skills/rig");
  return new Promise<RunResult>((resolve) => {
    const child = spawn("node", ["rig.ts", flag], { cwd });
    let stdout = "";
    let stderr = "";
    let timedOut = false;
    const timer = setTimeout(() => {
      timedOut = true;
      child.kill("SIGKILL");
    }, timeoutMs);
    child.stdout.on("data", (d: Buffer) => { stdout += d.toString(); });
    child.stderr.on("data", (d: Buffer) => { stderr += d.toString(); });
    child.on("error", (e: Error) => {
      clearTimeout(timer);
      resolve({ ok: false, stdout, stderr: stderr + String(e), status: "failed" });
    });
    child.on("close", (code: number | null) => {
      clearTimeout(timer);
      if (timedOut) resolve({ ok: false, stdout, stderr: stderr + `\ntimed out after ${timeoutMs}ms`, status: "timeout" });
      else resolve({ ok: code === 0, stdout, stderr, status: code === 0 ? "passed" : "failed" });
    });
    child.stdin.on("error", () => {});
    child.stdin.end(source);
  });
}

function truncate(text: string, max = 6000): string {
  return text.length > max ? text.slice(0, max) + `\n...[truncated ${text.length - max} chars]` : text;
}

// Agent role: picks a benchmark task
const pickTask = agent({
  name: "taskPicker",
  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 distinct design/implementation/review phases, a structured report combining several 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. Prefer variety across domains; choose an unusual domain. Provide 3-6 concrete, checkable success criteria.",
  output: s.object({
    title: s.string,
    domain: s.string,
    description: s.string,
    successCriteria: s.array(s.string),
  }),
});

// Agent role: single-call solver
const solveSingle = agent({
  name: "singleSolver",
  model: "medium",
  maxTurns: 1,
  instructions:
    "Solve the entire task yourself in one response, addressing every success criterion. No delegation, no tools. Return the full solution (use Markdown code fences where appropriate).",
  output: s.string,
});

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 CONTRACT = `Rules for the program (you have no other documentation, follow exactly):
- Import only from "rig". Output only TypeScript source code, no prose; Markdown fences are tolerated.
- Use at least two agent() declarations, each preceded by a "// Agent role: ..." comment, with model "small" for simple sub-steps and "medium" or "large" for harder ones.
- Agents use \`output: s.string\` for any free-form text or source code. Do not wrap large solutions in object schemas.
- The root is export default of a workflow(). 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. Take no required input. Do not invoke the root.
- body receives { call }; call(agent, inputString) returns the output or null on failure. Combine the agents' outputs into the final { solution: string }, rather than having one agent solve everything.
- Do not use console.log.
Required shape (abridged to one agent; yours needs at least two):

${SKELETON}`;

// Agent role: writes the decomposed rig program
const writer = agent({
  name: "decompositionWriter",
  model: "medium",
  maxTurns: 1,
  instructions: `You write a self-contained rig TypeScript program that solves a given task by splitting it across multiple agents.\n\n${CONTRACT}`,
  output: s.string,
});

// Agent role: fixes or fully generates the decomposed rig program
const fixer = agent({
  name: "decompositionFixer",
  model: "medium",
  maxTurns: 1,
  instructions: `You repair or generate rig TypeScript programs. When given source and an error, fix precisely what the error names while preserving what already worked; do not make unrelated edits. When told there is no source, generate the complete program from the task.\n\n${CONTRACT}`,
  output: s.string,
});

// Agent role: grader
const grader = agent({
  name: "grader",
  model: "large",
  maxTurns: 1,
  instructions:
    "Score two candidate solutions to the same task from 0-10 on how completely and correctly each 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;
  output: string;
  note: string;
};

function stripFence(src: string): string {
  const m = src.match(/```(?:ts|typescript|rig)?\n([\s\S]*?)```/);
  return (m ? m[1] : src).trim();
}

export default workflow({
  meta: { name: "rig-decomposition-bench", description: "Single call vs decomposed rig program" },
  body: async ({ call }) => {
    const task = await call(pickTask, "Pick the benchmark task.");
    if (!task) return { status: "failed: task picker returned null" };
    const taskText = `Title: ${task.title}\nDomain: ${task.domain}\nDescription: ${task.description}\nSuccess criteria:\n${task.successCriteria.map((c: string) => `- ${c}`).join("\n")}`;

    const t0 = Date.now();
    const single = await call(solveSingle, taskText, { timeout: 5 * 60_000 });
    const singleMs = Date.now() - t0;
    const singleSolution = single ?? "";

    const t1 = Date.now();
    const attempts: Attempt[] = [];
    let source = "";
    let decomposedSolution = "";
    let passed = false;
    let lastError = "";
    for (let i = 1; i <= 2 && !passed; i++) {
      if (remaining() < 4 * 60_000) break;
      const rec: Attempt = { attempt: i, generation: "", typecheck: "skipped", execute: "skipped", output: "", note: "" };
      attempts.push(rec);
      let raw: string | null;
      if (i === 1) {
        raw = await call(writer, `Task:\n${taskText}`, { timeout: 5 * 60_000 });
      } else if (source.trim()) {
        raw = await call(
          fixer,
          `Task:\n${taskText}\n\nCurrent source:\n${source}\n\nCaptured error:\n${lastError}\n\nFix precisely what the error names, preserving what worked. Required skeleton:\n${SKELETON}`,
          { timeout: 5 * 60_000 },
        );
      } else {
        raw = await call(
          fixer,
          `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}`,
          { timeout: 5 * 60_000 },
        );
      }
      const cleaned = raw && raw.trim() ? stripFence(raw) : "";
      if (!cleaned) {
        rec.generation = "empty (null or whitespace-only)";
        lastError = "";
        rec.note = i === 1 ? "Second attempt regenerates from scratch." : "";
        continue;
      }
      source = cleaned;
      rec.generation = `ok (${cleaned.length} chars)`;
      const tc = await runRig(cleaned, "--typecheck", 2 * 60_000);
      rec.typecheck = tc.status;
      if (!tc.ok) {
        lastError = truncate(tc.stdout + tc.stderr);
        rec.output = lastError;
        rec.note = "Next attempt fixes the typecheck error.";
        continue;
      }
      const ex = await runRig(cleaned, "--server", 5 * 60_000);
      rec.execute = ex.status;
      if (!ex.ok) {
        lastError = truncate(ex.stderr + (ex.stdout ? "\nstdout:\n" + ex.stdout : ""));
        rec.output = lastError;
        rec.note = "Next attempt fixes the execution error.";
        continue;
      }
      try {
        const parsed = JSON.parse(ex.stdout) as { solution?: unknown };
        if (typeof parsed.solution === "string" && parsed.solution.trim()) {
          decomposedSolution = parsed.solution;
          passed = true;
        } else {
          rec.execute = "failed";
          lastError = "stdout JSON had no non-empty string 'solution': " + truncate(ex.stdout);
          rec.output = lastError;
          rec.note = "Next attempt fixes the missing solution.";
        }
      } catch (e) {
        rec.execute = "failed";
        lastError = "stdout was not valid JSON: " + String(e) + "\n" + truncate(ex.stdout);
        rec.output = lastError;
        rec.note = "Next attempt fixes the output format.";
      }
    }
    const decomposedMs = Date.now() - t1;

    const decomposedForGrade = decomposedSolution || "(Decomposition produced no solution: no generated program executed successfully.)";
    const grade = await call(
      grader,
      `${taskText}\n\n=== SOLUTION A (single-call) ===\n${singleSolution || "(empty)"}\n\n=== SOLUTION B (decomposed) ===\n${decomposedForGrade}`,
      { timeout: 5 * 60_000 },
    );

    return {
      task,
      singleMs,
      decomposedMs,
      singleSolution,
      decomposedSolution,
      attempts,
      status: passed ? "pass" : "fail",
      decomposedSource: source,
      grade,
    };
  },
});

Verdict: tie — both solutions were empty (single-call returned null; decomposition failed with spawn node ENOENT on both attempts), so both scored 0.

Generated by Daily Rig Decomposition Benchmark · copilot · auto · 65.9 AIC · ⌖ 0.729 AIC · ⊞ 9.9K · ◷

  • expires on Oct 23, 2026, 1:12 PM UTC
Ngôn ngữ chính
TypeScript
Star
14
Fork
0
Merge trung bình
3 giờ 38 phút
Pull request đã merge (30 ngày)
29

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của githubnext/rig

Tất cả issue của githubnext/rig

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.