## What changed?
On the passive replication-apply path, when `dispatchForNewWorkflow`
finds no current execution record (`currentRunID == ""`), it now
preserves the incoming execution state and persists the run via
`CreateWorkflowModeBypassCurrent` in either of these cases:
- the incoming execution is already `ZOMBIE`; or
- the incoming execution is `COMPLETED` or `CORRUPTED` and already
records a successor through `NewExecutionRunId` or `SuccessorRunId`.
The new `nDCTransactionPolicyCreateBypassCurrent` policy makes this
behavior explicit: it does not call `SuppressBy`, does not transition
the workflow state, and does not create or update the current execution
record. `nDCTransactionPolicyCreateAsZombie` remains reserved for paths
that actually suppress an execution.
A genuinely brand-new workflow, or a completed/corrupted run with no
successor (the latest run), still becomes current as before.
<details>
<summary>Case 1: incoming snapshot is already Zombie</summary>
```text
SOURCE / RETIRED CELL | REPLICATION TARGET
------------------------------------|--------------------------------------
|
R1 [Zombie, non-current] | R1 absent
| cur -> none
|
FORCE-REPLICATE R1 SNAPSHOT |
state = Zombie ==R1==> | apply R1 (create path)
| currentRunID == ""
| |
| v
| BEFORE: CreateAsCurrent (BrandNew)
| -> rejected by persistence:
| "Invalid workflow create mode 0,
| state: Zombie"
|
| AFTER: CreateBypassCurrent
| -> R1 remains Zombie
| -> cur -> none
```
The snapshot is already Zombie before it reaches the transaction
manager. Promoting it to current would violate the Zombie/current
invariant; converting it is also unnecessary. The correct operation is
to preserve it as a non-current execution.
</details>
<details>
<summary>Case 2: closed orphan still points to a deleted
successor</summary>
```text
SOURCE | REPLICATION TARGET
------------------------------------|--------------------------------------
|
[1] R1 continue-as-new -> R2 |
R1 [completed, ->R2] | R1 absent
R2 running | R2 absent
cur -> R2 | cur -> none
|
[2] DELETE R2 (current run) |
R1 [completed, orphan, ->R2] |
R2 [deleted] |
cur -> none | cur -> none
|
[3] FORCE-REPLICATE R1 |
R1 [completed, ->R2] ==R1==> | apply R1 (create path)
| run absent, cur -> none
| |
| v
| BEFORE: CreateAsCurrent (BrandNew)
| -> cur -> R1
| [deleted lineage resurrected]
|
| AFTER: CreateBypassCurrent
| -> R1 remains Completed
| -> cur -> none
| [no resurrection]
```
Because R1 records R2 as its successor, R1 cannot be the lineage head
even though the target no longer has a current record. Bypass-current
preserves R1's history and state without promoting it.
</details>
## Why?
Force replication can reach the create path with a missing current
record in more than one form:
1. A migration/replication snapshot may already be `ZOMBIE`.
`CreateWorkflowModeBrandNew` rejects that state because a Zombie must
never own the current execution record.
2. A reset, continue-as-new, retry, or cron transition followed by
deletion of the successor can leave a completed/corrupted orphan that
still records its successor. Promoting that older run with
`CreateWorkflowModeBrandNew` resurrects a workflow whose lineage has
already moved on.
In both cases, the incoming run is known to be non-current. Persisting
it without changing its state and without writing a current record
preserves the replicated data while maintaining the current-execution
invariants.
## How did you test it?
- [x] built
- [ ] run locally and tested manually
- [ ] covered by existing tests
- [x] added new unit test(s)
- [ ] added new functional test(s)
Added unit coverage verifies that:
- a Zombie without successor metadata is persisted with
`CreateWorkflowModeBypassCurrent` and remains Zombie;
- Completed executions with `NewExecutionRunId` or `SuccessorRunId`
remain Completed;
- a Corrupted execution with a successor remains Corrupted; and
- the preserve-state path does not call suppression or update the
current execution record.
Validated with:
```text
go test ./service/history/ndc -run TestTransactionMgrForNewWorkflowSuite -count=1
```
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Temporal—durable execution platform
Introduction • Getting Started • Contributing • Temporal Docs • Temporal 101
Introduction
Temporal is a durable execution platform that enables developers to build scalable applications without sacrificing productivity or reliability. The Temporal server executes units of application logic called Workflows in a resilient manner that automatically handles intermittent failures, and retries failed operations.
Temporal is a mature technology that originated as a fork of Uber's Cadence. It is developed by Temporal Technologies, a startup by the creators of Cadence.
Getting Started
Download and Start Temporal Server Locally
Execute the following commands to start a pre-built image along with all the dependencies.
brew install temporal
temporal server start-dev
Refer to Temporal CLI documentation for more installation options.
Run the Samples
Clone or download samples for Go or Java and run them with the local Temporal server. We have a number of HelloWorld type scenarios available, as well as more advanced ones. Note that the sets of samples are currently different between Go and Java.
Use CLI
Use Temporal CLI to interact with the running Temporal server.
temporal operator namespace list
temporal workflow list
Use Temporal Web UI
Try Temporal Web UI by opening http://localhost:8233 for viewing your sample workflows executing on Temporal.
Repository
This repository contains the source code of the Temporal server. To implement Workflows, Activities and Workers, use one of the supported languages.
Contributing
We'd love your help in making Temporal great.
Helpful links to get started:
- work on or propose a new feature
- learn about the Temporal Server architecture
- learn how to build and run the Temporal Server locally
- learn about Temporal Server testing tools and best practices
- join the Temporal community forum and Slack