FN-6025: fix builtin coding auto-merge review flags
Restore the built-in coding workflow's in-review auto-merge metadata. - switch builtin:coding to the authored v2 workflow IR so in-review retains merge-blocker and human-review traits - add core regression coverage for builtin workflow resolution and column flag propagation - extend dashboard board workflow route/payload tests to assert the in-review flags and payload contents Files changed: .changeset/fn-6025-auto-merge-toggle-flags.md | 5 +++++ packages/core/src/__tests__/builtin-workflows.test.ts | 19 +++++++++++++++++++ packages/core/src/__tests__/workflow-ir-resolver.test.ts | 14 ++++++++++++++ packages/core/src/builtin-workflows.ts | 21 ++++++++++++++------- packages/dashboard/src/routes/__tests__/board-workflows-route.test.ts | 12 ++++++++++-- packages/dashboard/src/routes/__tests__/board-workflows.test.ts | 4 ++++ 6 files changed, 66 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-6025 Fusion-Task-Lineage: 838ced1f-0fdf-413b-94a0-52ddb21d75c9
This commit is contained in:
@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
|
||||
import { BUILTIN_WORKFLOWS, getBuiltinWorkflow, isBuiltinWorkflowId } from "../builtin-workflows.js";
|
||||
import { BUILTIN_CODING_WORKFLOW_IR } from "../builtin-coding-workflow-ir.js";
|
||||
import { resolveColumnFlags } from "../trait-registry.js";
|
||||
import { compileWorkflowToSteps } from "../workflow-compiler.js";
|
||||
import { DEFAULT_WORKFLOW_COLUMN_IDS, parseWorkflowIr, serializeWorkflowIr } from "../workflow-ir.js";
|
||||
import { createTaskStoreTestHarness } from "./store-test-helpers.js";
|
||||
@@ -86,6 +87,24 @@ describe("built-in workflows", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("builtin:coding exposes merge-blocker and human-review traits on in-review", () => {
|
||||
const coding = getBuiltinWorkflow("builtin:coding");
|
||||
expect(coding).toBeDefined();
|
||||
const ir = parseWorkflowIr(coding!.ir);
|
||||
expect(ir.version).toBe("v2");
|
||||
if (ir.version !== "v2") throw new Error("expected v2");
|
||||
|
||||
const inReview = ir.columns.find((column) => column.id === "in-review");
|
||||
expect(inReview).toBeDefined();
|
||||
expect(inReview!.traits.length).toBeGreaterThan(0);
|
||||
expect(inReview!.traits.map((trait) => trait.trait)).toContain("merge-blocker");
|
||||
expect(inReview!.traits.map((trait) => trait.trait)).toContain("human-review");
|
||||
|
||||
const flags = resolveColumnFlags(inReview!);
|
||||
expect(flags.mergeBlocker).toBe(true);
|
||||
expect(flags.humanReview).toBe(true);
|
||||
});
|
||||
|
||||
it("includes a coding and a compound-engineering workflow", () => {
|
||||
expect(getBuiltinWorkflow("builtin:coding")).toBeDefined();
|
||||
expect(getBuiltinWorkflow("builtin:compound-engineering")).toBeDefined();
|
||||
|
||||
@@ -89,6 +89,20 @@ describe("resolveWorkflowIrForTask", () => {
|
||||
});
|
||||
|
||||
describe("resolveWorkflowIrById", () => {
|
||||
it("resolves builtin:coding to the authored v2 IR with review column traits", async () => {
|
||||
const store = makeStore({});
|
||||
const ir = await resolveWorkflowIrById(store, "builtin:coding");
|
||||
|
||||
expect(ir.version).toBe("v2");
|
||||
if (ir.version !== "v2") throw new Error("expected v2");
|
||||
const inReview = ir.columns.find((column) => column.id === "in-review");
|
||||
expect(inReview).toBeDefined();
|
||||
expect(inReview!.traits.length).toBeGreaterThan(0);
|
||||
expect(inReview!.traits).toEqual(
|
||||
expect.arrayContaining([{ trait: "merge-blocker" }, { trait: "human-review" }]),
|
||||
);
|
||||
});
|
||||
|
||||
it("parses a raw-string IR from the definition", async () => {
|
||||
const raw = JSON.stringify(CUSTOM_IR);
|
||||
const store = makeStore({ defs: { "wf-raw": { ir: raw } } });
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { BUILTIN_CODING_WORKFLOW_IR } from "./builtin-coding-workflow-ir.js";
|
||||
import { BUILTIN_PR_WORKFLOW_IR } from "./builtin-pr-workflow-ir.js";
|
||||
import { BUILTIN_STEPWISE_CODING_WORKFLOW_IR } from "./builtin-stepwise-coding-workflow-ir.js";
|
||||
import { BUILTIN_WORKFLOW_SETTINGS } from "./builtin-workflow-settings.js";
|
||||
@@ -84,16 +85,22 @@ function linear(spec: BuiltinSpec): WorkflowDefinition {
|
||||
* seams are honored only by the graph interpreter (flag on).
|
||||
*/
|
||||
export const BUILTIN_WORKFLOWS: WorkflowDefinition[] = [
|
||||
linear({
|
||||
{
|
||||
id: "builtin:coding",
|
||||
name: "Coding (built-in)",
|
||||
description: "The standard coding pipeline: implement, review, then merge. Equivalent to the default behavior.",
|
||||
nodes: [
|
||||
{ id: "execute", kind: "prompt", config: builtinPromptConfig("execute", "Execute") },
|
||||
{ id: "review", kind: "prompt", config: builtinPromptConfig("review", "Review") },
|
||||
{ id: "merge", kind: "prompt", config: builtinPromptConfig("merge", "Merge boundary") },
|
||||
],
|
||||
}),
|
||||
kind: "workflow",
|
||||
ir: BUILTIN_CODING_WORKFLOW_IR,
|
||||
layout: {
|
||||
start: { x: 60, y: 160 },
|
||||
execute: { x: 230, y: 160 },
|
||||
review: { x: 400, y: 160 },
|
||||
merge: { x: 570, y: 160 },
|
||||
end: { x: 740, y: 160 },
|
||||
},
|
||||
createdAt: BUILTIN_TS,
|
||||
updatedAt: BUILTIN_TS,
|
||||
},
|
||||
linear({
|
||||
id: "builtin:quick-fix",
|
||||
name: "Quick fix (built-in)",
|
||||
|
||||
@@ -112,7 +112,11 @@ describe("GET /tasks/board-workflows", () => {
|
||||
const body = res.body as {
|
||||
flagEnabled: boolean;
|
||||
defaultWorkflowId: string;
|
||||
workflows: Array<{ id: string; name: string; columns: unknown[] }>;
|
||||
workflows: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
columns: Array<{ id: string; flags: { mergeBlocker?: boolean; humanReview?: boolean } }>;
|
||||
}>;
|
||||
taskWorkflowIds: Record<string, string>;
|
||||
};
|
||||
|
||||
@@ -149,6 +153,9 @@ describe("GET /tasks/board-workflows", () => {
|
||||
const defaultLane = body.workflows.find((w) => w.id === DEFAULT_LANE);
|
||||
expect(Array.isArray(defaultLane?.columns)).toBe(true);
|
||||
expect((defaultLane?.columns.length ?? 0)).toBeGreaterThan(0);
|
||||
const inReview = defaultLane?.columns.find((column) => column.id === "in-review");
|
||||
expect(inReview?.flags.mergeBlocker).toBe(true);
|
||||
expect(inReview?.flags.humanReview).toBe(true);
|
||||
});
|
||||
|
||||
it("payload contract (flag ON): mixed default + custom ids → full taskWorkflowIds + deduped workflows", async () => {
|
||||
@@ -173,7 +180,8 @@ describe("GET /tasks/board-workflows", () => {
|
||||
});
|
||||
const ids = payload.workflows.map((w) => w.id);
|
||||
expect(new Set(ids).size).toBe(ids.length); // deduped
|
||||
expect(ids.sort()).toEqual([DEFAULT_LANE, custom.id].sort());
|
||||
expect(ids).toContain(DEFAULT_LANE);
|
||||
expect(ids).toContain(custom.id);
|
||||
const customLane = payload.workflows.find((w) => w.id === custom.id);
|
||||
expect(customLane?.name).toBe("Custom");
|
||||
expect((customLane?.columns.length ?? 0)).toBeGreaterThan(0);
|
||||
|
||||
@@ -82,6 +82,10 @@ describe("buildBoardWorkflowsPayload", () => {
|
||||
"Done",
|
||||
"Archived",
|
||||
]);
|
||||
const inReview = defaultWf!.columns.find((c) => c.id === "in-review");
|
||||
expect(inReview).toBeDefined();
|
||||
expect(inReview!.flags.mergeBlocker).toBe(true);
|
||||
expect(inReview!.flags.humanReview).toBe(true);
|
||||
});
|
||||
|
||||
it("includes user-defined workflows even when no visible task references them", async () => {
|
||||
|
||||
Reference in New Issue
Block a user