refactor: replace maxRounds with supervisor check interval
Removes maxRounds as a hard stop limit from the entire stack. The supervisor (already configured via workflow.yaml supervisorInterval) is now the sole termination authority. Changes across 27 files in 11 packages: - workflow-protocol: StartStep.meta is now empty, StartNodePayload drops maxRounds - workflow-cas: isStartPayload no longer checks maxRounds - workflow-execute: engine, worker, fork-thread all stripped of maxRounds plumbing - cli-workflow: --max-rounds flag removed from CLI and HTTP API - workflow-runtime: build-context and create-workflow no longer reference maxRounds - workflow-dashboard: UI no longer sends maxRounds - workflow-template-develop/solve-issue: moderator no longer checks rounds remaining - All tests updated Fixes #185
This commit is contained in:
@@ -13,23 +13,20 @@ const DEFAULT_PHASES: PlannerMeta["phases"] = [
|
||||
},
|
||||
];
|
||||
|
||||
function makeStart(maxRounds: number): ModeratorContext<DevelopMeta>["start"] {
|
||||
function makeStart(): ModeratorContext<DevelopMeta>["start"] {
|
||||
return {
|
||||
role: START,
|
||||
content: "Implement the feature",
|
||||
meta: { maxRounds },
|
||||
meta: {},
|
||||
timestamp: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function makeCtx(
|
||||
maxRounds: number,
|
||||
steps: ModeratorContext<DevelopMeta>["steps"],
|
||||
): ModeratorContext<DevelopMeta> {
|
||||
function makeCtx(steps: ModeratorContext<DevelopMeta>["steps"]): ModeratorContext<DevelopMeta> {
|
||||
return {
|
||||
threadId: "01TEST000000000000000000TR",
|
||||
depth: 0,
|
||||
start: makeStart(maxRounds),
|
||||
start: makeStart(),
|
||||
steps,
|
||||
};
|
||||
}
|
||||
@@ -90,20 +87,18 @@ function committerStep(meta: CommitterMeta): RoleStep<DevelopMeta> {
|
||||
|
||||
describe("developModerator", () => {
|
||||
test("routes initial → planner → coder → reviewer → tester → committer → END", () => {
|
||||
expect(developModerator(makeCtx(20, []))).toBe("planner");
|
||||
expect(developModerator(makeCtx(20, [plannerStep()]))).toBe("coder");
|
||||
expect(developModerator(makeCtx(20, [plannerStep(), coderStep()]))).toBe("reviewer");
|
||||
expect(developModerator(makeCtx(20, [plannerStep(), coderStep(), reviewerStep(true)]))).toBe(
|
||||
expect(developModerator(makeCtx([]))).toBe("planner");
|
||||
expect(developModerator(makeCtx([plannerStep()]))).toBe("coder");
|
||||
expect(developModerator(makeCtx([plannerStep(), coderStep()]))).toBe("reviewer");
|
||||
expect(developModerator(makeCtx([plannerStep(), coderStep(), reviewerStep(true)]))).toBe(
|
||||
"tester",
|
||||
);
|
||||
expect(
|
||||
developModerator(
|
||||
makeCtx(20, [plannerStep(), coderStep(), reviewerStep(true), testerStep(true)]),
|
||||
),
|
||||
developModerator(makeCtx([plannerStep(), coderStep(), reviewerStep(true), testerStep(true)])),
|
||||
).toBe("committer");
|
||||
expect(
|
||||
developModerator(
|
||||
makeCtx(20, [
|
||||
makeCtx([
|
||||
plannerStep(),
|
||||
coderStep(),
|
||||
reviewerStep(true),
|
||||
@@ -120,7 +115,7 @@ describe("developModerator", () => {
|
||||
coderStep(),
|
||||
reviewerStep(false),
|
||||
];
|
||||
expect(developModerator(makeCtx(20, steps))).toBe("coder");
|
||||
expect(developModerator(makeCtx(steps))).toBe("coder");
|
||||
});
|
||||
|
||||
test("reviewer rejects → END when max rounds exhausted", () => {
|
||||
@@ -129,7 +124,7 @@ describe("developModerator", () => {
|
||||
coderStep(),
|
||||
reviewerStep(false),
|
||||
];
|
||||
expect(developModerator(makeCtx(4, steps))).toBe(END);
|
||||
expect(developModerator(makeCtx(steps))).toBe(END);
|
||||
});
|
||||
|
||||
test("tester failed → coder retry when budget allows", () => {
|
||||
@@ -139,7 +134,7 @@ describe("developModerator", () => {
|
||||
reviewerStep(true),
|
||||
testerStep(false),
|
||||
];
|
||||
expect(developModerator(makeCtx(20, steps))).toBe("coder");
|
||||
expect(developModerator(makeCtx(steps))).toBe("coder");
|
||||
});
|
||||
|
||||
test("tester failed → END when max rounds exhausted", () => {
|
||||
@@ -149,7 +144,7 @@ describe("developModerator", () => {
|
||||
reviewerStep(true),
|
||||
testerStep(false),
|
||||
];
|
||||
expect(developModerator(makeCtx(5, steps))).toBe(END);
|
||||
expect(developModerator(makeCtx(steps))).toBe(END);
|
||||
});
|
||||
|
||||
test("multiple planner phases → coder until all complete, then reviewer", () => {
|
||||
@@ -157,13 +152,11 @@ describe("developModerator", () => {
|
||||
{ hash: "AA000001", title: "first phase" },
|
||||
{ hash: "AA000002", title: "second phase" },
|
||||
];
|
||||
expect(developModerator(makeCtx(20, [plannerStep(phases)]))).toBe("coder");
|
||||
expect(developModerator(makeCtx(20, [plannerStep(phases), coderStep("AA000001")]))).toBe(
|
||||
"coder",
|
||||
);
|
||||
expect(developModerator(makeCtx([plannerStep(phases)]))).toBe("coder");
|
||||
expect(developModerator(makeCtx([plannerStep(phases), coderStep("AA000001")]))).toBe("coder");
|
||||
expect(
|
||||
developModerator(
|
||||
makeCtx(20, [plannerStep(phases), coderStep("AA000001"), coderStep("AA000002")]),
|
||||
makeCtx([plannerStep(phases), coderStep("AA000001"), coderStep("AA000002")]),
|
||||
),
|
||||
).toBe("reviewer");
|
||||
});
|
||||
@@ -175,7 +168,7 @@ describe("developModerator", () => {
|
||||
{ hash: "BB000003", title: "verify" },
|
||||
{ hash: "BB000004", title: "polish" },
|
||||
];
|
||||
expect(developModerator(makeCtx(20, [plannerStep(phases), coderStep("BB000004")]))).toBe(
|
||||
expect(developModerator(makeCtx([plannerStep(phases), coderStep("BB000004")]))).toBe(
|
||||
"reviewer",
|
||||
);
|
||||
});
|
||||
@@ -185,9 +178,7 @@ describe("developModerator", () => {
|
||||
{ hash: "CC000001", title: "first phase" },
|
||||
{ hash: "CC000002", title: "second phase" },
|
||||
];
|
||||
expect(developModerator(makeCtx(20, [plannerStep(phases), coderStep("all-done")]))).toBe(
|
||||
"coder",
|
||||
);
|
||||
expect(developModerator(makeCtx([plannerStep(phases), coderStep("all-done")]))).toBe("coder");
|
||||
});
|
||||
|
||||
test("incomplete phases → END when max rounds exhausted", () => {
|
||||
@@ -199,7 +190,7 @@ describe("developModerator", () => {
|
||||
plannerStep(phases),
|
||||
coderStep("DD000001"),
|
||||
];
|
||||
expect(developModerator(makeCtx(3, steps))).toBe(END);
|
||||
expect(developModerator(makeCtx(steps))).toBe(END);
|
||||
});
|
||||
|
||||
test("committer → END for any committer meta status", () => {
|
||||
@@ -220,9 +211,9 @@ describe("developModerator", () => {
|
||||
reviewerStep(true),
|
||||
testerStep(true),
|
||||
];
|
||||
expect(developModerator(makeCtx(20, [...base, committed]))).toBe(END);
|
||||
expect(developModerator(makeCtx(20, [...base, recoverable]))).toBe(END);
|
||||
expect(developModerator(makeCtx(20, [...base, unrecoverable]))).toBe(END);
|
||||
expect(developModerator(makeCtx([...base, committed]))).toBe(END);
|
||||
expect(developModerator(makeCtx([...base, recoverable]))).toBe(END);
|
||||
expect(developModerator(makeCtx([...base, unrecoverable]))).toBe(END);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ const allPhasesComplete: ModeratorCondition<DevelopMeta> = {
|
||||
|
||||
const hasRoundsRemaining: ModeratorCondition<DevelopMeta> = {
|
||||
name: "hasRoundsRemaining",
|
||||
description: "There are rounds remaining before hitting maxRounds",
|
||||
check: (ctx) => ctx.steps.length < ctx.start.meta.maxRounds - 1,
|
||||
description: "Always true — supervisor controls termination",
|
||||
check: () => true,
|
||||
};
|
||||
|
||||
const reviewApproved: ModeratorCondition<DevelopMeta> = {
|
||||
|
||||
@@ -2,7 +2,11 @@ import type { RoleDefinition } from "@uncaged/workflow-runtime";
|
||||
import * as z from "zod/v4";
|
||||
|
||||
export const coderMetaSchema = z.object({
|
||||
completedPhase: z.string().describe("The planner phase hash finished this round. If multiple phases were completed, use the last finished phase hash."),
|
||||
completedPhase: z
|
||||
.string()
|
||||
.describe(
|
||||
"The planner phase hash finished this round. If multiple phases were completed, use the last finished phase hash.",
|
||||
),
|
||||
filesChanged: z.array(z.string()),
|
||||
summary: z.string(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user