@@ -21,6 +21,19 @@ Async authoring (experimental)
2121
2222This package supports authoring workflows with ``async def `` in addition to the existing generator-based orchestrators.
2323
24+ **When to use async workflows: **
25+
26+ Async workflows are a **special case ** for integrating external async libraries into the deterministic workflow
27+ execution path. Use async workflows when:
28+
29+ - You need to call async libraries that provide deterministic operations (e.g., async graph execution frameworks,
30+ async state machines, async DSL interpreters)
31+ - You're building workflow orchestration on top of existing async code that must run deterministically
32+ - You want to use ``async ``/``await `` syntax with durable task operations for code clarity
33+
34+ **Note: ** Most workflows should use regular (generator-based) orchestrators and call async I/O through activities.
35+ Async workflows don't run a real event loop - the durable task runtime drives execution deterministically.
36+
2437- Register async workflows using ``WorkflowRuntime.workflow `` (auto-detects coroutine) or ``async_workflow `` / ``register_async_workflow ``.
2538- Use ``AsyncWorkflowContext `` for deterministic operations:
2639
@@ -35,11 +48,18 @@ Interceptors (client/runtime/outbound)
3548--------------------------------------
3649
3750Interceptors provide a simple, composable way to apply cross-cutting behavior with a single
38- enter/exit per call. There are three types:
51+ enter/exit per call.
3952
40- - Client interceptors: wrap outbound scheduling from the client (schedule_new_workflow).
41- - Workflow outbound interceptors: wrap calls made inside workflows (call_activity, call_child_workflow).
42- - Runtime interceptors: wrap inbound execution of workflows and activities (before user code).
53+ **Inbound vs Outbound: **
54+
55+ - **Outbound **: Calls going OUT from your code (scheduling workflows, calling activities/children)
56+ - **Inbound **: Calls coming IN to execute your code (runtime invoking workflows/activities)
57+
58+ **Three interceptor types: **
59+
60+ - **Client interceptors **: wrap outbound scheduling from the client (``schedule_new_workflow ``)
61+ - **Workflow outbound interceptors **: wrap outbound calls made inside workflows (``call_activity ``, ``call_child_workflow ``)
62+ - **Runtime interceptors **: wrap inbound execution when the runtime invokes workflows and activities (before user code runs)
4363
4464Use cases include context propagation, request metadata stamping, replay-aware logging, validation,
4565and policy enforcement.
@@ -454,25 +474,12 @@ Recommended tracing restoration
454474- Suppress workflow spans during replay by checking ``input.ctx.is_replaying `` in runtime
455475 interceptors.
456476
457- Engine-provided tracing
458- ~~~~~~~~~~~~~~~~~~~~~~~
459-
460- - When available from the runtime, use engine-provided fields surfaced on the contexts instead of
461- reconstructing from headers/metadata:
462-
463- - ``ctx.trace_parent `` / ``ctx.trace_state `` (and the same on ``activity_ctx ``)
464- - ``ctx.workflow_span_id `` (identifier for the workflow span)
465-
466- - Interceptors should prefer these fields. Use headers/metadata only as a fallback or for
467- application-specific context.
468-
469477Execution info (minimal) and context properties
470478-----------------------------------------------
471479
472- ``execution_info `` is now minimal and only includes the durable ``inbound_metadata `` that was
480+ ``execution_info `` is minimal and only includes the durable ``inbound_metadata `` that was
473481propagated into this activation. Use context properties directly for all engine fields:
474482
475- - ``ctx.trace_parent ``, ``ctx.workflow_span_id ``, ``ctx.workflow_attempt `` on workflow contexts.
476483- Manage outbound propagation via ``ctx.set_metadata(...) `` / ``ctx.get_metadata() ``. The runtime
477484 persists and propagates these values through the metadata envelope.
478485
@@ -564,15 +571,6 @@ when_any losers diagnostics (integration)
564571
565572- When the sidecar exposes command diagnostics, you can assert only a single command set is emitted for a ``when_any `` (the orchestrator completes after the first winner without emitting cancels). Until then, unit tests assert single-yield behavior and README documents the expected semantics.
566573
567- Micro-bench guidance
568- --------------------
569-
570- - The coroutine-to-generator driver yields at each deterministic suspension point and avoids polling. In practice, overhead vs. generator orchestrators is negligible relative to activity I/O. To measure locally:
571-
572- - Create paired generator/async orchestrators that call N no-op activities and 1 timer.
573- - Drive them against a local sidecar and compare wall-clock per activation and total completion time.
574- - Ensure identical history/inputs; differences should be within noise vs. activity latency.
575-
576574Notes
577575-----
578576
0 commit comments