diff --git a/.changeset/fn-6891-ce-review-skill.md b/.changeset/fn-6891-ce-review-skill.md new file mode 100644 index 0000000000..8a08e8eaf3 --- /dev/null +++ b/.changeset/fn-6891-ce-review-skill.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": patch +--- + +Update the built-in compound-engineering workflow so its Review stage runs the `compound-engineering:ce-code-review` skill directly. The redundant generic reviewer seam node was removed, leaving the CE code-review gate as the sole review stage. diff --git a/packages/core/src/__tests__/builtin-workflows.test.ts b/packages/core/src/__tests__/builtin-workflows.test.ts index 8ded12ff1d..8abb7684b3 100644 --- a/packages/core/src/__tests__/builtin-workflows.test.ts +++ b/packages/core/src/__tests__/builtin-workflows.test.ts @@ -354,13 +354,15 @@ describe("built-in workflows", () => { } }); - it("compound-engineering compiles its skill nodes to steps", () => { + it("compound-engineering compiles exactly one ce-code-review step and no generic review seam", () => { const ce = getBuiltinWorkflow("builtin:compound-engineering")!; const steps = compileWorkflowToSteps(ce.ir); - // plan + execute (ce-work) + code-review (pre-merge) + document (post-merge) - // — review/merge seams are skipped. - expect(steps.length).toBeGreaterThanOrEqual(4); + // plan + execute (ce-work) + code-review (pre-merge) + commit-pr + + // resolve-feedback + document (post-merge) — merge seams are skipped. + expect(steps.length).toBeGreaterThanOrEqual(6); expect(steps.some((s) => s.name === "Plan")).toBe(true); + expect(steps.filter((s) => s.skillName === "compound-engineering:ce-code-review")).toHaveLength(1); + expect(steps.some((s) => s.name === "Review" && !s.skillName)).toBe(false); }); it("compound-engineering runs ce-work for the execute step in coding mode", () => { @@ -393,6 +395,44 @@ describe("built-in workflows", () => { expect(ids.indexOf("merge")).toBeLessThan(ids.indexOf("document")); }); + it("compound-engineering review stage is ce-code-review, with graph ordering and layout intact", () => { + const ce = getBuiltinWorkflow("builtin:compound-engineering")!; + const byId = (id: string) => ce.ir.nodes.find((n) => n.id === id); + const authoredNodeIds = ce.ir.nodes.filter((node) => node.id !== "start" && node.id !== "end").map((node) => node.id); + expect(authoredNodeIds).toEqual([ + "plan", + "execute", + "code-review", + "commit-pr", + "resolve-feedback", + "merge", + "document", + ]); + expect(ce.ir.nodes.some((node) => node.config?.seam === "review")).toBe(false); + + const codeReview = byId("code-review"); + expect(codeReview?.kind).toBe("gate"); + expect(codeReview?.config?.skillName).toBe("compound-engineering:ce-code-review"); + expect(codeReview?.config?.gateMode).toBe("gate"); + expect(codeReview?.config?.toolMode).toBe("coding"); + + const layout = ce.layout ?? {}; + expect(Object.keys(layout).sort()).toEqual(ce.ir.nodes.map((node) => node.id).sort()); + for (let i = 1; i < ce.ir.nodes.length; i += 1) { + expect(layout[ce.ir.nodes[i].id].x - layout[ce.ir.nodes[i - 1].id].x).toBe(170); + } + expect(ce.ir.edges.some((edge) => edge.from === "execute" && edge.to === "code-review")).toBe(true); + expect(ce.ir.edges.some((edge) => edge.from === "code-review" && edge.to === "commit-pr")).toBe(true); + }); + + it("other built-in workflows retain their generic review nodes", () => { + const coding = getBuiltinWorkflow("builtin:coding")!; + const reviewHeavy = getBuiltinWorkflow("builtin:review-heavy")!; + + expect(coding.ir.nodes.some((node) => node.id === "review" && node.config?.seam === "review")).toBe(true); + expect(reviewHeavy.ir.nodes.some((node) => node.id === "review" && node.config?.seam === "review")).toBe(true); + }); + it("compound-engineering runs plan/code-review/document in coding mode and carries skillName onto compiled steps (U1/U4)", () => { const ce = getBuiltinWorkflow("builtin:compound-engineering")!; const byId = (id: string) => ce.ir.nodes.find((n) => n.id === id); @@ -407,8 +447,10 @@ describe("built-in workflows", () => { const plan = steps.find((s) => s.name === "Plan"); expect(plan?.skillName).toBe("compound-engineering:ce-plan"); expect(plan?.toolMode).toBe("coding"); - const codeReview = steps.find((s) => s.skillName === "compound-engineering:ce-code-review"); - expect(codeReview?.toolMode).toBe("coding"); + const codeReviewSteps = steps.filter((s) => s.skillName === "compound-engineering:ce-code-review"); + expect(codeReviewSteps).toHaveLength(1); + expect(codeReviewSteps[0].gateMode).toBe("gate"); + expect(codeReviewSteps[0].toolMode).toBe("coding"); const document = steps.find((s) => s.skillName === "compound-engineering:ce-compound"); expect(document?.toolMode).toBe("coding"); }); diff --git a/packages/core/src/builtin-workflows.ts b/packages/core/src/builtin-workflows.ts index 936c33f403..b9382f4ee0 100644 --- a/packages/core/src/builtin-workflows.ts +++ b/packages/core/src/builtin-workflows.ts @@ -208,7 +208,6 @@ export const BUILTIN_WORKFLOWS: WorkflowDefinition[] = [ prompt: "Execute the plan for this task, following existing patterns and maintaining quality throughout.", }, }, - { id: "review", kind: "prompt", config: builtinPromptConfig("review", "Review") }, { id: "code-review", kind: "gate", @@ -217,6 +216,10 @@ export const BUILTIN_WORKFLOWS: WorkflowDefinition[] = [ executor: "skill", skillName: "compound-engineering:ce-code-review", gateMode: "gate", + /* + * FNXC:Workflows 2026-06-21-00:00: + * FN-6891 requires the compound-engineering Review stage to invoke compound-engineering:ce-code-review directly. The prior generic reviewer seam was removed so CE review runs through the CE skill and still blocks merge as a gate. + */ // Coding mode so ce-code-review can fan out to its reviewer-persona // subagents via fn_spawn_agent. As a gate step it still emits the // verdict JSON (KTD-6); it is not meant to write the tree (Risk-1).