Status: accepted (2026-07-13)
Related decisions: ADR 0038 (content-addressed executable identity), ADR 0039 (layout on source
references), and ADR 0040 (reference- and execution-derived artifact lifetime).
Plan of record: specs/092-domain-owned-apis/.
Publishing currently appends a Published source reference and indexes trigger bindings for each
publish. If an HTTP-triggered workflow is published at /foo, edited to /bar, and published
again, both artifacts can remain eligible to start new executions. Elsa needs that side-by-side
behavior, but making it the implicit result of ordinary publishing is surprising and unsafe.
Artifact identity cannot express publication intent. Executables are immutable and content-addressed, so two publications may intentionally share an artifact while differing in lifecycle, slot, policy, or serving state. A source reference records provenance and retention but is not by itself a sufficient authority for accepting new work. Trigger bindings, schedules, and in-memory HTTP route tables are serving projections; allowing any of them to become independent authority permits partial failures and restart drift.
Publishing also crosses stores that may not share one transaction. Compilation, source-reference creation, trigger indexing, recurring-schedule projection, and HTTP route projection can fail at different points. A replacement must never disable the current publication because preparation of its candidate failed, and a successful response must not conceal a partially activated candidate.
Publishing owns a PublicationSlot identified by (WorkflowDefinitionId, SlotName). A slot selects
zero or one active PublicationRecord. Only the publication selected by an authoritative slot may
contribute trigger bindings or recurring-start schedules visible to new-start routing.
Neither an executable artifact, a source reference, a trigger binding, a schedule, nor an in-memory route independently grants start authority. They carry or project the publication identity selected by the slot.
A publication record has a unique publication ID even when it reuses an existing content-addressed artifact. It records the target slot, Design version, artifact, source reference, expected slot revision, lifecycle status, and audit timestamps. Publication history remains append-only except for controlled lifecycle transitions.
Every workflow definition has the conventional slot name default. An ordinary publish request with
no explicit slot resolves to the default slot and replaces its current authority.
Publishing side by side requires an explicit, meaningful named slot. Distinct named slots may remain active for the same definition when their trigger claims satisfy cardinality rules. Implementations do not generate an implicit side-by-side slot name, and repeated ordinary publishes do not accumulate unnamed active publications.
Soft-deleting or restoring a Design definition does not change its publication slots. Studio may offer a coordinated user flow, but Design deletion and Publishing unpublish remain separate operations.
Publishing resolves intent in this precedence order:
explicit publish request > per-workflow publication policy > host publication policy
The safe host default is replacement of the default slot. A per-workflow policy may require an
explicit slot or select another declared default, but it cannot silently turn an ordinary publish into
unlabelled coexistence. The resolved action, slot, and policy source are returned by preflight so a
management client can display the actual effect before confirmation.
Every start-trigger provider declares one of:
Exclusive: one authoritative publication in a shell may claim the normalized stimulus identity.FanOut: multiple authoritative publications may intentionally receive the same stimulus.
HTTP endpoint triggers are Exclusive. Event and timer providers may declare FanOut when their delivery semantics support it.
Preflight derives publication-scoped trigger claims and reports added, removed, retained, and conflicting claims. An Exclusive candidate is checked against all other authoritative slots in the active shell. The only excluded claimant is the old publication being replaced in the candidate's own slot. Sharing a workflow definition or executable artifact is not an exemption.
Trigger bindings and recurring schedules carry PublicationId and SlotId. Their identities and
store operations are publication-scoped so two named slots may point to the same artifact without
collapsing into one lifecycle.
Publishing performs a replacement in this order:
- Resolve policy and target slot.
- Compile and validate the candidate executable.
- Derive claims and preflight conflicts against current authoritative slots.
- Save or reuse the immutable artifact in a staged, creation-grace-protected state.
- Prepare every required serving projection without exposing it to routing.
- Compare-and-swap the slot using the revision observed by the attempt.
- As one logical activation, select the candidate, create its live Published source reference, retire the replaced publication and reference, and switch serving projection visibility.
Every successful activation or unpublish increments the slot revision. Concurrent attempts from the same observed revision have at most one winner. Conflict validation is repeated or protected inside the activation boundary so concurrent Exclusive claims cannot both activate after passing stale preflight reads.
If compilation, validation, preparation, activation, or compare-and-swap fails, the old publication remains authoritative and its serving projections remain active. A failed candidate is recorded as failed or cleaned up; it never partially displaces the current authority.
Unpublish clears slot authority and retires its publication through the same revisioned transition. Restore is a new authority transition: it rechecks artifact availability, policy, trigger conflicts, and projection preparation before compare-and-swap. It is not implemented by merely clearing a retirement timestamp.
When the slot store and every serving projection cannot share a transaction, Publishing records
durable PublicationProjectionIntent entries. An intent identifies the publication, projection kind,
operation, delivery status, and bounded retry diagnostics.
Intent delivery is idempotent by publication, projection kind, and operation, and is safe to replay
after process restart. Prepared candidate projections remain invisible. Until every required
preparation succeeds, the candidate is PendingProjection and the prior slot publication remains
authoritative. The API may report pending status; it must not report successful activation early.
The final slot compare-and-swap and the visibility switch must be atomic from a workflow starter's perspective. A reconciler repairs or removes orphaned prepared projections and converges delivered intent with slot authority. Projections always converge toward the slot; they never override it.
A Published WorkflowExecutableSourceReference carries its PublicationId and SlotId. It is live
publication provenance only while it is not retired and its publication is active. Publishing owns
creation, retirement, and restoration of these references. Runtime may expose them as read-only
executable provenance.
TestRun references remain outside publication slots: they identify a persisted Design version or draft snapshot, expire according to test-run policy, and do not grant production start authority.
Retiring or replacing a publication does not delete its executable. Existing workflow executions continue with their pinned artifact. In accordance with ADR 0040, garbage collection retains the union of artifacts named by live source references and artifacts pinned by retained workflow execution records. Completed, faulted, suspended, canceled, and running executions protect their artifact for as long as their execution record is retained.
- A
(WorkflowDefinitionId, SlotName)pair identifies exactly one slot. - A slot selects zero or one active publication, and a publication is active in at most one slot.
- Only the selected active publication contributes new-start routing projections.
- Ordinary publishing replaces
default; intentional coexistence uses explicit named slots. - An Exclusive stimulus has at most one authoritative claimant per shell; FanOut claims may coexist.
- A failed or losing candidate leaves prior slot authority and serving behavior unchanged.
- Publication identity and lifecycle never change immutable artifact identity.
- Source references and serving projections reflect slot authority; they do not create authority.
- Definition soft deletion does not implicitly unpublish.
- Replaced publications do not affect executions already pinned to their artifact.
- Keep append-only publishing as the default. Rejected because ordinary edits unintentionally preserve old triggers and make publication intent implicit.
- Delete the old executable during replacement. Rejected because artifacts may be shared and retained executions must continue to resume and remain inspectable.
- Use artifact or definition identity as authority. Rejected because neither distinguishes named publication intent, especially when content-addressed artifacts are reused.
- Best-effort index the candidate and retire the old publication afterward. Rejected because failure exposes duplicate, missing, or partially switched routing.
- Make every trigger Exclusive. Rejected because event and timer providers may intentionally support deterministic fan-out.
- Require a distributed transaction across all projections. Rejected as a universal requirement because supported providers may span transactional boundaries; durable intent and reconciliation preserve the invariant without imposing one storage technology.
Ordinary Elsa publishing regains intuitive replacement semantics while explicitly supporting the long-requested side-by-side scenario. Studio and other supported clients must preflight publication, show the resolved slot/action, require a name for coexistence, and surface Pending or Failed states.
Publishing gains durable slot, publication, policy, trigger-claim, and projection-intent models plus compare-and-swap persistence. Trigger and schedule stores gain publication-scoped operations, and providers must declare cardinality. Persistence providers need unique slot identities, revisioned writes, indexes, serialization versions, and restart-safe intent processing.
The activation path is more involved than append-only indexing, and temporarily prepared artifacts or projections may require reconciliation cleanup. In return, there is one auditable source of truth for which executable may start new work, concurrent publishers have a deterministic winner, partial failure cannot silently displace production behavior, and old executions remain safe.