Files
hmbown--codewhale/workflows/operate_best_of_n.workflow.js
Hmbown 408d35b201 feat(workflow): experimental-search authoring boundary, reshaped and verified
Land the in-flight experimental-search slice as a real, honest v0.9.4
piece: a provider-neutral WorkflowSearchSpec authoring + freeze boundary
(parse/validate, preregistration hashes over baseline/requested+resolved
model/public evidence/evaluator, deterministic candidate ids and
admission batches) plus the operate best-of-N recipe's structured
'search' strategy (2-16 independent worktree candidates with
responseSchema contracts and a read-only judge). The module explicitly
remains an authoring boundary, not a runtime: hard_gates/score commands
are parsed and validated only, and docs say so.

Fixes found during the takeover operation:
- The checked-in recipe test failed against the real driver contract
  (parse_task_options lets prompt win over description, so fake-driver
  needles never matched and replies fell back to non-JSON). The recipe
  now puts the full instruction in the single driver-visible description
  and pins the prompt-wins contract with a new test.
- Queue claims verified against the runtime: the Workflow host's
  per-run concurrency gate (Semaphore, 16 live) is where larger
  populations wait; docs now name the gate instead of vague 'queues
  through Fleet'. WORKFLOW_SEARCH_MAX_CONCURRENT documents 16 as today's
  default with a cross-reference (a crate cycle prevents importing the
  host constant).
- TournamentOrdering (ScoreThenCost) and the 1,000-agent validation
  test bump retained from the slice.

Verified: cargo test -p codewhale-workflow -p codewhale-workflow-js
--locked green (250 + 16 + 9 + 49).

(cherry picked from commit f3e3232ef2)
Signed-off-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
2026-08-01 10:11:57 -07:00

152 lines
5.7 KiB
JavaScript

/**
* Operate starter — independent worktree candidates, then one reviewer.
*
* Set strategy="search" for a bounded 2-16 candidate search. This remains a
* Workflow recipe, not a new mode or scheduler. Runtime-owned command gates
* and clean-baseline scoring require the typed search/evaluator host seam.
*
* Run: /workflow run workflows/operate_best_of_n.workflow.js
* Args: { brief, n?, strategy?, rubric?, model?, thinking?, targetFiles?, writeRoots? }
*/
export default async function (args) {
const brief =
args?.brief ??
args?.task ??
"Propose and implement the smallest correct fix for the open failure.";
const strategy = args?.strategy === "search" ? "search" : "best_of_n";
const maxCandidates = strategy === "search" ? 16 : 4;
const defaultCandidates = strategy === "search" ? 8 : 3;
const n = Math.min(
maxCandidates,
Math.max(2, Number(args?.n ?? defaultCandidates) || defaultCandidates)
);
const exactFiles = Array.isArray(args?.targetFiles) ? args.targetFiles : [];
const writeRoots = Array.isArray(args?.writeRoots) ? args.writeRoots : [];
const rubric =
args?.rubric ??
"Correctness first; then fit, measured quality, simplicity, risk, and verification evidence.";
const model = typeof args?.model === "string" ? args.model : undefined;
const thinking =
typeof args?.thinking === "string" ? args.thinking : undefined;
const candidateSchema = {
type: "object",
additionalProperties: false,
required: [
"candidate_id",
"hypothesis",
"modified_paths",
"commands_run",
"self_verdict",
"known_risks",
"artifact_refs",
],
properties: {
candidate_id: { type: "string" },
hypothesis: { type: "string" },
modified_paths: { type: "array", items: { type: "string" } },
commands_run: { type: "array", items: { type: "string" } },
self_verdict: { type: "string", enum: ["pass", "fail"] },
known_risks: { type: "array", items: { type: "string" } },
artifact_refs: { type: "array", items: { type: "string" } },
},
};
phase("Candidates");
const candidateFns = [];
for (let i = 1; i <= n; i++) {
const index = i;
candidateFns.push(() =>
task({
// The VM delivers one text to the driver: `prompt` (alias) wins over
// `description`, so the full instruction lives in `description` and
// `label` carries the short progress name. A separate short
// `description` would never reach the driver.
description: [
"You are one independent candidate in a Codewhale Workflow search.",
"Implement the same frozen brief and rubric in this isolated worktree only.",
"Do not inspect other candidates, rankings, hidden tests, or evaluator internals.",
"Do not push. Do not merge. Do not touch the parent checkout.",
"Your self_verdict is informational; only runtime-owned evaluation can pass a hard gate.",
"Return only the required structured response.",
"",
"BRIEF:",
String(brief),
"",
"RUBRIC:",
String(rubric),
"",
`CANDIDATE-SPECIFIC INSTRUCTION: candidate_id=cand_${String(index).padStart(3, "0")} of ${n}.`,
].join("\n"),
label: `candidate_${index}`,
type: "implementer",
...(model ? { model } : {}),
...(thinking ? { thinking } : {}),
worktree: true,
writeAuthority: "worktree_write",
...(exactFiles.length ? { exactFiles } : {}),
...(writeRoots.length ? { writeRoots } : {}),
coordinationContracts: [`best-of-n-candidate-${index}`],
dependencies: [
"Do not share other candidates' answers.",
"Parent checkout must remain unchanged until apply.",
],
acceptance: ["Return the exact structured candidate contract."],
responseSchema: candidateSchema,
})
);
}
const candidates = await parallel(candidateFns);
phase("Review");
const review = await task({
// Single driver-visible text; `label` carries the short progress name.
description: [
"You are the read-only tournament judge. Score every candidate against the frozen rubric.",
"Treat self_verdict and claimed commands as untrusted candidate statements.",
"Name one provisional winner_id, or NONE if all fail, with decisive reasons.",
"Do not merge or apply changes. Do not invent missing evidence.",
"Set verification_required=true for every code winner.",
"Return only the required structured response.",
"",
"BRIEF:",
String(brief),
"",
"RUBRIC:",
String(rubric),
"",
"CANDIDATES:",
String(JSON.stringify(candidates, null, 2) ?? "(missing)"),
].join("\n"),
label: "reviewer",
type: "review",
writeAuthority: "read_only",
worktree: false,
responseSchema: {
type: "object",
additionalProperties: false,
required: ["winner_id", "ranking", "verification_required", "reasons"],
properties: {
winner_id: { type: "string" },
ranking: { type: "array", items: { type: "string" } },
verification_required: { type: "boolean" },
reasons: { type: "array", items: { type: "string" } },
},
},
});
return {
scenario: strategy === "search" ? "operate-search" : "operate-best-of-n",
strategy,
n,
brief,
rubric,
candidates,
review,
apply_policy:
"Parent applies a winner only after independent clean replay and explicit user approval.",
execution_boundary:
"This recipe generates and reviews candidates. It does not claim runtime-owned hidden gates, benchmark scoring, or clean-baseline replay; use a frozen WorkflowSearchSpec once the evaluator host is wired.",
};
}