You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A workflow is either a draft (editable, not processing data) or live (running in production, read-only on main). To change a live workflow you edit it in a sandbox and promote the change back to the parent (the existing sandbox merge for now, a PR-style review later). Where sandboxes are not available, the fallback is switch back to draft, which takes the workflow offline while you edit.
It is built on the sandboxes we already have. "Edit in sandbox" creates or joins an ordinary project sandbox via Lightning.Projects.Sandboxes; "Promote" runs the existing Sandboxes.merge/4 back to the parent. There is no new sandbox primitive, and a sandbox is not narrowed to one workflow at creation (that would break the portable-project.yaml invariant). What is new is the lifecycle state, restore, in-editor switching, and the editor UX. Promotion for now is just that merge with today's authorization; a PR-style review is a later phase.
Concept: the "Sandbox Developer Experience" walkthrough. Builds on V1 sandboxes and the Sandboxes Vision. Reviewed with product and design on 2026-06-11.
Delivery plan and sequencing
Two phases. Phase 1 is everything we can deliver now. Phase 2 is everything that should wait, because it needs a design decision, a prerequisite investigation, or belongs to the later branch-protection step. Each sub-issue carries a phase:1-now or phase:2-later label.
Phase 1: deliver now. The draft/live lifecycle is already merged (#4857 and #4858, via #4867, #4869, #4872, #4873, and coherence in #4900). Finishing it means making the read-only lock write-authoritative (#4898, since today it is UI-only and advisory) and the Apollo prompt cleanup (#4899), both to main. On top of that, the sandbox round trip for paid plans: edit in sandbox (#4859), promote as the existing sandbox merge with today's authorization (#4929, merge_sandbox, owner and admin), diverged surfacing (#4863), and the production-credentials warning (#4860). The parent-added-workflow deletion is a pre-existing live bug in the sandbox merge, tracked separately as #4919 (not epic scope); since promote reuses that merge, land #4919 before or alongside the round trip. Also in Phase 1: recording a version at each go-live and promote (#4866), restore from a version (#4864, the safety net that rolls a broken live workflow back), in-editor version and sandbox switching (#4865, using the interim in-memory project-id map), and the locked Edit-in-sandbox upsell for plans without sandboxes (#4920). Sensible internal order: land the lifecycle finish to main, then the round trip, then these. The interim wire-format inference holds while #4897 waits.
Phase 2: wait. Promotion requests and approval are the branch-protection step (#4861 and #4862): they build on the Phase 1 merge once we invest in review and a required-reviewer policy (deliberately deferred). The protected-project flag and CLI push rules (#4921) wait on the push-rule decisions, and the open decisions to close are tracked in #4922. Carrying workflow state in the wire format plus CLI (#4897) is held here, cross-repo and dependent on the push-rule decision. The change-size indicator on versions (#4931) is a Phase 2 enhancement on top of Phase 1 version recording.
Decisions needed before parts of the later work can build are collected in the open-decisions tracker: the draft-necessity and live-vs-enabled question, the CLI and provisioner push rules plus a protected-project flag, the in-editor switching approach (interim in-memory project-id map vs project slugs), and endpoint naming on promote. Agreed path to closure: a states and branches flowchart plus a compiled comparison of the prior models, then reconvene.
Definition of done (Phase 1)
Each Phase 1 item ships with test coverage for its new surface: backend for release recording, restore, and the promote path; frontend for the version list, the restore action, and the in-sandbox header. The already-merged lifecycle UI has thin frontend test coverage today, so closing that gap is part of finishing Phase 1 well. Authorization is explicit per action: go live and switch to draft reuse :edit_workflow; edit in sandbox uses :provision_sandbox; promote and restore use :merge_sandbox.
Why
Today a workflow has no notion of "published": its only state is whether a trigger is enabled. Editing one that is processing production data means editing the live thing in place, with no separation between what is running and what you are changing, no review before a change reaches production, and no record of what was released and by whom.
V1 sandboxes gave us the isolation primitive (a full project clone you can change without touching the parent, with merge/4 to bring workflows back), but they are surfaced as a project-management feature, disconnected from editing a workflow. This epic makes the editor itself lifecycle-aware: a published workflow is locked on main, and changing it branches into a sandbox, keeps production on the old version, and comes back through a review. That is the safety story without new isolation or merge machinery, because both already exist.
What
A workflow gains an explicit lifecycle state.
State (badge)
Where it lives
Editable?
Processing data?
Primary action
Secondary
draft
the workflow on its own project
yes
no
Go live
Run (test)
live
the workflow on its own project (main)
no (read-only)
yes
Edit in sandbox (where available) or Switch to draft (otherwise)
none
sandbox
a cloned workflow inside a project sandbox
yes
yes (own trigger endpoint)
Promote
Run (test)
sandbox + promote pending(later)
the sandbox copy with an open promote
yes (edits update the request)
yes
View request
Withdraw
archived sandbox
a sandbox scheduled for deletion after promote
no (read-only)
no (trigger disabled)
Delete
none
The badge reflects context: on its own project a workflow shows draft or live; opened inside a sandbox it shows sandbox (plus promote pending, diverged, or archived sandbox). It renders in the collaborative editor header, next to the workflow name.
Lifecycle where sandboxes are available
A new workflow starts as draft: editable, auto-saving, test-runnable, not processing data.
Go live publishes it (live, v1), enables its triggers, and makes main read-only.
Edit in sandbox opens a picker: create a new sandbox from the live version, or join an active one (sorted by last-edited, freshest first). The workflow opens in the sandbox editor, fully editable, with its own live trigger endpoint.
Promote merges the change back to the parent using the existing sandbox merge with today's authorization, the version increments (v2), and the sandbox is archived. A PR-style review with pending, withdraw, and approval is the later branch-protection phase.
Go live makes it live and read-only. Edit in sandbox is visible but locked; the available action is Switch to draft.
Switch to draft takes the workflow offline (disables triggers), behind a confirmation dialog, so it can be edited. Edit, test, Go live again.
Two user segments: organizational users route every change through the sandbox-and-approval path; individuals on smaller plans can edit directly on main via switch-to-draft.
Restore
Restore is the git-revert of workflows: it republishes a chosen earlier version as a new version and never overwrites the live workflow in place. It is an exact state replacement (the old version's content becomes the new content), published through the plan's normal path, promote on sandbox plans, switch-to-draft-then-go-live without. So a restore reads as a new forward version with a clean trail, and it is the safety net for a bad go-live or promote. See #4864.
Archived sandbox. After promote: read-only, trigger disabled, deletable or kept for audit (existing scheduled-deletion). Stale sandboxes are acceptable and sort to the bottom by last-edited.
Design decisions
Lifecycle state is an explicit field, not derived from triggers.
Today "live" is implicit (a workflow is active when a trigger is enabled), which is why there is no read-only concept. Add state to Lightning.Workflows.Workflow:
Go live sets :live and enables triggers in one transaction; switch to draft does the reverse. Triggers stay the execution mechanism; state is the user-facing contract that gates editing. It lives on the workflow, not the project, since the lifecycle is per-workflow.
Read-only on main reuses the existing editor permission.
useWorkflowReadOnly() (assets/js/collaborative-editor/hooks/useWorkflow.tsx) reads can_edit_workflow, computed in LightningWeb.Channels.WorkflowChannel, and already disables Save, the trigger toggle, and the editors. We extend that computation: read-only when the workflow is :live and the project is not a sandbox. Inside a sandbox the clone stays editable. One source of truth, so every surface that respects read-only gets the live lock for free.
Edit in sandbox reuses full-clone sandboxes; the one change is the edited workflow's trigger goes live.
"Create new sandbox" calls Sandboxes.provision/3 as today: a full clone of the parent's configuration. We do not let users pick a subset of workflows at creation, because a sandbox must stay a faithful, portable project.yaml, which is what makes promote, CLI deploy, GitHub sync, and migration one operation. The one-workflow experience comes from scoping the editor; selectivity belongs at promotion. The one divergence from V1: cloning disables triggers (clone_triggers_with_disabled_state), but here the edited workflow's trigger is enabled so the sandbox is live with its own /i/... endpoint.
Promote is the existing merge now; the review object is a later phase.
For this release, Promote is exactly the existing sandbox merge (Sandboxes.merge/4 to MergeProjects.merge_project/3 with selected_workflow_ids) with today's authorization (merge_sandbox, owner and admin). There is no request object and no approval step (#4929). The branch-protection phase (#4861, #4862) later wraps that merge in a PR-style review:
In that phase Promote creates a :pending record, the sandbox stays editable while pending, withdraw cancels, and approval runs the merge for that workflow, records a new version, and archives the sandbox, with a required-reviewer policy as the following step. Independent of phase, the merge must not delete a workflow the parent gained after the fork: that row is currently flagged "Deleted in sandbox" and pre-selected, so a select-all merge soft-deletes it. Default those rows to unchecked (keep) with a neutral label (#4919); deliberate deletion still works.
A "version" is recorded at go-live and promote, not per save.
workflow_snapshots captures state per save (the recovery and content layer) and workflow_versions is an internal hash log for divergence and CLI export. Neither is the user-facing history. A new thin workflow_releases table records a version only at go-live and promote, with a sequential number, author, kind, and source sandbox, pointing at the snapshot for content; the version list reads from that. There is no per-keystroke database autosave, so no session-collapse mechanism is needed. Change-size metadata is a Phase 2 enhancement (#4931). See #4866.
Switching versions and sandboxes happens inside the editor.
This needs project-id mapping, since the editor URL carries the project UID and a sandbox is a different project. Long-term: project slugs; interim: an in-memory sandbox-to-project map. A proper cross-entity diff is out of scope.
Target (Vision): connections are env-bound, so a sandbox uses test credentials.
Today: clone_credentials_from_parent shares the parent's real credentials.
This epic: a sandbox using production credentials shows a prominent red warning; nothing is disabled.
True isolation (env-bound connections, mocked endpoints) is out of scope, as is disabling cron triggers in a sandbox.
Feature gating reuses the usage-limiting extension.
UsageLimiting.limit_action/2 already gates :activate_workflow and :new_sandbox. Edit in sandbox is gated the same way and Go live through :activate_workflow; when disallowed, Edit in sandbox locks with the limiter's tooltip and Switch to draft is the path. Upsell copy comes from the extension, not hardcoded.
Trigger and channel endpoint naming on promote. Editing a webhook's custom path in a sandbox currently changes main's public URL on promote. Needs a stable-on-promote rule; the Vision suggests namespaced sandbox endpoints. Tracked in Open design decisions tracker #4922.
Estimates live on the individual sub-issues, split across Phase 1 and Phase 2. The original single 61-point figure predates the phase split and the scope changes (promote reduced to the existing merge, versioning reframed onto go-live and promote, two bugs pulled out), so it is superseded by the per-issue points.
A workflow is either a draft (editable, not processing data) or live (running in production, read-only on main). To change a live workflow you edit it in a sandbox and promote the change back to the parent (the existing sandbox merge for now, a PR-style review later). Where sandboxes are not available, the fallback is switch back to draft, which takes the workflow offline while you edit.
It is built on the sandboxes we already have. "Edit in sandbox" creates or joins an ordinary project sandbox via
Lightning.Projects.Sandboxes; "Promote" runs the existingSandboxes.merge/4back to the parent. There is no new sandbox primitive, and a sandbox is not narrowed to one workflow at creation (that would break the portable-project.yamlinvariant). What is new is the lifecycle state, restore, in-editor switching, and the editor UX. Promotion for now is just that merge with today's authorization; a PR-style review is a later phase.Concept: the "Sandbox Developer Experience" walkthrough. Builds on V1 sandboxes and the Sandboxes Vision. Reviewed with product and design on 2026-06-11.
Delivery plan and sequencing
Two phases. Phase 1 is everything we can deliver now. Phase 2 is everything that should wait, because it needs a design decision, a prerequisite investigation, or belongs to the later branch-protection step. Each sub-issue carries a
phase:1-noworphase:2-laterlabel.Phase 1: deliver now. The draft/live lifecycle is already merged (#4857 and #4858, via #4867, #4869, #4872, #4873, and coherence in #4900). Finishing it means making the read-only lock write-authoritative (#4898, since today it is UI-only and advisory) and the Apollo prompt cleanup (#4899), both to main. On top of that, the sandbox round trip for paid plans: edit in sandbox (#4859), promote as the existing sandbox merge with today's authorization (#4929,
merge_sandbox, owner and admin), diverged surfacing (#4863), and the production-credentials warning (#4860). The parent-added-workflow deletion is a pre-existing live bug in the sandbox merge, tracked separately as #4919 (not epic scope); since promote reuses that merge, land #4919 before or alongside the round trip. Also in Phase 1: recording a version at each go-live and promote (#4866), restore from a version (#4864, the safety net that rolls a broken live workflow back), in-editor version and sandbox switching (#4865, using the interim in-memory project-id map), and the locked Edit-in-sandbox upsell for plans without sandboxes (#4920). Sensible internal order: land the lifecycle finish to main, then the round trip, then these. The interim wire-format inference holds while #4897 waits.Phase 2: wait. Promotion requests and approval are the branch-protection step (#4861 and #4862): they build on the Phase 1 merge once we invest in review and a required-reviewer policy (deliberately deferred). The protected-project flag and CLI push rules (#4921) wait on the push-rule decisions, and the open decisions to close are tracked in #4922. Carrying workflow state in the wire format plus CLI (#4897) is held here, cross-repo and dependent on the push-rule decision. The change-size indicator on versions (#4931) is a Phase 2 enhancement on top of Phase 1 version recording.
Decisions needed before parts of the later work can build are collected in the open-decisions tracker: the draft-necessity and live-vs-enabled question, the CLI and provisioner push rules plus a protected-project flag, the in-editor switching approach (interim in-memory project-id map vs project slugs), and endpoint naming on promote. Agreed path to closure: a states and branches flowchart plus a compiled comparison of the prior models, then reconvene.
Definition of done (Phase 1)
Each Phase 1 item ships with test coverage for its new surface: backend for release recording, restore, and the promote path; frontend for the version list, the restore action, and the in-sandbox header. The already-merged lifecycle UI has thin frontend test coverage today, so closing that gap is part of finishing Phase 1 well. Authorization is explicit per action: go live and switch to draft reuse
:edit_workflow; edit in sandbox uses:provision_sandbox; promote and restore use:merge_sandbox.Why
Today a workflow has no notion of "published": its only state is whether a trigger is enabled. Editing one that is processing production data means editing the live thing in place, with no separation between what is running and what you are changing, no review before a change reaches production, and no record of what was released and by whom.
V1 sandboxes gave us the isolation primitive (a full project clone you can change without touching the parent, with
merge/4to bring workflows back), but they are surfaced as a project-management feature, disconnected from editing a workflow. This epic makes the editor itself lifecycle-aware: a published workflow is locked on main, and changing it branches into a sandbox, keeps production on the old version, and comes back through a review. That is the safety story without new isolation or merge machinery, because both already exist.What
A workflow gains an explicit lifecycle state.
draftlivesandboxsandbox+promote pending(later)archived sandboxThe badge reflects context: on its own project a workflow shows
draftorlive; opened inside a sandbox it showssandbox(pluspromote pending,diverged, orarchived sandbox). It renders in the collaborative editor header, next to the workflow name.Lifecycle where sandboxes are available
draft: editable, auto-saving, test-runnable, not processing data.live, v1), enables its triggers, and makes main read-only.Lifecycle where sandboxes are not available
draft, same as above.liveand read-only. Edit in sandbox is visible but locked; the available action is Switch to draft.Two user segments: organizational users route every change through the sandbox-and-approval path; individuals on smaller plans can edit directly on main via switch-to-draft.
Restore
Restore is the git-revert of workflows: it republishes a chosen earlier version as a new version and never overwrites the live workflow in place. It is an exact state replacement (the old version's content becomes the new content), published through the plan's normal path, promote on sandbox plans, switch-to-draft-then-go-live without. So a restore reads as a new forward version with a clean trail, and it is the safety net for a bad go-live or promote. See #4864.
Edge cases
MergeProjects.diverged_workflows/2. See Promote safety: surface diverged workflows in the merge #4863.Design decisions
Lifecycle state is an explicit field, not derived from triggers.
Today "live" is implicit (a workflow is active when a trigger is enabled), which is why there is no read-only concept. Add
statetoLightning.Workflows.Workflow:Go live sets
:liveand enables triggers in one transaction; switch to draft does the reverse. Triggers stay the execution mechanism;stateis the user-facing contract that gates editing. It lives on the workflow, not the project, since the lifecycle is per-workflow.Read-only on main reuses the existing editor permission.
useWorkflowReadOnly()(assets/js/collaborative-editor/hooks/useWorkflow.tsx) readscan_edit_workflow, computed inLightningWeb.Channels.WorkflowChannel, and already disables Save, the trigger toggle, and the editors. We extend that computation: read-only when the workflow is:liveand the project is not a sandbox. Inside a sandbox the clone stays editable. One source of truth, so every surface that respects read-only gets the live lock for free.Edit in sandbox reuses full-clone sandboxes; the one change is the edited workflow's trigger goes live.
"Create new sandbox" calls
Sandboxes.provision/3as today: a full clone of the parent's configuration. We do not let users pick a subset of workflows at creation, because a sandbox must stay a faithful, portableproject.yaml, which is what makes promote, CLI deploy, GitHub sync, and migration one operation. The one-workflow experience comes from scoping the editor; selectivity belongs at promotion. The one divergence from V1: cloning disables triggers (clone_triggers_with_disabled_state), but here the edited workflow's trigger is enabled so the sandbox is live with its own/i/...endpoint.Promote is the existing merge now; the review object is a later phase.
For this release, Promote is exactly the existing sandbox merge (
Sandboxes.merge/4toMergeProjects.merge_project/3withselected_workflow_ids) with today's authorization (merge_sandbox, owner and admin). There is no request object and no approval step (#4929). The branch-protection phase (#4861, #4862) later wraps that merge in a PR-style review:In that phase Promote creates a
:pendingrecord, the sandbox stays editable while pending, withdraw cancels, and approval runs the merge for that workflow, records a new version, and archives the sandbox, with a required-reviewer policy as the following step. Independent of phase, the merge must not delete a workflow the parent gained after the fork: that row is currently flagged "Deleted in sandbox" and pre-selected, so a select-all merge soft-deletes it. Default those rows to unchecked (keep) with a neutral label (#4919); deliberate deletion still works.A "version" is recorded at go-live and promote, not per save.
workflow_snapshotscaptures state per save (the recovery and content layer) andworkflow_versionsis an internal hash log for divergence and CLI export. Neither is the user-facing history. A new thinworkflow_releasestable records a version only at go-live and promote, with a sequential number, author, kind, and source sandbox, pointing at the snapshot for content; the version list reads from that. There is no per-keystroke database autosave, so no session-collapse mechanism is needed. Change-size metadata is a Phase 2 enhancement (#4931). See #4866.Switching versions and sandboxes happens inside the editor.
This needs project-id mapping, since the editor URL carries the project UID and a sandbox is a different project. Long-term: project slugs; interim: an in-memory sandbox-to-project map. A proper cross-entity diff is out of scope.
Sandbox credential safety: fail loudly now, isolate later.
clone_credentials_from_parentshares the parent's real credentials.True isolation (env-bound connections, mocked endpoints) is out of scope, as is disabling cron triggers in a sandbox.
Feature gating reuses the usage-limiting extension.
UsageLimiting.limit_action/2already gates:activate_workflowand:new_sandbox. Edit in sandbox is gated the same way and Go live through:activate_workflow; when disallowed, Edit in sandbox locks with the limiter's tooltip and Switch to draft is the path. Upsell copy comes from the extension, not hardcoded.Still open
Estimate
Estimates live on the individual sub-issues, split across Phase 1 and Phase 2. The original single 61-point figure predates the phase split and the scope changes (promote reduced to the existing merge, versioning reframed onto go-live and promote, two bugs pulled out), so it is superseded by the per-issue points.