feat(FN-4468): add workflow step gateMode advisory gating
Adds an "advisory" `gateMode` option for workflow steps: steps in advisory mode log findings but no longer block merge completion, while steps that must block can still be configured as blocking. The change covers the workflow step types and schema, executor gating logic, dashboard UI in WorkflowSte Fusion-Task-Id: FN-4468
This commit is contained in:
@@ -2133,6 +2133,7 @@ describe("Workflow Steps Execution", () => {
|
||||
name: "Security Audit",
|
||||
description: "Check for vulnerabilities",
|
||||
prompt: "Scan for security issues.",
|
||||
gateMode: "gate",
|
||||
enabled: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
@@ -2256,6 +2257,7 @@ describe("Workflow Steps Execution", () => {
|
||||
name: "Security Audit",
|
||||
description: "Check for vulnerabilities",
|
||||
prompt: "Scan for security issues.",
|
||||
gateMode: "gate",
|
||||
enabled: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
@@ -2353,6 +2355,7 @@ describe("Workflow Steps Execution", () => {
|
||||
name: "Security Audit",
|
||||
description: "Check for vulnerabilities",
|
||||
prompt: "Scan for security issues.",
|
||||
gateMode: "gate",
|
||||
enabled: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
@@ -2457,6 +2460,7 @@ describe("Workflow Steps Execution", () => {
|
||||
name: "Security Audit",
|
||||
description: "Check for vulnerabilities",
|
||||
prompt: "Scan for security issues.",
|
||||
gateMode: "gate",
|
||||
enabled: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
|
||||
@@ -24,7 +24,9 @@ function createWorkflowStep(overrides: Record<string, unknown> = {}) {
|
||||
id: "frontend-ux-design",
|
||||
name: "Frontend UX Design",
|
||||
description: "UI review",
|
||||
mode: "prompt",
|
||||
prompt: "Review UI",
|
||||
gateMode: "gate",
|
||||
enabled: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
|
||||
@@ -6049,6 +6049,7 @@ ${failureFeedback}
|
||||
|
||||
// Normalize legacy steps without mode to prompt-mode
|
||||
const stepMode: "prompt" | "script" = ws.mode || "prompt";
|
||||
const gateMode: "gate" | "advisory" = ws.gateMode || (stepMode === "script" ? "gate" : "advisory");
|
||||
|
||||
// Skip validation per mode
|
||||
if (stepMode === "prompt" && !ws.prompt?.trim()) {
|
||||
@@ -6179,12 +6180,17 @@ ${failureFeedback}
|
||||
if (existingIdx >= 0) {
|
||||
results[existingIdx] = {
|
||||
...results[existingIdx],
|
||||
status: "failed",
|
||||
status: gateMode === "advisory" ? "advisory_failure" : "failed",
|
||||
output: scopeLeakMessage,
|
||||
completedAt,
|
||||
};
|
||||
}
|
||||
await this.store.updateTask(task.id, { workflowStepResults: results });
|
||||
if (gateMode === "advisory") {
|
||||
await this.store.updateTask(task.id, { status: "advisory_failure" });
|
||||
await this.store.logEntry(task.id, `[pre-merge] Advisory workflow step scope warning: ${ws.name}`);
|
||||
continue;
|
||||
}
|
||||
return {
|
||||
allPassed: false,
|
||||
revisionRequested: true,
|
||||
@@ -6224,12 +6230,16 @@ ${failureFeedback}
|
||||
if (existingIdx >= 0) {
|
||||
results[existingIdx] = {
|
||||
...results[existingIdx],
|
||||
status: "failed",
|
||||
status: gateMode === "advisory" ? "advisory_failure" : "failed",
|
||||
output: result.output || "Revision requested",
|
||||
completedAt,
|
||||
};
|
||||
}
|
||||
await this.store.updateTask(task.id, { workflowStepResults: results });
|
||||
if (gateMode === "advisory") {
|
||||
await this.store.logEntry(task.id, `[pre-merge] Advisory workflow step failed: ${ws.name}`);
|
||||
continue;
|
||||
}
|
||||
return {
|
||||
allPassed: false,
|
||||
revisionRequested: true,
|
||||
@@ -6250,12 +6260,17 @@ ${failureFeedback}
|
||||
if (existingIdx >= 0) {
|
||||
results[existingIdx] = {
|
||||
...results[existingIdx],
|
||||
status: "failed",
|
||||
status: gateMode === "advisory" ? "advisory_failure" : "failed",
|
||||
output: result.error || "Workflow step failed",
|
||||
completedAt,
|
||||
};
|
||||
}
|
||||
await this.store.updateTask(task.id, { workflowStepResults: results });
|
||||
if (gateMode === "advisory") {
|
||||
await this.store.updateTask(task.id, { status: "advisory_failure" });
|
||||
await this.store.logEntry(task.id, `[pre-merge] Advisory workflow step failed: ${ws.name}`);
|
||||
continue;
|
||||
}
|
||||
return {
|
||||
allPassed: false,
|
||||
revisionRequested: false,
|
||||
@@ -6280,12 +6295,17 @@ ${failureFeedback}
|
||||
if (existingIdx >= 0) {
|
||||
results[existingIdx] = {
|
||||
...results[existingIdx],
|
||||
status: "failed",
|
||||
status: gateMode === "advisory" ? "advisory_failure" : "failed",
|
||||
output: errorMessage || "Workflow step error",
|
||||
completedAt,
|
||||
};
|
||||
}
|
||||
await this.store.updateTask(task.id, { workflowStepResults: results });
|
||||
if (gateMode === "advisory") {
|
||||
await this.store.updateTask(task.id, { status: "advisory_failure" });
|
||||
await this.store.logEntry(task.id, `[pre-merge] Advisory workflow step error: ${ws.name}`);
|
||||
continue;
|
||||
}
|
||||
return {
|
||||
allPassed: false,
|
||||
revisionRequested: false,
|
||||
|
||||
Reference in New Issue
Block a user