From 4c0dfbcfd69d33e19c977d3362184aa5566dbd60 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Mon, 20 Jul 2026 15:26:23 -0700 Subject: [PATCH] fix(engine): preserve workflow completion summaries Keep approved-contract retry instructions scoped to review nodes so advisory and completion-summary agents can produce their intended output. --- .changeset/fix-completion-summary-contract.md | 7 ++++++ .../ce-workflow-step-executor.test.ts | 22 +++++++++++++++++++ packages/engine/src/executor.ts | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-completion-summary-contract.md diff --git a/.changeset/fix-completion-summary-contract.md b/.changeset/fix-completion-summary-contract.md new file mode 100644 index 0000000000..d864aeea6f --- /dev/null +++ b/.changeset/fix-completion-summary-contract.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Prevent review-contract retry instructions from replacing workflow completion summaries. +category: fix +dev: Limits approved PROMPT.md contract injection to review-type workflow nodes. diff --git a/packages/engine/src/__tests__/ce-workflow-step-executor.test.ts b/packages/engine/src/__tests__/ce-workflow-step-executor.test.ts index 288ceea239..aa1f6d7de7 100644 --- a/packages/engine/src/__tests__/ce-workflow-step-executor.test.ts +++ b/packages/engine/src/__tests__/ce-workflow-step-executor.test.ts @@ -955,6 +955,28 @@ Ship FIVE kinds. Do NOT add roadmap-item in this task. expect(cap.last?.systemPrompt).toContain("Return REVISE with the single reason that the approved contract could not be loaded"); }); + it.each([ + ["completion summary", { name: "Completion summary", summaryTarget: "task" }], + ["ordinary advisory prompt", { name: "Publish artifacts" }], + ])("does not inject review-contract instructions into the %s surface", async (_label, stepOverrides) => { + const store = createMockStore(); + const { executor } = makeExecutor(store); + const cap = captureSession("Finished the requested work and verified the affected behavior."); + const readTaskArtifact = vi.spyOn(executor as any, "readTaskArtifact"); + + await (executor as any).executeWorkflowStep( + baseStepTask(), + makeStep(stepOverrides), + "/tmp/wt", + {}, + ); + + expect(readTaskArtifact).not.toHaveBeenCalled(); + expect(cap.last?.systemPrompt).not.toContain("Approved Task Contract:"); + expect(cap.last?.systemPrompt).not.toContain("Approved Task Contract Unavailable:"); + expect(cap.last?.systemPrompt).not.toContain("canonical scope"); + }); + it("sets FUSION_HEADLESS=1 only when unattended=true; always sets FUSION_WORKFLOW_STEP", async () => { const store = createMockStore(); const { executor } = makeExecutor(store); diff --git a/packages/engine/src/executor.ts b/packages/engine/src/executor.ts index a0d7f7d38a..7e4fb991ea 100644 --- a/packages/engine/src/executor.ts +++ b/packages/engine/src/executor.ts @@ -16309,7 +16309,7 @@ ${scopeGuard} * unrelated local commits can make a plan-only gate reject implementation * state and loop back to triage after the planner already approved the spec. */ - const approvedContractBlock = !isPlanReviewStep + const approvedContractBlock = isReviewTypeWorkflowStep && !isPlanReviewStep ? workflowReviewSpecText ? `