Skip to main content
Conseqa’s default effect execution is synchronous: execute_effect A; execute_effect B establishes the causal order complete(A) < start(B). Async execution relaxes that so multiple effects may run concurrently, but Conseqa keeps synchronization explicit through handles and join_all / race barriers. Async is an execution mode, not a new kind of effect.
Async execution is available for publication, request, and external effects. It is not available for outbox_write (which is atomic with its transaction commit) and not available for transaction, block, branch, or terminal steps.

Async launch steps

execute_effect_async

Launches an inline effect concurrently with the surrounding program.
The launch establishes only start(A) < start(B) between async A and any subsequent step B. It does not establish complete(A) < start(B). Any result binding — even for a result-bearing effect — is deferred to a synchronization barrier.

execute_effect_intent_async

Executes a previously established effect intent in async mode.
Semantics mirror execute_effect_intent, with the same handle rules as execute_effect_async.

Handles are semantic, not values

An async handle is an operation-local synchronization artifact. It has:
  • no schema
  • no field path
  • no ValueSource kind
Handles cannot be persisted, cannot be used as an idempotency key, cannot be returned from the operation, and cannot appear inside values derivations. They exist only to name a pending completion for a later join_all or race step.
Do not confuse an async handle with an effect_id. The effect_id is a stable execution-site identity that persists in the model file, appears in reports, and anchors keyed commit and intent identity. A handle is transient runtime plumbing scoped to one program execution.

Synchronization barriers

join_all

Waits for every referenced completion. Each entry may optionally bind the result of a result-bearing effect.
Semantics:
  • Establishes complete(H_i) < continuation for every referenced handle.
  • Establishes no relative order among the joined completions.
  • Empty handles is invalid.
  • No short-circuit: a failing Err on one joined effect does not cancel the others.
  • Result bindings become available to steps after the barrier through effect_result_ok / effect_result_err.

race

Waits for the first completion among two or more handles.
Semantics:
  • At least two handles required.
  • If bind is present, every candidate must be result-bearing and expose the same logical result contract (same ok schema and same err contract including disposition).
  • No cancellation of losing candidates; they continue to completion as separate work.
  • The winning completion is not replay-stable: which candidate wins may differ across attempts. A race-bound result is treated as an obstacle in idempotency and result-replay proofs (see Requirements).

Terminal does not join

An operation reaching return or complete does not implicitly join outstanding handles. Fire-and-forget async is a legal, deliberate modeling pattern:
If you need the effect to have completed before the operation terminates, add an explicit join_all before the terminal.

Program reachability

Reachability in the program still walks synchronous control. An unreachable synchronous step is unreachable even if an async handle from an earlier step is still pending:

Validation rules

The additional validation rules that apply to async programs:
  • Every referenced handle in a join_all or race must be established by a preceding async launch on every path reaching the barrier.
  • A handle may be joined or raced at most once on any given path.
  • A bind on join_all or race requires the underlying effect (or every candidate, for race) to be result-bearing.
  • Async launches never bind results directly.
  • Race binding produces a non-replay-stable value; downstream decisions that depend on it fail the control-leg check unless separately stabilized.

When to use async

Async execution is a modeling tool, not an optimization. Reach for it when the architecture genuinely runs work in parallel, when a race between primary and fallback is the semantic story, or when the operation needs to fire off best-effort side effects that must not block the terminal. Otherwise, synchronous execute_effect is simpler to reason about and yields stronger replay-stability facts by default.

Program Control

Synchronous step kinds, terminals, and decision steps.

Effects

Publication, request, external, and outbox write effect contracts.

Requirements

How replay-stability affects idempotency and result replay verdicts.

Value References

ValueRef kinds, including effect_result_ok and effect_result_err.