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:
Fusion
2026-05-14 05:30:13 -07:00
committed by gsxdsm
parent d318f08eb6
commit 95d742abb3
23 changed files with 213 additions and 42 deletions

View File

@@ -2659,7 +2659,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
router.post("/workflow-steps", async (req, res) => {
try {
const { store: scopedStore } = await getProjectContext(req);
const { name, description, mode, phase, prompt, toolMode, scriptName, enabled, defaultOn, modelProvider, modelId } = req.body;
const { name, description, mode, phase, prompt, gateMode, toolMode, scriptName, enabled, defaultOn, modelProvider, modelId } = req.body;
if (!name || typeof name !== "string" || !name.trim()) {
throw badRequest("name is required");
@@ -2682,6 +2682,9 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
if (prompt !== undefined && typeof prompt !== "string") {
throw badRequest("prompt must be a string");
}
if (gateMode !== undefined && gateMode !== "gate" && gateMode !== "advisory") {
throw badRequest("gateMode must be 'gate' or 'advisory'");
}
if (toolMode !== undefined && toolMode !== "readonly" && toolMode !== "coding") {
throw badRequest("toolMode must be 'readonly' or 'coding'");
}
@@ -2722,6 +2725,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
mode: resolvedMode,
phase,
prompt: prompt?.trim(),
gateMode,
toolMode,
scriptName: scriptName?.trim(),
enabled,
@@ -2748,7 +2752,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
router.patch("/workflow-steps/:id", async (req, res) => {
try {
const { store: scopedStore } = await getProjectContext(req);
const { name, description, mode, phase, prompt, toolMode, scriptName, enabled, defaultOn, modelProvider, modelId } = req.body;
const { name, description, mode, phase, prompt, gateMode, toolMode, scriptName, enabled, defaultOn, modelProvider, modelId } = req.body;
const updates: Record<string, unknown> = {};
if (name !== undefined) {
@@ -2781,6 +2785,12 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
}
updates.prompt = prompt;
}
if (gateMode !== undefined) {
if (gateMode !== "gate" && gateMode !== "advisory") {
throw badRequest("gateMode must be 'gate' or 'advisory'");
}
updates.gateMode = gateMode;
}
if (toolMode !== undefined) {
if (toolMode !== "readonly" && toolMode !== "coding") {
throw badRequest("toolMode must be 'readonly' or 'coding'");