FN-7986: raise plan review replan cap to 8

Allow more automatic Plan Review revisions before escalating tasks for human approval.

- Raise the consecutive REVISE replan cap from 3 to 8.
- Cover the seven- and eight-revision boundaries in triage tests.
- Add a patch changeset describing the revised default.

Files changed:
 .changeset/fn-7986-plan-review-cap.md              |  7 +++++++
 .../triage-plan-review-replan-cap.test.ts          | 23 +++++++++++++---------
 packages/engine/src/triage.ts                      |  8 ++++----
 3 files changed, 25 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-7986

Fusion-Task-Lineage: 3b61f333-be9a-414a-bd27-aabdbb45caa0

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-15 19:57:34 -07:00
parent d4553be678
commit 0b332816d5
3 changed files with 25 additions and 13 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Plan Review now allows more automatic replan attempts (default 8) before asking a human.
category: internal
dev: Raised triage PLAN_REVIEW_GATE_REPLAN_CAP from 3 to 8 in packages/engine/src/triage.ts; escalation to awaiting-approval (awaitingApprovalReason "plan-review-replan-cap") now fires at 8 consecutive REVISE replans.

View File

@@ -3,7 +3,7 @@ import type { Settings, Task, TaskStore } from "@fusion/core";
import { join } from "node:path";
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { PLAN_REVIEW_GATE_REPLAN_CAP, TriageProcessor } from "../triage.js";
import { TriageProcessor } from "../triage.js";
/*
* Bug A (part 2): the triage pre-execution Plan Review gate must bound consecutive
@@ -142,11 +142,11 @@ describe("Plan Review replan cap", () => {
it("escalates to awaiting-approval instead of replanning once the cap is reached", async () => {
const rootDir = await createFixtureRoot();
roots.push(rootDir);
// Cap is PLAN_REVIEW_GATE_REPLAN_CAP (8): a task that has already consumed that many
// consecutive REVISE replans must escalate on the next REVISE rather than replanning again.
// Cap is 8: a task that has already consumed that many consecutive REVISE replans must
// escalate on the next REVISE rather than replanning again.
const task = createRetryTask({
id: "FN-REPLAN-CAP-HIT",
planReviewReplanCount: PLAN_REVIEW_GATE_REPLAN_CAP,
planReviewReplanCount: 8,
});
const prompt = `# Task: ${task.id} - Existing draft\n\n## Mission\n\nOnly rewrite after reviewer feedback.\n`;
await writePrompt(rootDir, task.id, prompt);
@@ -171,17 +171,22 @@ describe("Plan Review replan cap", () => {
expect(store.logEntry).toHaveBeenCalledWith(
task.id,
"Plan Review replan cap reached — escalating to manual approval",
expect.stringContaining(`cap ${PLAN_REVIEW_GATE_REPLAN_CAP}`),
expect.stringContaining("cap 8"),
);
});
it("still replans when one attempt remains under the cap", async () => {
it("still replans from seven consecutive REVISE verdicts", async () => {
const rootDir = await createFixtureRoot();
roots.push(rootDir);
const priorCount = PLAN_REVIEW_GATE_REPLAN_CAP - 1;
/*
FNXC:PlanReviewReplan 2026-07-15-11:30:
Keep this boundary literal rather than deriving it from the production constant. FN-7986
requires proof that the default is truly 8: with the former cap of 3, seven prior REVISE
verdicts would escalate instead of returning the task to `needs-replan` at count 8.
*/
const task = createRetryTask({
id: "FN-REPLAN-CAP-LAST",
planReviewReplanCount: priorCount,
planReviewReplanCount: 7,
});
const prompt = `# Task: ${task.id} - Existing draft\n\n## Mission\n\nOnly rewrite after reviewer feedback.\n`;
await writePrompt(rootDir, task.id, prompt);
@@ -192,7 +197,7 @@ describe("Plan Review replan cap", () => {
expect(store.updateTask).toHaveBeenCalledWith(task.id, expect.objectContaining({
status: "needs-replan",
planReviewReplanCount: PLAN_REVIEW_GATE_REPLAN_CAP,
planReviewReplanCount: 8,
}));
expect(store.updateTask).not.toHaveBeenCalledWith(task.id, expect.objectContaining({
status: "awaiting-approval",

View File

@@ -61,10 +61,10 @@ The triage pre-execution Plan Review gate (runPlanReviewBeforeExecution) routes
verdict back to `needs-replan`, which re-plans and re-reviews. Without a ceiling, a planner
and reviewer that persistently disagree loop plan → Plan Review REVISE → replan forever
(observed on TC-002), and in `planApprovalMode: require-all` there is no human escape because
the task never reaches `awaiting-approval`. Bound the consecutive REVISE replans with a small
cap (mirroring the executor graph's PLAN_REVIEW_REPLAN_HARD_CAP backstop): after this many
replans the gate escalates the task to `awaiting-approval` for a human decision instead of
replanning again. The counter (Task.planReviewReplanCount) resets when the gate passes.
the task never reaches `awaiting-approval`. Bound the consecutive REVISE replans with a
cap (default 8, mirroring the executor graph's PLAN_REVIEW_REPLAN_HARD_CAP backstop): after
this many replans the gate escalates the task to `awaiting-approval` for a human decision
instead of replanning again. The counter (Task.planReviewReplanCount) resets when the gate passes.
FNXC:PlanReviewReplan 2026-07-15-11:09:
Raise the automatic REVISE replan ceiling from 3 to 8 so planner/reviewer pairs get more