Skip to content

fix: enforce workflow validation before deploy and run#3473

Open
MaxwellCalkin wants to merge 2 commits intosimstudioai:mainfrom
MaxwellCalkin:fix/enforce-workflow-validation-on-deploy
Open

fix: enforce workflow validation before deploy and run#3473
MaxwellCalkin wants to merge 2 commits intosimstudioai:mainfrom
MaxwellCalkin:fix/enforce-workflow-validation-on-deploy

Conversation

@MaxwellCalkin
Copy link

Summary

Fixes #3444

The deploy endpoint (/api/workflows/[id]/deploy) only validated schedule data but never checked workflow state integrity. Additionally:

  • The Run button had hasValidationErrors hardcoded to false, so it was never blocked by validation errors
  • Redeployments (clicking "Update" on an already-deployed workflow) skipped all pre-deploy checks entirely

Changes

  • Backend (deploy/route.ts): Call validateWorkflowState() before deploying to reject workflows with unknown block types, dangling edge references, or invalid tool references. Returns HTTP 400 with a detailed error summary.
  • Frontend (panel.tsx): Replace hasValidationErrors = false with a live check that blocks are connected via edges. The Run button is now disabled when blocks exist but have no connections.
  • Frontend (use-deployment.ts): Run runPreDeployChecks() for redeployments too (not just first deploys), so the "Update" button also validates required fields, schedule config, and serialization before opening the deploy modal.

How it works

  1. User adds unconfigured/disconnected blocks and clicks Deploy -> frontend pre-deploy checks catch it with a notification error
  2. Even if the frontend check is bypassed (e.g., API call), the backend now rejects the deploy with a 400 error detailing which blocks/edges are invalid
  3. The Run button is visually disabled when blocks exist but aren't connected together

This PR was authored by Claude Opus 4.6 (AI), operated by @MaxwellCalkin

The deploy endpoint only validated schedule data but never checked
workflow state (block types, edges, tool references). The Run button
had validation hardcoded to `false`. Redeployments (Update) skipped
all pre-deploy checks entirely.

Changes:
- Backend: call validateWorkflowState() before deploying to reject
  workflows with unknown block types, dangling edges, or invalid
  tool references (returns 400 with details)
- Frontend panel: replace hardcoded `hasValidationErrors = false`
  with a check that blocks are connected via edges
- Frontend deploy hook: run pre-deploy checks for redeployments too,
  not just first deploys

Fixes simstudioai#3444

This PR was authored by Claude Opus 4.6 (AI), operated by @MaxwellCalkin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cursor
Copy link

cursor bot commented Mar 9, 2026

PR Summary

Medium Risk
Changes enforce additional workflow validation in both deploy and run paths, which may newly reject previously deployable/runnable workflows if validation is overly strict or edge cases exist.

Overview
Deployment now enforces workflow integrity checks end-to-end. The deploy API (POST /api/workflows/[id]/deploy) now calls validateWorkflowState before schedule validation and returns a 400 with a combined error summary when blocks/edges/tool references are invalid.

UI now prevents invalid operations earlier. The deploy button runs runPreDeployChecks() even for already-deployed workflows before opening the redeploy modal, and the Run action (including the global shortcut) is disabled when a workflow has multiple blocks but no edges (disconnected graph).

Written by Cursor Bugbot for commit 2766a39. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Mar 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Mar 9, 2026 1:19am

Request Review

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 9, 2026

Greptile Summary

This PR closes a meaningful security/UX gap by adding workflow state validation at three enforcement points: a backend validateWorkflowState() gate before deployment, front-end pre-deploy checks that now apply to redeployments as well as first deploys, and a live Run-button guard based on edge connectivity. The approach is sound and the backend change is clean, but the new Run-button heuristic has two issues that need attention before merge.

  • Backend (deploy/route.ts): validateWorkflowState() is correctly placed before the schedule check and will catch unknown block types, dangling edge references, and bad tool references. The only minor concern is the as WorkflowState cast that omits the variables field.
  • Frontend (use-deployment.ts): Moving the isDeployed short-circuit to after runPreDeployChecks is a clean, correct change with no issues.
  • Frontend (panel.tsx): The hasBlocks && !hasEdges guard replaces the always-false placeholder, which is an improvement, but it is too broad — any workflow with exactly one block (a valid single-block scenario) will have the Run button permanently disabled. Additionally, the Mod+Enter keyboard shortcut handler does not check hasValidationErrors/isWorkflowBlocked, meaning the same keyboard path that fires the button handler can still execute a blocked workflow.

Confidence Score: 3/5

  • Safe to merge after fixing the keyboard-shortcut bypass and reconsidering the single-block edge validation heuristic.
  • The backend and deployment-hook changes are solid. The panel change introduces a genuine logic gap (keyboard shortcut bypasses the new guard) and a potentially incorrect validation condition (blocking valid single-block workflows), which together lower confidence below a straightforward merge.
  • Pay close attention to panel.tsx — specifically the hasValidationErrors condition and the run-workflow keyboard command handler.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx Replaces the hardcoded hasValidationErrors = false with a live edge-count check, but the new condition is too broad (blocks single-block workflows) and the Mod+Enter keyboard shortcut bypasses the new guard entirely.
apps/sim/app/api/workflows/[id]/deploy/route.ts Adds validateWorkflowState() before the schedule check to reject malformed block types, dangling edges, and bad tool references; the as WorkflowState cast omits the variables field but is low-risk given current usage.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/hooks/use-deployment.ts Clean refactor: moves the isDeployed early-return to after the pre-deploy checks so redeployments are now validated the same as first deploys; no issues found.

Sequence Diagram

sequenceDiagram
    participant User
    participant panel.tsx
    participant use-deployment.ts
    participant use-predeploy-checks.ts
    participant deploy/route.ts
    participant validateWorkflowState

    User->>panel.tsx: Click Run button
    panel.tsx->>panel.tsx: Check hasBlocks && !hasEdges
    alt hasValidationErrors
        panel.tsx-->>User: Button disabled (no run)
    else no validation errors
        panel.tsx->>panel.tsx: handleRunWorkflow()
    end

    User->>panel.tsx: Click Deploy / Update button
    panel.tsx->>use-deployment.ts: handleDeployClick()
    use-deployment.ts->>use-predeploy-checks.ts: runPreDeployChecks(blocks, edges, loops, parallels)
    alt checks fail
        use-predeploy-checks.ts-->>use-deployment.ts: { passed: false, error }
        use-deployment.ts-->>User: Notification error
    else checks pass & isDeployed
        use-deployment.ts-->>panel.tsx: { success: true, shouldOpenModal: true }
        panel.tsx-->>User: Open redeploy modal
    else checks pass & first deploy
        use-deployment.ts->>deploy/route.ts: POST /api/workflows/[id]/deploy
        deploy/route.ts->>validateWorkflowState: validateWorkflowState(blocks, edges, loops, parallels)
        alt validation fails
            validateWorkflowState-->>deploy/route.ts: { valid: false, errors }
            deploy/route.ts-->>use-deployment.ts: HTTP 400 error
            use-deployment.ts-->>User: Notification error
        else validation passes
            deploy/route.ts->>deploy/route.ts: validateWorkflowSchedules()
            deploy/route.ts->>deploy/route.ts: deployWorkflow()
            deploy/route.ts-->>use-deployment.ts: HTTP 200 success
            use-deployment.ts-->>panel.tsx: { success: true, shouldOpenModal: true }
            panel.tsx-->>User: Open deploy modal
        end
    end
Loading

Comments Outside Diff (1)

  1. apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx, line 373-399 (link)

    Keyboard shortcut bypasses new validation check

    The Mod+Enter keyboard handler calls runWorkflow() directly without checking hasValidationErrors or isWorkflowBlocked. The Run button is visually disabled via isButtonDisabled, but the keyboard shortcut still invokes the handler unconditionally — so a user can press Mod+Enter to execute a workflow that the button disallows.

Last reviewed commit: a072602

Comment on lines +361 to +362
const hasEdges = useWorkflowStore((state) => state.edges.length > 0)
const hasValidationErrors = hasBlocks && !hasEdges
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Single-block workflows incorrectly blocked by edge check

hasValidationErrors = hasBlocks && !hasEdges disables the Run button for any workflow that has blocks but zero edges. A workflow consisting of exactly one standalone block (e.g. a single agent block) has no edges by definition and would always show the button as disabled — even though it may be a perfectly valid, runnable workflow.

The intent (catching disconnected/dangling blocks) is good, but using edges.length === 0 as the sole proxy is too coarse. Consider checking that the starter block has at least one outgoing edge, or that the graph is connected, rather than requiring any edge to exist across the whole workflow.

Comment on lines +139 to +144
const workflowValidation = validateWorkflowState({
blocks: normalizedData.blocks,
edges: normalizedData.edges,
loops: normalizedData.loops,
parallels: normalizedData.parallels,
} as WorkflowState)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type assertion papers over a missing variables field

normalizedData may not include a variables property, so the cast as WorkflowState silently satisfies TypeScript while potentially producing undefined at runtime for that field. validateWorkflowState only uses blocks and edges, but if the function signature ever evolves, this cast could hide a real gap.

Consider spreading a safe default instead:

Suggested change
const workflowValidation = validateWorkflowState({
blocks: normalizedData.blocks,
edges: normalizedData.edges,
loops: normalizedData.loops,
parallels: normalizedData.parallels,
} as WorkflowState)
const workflowValidation = validateWorkflowState({
blocks: normalizedData.blocks,
edges: normalizedData.edges,
loops: normalizedData.loops,
parallels: normalizedData.parallels,
variables: {},
} as WorkflowState)

- Allow single-block workflows to run (blockCount > 1 check instead of
  hasBlocks && !hasEdges, which incorrectly blocked standalone blocks)
- Guard Mod+Enter keyboard shortcut with isButtonDisabled to match Run
  button behavior and prevent bypassing validation
- Add explicit variables: {} default in deploy route to avoid relying on
  type assertion to paper over a missing field

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incomplete/broken workflows can be deployed due to hardcoded validation bypass

1 participant