Files
fusion/packages/dashboard/app/components/workflow-setting-display.ts
Phil Larson b85a5d4531 fix(core): bound compound engineering review remediation (#2532)
## Summary

- cap Compound Engineering Code Review remediation at two Execute→Review
repair passes
- enable no-progress detection for the built-in CE workflow
- preserve explicit project/workflow overrides while making the authored
CE default visible in settings and docs
- update stale IR/changeset language that still described Code Review as
unbounded when unset

## Why

The previous CE default was effectively unbounded. A reviewer that
repeatedly returned `REVISE` could consume thousands of remediation
cycles without terminally parking the task. The built-in workflow should
fail closed after a small, explicit budget while still allowing
operators to author a different numeric cap.

## Verification

- `FUSION_PG_TEST_SKIP=1 corepack pnpm@10.33.0 --filter @fusion/core
exec vitest run src/__tests__/builtin-workflows.test.ts` — 46 passed, 17
skipped
- `corepack pnpm@10.33.0 --filter @fusion/core typecheck`
- `corepack pnpm@10.33.0 --filter @fusion/dashboard exec vitest run
app/components/__tests__/WorkflowSettingsPanel.test.tsx
app/components/__tests__/workflow-setting-display.test.ts` — 33 passed
- `corepack pnpm@10.33.0 --filter @fusion/dashboard typecheck`
- `corepack pnpm@10.33.0 changeset status --since=origin/main`
- `git diff --check origin/main...HEAD`


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Improvements**
- Compound Engineering Code Review now caps remediation attempts at 2;
after two unsuccessful attempts, the process parks instead of retrying
indefinitely.
- Post-restart review recovery now completes in a single maintenance
cycle to reduce delays.
  - Default post-review fix budget increased from 3 to 10.
- Review revision limits now consistently honor workflow-authored
defaults when settings are left empty, and `0` disables automatic
remediation.

- **Documentation**
- Updated the workflow editor, settings reference, workflow steps, and
operator panel text to clarify cap/default/disable semantics (including
CE: 2).

- **Tests**
- Added/updated unit tests to validate the new bounded remediation
behavior and messaging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-29 00:05:29 -07:00

226 lines
7.7 KiB
TypeScript

import type { WorkflowSettingDefinition } from "../api";
export type WorkflowSettingGroup = "models" | "review" | "oversight" | "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.",
},
/*
* FNXC:SettingsModels 2026-07-16-00:00:
* FN-8169 requires the Executor fallback to remain in the Models group so the shared lane catalog can render it immediately after Executor. Without these existing key classifications, pair filtering suppresses the fallback row entirely.
*/
executionFallbackProvider: {
group: "models",
label: "Executor fallback provider",
},
executionFallbackModelId: {
group: "models",
label: "Executor fallback model",
},
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 override for Code Review remediation without requiring a workflow duplicate. Empty values preserve each workflow's authored node cap.
*/
description: "Leave empty to use this workflow's authored Code Review default; set 0 to disable automatic revision.",
},
planReviewReplanCap: {
group: "review",
label: "Plan Review replan cap",
/*
* FNXC:WorkflowRevisionBudget 2026-07-15-12:00:
* FN-7985 exposes triage's separate consecutive Plan Review REVISE ceiling here. An
* empty value intentionally delegates to the engine constant rather than duplicating
* its default in the editable workflow setting.
*/
description: "Leave empty to use the built-in Plan Review replan default; set 0 to require approval after the first REVISE.",
},
triageProactiveSubtaskSplittingEnabled: {
group: "steps",
label: "Automatic large-task splitting",
/*
* FNXC:TriagePolicy 2026-07-04-00:00:
* Workflow Settings is the canonical operator surface for this workflow/project policy. The copy must make the default enabled state clear and preserve trust that explicit `breakIntoSubtasks: true` requests still split even when automatic large-task splitting is off.
*/
description:
"Default enabled. When off, triage keeps oversized tasks whole unless breakIntoSubtasks: true is explicitly requested.",
},
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",
},
/*
* FNXC:PlannerOversight 2026-07-04-00:00:
* First-class Values-tab display entry for the workflow-native plannerOversightLevel
* setting (FN-7508). This is the project/global default surface: the default
* workflow's stored value here is the effective oversight level for every task
* under it that does not set a per-task override (FN-7515, TaskForm selector).
*/
plannerOversightLevel: {
group: "oversight",
label: "Planner oversight level",
description:
"Off disables oversight; Observe watches only; Steer injects guidance or suggests revisions; Autonomous recovery enables bounded retry and targeted-fix recovery. Per-task overrides are set on individual tasks and win over this project/global default.",
},
};
export const WORKFLOW_SETTING_GROUP_ORDER: WorkflowSettingGroup[] = [
"models",
"review",
"oversight",
"steps",
"advanced",
];
export const WORKFLOW_SETTING_GROUP_LABELS: Record<WorkflowSettingGroup, string> = {
models: "Models",
review: "Review & Approval",
oversight: "Planner Oversight",
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);
}