Files
fusion/packages/dashboard/app/components/workflow-setting-display.ts
gsxdsm 7e74fe3a9b FN-7293: honor workflow review revision overrides
Allow workflow review steps to opt into unbounded or capped revision retries while keeping recovery caps accurate.

- Add workflow settings for Plan Review and Code Review max revisions with display, docs, and changeset coverage.
- Resolve optional-step revision budgets through workflow settings, node overrides, and existing fallback limits.
- Track revision attempts per workflow step and hydrate slim self-healing rows before enforcing retry caps.
- Cover unbounded, capped, per-step, and slim-row recovery behavior with focused tests.

Files changed:
 .changeset/fn-7293-unbounded-review-revisions.md   |   7 +
 docs/settings-reference.md                         |  12 +-
 docs/workflow-editor.md                            |   4 +-
 docs/workflow-steps.md                             |   3 +-
 .../builtin-workflow-settings-triage.test.ts       |  28 +++-
 .../core/src/__tests__/builtin-workflows.test.ts   |  29 ++++-
 .../__tests__/workflow-settings-resolver.test.ts   |  73 +++++++++++
 packages/core/src/builtin-plan-review-group.ts     |   6 +-
 packages/core/src/builtin-workflow-settings.ts     |  30 +++++
 packages/core/src/index.ts                         |   5 +
 packages/core/src/workflow-ir-types.ts             |  15 ++-
 packages/core/src/workflow-ir.ts                   |   5 +-
 packages/core/src/workflow-settings-resolver.ts    |  49 ++++++-
 .../__tests__/WorkflowSettingsPanel.test.tsx       |  52 ++++++++
 .../app/components/workflow-setting-display.ts     |  18 +++
 packages/engine/src/__tests__/self-healing.test.ts | 143 ++++++++++++++++++++-
 .../workflow-graph-optional-step-fix.test.ts       | 130 ++++++++++++++++++-
 packages/engine/src/executor.ts                    |  77 +++++++++--
 packages/engine/src/self-healing.ts                |  82 ++++++++++--
 packages/engine/src/workflow-graph-executor.ts     |   5 +-
 20 files changed, 725 insertions(+), 48 deletions(-)

Fusion-Task-Id: FN-7293

Fusion-Task-Lineage: 19ee533f-ab8d-4e20-9a27-9cf0cf6dfec9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-06-30 13:12:27 -07:00

178 lines
5.3 KiB
TypeScript

import type { WorkflowSettingDefinition } from "../api";
export type WorkflowSettingGroup = "models" | "review" | "steps" | "advanced";
export interface WorkflowSettingDisplay {
group: WorkflowSettingGroup;
label: string;
description?: string;
}
/*
FNXC:WorkflowSettings 2026-06-17-09:13:
Title summarization is project/global-scoped and configured in Project Models, so this workflow display map intentionally omits titleSummarizer* keys. If a custom workflow declares a same-named key, render it as Advanced instead of implying Fusion will execute it as a workflow model lane.
*/
const DISPLAY: Record<string, WorkflowSettingDisplay> = {
planningProvider: {
group: "models",
label: "Plan/Triage provider",
description: "Provider used when planning or triaging tasks.",
},
planningModelId: {
group: "models",
label: "Plan/Triage model",
description: "Model used when planning or triaging tasks.",
},
planningFallbackProvider: {
group: "models",
label: "Plan/Triage fallback provider",
},
planningFallbackModelId: {
group: "models",
label: "Plan/Triage fallback model",
},
executionProvider: {
group: "models",
label: "Executor provider",
description: "Provider used by task implementation agents.",
},
executionModelId: {
group: "models",
label: "Executor model",
description: "Model used by task implementation agents.",
},
validatorProvider: {
group: "models",
label: "Reviewer provider",
description: "Provider used by review and validation agents.",
},
validatorModelId: {
group: "models",
label: "Reviewer model",
description: "Model used by review and validation agents.",
},
validatorFallbackProvider: {
group: "models",
label: "Reviewer fallback provider",
},
validatorFallbackModelId: {
group: "models",
label: "Reviewer fallback model",
},
requirePrApproval: {
group: "review",
label: "Require PR approval",
},
requirePlanApproval: {
group: "review",
label: "Require plan approval",
},
reviewHandoffPolicy: {
group: "review",
label: "Review handoff policy",
},
maxReviewerContextRetries: {
group: "review",
label: "Reviewer context retries",
},
maxReviewerFallbackRetries: {
group: "review",
label: "Reviewer fallback retries",
},
reflectionEnabled: {
group: "review",
label: "Reflection enabled",
},
planReviewMaxRevisions: {
group: "review",
label: "Plan Review revision cap",
/*
* FNXC:WorkflowRevisionBudget 2026-06-30-19:47:
* The Values tab must tell operators that an empty built-in workflow value is intentionally unbounded, not the old three-pass Plan Review default. Numeric values cap retries and `0` turns off automatic revision.
*/
description: "Leave empty for unbounded automatic Plan Review/spec revision; set 0 to disable automatic revision.",
},
codeReviewMaxRevisions: {
group: "review",
label: "Code Review revision cap",
/*
* FNXC:WorkflowRevisionBudget 2026-06-30-19:47:
* Built-in workflow values are editable even when definitions are read-only, so this label is the operator-facing cap for Code Review remediation without requiring a workflow duplicate.
*/
description: "Leave empty for unbounded automatic Code Review remediation; set 0 to disable automatic revision.",
},
workflowStepTimeoutMs: {
group: "steps",
label: "Step timeout",
},
workflowStepScopeEnforcement: {
group: "steps",
label: "Step scope enforcement",
},
planOnlyScopeLeakEnforcement: {
group: "steps",
label: "Plan-only scope leak enforcement",
},
workflowRevisionForkOnScopeMismatch: {
group: "steps",
label: "Fork revision on scope mismatch",
},
strictScopeEnforcement: {
group: "steps",
label: "Strict scope enforcement",
},
runStepsInNewSessions: {
group: "steps",
label: "Run steps in new sessions",
},
maxParallelSteps: {
group: "steps",
label: "Max parallel steps",
},
buildRetryCount: {
group: "steps",
label: "Build retry count",
},
verificationFixRetries: {
group: "steps",
label: "Verification fix retries",
},
maxPostReviewFixes: {
group: "steps",
label: "Post-review fix passes",
},
};
export const WORKFLOW_SETTING_GROUP_ORDER: WorkflowSettingGroup[] = [
"models",
"review",
"steps",
"advanced",
];
export const WORKFLOW_SETTING_GROUP_LABELS: Record<WorkflowSettingGroup, string> = {
models: "Models",
review: "Review & Approval",
steps: "Step Execution",
advanced: "Advanced",
};
export function getWorkflowSettingDisplay(setting: WorkflowSettingDefinition): WorkflowSettingDisplay {
return DISPLAY[setting.id] ?? { group: "advanced", label: setting.name, description: setting.description };
}
export function groupWorkflowSettings(
settings: WorkflowSettingDefinition[],
): Array<{ group: WorkflowSettingGroup; settings: WorkflowSettingDefinition[] }> {
const byGroup = new Map<WorkflowSettingGroup, WorkflowSettingDefinition[]>();
for (const setting of settings) {
const group = getWorkflowSettingDisplay(setting).group;
const list = byGroup.get(group) ?? [];
list.push(setting);
byGroup.set(group, list);
}
return WORKFLOW_SETTING_GROUP_ORDER
.map((group) => ({ group, settings: byGroup.get(group) ?? [] }))
.filter((entry) => entry.settings.length > 0);
}