diff --git a/packages/core/src/builtin-stepwise-coding-workflow-ir.ts b/packages/core/src/builtin-stepwise-coding-workflow-ir.ts index e08bf67091..348125fd5a 100644 --- a/packages/core/src/builtin-stepwise-coding-workflow-ir.ts +++ b/packages/core/src/builtin-stepwise-coding-workflow-ir.ts @@ -123,8 +123,12 @@ const RAW_BUILTIN_STEPWISE_CODING_WORKFLOW_IR: WorkflowIr = { }, // KTD-5: rework exhaustion escalates to a manual hold (a human releases it). { id: "rework-hold", kind: "hold", column: "in-progress", config: { release: "manual" } }, - // Pre-merge workflow-step seam (parity with builtin-coding-workflow-ir): the - // ONLY node that makes the graph invoke `runWorkflowSteps`, so a per-task + // FNXC:WorkflowOptionalSteps 2026-06-21-00:00: + // The stepwise workflow must actually run a task's enabled optional steps (e.g. + // browser verification), so it needs the same pre-merge workflow-step seam the + // coding workflow has — declaring the optional step without this node would be a + // dead toggle. Pre-merge workflow-step seam (parity with builtin-coding-workflow-ir): + // the ONLY node that makes the graph invoke `runWorkflowSteps`, so a per-task // `enabledWorkflowSteps` (e.g. the optional browser-verification step declared // below) actually executes. Runs ONCE after the foreach completes, between // implementation and review — not per step-instance. diff --git a/packages/dashboard/app/components/TaskForm.tsx b/packages/dashboard/app/components/TaskForm.tsx index 1c73dd8ec8..d72803b3f6 100644 --- a/packages/dashboard/app/components/TaskForm.tsx +++ b/packages/dashboard/app/components/TaskForm.tsx @@ -295,6 +295,10 @@ export function TaskForm({ .catch(() => setGlobalSettings(null)); }, [isActive, projectId, onWorkflowIdChange]); + // FNXC:WorkflowOptionalSteps 2026-06-21-00:00: + // Creating a task should let the user opt into the selected workflow's optional + // steps, seeded from each step's defaultOn. TaskForm fetches + seeds these in + // create mode and lifts the enabled set to NewTaskModal for the create payload. // Optional workflow steps for the currently-selected workflow (create mode only). // `null` selection ("No workflow") → no steps; `undefined` → project default. const effectiveOptionalWorkflowId = diff --git a/packages/dashboard/app/components/workflow-flow-mapping.ts b/packages/dashboard/app/components/workflow-flow-mapping.ts index 4881e01514..cea334ada6 100644 --- a/packages/dashboard/app/components/workflow-flow-mapping.ts +++ b/packages/dashboard/app/components/workflow-flow-mapping.ts @@ -456,9 +456,12 @@ export function flowToIr( const hasFields = Array.isArray(fields) && fields.length > 0; const hasSettings = Array.isArray(settings) && settings.length > 0; const hasOptionalSteps = Array.isArray(optionalSteps) && optionalSteps.length > 0; - // Fields, settings, and optional steps are v2-only declarations: a workflow with - // any of them but no custom columns still serializes as v2 (with the synthesized - // default columns). Empty/absent → not a v2 signal (R6 byte-identity). + // FNXC:WorkflowOptionalSteps 2026-06-21-00:00: + // Optional steps must round-trip through the node editor without data loss, yet + // must never upgrade a legacy v1 graph. Fields, settings, and optional steps are + // v2-only declarations: a workflow with any of them but no custom columns still + // serializes as v2 (with the synthesized default columns). Empty/absent → not a + // v2 signal, and the key is omitted entirely (R6 byte-identity for legacy graphs). const v2 = (Array.isArray(columns) && columns.length > 0) || hasFields || hasSettings || hasOptionalSteps; const layout: Record = {};