Skip to content

Conversation

@waleedlatif1
Copy link
Collaborator

Summary

  • added the ability to lock blocks

Type of Change

  • Bug fix

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)

@vercel
Copy link

vercel bot commented Feb 1, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Feb 1, 2026 5:15am

Request Review

Resolved conflicts:
- Removed addBlock from store (now using batchAddBlocks)
- Updated lock tests to use test helper addBlock function
- Kept both staging's optimization tests and lock feature tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When duplicating a block that's inside a locked loop/parallel,
the duplicate is now placed outside the container since nothing
should be added to a locked container.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Server-side workflow duplication now sets locked: false for all blocks
- regenerateWorkflowStateIds also unlocks blocks for templates
- Client-side regenerateBlockIds already handled this (for paste/import)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@waleedlatif1 waleedlatif1 marked this pull request as ready for review February 1, 2026 02:46
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 1, 2026

Greptile Overview

Greptile Summary

This PR implements a comprehensive block locking feature for workflows, allowing admins to lock blocks to prevent accidental modifications.

Key Changes

  • Database: Added locked boolean column to workflow_blocks table with migration
  • Core Functionality: Implemented lock/unlock operations with cascading behavior for container blocks (loops/parallels)
  • UI Integration: Added lock/unlock buttons in action bar (admin-only), disabled editing/dragging/deletion for locked blocks
  • Smart Duplication: Duplicated blocks are always unlocked; blocks inside locked containers are placed outside
  • Collaborative Features: Full socket sync, undo/redo support, and real-time collaboration handling
  • Protective Measures: Locked blocks cannot be dragged, deleted, connected, edited, enabled/disabled, or auto-layouted
  • User Feedback: Informative notifications when operations are blocked due to locked state

Implementation Quality

The implementation is thorough and well-tested with comprehensive unit tests covering edge cases. The feature integrates cleanly with existing systems (undo/redo, collaborative editing, socket operations). The cascading lock behavior for containers ensures consistency.

Confidence Score: 4/5

  • Safe to merge with one minor issue to address regarding consistent logic in enabled toggle operations
  • The implementation is comprehensive with excellent test coverage and thoughtful handling of edge cases. One inconsistency exists in the enabled toggle logic between frontend and backend that should be addressed
  • apps/sim/socket/database/operations.ts - logic inconsistency in batch toggle enabled operation

Important Files Changed

Filename Overview
apps/sim/stores/workflows/workflow/store.ts Implements setBlockLocked, batchToggleLocked with cascading to children, handles locked parent logic in duplication and toggle operations
apps/sim/socket/database/operations.ts Adds BATCH_TOGGLE_LOCKED operation, respects locked state in toggle operations, cascades lock to children
apps/sim/hooks/use-collaborative-workflow.ts Added collaborativeBatchToggleLocked hook with undo/redo support, filters locked blocks in toggle enabled
apps/sim/hooks/use-undo-redo.ts Implements undo/redo for BATCH_TOGGLE_LOCKED operation with proper state restoration
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx Prevents drag, delete, connection operations on locked blocks and blocks in locked containers with user notifications
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx Added lock/unlock button for admins, disables enable/disable and toggle handles when locked
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx Disables editing for locked blocks, shows lock indicator with unlock option for admins

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as Action Bar/Context Menu
    participant Hook as useCollaborativeWorkflow
    participant Store as WorkflowStore
    participant Socket as Socket Operations
    participant DB as Database
    participant UndoRedo as Undo/Redo System

    User->>UI: Click Lock/Unlock button
    UI->>Hook: collaborativeBatchToggleLocked(blockIds)
    Hook->>Store: Get current block states
    Store-->>Hook: Return blocks with locked states
    Hook->>Hook: Collect blocks + children (if container)
    Hook->>Hook: Build previousStates map
    Hook->>Socket: addToQueue(BATCH_TOGGLE_LOCKED)
    Hook->>Store: batchToggleLocked(blockIds)
    Store->>Store: Toggle locked state for blocks
    Store->>Store: If container, cascade to all children
    Store-->>Hook: State updated
    Hook->>UndoRedo: recordBatchToggleLocked(blockIds, previousStates)
    UndoRedo-->>Hook: Operation recorded
    Socket->>DB: Update locked column in workflow_blocks
    DB-->>Socket: Update confirmed
    Socket-->>UI: State synchronized

    Note over User,DB: Locked blocks prevent:
    Note over User,DB: - Dragging (draggable=false)
    Note over User,DB: - Deletion (filtered out)
    Note over User,DB: - Connection changes (blocked)
    Note over User,DB: - Editing (canEditBlock=false)
    Note over User,DB: - Enable/disable toggle
    Note over User,DB: - Handle orientation change

    User->>UI: Try to drag locked block
    UI->>UI: Check block.locked
    UI-->>User: Show notification (cannot drag)

    User->>UI: Try to delete locked block
    UI->>UI: Filter out locked blocks
    UI-->>User: Show notification (cannot delete)

    User->>UI: Duplicate locked block
    UI->>Store: duplicateBlock(id)
    Store->>Store: Check if parent is locked
    alt Parent is locked
        Store->>Store: Place duplicate outside container
        Store->>Store: Remove parentId from duplicate
    else Parent not locked
        Store->>Store: Place duplicate with offset
    end
    Store->>Store: Set locked=false on duplicate
    Store-->>UI: New unlocked block created
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

7 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

- Fix toggle enabled using first toggleable block, not first block
- Delete button now checks isParentLocked
- Lock button now has disabled state
- Editor lock icon distinguishes block vs parent lock state

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Editor: can't unlock block if parent container is locked
- Action bar: can't unlock block if parent container is locked
- Shows "Parent container is locked" tooltip in both cases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Block Menu, Editor, Action Bar now all have identical behavior:
- Enable/Disable: disabled when locked OR parent locked
- Flip Handles: disabled when locked OR parent locked
- Delete: disabled when locked OR parent locked
- Remove from Subflow: disabled when locked OR parent locked
- Lock: always available for admins
- Unlock: disabled when parent is locked (unlock parent first)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
waleedlatif1 and others added 2 commits January 31, 2026 19:05
Same pattern as lock: must enable parent container first before
enabling children inside it.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added documentation for the lock/unlock block feature (admin only).
Note: Image placeholder added, pending actual screenshot.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Paste creates new blocks, doesn't modify selected ones. Changed from
disableEdit (includes lock state) to !userCanEdit (permission only),
matching the Duplicate action behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tion

Add isEdgeProtected, filterUnprotectedEdges, and hasProtectedBlocks
utilities. Refactor workflow.tsx to use these helpers for:
- onEdgesChange edge removal filtering
- onConnect connection prevention
- onNodeDragStart drag prevention
- Keyboard edge deletion
- Block menu disableEdit calculation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1. Store batchToggleEnabled now uses continue to skip locked blocks
   entirely, matching database operation behavior

2. Copilot add operation now checks if parent container is locked
   before adding nested nodes (defensive check for consistency)

3. Remove unused filterUnprotectedEdges function

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- insert_into_subflow: Check if existing block being moved is locked
- extract_from_subflow: Check if block or parent subflow is locked

These operations now match the UI behavior where locked blocks
cannot be moved into/out of containers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…BlockIds

1. regenerateBlockIds now checks if existing parent is locked before
   keeping the block inside it. If parent is locked, the duplicate
   is placed outside (parentId cleared) instead of creating an
   inconsistent state.

2. Remove unnecessary effectivePermissions.canAdmin and potentialParentId
   from onNodeDragStart dependency array.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1. BATCH_TOGGLE_LOCKED now uses first block from blocksToToggle set
   instead of blockIds[0], matching BATCH_TOGGLE_ENABLED pattern.
   Also added early exit if blocksToToggle is empty.

2. Blocks inside locked containers are now properly non-draggable.
   Changed draggable check from !block.locked to use isBlockProtected()
   which checks both block lock and parent container lock.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Both edit and delete operations now check if the block's parent
container is locked, not just if the block itself is locked. This
ensures consistent behavior with the UI which uses isBlockProtected
utility that checks both direct lock and parent lock.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1. BATCH_TOGGLE_LOCKED now requires admin role - non-admin users with
   write role can no longer bypass UI restriction via direct socket
   messages

2. BATCH_REMOVE_BLOCKS now validates lock status server-side - filters
   out protected blocks (locked or inside locked parent) before deletion

3. Remove duplicate/outdated comment in regenerateBlockIds

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
batch-toggle-locked is now admin-only, so write role should be denied.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The redo logic for BATCH_TOGGLE_ENABLED and BATCH_TOGGLE_LOCKED was
incorrectly computing each block's new state as !previousStates[blockId].
However, the store's batchToggleEnabled/batchToggleLocked set ALL blocks
to the SAME target state based on the first block's previous state.

Now redo computes targetState = !previousStates[firstBlockId] and applies
it to all blocks, matching the store's behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Based on audit findings, adds lock validation to multiple operations:

1. BATCH_TOGGLE_HANDLES - now skips locked/protected blocks at:
   - Store layer (batchToggleHandles)
   - Collaborative hook (collaborativeBatchToggleBlockHandles)
   - Server socket handler

2. BATCH_ADD_BLOCKS - server now filters blocks being added to
   locked parent containers

3. BATCH_UPDATE_PARENT - server now:
   - Skips protected blocks (locked or inside locked container)
   - Prevents moving blocks into locked containers

All validations use consistent isProtected() helper that checks both
direct lock and parent container lock.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
contextMenuBlocks already has locked and isParentLocked properties
computed in use-canvas-context-menu.ts, so there's no need to look
up blocks again via hasProtectedBlocks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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.

blocks: Record<string, BlockState>
): boolean {
return blockIds.some((id) => isBlockProtected(id, blocks))
}
Copy link

Choose a reason for hiding this comment

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

Exported function hasProtectedBlocks is never used

Low Severity

The hasProtectedBlocks function is exported but never used anywhere in the codebase. The grep search found only its definition (line 82) with no imports or usages. Other functions from this file (isBlockProtected, isEdgeProtected, filterProtectedBlocks) are all used in workflow.tsx, but this one is dead code.

Fix in Cursor Fix in Web

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.

2 participants