improvement(copilot): state persistence, subflow recreation, dynamic handle topologies by icecrasher321 · Pull Request #3569 · simstudioai/sim · GitHub
Skip to content

improvement(copilot): state persistence, subflow recreation, dynamic handle topologies#3569

Merged
icecrasher321 merged 2 commits intostagingfrom
improvement/copilot-state-mgmt
Mar 14, 2026
Merged

improvement(copilot): state persistence, subflow recreation, dynamic handle topologies#3569
icecrasher321 merged 2 commits intostagingfrom
improvement/copilot-state-mgmt

Conversation

@icecrasher321
Copy link
Copy Markdown
Collaborator

Summary

  • Unified workflow state validation pipeline: all full-state writes (hydration, copilot apply, socket sync, undo/redo) now route through normalizeWorkflowState, which validates parentIds, edges (existence + scope + trigger), and regenerates subflows. Eliminates ghost edges that persist in DB but don't render on canvas.

  • Fixed subflow interaction model: edges inside subflows are now directly clickable without needing to select a block first, condition/router handle topology is derived from canonical workflow block state with centralized React Flow internals refresh, and parent-child selection conflicts are resolved bidirectionally on shift-click.

  • Stabilized nested block IDs across copilot edits: edit operations with nestedNodes now merge by name instead of delete-and-recreate, preserving child block IDs. Condition/router branch IDs are generated server-side via normalizeConditionRouterIds so the LLM doesn't need to construct internal IDs.

  • Copilot feedback improvements: scope-crossing edges, sanitization warnings, and invalid branch configurations are now reported back to the LLM via skippedItems and validationErrors instead of being silently normalized. Removed the double-save race between server and client persistence paths.

Type of Change

  • Bug fix
  • Other: System improvement

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@cursor
Copy link
Copy Markdown

cursor Bot commented Mar 13, 2026

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 13, 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 13, 2026 11:59pm

Request Review

Comment thread apps/sim/lib/copilot/tools/server/workflow/edit-workflow/validation.ts Outdated
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 13, 2026

Greptile Summary

This PR introduces a unified workflow state normalization pipeline and several targeted fixes for subflow interaction, copilot stability, and dynamic handle topology. It's a substantial, well-structured improvement across the full stack.

Key changes:

  • normalizeWorkflowState is now the single entry point for all full-state writes (hydration, copilot apply, socket sync, undo/redo), validating parentId integrity, stripping invalid/scope-crossing edges, and regenerating loop/parallel metadata from canonical block state.
  • The copilot nestedNodes edit operation switches from delete-and-recreate to a name-based merge strategy, preserving child block IDs across edits and avoiding React Flow node remounts.
  • normalizeConditionRouterIds assigns canonical block-scoped branch IDs server-side, removing the requirement for the LLM to produce correct internal handle IDs.
  • useDynamicHandleRefresh centralises updateNodeInternals calls for condition/router blocks using topology signatures, replacing scattered setTimeout calls inside ConditionInput.
  • The double-save race in the copilot diff store (broadcasting and persisting independently) is eliminated by removing the fire-and-forget persist path.
  • Subflow background div is changed to pointer-events: none to let edges inside subflows receive clicks.

Confidence Score: 4/5

  • Safe to merge with minor fixes; no critical logic regressions identified.
  • The architecture is sound and well-tested. Two performance issues were found: a spurious blocks dependency in handleNodeClick that causes excessive callback re-creation on every block mutation, and unnecessary state-slice spreading in syncDynamicHandleSubblockValue that creates new edges/loops/parallels references on topology-only updates. A relative import in validation.ts also diverges from the established path-alias convention. None of these are functional regressions, but the first two can cause subtle re-render churn in the canvas.
  • Pay close attention to apps/sim/stores/workflows/workflow/store.ts (syncDynamicHandleSubblockValue unnecessary spreading) and apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx (handleNodeClick spurious blocks dependency).

Important Files Changed

Filename Overview
apps/sim/stores/workflows/workflow/store.ts Routes all full-state writes through normalizeWorkflowState; adds syncDynamicHandleSubblockValue which unnecessarily spreads unchanged edges/loops/parallels references, causing extra re-renders.
apps/sim/stores/workflows/workflow/validation.ts New normalizeWorkflowState utility that validates parentIds, clears invalid parents, and strips invalid edges; uses a relative import inconsistent with the rest of the file's path-alias convention.
apps/sim/stores/workflows/workflow/edge-validation.ts New dedicated edge validation module with validateEdges returning valid/dropped result sets; covers missing blocks, annotation-only blocks, trigger targets, and scope-crossing edges cleanly.
apps/sim/lib/copilot/tools/server/workflow/edit-workflow/engine.ts Adds post-operation removeInvalidScopeEdges pass after all deferred connections are created; correctly defers scope validation to after all block parentIds are finalized.
apps/sim/lib/copilot/tools/server/workflow/edit-workflow/operations.ts Switches nestedNodes from delete-and-recreate to name-based merge strategy preserving child block IDs; defers connections to pass 2 so forward references resolve correctly.
apps/sim/lib/copilot/tools/server/workflow/edit-workflow/index.ts Replaces full applyAutoLayout with targeted layout for only new/repositioned blocks; removes the double-save race; unifies DB-load validation through normalizeWorkflowState.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx Integrates useDynamicHandleRefresh, centralises zIndex via computeBlockZIndex, adds parent-child shift-click conflict resolution; handleNodeClick contains a spurious blocks dependency causing excessive callback re-creation.
apps/sim/lib/workflows/dynamic-handle-topology.ts New module centralising condition/router handle topology logic (getConditionRows, getRouterRows, topology signatures); well-isolated and fully tested.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Full-state write trigger\n(hydration / copilot apply /\nsocket sync / undo-redo)"] --> B["normalizeWorkflowState()"]

    B --> B1["Validate blocks\n(type + name required)"]
    B1 --> B2["Validate parentIds\n(container exists, not self-ref)"]
    B2 --> B3["validateEdges()\nstrip missing / annotation /\ntrigger / scope-crossing edges"]
    B3 --> B4["generateLoopBlocks()\ngenerateParallelBlocks()"]
    B4 --> C["NormalizationResult\n{ state, warnings }"]

    C --> D["replaceWorkflowState()\nWorkflow Zustand store"]
    C --> E["DB persist\n(PUT /api/workflows/id/state)"]
    C --> F["Socket broadcast\nenqueueReplaceWorkflowState()"]

    G["Copilot edit-workflow tool"] --> H["applyOperationsToWorkflowState()\nPass 1: block ops\nPass 2: deferred edges\nPass 3: removeInvalidScopeEdges()"]
    H --> I["applyTargetedLayout()\nnew + repositioned blocks only"]
    I --> J["normalizeWorkflowState()\nfinal DB write"]
    J --> E

    K["SubBlock value change\n(conditions / routes)"] --> L["syncDynamicHandleSubblockValue()\nWorkflow store – topology only"]
    L --> M["collectDynamicHandleTopologySignatures()"]
    M --> N["useDynamicHandleRefresh()\nupdateNodeInternals() via rAF\nfor changed blocks"]
Loading

Last reviewed commit: 20312df

Comment thread apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx Outdated
Comment thread apps/sim/stores/workflows/workflow/store.ts Outdated
Comment thread apps/sim/stores/workflows/workflow/validation.ts Outdated
@icecrasher321
Copy link
Copy Markdown
Collaborator Author

Copy link
Copy Markdown

@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 3 potential issues.

Fix All in Cursor

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

Comment thread apps/sim/lib/workflows/operations/socket-operations.ts
Comment thread apps/sim/stores/workflow-diff/store.ts
@icecrasher321 icecrasher321 merged commit 7e740e6 into staging Mar 14, 2026
12 checks passed
@icecrasher321 icecrasher321 deleted the improvement/copilot-state-mgmt branch March 14, 2026 00:47
adithyaakrishna pushed a commit to adithyaakrishna/sim that referenced this pull request Mar 20, 2026
…handle topologies (simstudioai#3569)

* improvement(copilot): state persistence, subflow recreation, dynamic handle topologies

* address comments
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.

1 participant