Add agent-swarm skill
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// agent-swarm verification runner — copy into the `script` field of the workflow tool.
|
||||
// args: {
|
||||
// wave?: number,
|
||||
// tasks: [{ id: string, review: string }], // review = .swarm/reviews/Vxx.md
|
||||
// concurrency?: number // verifier slots; default 4, keep free slots
|
||||
// }
|
||||
//
|
||||
// The detailed review is written to disk BEFORE the structured verdict is
|
||||
// returned. The workflow schema is intentionally tiny: a long findings array is
|
||||
// easy to get wrong and a schema miss must not destroy the verification work.
|
||||
|
||||
const VERDICT = {
|
||||
type: "object",
|
||||
properties: {
|
||||
task_id: { type: "string" },
|
||||
verdict: { type: "string", enum: ["pass", "pass_with_risks", "fail"] },
|
||||
summary: { type: "string" },
|
||||
review_file: { type: "string" }
|
||||
},
|
||||
required: ["task_id", "verdict", "summary", "review_file"]
|
||||
};
|
||||
|
||||
const CONCURRENCY = Math.max(
|
||||
1,
|
||||
Math.min(Number(args.concurrency) || 4, 8)
|
||||
);
|
||||
|
||||
const promptFor = (t, attempt) => [
|
||||
`You are verifier ${t.id}. You did not write this work; do not fix it and do not edit product source, tests, docs or manifests.`,
|
||||
`Read .swarm/brief.md, the relevant .swarm/tasks/*.md cards, .swarm/contracts.md, and the implementation.`,
|
||||
`Work only in ${t.review} and .swarm/tmp/${t.id}/. Copy or hash-check generated files instead of rewriting the tree.`,
|
||||
`Verify against the original acceptance criteria, not the worker's summary. Run the commands yourself and record exact commands + observed results. Findings need file:line, reproduction, expected vs actual.`,
|
||||
`Freeze assumption: the tree is not being edited during this wave. If you see a stale/inconsistent build, say so; do not fix it.`,
|
||||
`FIRST write the full review to ${t.review}. THEN return only {task_id, verdict, summary, review_file}; keep summary <= 2000 chars. The review file is the source of truth and must exist even if the structured reply fails.`,
|
||||
`Verdict rules: "fail" for critical/major product gaps; "pass_with_risks" for minor or documented-scope risks; "pass" only when no material issue remains.`,
|
||||
attempt > 1
|
||||
? `The previous structured reply was unusable. Preserve the review file; return the four required fields only.`
|
||||
: ""
|
||||
].filter(Boolean).join("\n");
|
||||
|
||||
const failed = (t, why) => ({
|
||||
task_id: t.id,
|
||||
verdict: "fail",
|
||||
summary: why,
|
||||
review_file: t.review
|
||||
});
|
||||
|
||||
async function runOne(t) {
|
||||
for (let attempt = 1; attempt <= 2; attempt++) {
|
||||
try {
|
||||
const r = await agent(promptFor(t, attempt), {
|
||||
label: t.id,
|
||||
phase: `verify ${args.wave || ""}`.trim(),
|
||||
schema: VERDICT
|
||||
});
|
||||
if (r && r.verdict) return r;
|
||||
log(`${t.id}: attempt ${attempt} produced no usable verdict`);
|
||||
} catch (error) {
|
||||
const message = String(error && error.message ? error.message : error);
|
||||
if (/(limit|spawn|could not start|active child|capacity|start a child)/i.test(message)) {
|
||||
log(`${t.id}: scheduling error: ${message}`);
|
||||
return failed(t, `scheduling error: ${message}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return failed(t, "no usable structured verdict; see review file");
|
||||
}
|
||||
|
||||
phase(`verify${args.wave ? ` ${args.wave}` : ""}`);
|
||||
log(`Dispatching ${args.tasks.length} verifier(s) in batches of ${CONCURRENCY}`);
|
||||
|
||||
const results = [];
|
||||
for (let start = 0; start < args.tasks.length; start += CONCURRENCY) {
|
||||
const batch = args.tasks.slice(start, start + CONCURRENCY);
|
||||
const batchResults = await parallel(batch.map((t) => () => runOne(t)));
|
||||
results.push(...batchResults);
|
||||
}
|
||||
|
||||
return {
|
||||
wave: args.wave || null,
|
||||
concurrency: CONCURRENCY,
|
||||
total: results.length,
|
||||
pass: results.filter((r) => r && r.verdict === "pass").length,
|
||||
pass_with_risks: results.filter((r) => r && r.verdict === "pass_with_risks").length,
|
||||
fail: results.filter((r) => !r || r.verdict === "fail").length,
|
||||
results
|
||||
};
|
||||
Reference in New Issue
Block a user