Skip to content

feat(workspaces): fork + push/pull#5210

Open
icecrasher321 wants to merge 16 commits into
stagingfrom
feat/ws-fork
Open

feat(workspaces): fork + push/pull#5210
icecrasher321 wants to merge 16 commits into
stagingfrom
feat/ws-fork

Conversation

@icecrasher321

@icecrasher321 icecrasher321 commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Be able to Fork Workspaces. And push/pull changes into or from parent workspaces. Allows one click promotion between environments and supports mapping credentials, secrets, and resources.

Type of Change

  • New feature

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

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 26, 2026 3:58am

Request Review

@icecrasher321 icecrasher321 marked this pull request as ready for review June 25, 2026 21:38
@cursor

cursor Bot commented Jun 25, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Bulk workflow deployment replacement, secret/credential remapping, and cross-workspace data copy affect core workspace state and authorization; incorrect mapping or promote could break production workflows or leak misconfigured secrets.

Overview
Introduces workspace forking end-to-end: admins can create a child workspace from a parent (copying deployed workflows and optionally files, tables, KBs, tools, skills, MCP configs), with heavy table/KB/file blob work deferred to a Trigger.dev (or inline) background job.

New /api/workspaces/[id]/fork/* routes cover create, lineage, copyable resources, mapping (GET/PUT), promote diff preview, promote, and rollback—gated by Enterprise (or FORKING_ENABLED on self-host) and workspace admin, with audit events on fork/promote/rollback.

Sync (push/pull) along the direct parent edge remaps credentials, env vars, and other resources via workspace_fork_resource_map, previews workflow create/update/archive plus drift and MCP re-auth warnings, blocks promote when required mappings are missing (unless force on drift), and supports undo of the last promote into a workspace.

The sidebar gains Fork / Sync / Manage forks modals, fork lineage indicators in the workspace list, and useForkingAvailable client gating; workflow duplication logic is refactored into shared remap-internal-ids helpers used by fork copy/promote (including clearing unmapped cross-workflow references).

Reviewed by Cursor Bugbot for commit 055cb47. Configure here.

Comment thread apps/sim/lib/workspaces/fork/promote/promote-run-store.ts Outdated
Comment thread apps/sim/hooks/queries/workspace-fork.ts
Comment thread apps/sim/lib/workspaces/fork/promote/rollback.ts Outdated
@greptile-apps

greptile-apps Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces workspace forking (clone a workspace's deployed workflows + selected resources), push/pull promotion between fork edges, rollback support, and a mapping editor for remapping credentials/secrets/resources across workspaces.

  • Fork creation: copies deployed workflows (undeployed in child), snapshots member permissions, copies user-selected resources (tables, KBs, MCP servers, custom tools, skills, files) with identity mappings seeded for each.
  • Promote (push/pull): serialized under a PostgreSQL advisory lock, computes a plan (diff), rewrites sub-block references via a resolver, propagates credential access to target workspace members, captures a rollback snapshot, and deploys all promoted workflows.
  • Rollback: reactivates prior deployed versions for updated workflows, archives newly-created ones, un-archives previously-archived orphans—all with concurrent-promote detection via undo-point id comparison.
  • Mapping editor: lets users map source resources (credentials, env vars, tables, etc.) to target equivalents; workflow-type entries are rejected as system-managed; push mappings are cleared-then-reinserted to keep the parent-keyed unique index consistent.

Confidence Score: 5/5

Safe to merge; all three auth layers (forking gate, source-read, target-admin) are correctly enforced on every route, the advisory-lock serialization prevents concurrent promote races, and the rollback undo-point design is durable.

The large new surface area is well-guarded: authorization checks are layered correctly across all six routes, the advisory lock prevents concurrent promote/rollback on the same edge, and the rollback undo-point uses a run-id comparison to detect superseded promotes before doing any destructive cleanup. The only issues found are a folder-key collision when sibling folders share a name, a cycle-unsafe topological sort, and allowPersonalApiKeys not being inherited from the parent — all self-contained edge cases that do not affect correctness for the common path.

apps/sim/lib/workspaces/fork/copy/copy-workflows.ts (folder mapping logic) and apps/sim/lib/workspaces/fork/create-fork.ts (allowPersonalApiKeys inheritance)

Important Files Changed

Filename Overview
apps/sim/lib/workspaces/fork/lineage/authz.ts Authorization layer for all fork/promote/rollback routes; correctly gates forking by billing plan or FORKING_ENABLED env flag and enforces read-on-source + admin-on-target per direction.
apps/sim/lib/workspaces/fork/promote/promote.ts Promote orchestrator: acquires advisory edge lock, runs plan inside transaction, propagates credential membership to target workspace members, captures rollback snapshot, and deploys promoted workflows post-commit.
apps/sim/lib/workspaces/fork/promote/rollback.ts Rollback implementation with durable undo-point semantics; correctly detects superseded promotes via run-id comparison under the edge lock.
apps/sim/lib/workspaces/fork/copy/copy-workflows.ts Copies workflow state into target; the folder-mapping helper has a key-collision bug for same-name sibling folders and an infinite-recursion risk for cyclic parentId references.
apps/sim/lib/workspaces/fork/create-fork.ts Fork creation transaction; hardcodes allowPersonalApiKeys: true regardless of the parent workspace's setting, which may bypass organisation governance policy.
apps/sim/lib/workspaces/fork/mapping/mapping-service.ts Mapping view and apply service; correctly rejects workflow-type entries (system-managed) and validates all non-null targetIds against the target workspace's resources.
apps/sim/lib/workspaces/fork/promote/promote-plan.ts Computes promote diff (create/replace/archive items, unmapped references, drift flag); plan is re-evaluated inside the promote transaction under the advisory lock.
packages/db/migrations/0250_workspace_forking.sql Adds two new tables and an FK column; uses DO$$...EXCEPTION blocks for idempotency and CONCURRENTLY index build to avoid ACCESS EXCLUSIVE lock on the existing workspace table.
apps/sim/lib/workspaces/fork/lineage/lineage.ts Fork edge resolution; correctly filters archived workspaces and uses hashtext-based advisory locking for edge serialization.
apps/sim/app/api/workspaces/[id]/fork/route.ts Fork POST route; authorization happens before body parsing which is the correct fail-fast order for fork creation.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI
    participant ForkRoute as POST /fork
    participant PromoteRoute as POST /fork/promote
    participant RollbackRoute as POST /fork/rollback
    participant Authz
    participant DB
    participant BG as Background (Trigger.dev)

    UI->>ForkRoute: fork workspace (source id, name, selection)
    ForkRoute->>Authz: assertCanFork(sourceId, userId)
    Authz->>DB: checkWorkspaceAccess + assertForkingEnabled
    ForkRoute->>DB: tx: insert workspace, copy permissions, copy workflow states, copy resources, seed identity mappings
    ForkRoute->>BG: runForkContentCopy (table rows, KB docs, blobs)
    ForkRoute-->>UI: "{ workspace, workflowsCopied }"

    UI->>PromoteRoute: promote (id, otherWorkspaceId, direction, force)
    PromoteRoute->>Authz: assertCanPromote (read source, admin target)
    PromoteRoute->>DB: tx: acquireEdgeLock → computePlan → drift/unmapped check → copyWorkflows → captureSnapshot → upsertPromoteRun
    PromoteRoute->>DB: performFullDeploy (per promoted workflow, outside tx)
    PromoteRoute-->>UI: "{ promoteRunId, updated, created, archived, drift, blocked }"

    UI->>RollbackRoute: rollback (id, otherWorkspaceId)
    RollbackRoute->>Authz: assertCanRollback (admin target)
    RollbackRoute->>DB: tx1: acquireEdgeLock → un-archive orphans
    RollbackRoute->>DB: reactivate prior versions (outside tx, serial)
    RollbackRoute->>DB: tx2: acquireEdgeLock → confirm run id → undeploy/archive created → deleteIdentityRows → deletePromoteRun
    RollbackRoute-->>UI: "{ restored, archived, unarchived, skipped }"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI
    participant ForkRoute as POST /fork
    participant PromoteRoute as POST /fork/promote
    participant RollbackRoute as POST /fork/rollback
    participant Authz
    participant DB
    participant BG as Background (Trigger.dev)

    UI->>ForkRoute: fork workspace (source id, name, selection)
    ForkRoute->>Authz: assertCanFork(sourceId, userId)
    Authz->>DB: checkWorkspaceAccess + assertForkingEnabled
    ForkRoute->>DB: tx: insert workspace, copy permissions, copy workflow states, copy resources, seed identity mappings
    ForkRoute->>BG: runForkContentCopy (table rows, KB docs, blobs)
    ForkRoute-->>UI: "{ workspace, workflowsCopied }"

    UI->>PromoteRoute: promote (id, otherWorkspaceId, direction, force)
    PromoteRoute->>Authz: assertCanPromote (read source, admin target)
    PromoteRoute->>DB: tx: acquireEdgeLock → computePlan → drift/unmapped check → copyWorkflows → captureSnapshot → upsertPromoteRun
    PromoteRoute->>DB: performFullDeploy (per promoted workflow, outside tx)
    PromoteRoute-->>UI: "{ promoteRunId, updated, created, archived, drift, blocked }"

    UI->>RollbackRoute: rollback (id, otherWorkspaceId)
    RollbackRoute->>Authz: assertCanRollback (admin target)
    RollbackRoute->>DB: tx1: acquireEdgeLock → un-archive orphans
    RollbackRoute->>DB: reactivate prior versions (outside tx, serial)
    RollbackRoute->>DB: tx2: acquireEdgeLock → confirm run id → undeploy/archive created → deleteIdentityRows → deletePromoteRun
    RollbackRoute-->>UI: "{ restored, archived, unarchived, skipped }"
Loading

Reviews (2): Last reviewed commit: "address comments" | Re-trigger Greptile

Comment thread apps/sim/lib/workspaces/fork/mapping/mapping-service.ts Outdated
Comment thread packages/db/migrations/0250_workspace_forking.sql
Comment thread apps/sim/lib/workspaces/fork/mapping/mapping-service.ts Outdated
Comment thread apps/sim/lib/workspaces/fork/copy/copy-resources.ts
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

@greptile

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

Comment thread apps/sim/lib/workspaces/fork/promote/promote.ts
Comment thread apps/sim/lib/workspaces/fork/promote/promote.ts Outdated
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

Comment thread apps/sim/lib/workspaces/fork/promote/rollback.ts
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

Comment thread apps/sim/lib/workspaces/fork/copy/copy-resources.ts
@icecrasher321

Copy link
Copy Markdown
Collaborator Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 055cb47. Configure here.

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