diff --git a/.changeset/review-column-auto-merge.md b/.changeset/review-column-auto-merge.md new file mode 100644 index 0000000000..8fdb2f2021 --- /dev/null +++ b/.changeset/review-column-auto-merge.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": minor +--- + +Allow built-in workflow review columns to surface the auto-merge toggle. diff --git a/packages/core/src/__tests__/builtin-coding-workflow-ir.test.ts b/packages/core/src/__tests__/builtin-coding-workflow-ir.test.ts index 977739e070..8ad04711b9 100644 --- a/packages/core/src/__tests__/builtin-coding-workflow-ir.test.ts +++ b/packages/core/src/__tests__/builtin-coding-workflow-ir.test.ts @@ -44,7 +44,7 @@ describe("builtin coding workflow ir", () => { expect(traitsFor("triage")).toEqual(["intake"]); expect(traitsFor("todo")).toEqual(["hold", "reset-on-entry"]); expect(traitsFor("in-progress")).toEqual(["wip", "abort-on-exit", "timing"]); - expect(traitsFor("in-review")).toEqual(["merge-blocker", "stall-detection", "merge"]); + expect(traitsFor("in-review")).toEqual(["merge-blocker", "human-review", "stall-detection", "merge"]); expect(traitsFor("done")).toEqual(["complete"]); expect(traitsFor("archived")).toEqual(["archived"]); // todo's hold is capacity-released (legacy "pull from todo when a slot frees"). diff --git a/packages/core/src/__tests__/builtin-traits.test.ts b/packages/core/src/__tests__/builtin-traits.test.ts index 10f0e76527..5938073064 100644 --- a/packages/core/src/__tests__/builtin-traits.test.ts +++ b/packages/core/src/__tests__/builtin-traits.test.ts @@ -6,6 +6,7 @@ import { } from "../builtin-traits.js"; import { TraitRegistry } from "../trait-registry.js"; import { BUILTIN_CODING_WORKFLOW_IR } from "../builtin-coding-workflow-ir.js"; +import { BUILTIN_STEPWISE_CODING_WORKFLOW_IR } from "../builtin-stepwise-coding-workflow-ir.js"; import type { WorkflowIrV2 } from "../workflow-ir-types.js"; function freshRegistry(): TraitRegistry { @@ -107,4 +108,24 @@ describe("default workflow columns validate cleanly", () => { expect(flags.abortOnExit).toBe(true); expect(flags.timing).toBe(true); }); + + it("the default workflow's in-review column resolves review and merge flags", () => { + const r = freshRegistry(); + const ir = BUILTIN_CODING_WORKFLOW_IR as WorkflowIrV2; + const inReview = ir.columns.find((c) => c.id === "in-review")!; + const flags = r.resolveColumnFlags(inReview); + expect(flags.mergeBlocker).toBe(true); + expect(flags.humanReview).toBe(true); + expect(flags.mergeOrchestration).toBe(true); + }); + + it("the stepwise workflow's in-review column resolves review and merge flags", () => { + const r = freshRegistry(); + const ir = BUILTIN_STEPWISE_CODING_WORKFLOW_IR as WorkflowIrV2; + const inReview = ir.columns.find((c) => c.id === "in-review")!; + const flags = r.resolveColumnFlags(inReview); + expect(flags.mergeBlocker).toBe(true); + expect(flags.humanReview).toBe(true); + expect(flags.mergeOrchestration).toBe(true); + }); }); diff --git a/packages/core/src/builtin-coding-workflow-ir.ts b/packages/core/src/builtin-coding-workflow-ir.ts index 3053add866..2567fcb115 100644 --- a/packages/core/src/builtin-coding-workflow-ir.ts +++ b/packages/core/src/builtin-coding-workflow-ir.ts @@ -14,7 +14,7 @@ import { builtinPromptConfig } from "./builtin-workflow-prompts.js"; * triage = intake * todo = hold(capacity) + reset-on-entry * in-progress = wip + abort-on-exit + timing - * in-review = merge-blocker + stall-detection + merge + * in-review = merge-blocker + human-review + stall-detection + merge * done = complete * archived = archived * @@ -40,7 +40,7 @@ const RAW_BUILTIN_CODING_WORKFLOW_IR: WorkflowIr = { { id: "in-review", name: "In review", - traits: [{ trait: "merge-blocker" }, { trait: "stall-detection" }, { trait: "merge" }], + traits: [{ trait: "merge-blocker" }, { trait: "human-review" }, { trait: "stall-detection" }, { trait: "merge" }], }, { id: "done", name: "Done", traits: [{ trait: "complete" }] }, { id: "archived", name: "Archived", traits: [{ trait: "archived" }] }, diff --git a/packages/core/src/builtin-stepwise-coding-workflow-ir.ts b/packages/core/src/builtin-stepwise-coding-workflow-ir.ts index e6e5419d86..13d3c2a33f 100644 --- a/packages/core/src/builtin-stepwise-coding-workflow-ir.ts +++ b/packages/core/src/builtin-stepwise-coding-workflow-ir.ts @@ -27,8 +27,9 @@ import { builtinPromptConfig } from "./builtin-workflow-prompts.js"; * → merge seam * * The columns/traits are identical to the default builtin so the full lifecycle - * (merge-blocker, capacity, hold, complete, archived) behaves exactly as it does - * for the default workflow — only the in-progress step modeling differs. + * (merge-blocker, human review, capacity, hold, complete, archived) behaves + * exactly as it does for the default workflow — only the in-progress step + * modeling differs. * * It declares its step-source artifact (KTD-12): PROMPT.md produced by the * planning seam. The IR is v2-only (foreach/step-review/parse-steps are v2 node @@ -53,7 +54,7 @@ const RAW_BUILTIN_STEPWISE_CODING_WORKFLOW_IR: WorkflowIr = { { id: "in-review", name: "In review", - traits: [{ trait: "merge-blocker" }, { trait: "stall-detection" }, { trait: "merge" }], + traits: [{ trait: "merge-blocker" }, { trait: "human-review" }, { trait: "stall-detection" }, { trait: "merge" }], }, { id: "done", name: "Done", traits: [{ trait: "complete" }] }, { id: "archived", name: "Archived", traits: [{ trait: "archived" }] }, diff --git a/packages/dashboard/app/components/Lane.tsx b/packages/dashboard/app/components/Lane.tsx index b89507ddb2..4b912e1171 100644 --- a/packages/dashboard/app/components/Lane.tsx +++ b/packages/dashboard/app/components/Lane.tsx @@ -205,7 +205,7 @@ function LaneComponent(props: LaneProps) { prAuthAvailable={props.prAuthAvailable} autoMerge={props.autoMerge} {...(isCreateColumn ? { onQuickCreate: props.onQuickCreate, onNewTask: props.onNewTask, onPlanningMode: props.onPlanningMode, onSubtaskBreakdown: props.onSubtaskBreakdown } : {})} - {...(col.flags.mergeBlocker ? { onToggleAutoMerge: props.onToggleAutoMerge } : {})} + {...((col.flags.mergeBlocker || col.flags.humanReview) && props.onToggleAutoMerge ? { onToggleAutoMerge: props.onToggleAutoMerge } : {})} /> ); })} diff --git a/packages/dashboard/app/components/__tests__/Lane.test.tsx b/packages/dashboard/app/components/__tests__/Lane.test.tsx index 8791f38c55..cf9601b7e9 100644 --- a/packages/dashboard/app/components/__tests__/Lane.test.tsx +++ b/packages/dashboard/app/components/__tests__/Lane.test.tsx @@ -33,6 +33,7 @@ const WORKFLOW: BoardWorkflowDefinition = { { id: "triage", name: "Triage", flags: { intake: true } }, { id: "todo", name: "Todo", flags: { hold: true } }, { id: "in-progress", name: "In progress", flags: { countsTowardWip: true } }, + { id: "in-review", name: "In review", flags: { humanReview: true } }, { id: "done", name: "Done", flags: { complete: true } }, { id: "archived", name: "Archived", flags: { archived: true } }, ], @@ -86,6 +87,7 @@ describe("Lane", () => { expect(headings).toContain("Triage"); expect(headings).toContain("Todo"); expect(headings).toContain("In progress"); + expect(headings).toContain("In review"); expect(headings).toContain("Done"); // Archived column is hidden. expect(headings).not.toContain("Archived"); @@ -112,6 +114,11 @@ describe("Lane", () => { expect(props.onToggleCollapse).toHaveBeenCalledWith("builtin:coding"); }); + it("shows the auto-merge toggle for human-review workflow columns", () => { + render(); + expect(screen.getByText("Auto-merge")).toBeDefined(); + }); + it("shows a Promote button on hold-column cards and calls onPromote", async () => { const props = baseProps(); render();