Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: enable workflow validation in panel
  • Loading branch information
guoyangzhen committed Mar 14, 2026
commit 70dd04d50b959ddc26f0b8230eb343c08343b9cb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from '@/components/emcn'
import { VariableIcon } from '@/components/icons'
import { generateWorkflowJson } from '@/lib/workflows/operations/import-export'
import { validateWorkflowState } from '@/lib/workflows/sanitization/validation'
import { useRegisterGlobalCommands } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
import { createCommands } from '@/app/workspace/[workspaceId]/utils/commands-utils'
Expand Down Expand Up @@ -356,7 +357,14 @@ export const Panel = memo(function Panel() {
// Compute run button state
const canRun = userPermissions.canRead // Running only requires read permissions
const isLoadingPermissions = userPermissions.isLoading
const hasValidationErrors = false // TODO: Add validation logic if needed
const hasValidationErrors = useWorkflowStore((state) => {
if (Object.keys(state.blocks).length === 0) return false
const result = validateWorkflowState({
blocks: state.blocks,
edges: state.edges,
})
return !result.valid
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Missing loops and parallels causes false-positive validation errors

validateWorkflowState checks edges against loop and parallel container IDs (lines 270–292 of validation.ts). Because state.loops and state.parallels are not passed here, any workflow that uses a loop or parallel block will have all edges connected to those containers reported as referencing "non-existent" source/target blocks — causing hasValidationErrors to be true for valid workflows and permanently disabling the Run button.

Suggested change
const hasValidationErrors = useWorkflowStore((state) => {
if (Object.keys(state.blocks).length === 0) return false
const result = validateWorkflowState({
blocks: state.blocks,
edges: state.edges,
})
return !result.valid
})
const hasValidationErrors = useWorkflowStore((state) => {
if (Object.keys(state.blocks).length === 0) return false
const result = validateWorkflowState({
blocks: state.blocks,
edges: state.edges,
loops: state.loops,
parallels: state.parallels,
})
return !result.valid
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Expensive validation runs on every Zustand store update

validateWorkflowState iterates all blocks, calls getBlock() and getTool() per block, and checks all edges — all synchronously inside a Zustand selector. Selectors run on every state change, including every single keystroke in any block's input field. For larger workflows this will cause perceptible UI lag.

Consider memoising outside the selector or debouncing:

// Coarse selector — only re-validates when blocks/edges identity changes
const { blocks, edges, loops, parallels } = useWorkflowStore(
  useShallow((state) => ({
    blocks: state.blocks,
    edges: state.edges,
    loops: state.loops,
    parallels: state.parallels,
  }))
)
const hasValidationErrors = useMemo(() => {
  if (Object.keys(blocks).length === 0) return false
  return !validateWorkflowState({ blocks, edges, loops, parallels }).valid
}, [blocks, edges, loops, parallels])

This keeps the computation lazy but avoids re-running it on unrelated store updates (e.g. execution state, cursor position).

Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
const isWorkflowBlocked = isExecuting || hasValidationErrors
const isButtonDisabled = !isExecuting && (isWorkflowBlocked || (!canRun && !isLoadingPermissions))

Expand Down