Changelog
July 29, 2026 · View on GitHub
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.1.30] - 2026-07-29
Release: https://crates.io/crates/duroxide/0.1.30
Changed
- Reserved the
sub::marker for runtime-generated sub-orchestration instance ids.Client::start_orchestrationandClient::start_orchestration_versionednow returnClientError::InvalidInputfor root instance ids that start withsub::or contain::sub::; other uses of::remain supported. Applications that used the reserved marker in root instance ids must rename those ids before upgrading. See docs/migration-guide.md for guidance. ctx.schedule_orchestration()andctx.schedule_orchestration_versioned()now enforce the same reserved-marker rule. These start a root instance with a caller-supplied id used verbatim, so they could previously squat an id the runtime would later generate for a sub-orchestration. They return(), so a violation panics, surfacing as a deterministic orchestration failure on the first execution.ctx.schedule_sub_orchestration_with_id()andctx.schedule_sub_orchestration_versioned_with_id()now reject explicit child ids that start withsub::. The returned future resolves immediately to anErrand nothing is scheduled or written to history. That prefix is a control signal the runtime reads to mean "auto-generated suffix", so such an id was previously rewritten rather than used verbatim (sub::my-childbecame{parent}::sub::my-child, and the internalsub::pending_placeholder shape was discarded outright). The::sub::infix remains valid for explicit child ids — the runtime generates it itself, e.g. a grandchild ofrootisroot::sub::2::sub::2.ctx.new_guid()now returns a standard UUID v4. The previous implementation derived the value fromSystemTime::now()nanoseconds plus a thread-local counter, which produced low-entropy, structured values (the leading groups were always zero and the rest was largely sequential). It now usesuuid::Uuid::new_v4(). The value is still recorded in history, so replays remain deterministic.- SQLite provider lock tokens now use a random UUID instead of
nanos + process id, removing a predictable-token pattern in work-item ownership checks.
Fixed
- Parent link lost when a sub-orchestration continued as new — A child that called
continue_as_newdropped its parent instance, scheduling event, and execution ids. When the child later completed or failed, the replacement execution could not notify the parent, leaving the parent waiting indefinitely. Continue-as-new work items now preserve the full parent link across child executions, including when the child continues as new in its first turn. The new fields are optional for wire compatibility with work items created by older runtimes. (#31) - Parent hang on sub-orchestration instance-id collision — When an auto-generated
child instance id already named a terminal instance, the scheduling parent could await
a completion that never arrived. The runtime now notifies the parent with a
sub-orchestration failure so it fails fast. The parent execution that scheduled the child
is stamped onto the child start at schedule time and persisted in the child's
OrchestrationStartedevent, so the failure (and all sub-orchestration completion/failure notifications) is routed to exactly that parent execution. This is correct across runtime restarts and multiple dispatcher nodes, and avoids a TOCTOU window where the parent's current execution at completion time could differ from the execution that scheduled the child. When the stamp is absent (children started by an older runtime, or work items from before this change), routing falls back to a durable provider read, keeping mixed-version clusters correct during rolling upgrades. - Sub-orchestration id reuse across continue-as-new — Child instance ids generated
after a parent
continue_as_newnow include the parent execution id ({parent}::sub::{execution_id}_{event_id}), preventing collisions with the terminal child of a previous iteration that schedules at the same position.
[0.1.29] - 2026-05-08
Release: https://crates.io/crates/duroxide/0.1.29
Proposal: Standard Async/Await Support
Changed
- Replay-safe orchestration combinators — Replaced the use of
futures::join_all,futures::join,futures::join3, andfutures::select_biased!insideOrchestrationContext::join,join2,join3,select2, andselect3with new crate-local implementations (PollAllJoin,PollAllJoin2,PollAllJoin3,Select2,Select3insrc/combinators.rs). The local implementations poll every pending child future on each replay pass, which is required for correctness under Duroxide's deterministic replay engine —futures::join_allswitches large fan-ins to a waker-drivenFuturesOrdered/FuturesUnorderedpath that relies on child wake notifications the replay engine intentionally does not drive. This eliminates a latent large-fan-in replay hang that surfaced at ≥ 1024 children. futurescrate is now optional — Moved to an optional dependency enabled only by theprovider-testfeature (provider validation code outside orchestration replay still usesfutureshelpers). Main-crate builds no longer pull in thefuturescrate by default, trimming the dependency tree for users who only need the runtime.
Added
- Large fan-in replay regression test — Added
large_fan_in_replay_regressioncovering 1024-child parallel fan-in intests/replay_engine/composition.rs. Verifies that the newPollAllJoinimplementation completes without deadlock or spurious nondeterminism errors.
Fixed
- Clippy
needless_borrowinpoll_once— Removed an unnecessary&inContext::from_wakercall insidereplay_engine.rs(&waker→waker).
[0.1.28] - 2026-04-23
Changed
- TLS backend — Switched the optional
sqlitefeature's SQLx runtime feature fromruntime-tokio-rustlstoruntime-tokio-native-tls. This eliminates the transitive dependency on theringcrate (not FIPS compliant for our policy requirements). The Linux crypto path now goes through OpenSSL vianative-tls; macOS uses Secure Transport, Windows uses SChannel. No source code or runtime behavior changes. (#12)
[0.1.27] - 2026-04-04
Added
- Orchestration runtime stats API — Added
Client::get_orchestration_stats()returningSystemStatsfor per-instance history and KV usage introspection. - Provider management contract for stats — Added
Provider::get_instance_stats()plus provider validation coverage for non-existent instances, history metrics, and KV metrics. - New orchestration stats sample coverage — Added management and e2e tests covering the new orchestration stats surface.
- Corrupted history validation tests — Added
test_read_corrupted_history_returns_errorandtest_read_with_execution_corrupted_history_returns_errorto the provider validation suite.
Changed
- Higher orchestration limits — Increased
MAX_CARRY_FORWARD_EVENTSfrom 20 to 100,MAX_KV_KEYSfrom 100 to 150, andMAX_KV_VALUE_BYTESfrom 16 KiB to 64 KiB. - Documentation refresh for stats and limits — Updated orchestration and provider docs to describe the new instance stats APIs, provider requirements, and revised limits.
Fixed
- SQLite get_instance_stats deserialization — Fixed silent
.ok()swallowing on carry_forward event parsing; errors now propagate asProviderError::permanent.
[0.1.26] - 2026-03-15
Release: https://crates.io/crates/duroxide/0.1.26
Proposal: KV Delta Table
Fixed
- KV read-modify-write replay poisoning — Orchestrations using read-modify-write patterns
on KV state (e.g.,
get_kv_value → compute → set_kv_valuein a loop with activities) would hit nondeterminism errors on replay. The provider snapshot was seedingkv_statewith the latest accumulated value instead of the prior-execution state.
Changed
- Two-table KV model — KV mutations during the current execution now go to a
kv_deltatable. The existingkv_storetable is only written at execution completion boundaries (Completed, ContinueAsNew, Failed). Replay seeding reads fromkv_storeonly (prior-execution state). Client reads mergekv_store + kv_deltafor a live view. execution_idcolumn dropped fromkv_store(SQLite reference provider) — No longer needed since execution pruning no longer touches KV entries.
Added
- 9 new provider validation tests for KV delta behavior:
test_kv_delta_snapshot_excludes_current_execution,test_kv_delta_snapshot_includes_completed_execution,test_kv_delta_client_reads_merged,test_kv_delta_tombstone_overrides_store,test_kv_delta_clear_all_tombstones_store,test_kv_delta_merged_on_completion,test_kv_delta_merged_on_can,test_kv_delta_delete_instance_cascades,test_kv_delta_prune_untouched_key_survives - New E2E test
sample_kv_read_modify_write_counter— Demonstrates the RMW counter pattern that previously triggered nondeterminism errors - New migration
20240112000000_add_kv_delta.sql— Createskv_deltatable, dropsexecution_idfromkv_store
[0.1.25] - 2026-03-14
Release: https://crates.io/crates/duroxide/0.1.25
Breaking Changes
- KV API renamed — All KV methods now use
kv_prefix for namespace clarity:ctx.set_value()→ctx.set_kv_value(),ctx.get_value()→ctx.get_kv_value(),ctx.clear_value()→ctx.clear_kv_value(),ctx.clear_all_values()→ctx.clear_all_kv_values()client.get_value()→client.get_kv_value(),client.wait_for_value()→client.wait_for_kv_value()- All typed variants similarly renamed
KeyValueSetevent now carrieslast_updated_at_ms: u64(defaults to 0 via#[serde(default)])OrchestrationItem.kv_snapshottype changed fromHashMap<String, String>toHashMap<String, KvEntry>whereKvEntry { value: String, last_updated_at_ms: u64 }- Provider trait: new required method
get_kv_all_values(instance)for bulk KV reads - KV pruning semantics: KV entries are now instance-scoped — execution pruning no longer deletes KV entries (previously, orphan keys were deleted when their execution was pruned)
MAX_KV_KEYSraised from 10 → 100
Added
- KV timestamps — Every
set_kv_value()call stampslast_updated_at_msfrom the runtime wall clock. Persisted in thekv_storetable and carried inKeyValueSetevents.- New migration:
20240111000000_add_kv_last_updated.sql - New struct:
KvEntry { value, last_updated_at_ms }inprovidersmodule
- New migration:
- Bulk KV reads —
ctx.get_kv_all_values(),ctx.get_kv_all_keys(),ctx.get_kv_length()for reading all KV state from within an orchestration - KV pruning by age —
ctx.prune_kv_values_updated_before(cutoff_ms)removes keys whoselast_updated_at_msis older than the cutoff. Keys written in the current turn are protected. - Client bulk read —
client.get_kv_all_values(instance)returns all KV pairs for an instance - Provider validation test renamed:
test_kv_prune_removes_orphan_keys→test_kv_prune_preserves_all_keysto reflect new instance-scoped KV lifetime
[0.1.24] - 2026-03-12
Release: https://crates.io/crates/duroxide/0.1.24
Proposal: Orchestration KV Store
Added
-
Durable KV store for per-instance state — Store key-value pairs scoped to orchestration instances via
ctx.set_value(key, value)/ctx.get_value(key). Values survive replay,continue_as_new, and are readable by external clients. Cross-instance reads viactx.get_value_from_instance(instance, key).ctx.set_value(),ctx.get_value(),ctx.clear_value(),ctx.clear_all_values()- Typed variants:
ctx.set_value_typed(),ctx.get_value_typed() - Cross-instance:
ctx.get_value_from_instance(),ctx.get_value_from_instance_typed() - Client API:
client.get_value(),client.get_value_typed(),client.wait_for_value(),client.wait_for_value_typed() - Provider trait:
get_kv_value()for materialized KV reads - SQLite migration
20240110000000_add_kv_store.sqladdingkv_storetable - KV materialization in
ack_orchestration_item(KeyValueSet, KeyValueCleared, KeyValuesCleared) - KV snapshot loading in
fetch_orchestration_item - Replay engine: KV action matching with nondeterminism detection
- Limits:
MAX_KV_KEYS=10,MAX_KV_VALUE_BYTES=16KB - Execution ID tracking (last-writer-wins) for pruning safety
- Instance deletion cascades to KV cleanup
-
26 provider validation tests for KV store (
src/provider_validation/kv_store.rs) -
15 replay engine tests for KV action matching and nondeterminism detection
-
45 E2E tests for KV store including single-thread, stress, cross-instance, sub-orchestration isolation, and request/response patterns
-
2 serde backward-compatibility tests for KV event kinds
Documentation
- Updated ORCHESTRATION-GUIDE.md with KV Store API reference and Client KV operations
- Updated provider-implementation-guide.md with
kv_storetable schema,get_kv_value(), KV materialization in ack, and validation checklist - Updated provider-testing-guide.md with KV test category (176 → 202 total tests)
- Updated README.md with KV store feature
[0.1.23] - 2026-03-07
Release: https://crates.io/crates/duroxide/0.1.23
Added
- Provider validation test for tag preservation through ack — New test
test_tag_preserved_through_ack_orchestration_itemverifies that tags on worker items survive theack_orchestration_itempath (orchestrator ack → worker queue fetch with tag filter). Catches providers that drop the tag column during worker item insertion.
Fixed
- Flaky
sample_config_hot_reload_persistent_events_fstest — Replaced fixedtokio::time::sleep(300ms)delay withwait_for_historypolling that waits for thecycle_0activity to be scheduled before sending the mid-flight event. Increased drain timeouts (50ms → 100ms) and cycle timer (100ms → 1s) to provide reliable timing margins.
Documentation
- Updated provider-testing-guide: tag filtering tests 9 → 10, total test count updated to 176
[0.1.22] - 2026-03-07
Release: https://crates.io/crates/duroxide/0.1.22
Proposal: Activity Tags
Added
-
Activity tag routing for worker specialization — Route activities to specialized worker pools via
.with_tag("gpu")onDurableFuture. Workers subscribe to tags viaRuntimeOptions { worker_tag_filter: TagFilter::tags(["gpu"]) }. Supports five filter modes:DefaultOnly,Tags,DefaultAnd,Any, andNone(orchestrator-only). Tags compose with sessions, retry, join, select2, and cancellation.TagFiltertype withmatches(), constructor validation (1–5 tags, no empty sets)MAX_WORKER_TAGS(5) andMAX_TAG_NAME_BYTES(256) limits enforced at runtimeActivityContext::tag()accessor for activity handlers to inspect their routing tag- SQLite migration adding
tagcolumn + index toworker_queue - Provider trait
fetch_work_item()extended withtag_filterparameter - Tag included in replay determinism checks (tag mismatch → nondeterminism error)
- Tag propagated through
Action::CallActivity→EventKind::ActivityScheduled→WorkItem::ActivityExecute activity_taglabel added to activity metrics (duroxide_activity_executions_total,duroxide_activity_duration_seconds)activity_tagspan attribute on all worker tracing
-
9 provider validation tests for tag filtering (
src/provider_validation/tag_filtering.rs) -
11 tag serde + e2e tests in
tests/tag_serde_tests.rs(serde roundtrip, backward compat, routing, starvation timeout, dual-runtime cooperation, oversized tag rejection, boundary tag, tag inActivityContext, multi-worker separation) -
5 replay engine tests for tag determinism (tag mismatch, tag change, tag removal → nondeterminism)
-
3 e2e sample tests in
tests/e2e_samples.rs(heterogeneous workers, starvation-safe timeout pattern, dual-runtime tag cooperation) -
Orphan event dropping test (
events_enqueued_before_start_orchestration_are_dropped) intests/queue_event_tests.rs
Fixed
- Flaky queue event tests —
multi_queue_staggered_deliveryandmulti_queue_isolation_and_independent_fifofixed by movingstart_orchestrationbeforeenqueue_event(events enqueued before orchestration start are dropped as orphans per 0.1.21).persistent_event_survives_select_cancellationsplit into two deterministic tests: same-batch (dispatcher stopped during enqueue) and late-extra-discarded (event after terminal).
Changed
Provider::fetch_work_item()signature now requirestag_filter: &TagFilterparameter (breaking for provider implementors)WorkItem::ActivityExecutegainstag: Option<String>fieldEventKind::ActivityScheduledgainstag: Option<String>field (backward-compatible via#[serde(default)])ActivityContext::new_with_cancellation()gainstag: Option<String>parameter
Documentation
- Updated ORCHESTRATION-GUIDE with activity tags section (routing,
.with_tag(),TagFiltervariants, starvation warning, replay determinism) - Updated provider-implementation-guide with
fetch_work_itemtag filtering, validation checklist - Updated provider-testing-guide with tag filtering test category (18th category, 166 tests)
- Updated metrics-specification with
activity_taglabel on activity metrics - Updated activity-tags proposal to reflect implementation (
TagFilter::Any,worker_tag_filternaming, empty-set rejection) - Added flaky test investigation policy to
.github/copilot-instructions.md
[0.1.21] - 2026-03-06
Release: https://crates.io/crates/duroxide/0.1.21
Fixed
- Orphan queue message handling —
QueueMessageitems enqueued before an orchestration starts are now dropped (deleted) with a warning instead of being left in the queue (which caused a busy-loop in the SQLite provider) or silently lost (in PG providers). Non-QueueMessage work items (e.g.,CancelInstance) that race withStartOrchestrationare correctly kept in the queue for retry.
Added
test_orphan_queue_messages_dropped— New provider validation test verifying that QueueMessage items for non-existent instances are dropped, while QueueMessage items for existing instances are kept and returned.
Changed
sample_config_hot_reload_persistent_events_fse2e test adapted to start the orchestration before enqueuing events (matching the corrected orphan message semantics).
Documentation
- Updated ORCHESTRATION-GUIDE.md, external-events.md, provider-implementation-guide.md, and provider-testing-guide.md to document pre-start event drop behavior.
[0.1.20] - 2026-02-21
Release: https://crates.io/crates/duroxide/0.1.20
Changed (Breaking for Provider Implementors)
- Custom status as history events —
set_custom_status()andreset_custom_status()now emitCustomStatusUpdatedhistory events instead of writing toExecutionMetadata.custom_status. Custom status is now fully durable, replayable, and deterministic across turns.- Removed
CustomStatusUpdateenum from providers - Removed
ExecutionMetadata.custom_statusfield - Provider
ack_orchestration_item()must now scanhistory_deltafor the lastCustomStatusUpdatedevent and apply it to the instances table (see provider-implementation-guide.md) initial_custom_statusfield added toOrchestrationStartedevent andContinueAsNewwork item for carry-forward across continue-as-new boundaries
- Removed
Added
get_custom_status()on OrchestrationContext — Read the current custom status value, reflecting allset_custom_status/reset_custom_statuscalls across turns and CAN boundariesshort_poll_threshold()on ProviderFactory — Configurable timing for short polling validation tests; remote-database providers can override with higher values (closes #51)test_orphan_activity_after_instance_force_deletion— Provider validation test verifying graceful handling of activities orphaned by instance force-deletion (closes #37)
Fixed
test_cancelling_nonexistent_activities_is_idempotentnow usesexecution_id: 1instead of99to correctly validate same-execution cancellation semantics (closes #40)- Removed dead
ActivityContext::newconstructor (unused, runtime usesnew_with_cancellation) - Removed unused
clippy::clone_on_ref_ptrsuppression from observability.rs (closes #48)
[0.1.19] - 2026-02-20
Release: https://crates.io/crates/duroxide/0.1.19
Proposal: Custom Status Progress Proposal: External Event Semantics Proposal: Persistent Event Queuing
Added
-
Event Queue API — Persistent FIFO event queues that survive
continue_as_newctx.dequeue_event(queue)andctx.dequeue_event_typed::<T>(queue)for orchestrationsclient.enqueue_event(instance, queue, data)andclient.enqueue_event_typed::<T>()for clients- FIFO ordering with buffering — messages can arrive before orchestration subscribes
- Queue messages carry forward across continue-as-new boundaries
-
Custom Status — Orchestration progress reporting visible to external clients
ctx.set_custom_status(json)publishes structured progress from orchestrationsclient.wait_for_status_change(instance, version, poll, timeout)for efficient polling- Status persists across continue-as-new boundaries
- New Provider trait methods:
set_custom_status(),get_custom_status() - Schema migration
20240108000000_add_custom_status.sql
-
Retry on Session — Combine retry policies with session affinity (closes #56)
ctx.schedule_activity_with_retry_on_session(name, input, policy, session_id)ctx.schedule_activity_with_retry_on_session_typed::<In, Out>()- All retry attempts pinned to the same worker session
-
Typed event helpers —
client.raise_event_typed::<T>()andclient.enqueue_event_typed::<T>() -
Provider validation —
test_prune_bulk_includes_running_instancescatches providers that exclude Running instances from bulk prune (closes #50) -
Scenario test — Copilot Chat pattern: multi-turn chat using dequeue_event + set_custom_status + CAN
Changed
- Renamed persistent event internals:
ExternalRaisedPersistent→QueueMessage,ExternalSubscribedPersistent→QueueSubscribed
Deprecated
client.raise_event_persistent()— useclient.enqueue_event()insteadctx.schedule_wait_persistent()— usectx.dequeue_event()instead
[0.1.18] - 2026-02-16
Release: https://crates.io/crates/duroxide/0.1.18
Proposal: Activity Implicit Sessions v2
Added
-
Activity Session Affinity — Route activities to the same worker for in-memory state reuse
ctx.schedule_activity_on_session(name, input, session_id)pins activities by session IDctx.schedule_activity_on_session_typed()for serde-based typed inputs/outputsActivityContext::session_id()getter for process-local state lookup- Two-timeout model:
session_lock_timeout(heartbeat lease) +session_idle_timeout(inactivity expiry) - Automatic session lifecycle: implicit creation, heartbeat renewal, idle unpin, crash recovery
SessionTrackerenforcesmax_sessions_per_runtimeacross all worker slots via RAII guardsworker_node_idoption for stable session identity across restarts (e.g., K8s StatefulSet pods)- Session manager background task for lock renewal and orphan cleanup
-
Provider API changes (required)
fetch_work_item()gainssession: Option<&SessionFetchConfig>parameter for session routing- New
renew_session_lock()method — batched heartbeat for owned non-idle sessions - New
cleanup_orphaned_sessions()method — sweep expired session rows with no pending work ack_work_item()andrenew_work_item_lock()piggybacklast_activity_atupdates (guarded bylocked_until)
-
New
RuntimeOptionsfieldssession_lock_timeout(default 30s),session_lock_renewal_buffer(default 5s)session_idle_timeout(default 5min),session_cleanup_interval(default 5min)max_sessions_per_runtime(default 10),worker_node_id(default None)
-
33 provider validation tests for session routing, locks, races, and cross-concern interactions
-
22 E2E tests for single/multi-worker, fan-out, CAN, heterogeneous scenarios
-
Schema migration
20240107000000_add_sessions.sql— sessions table + worker_queue.session_id
Changed
session_id: Option<String>added toAction::CallActivity,EventKind::ActivityScheduled, andWorkItem::ActivityExecute(backward compatible viaserde(default))- Existing
schedule_activitycalls are completely unaffected (session_id = None) - Documentation updated across ORCHESTRATION-GUIDE, provider-implementation-guide, provider-testing-guide, README, and migration-guide
[0.1.17] - 2026-02-09
Release: https://crates.io/crates/duroxide/0.1.17
Proposal: Provider Capability Filtering
Added
-
Provider Capability Filtering (Phase 1) — Safe rolling upgrades in mixed-version clusters
- Orchestration dispatcher passes a version filter to the provider so it only returns
executions whose pinned
duroxide_versionfalls within the runtime's supported range - SQL-level filtering applied before lock acquisition and history deserialization
- NULL pinned version treated as always compatible (backward compat with pre-migration data)
RuntimeOptions::supported_replay_versionsfor custom version range configuration- Defense-in-depth: runtime-side compatibility check after fetch with 1-second abandon delay
- Startup log declaring supported version range; warning log on incompatible-version abandon
- Orchestration dispatcher passes a version filter to the provider so it only returns
executions whose pinned
-
New types:
SemverVersion,SemverRange,DispatcherCapabilityFilter,current_build_version() -
Provider API change:
fetch_orchestration_item()gainsfilter: Option<&DispatcherCapabilityFilter>parameter -
History deserialization contract — Providers must surface deserialization errors (not silently drop events)
history_errorfield on fetched items for deserialization failures- Transaction commits lock + attempt_count before returning errors (enables poison path)
-
ProviderFactory test helpers —
corrupt_instance_history()andget_max_attempt_count()optional methods for provider-agnostic deserialization contract tests -
38 new tests — 20 provider validation + 18 e2e scenario tests covering filtering, rolling deployment routing, metadata/migration, ContinueAsNew isolation, drain procedures, and observability
Changed
- Migration:
20240106000000_add_pinned_version.sqladdsduroxide_version_major/minor/patchcolumns toexecutionstable - Provider validation test total: 114 tests (up from 94)
[0.1.16] - 2026-02-02
Release: https://crates.io/crates/duroxide/0.1.16
Proposal: Persist Cancellation Decisions in History
Added
-
Cancellation History Events - Record cancellation decisions as durable history breadcrumbs
- New
ActivityCancelRequestedandSubOrchestrationCancelRequestedevent kinds - Dropped futures (select losers, terminal cleanup) now recorded in history
- Enables observability: history answers "was this cancelled?"
- Enables replay determinism: detect when cancellation decisions differ on replay
- Idempotent: side-channel cancellations only emitted once per decision
- New
-
Nondeterminism Tests for Completion Validation - 12 new tests covering:
- Completion kind mismatches (timer/activity/sub-orchestration cross-checks)
- Duplicate completion detection for closed schedules
- OrchestrationChained mismatch validation
Fixed
- Duplicate Completion Detection - Fixed oversight where
open_schedules.remove()was never called after delivering completions in replay engine- Previously, duplicate completions in history were silently accepted
- Now properly triggers nondeterminism error on duplicate completions
Changed
- Housekeeping - Moved implemented/rejected proposals to
docs/proposals-impl/:metrics-facade-migration.md(implemented)replay-simplification-PROGRESS.md(completed)activity-cancellation-queue-flag.md(rejected/superseded by lock-stealing)
[0.1.15] - 2026-01-30
Release: https://crates.io/crates/duroxide/0.1.15
Changed
- Simplified Metrics Facade - Internal observability uses consistent atomic counters with a cleaner facade pattern
Added
- Code Coverage Improvements - Test coverage improved to 91.9% with better organization
- New provider validation tests for error handling, management interface, and observability
- Removed duplicate tests that overlapped with provider validation suite
- Code Coverage Guide - New
docs/code-coverage-guide.mdwith llvm-cov setup instructions - Copilot Skill for Coverage - AI assistant skill for code coverage workflows
Fixed
- README Markdown Formatting - Fixed section heading syntax for better rendering
[0.1.14] - 2026-01-24
Release: https://crates.io/crates/duroxide/0.1.14
Fixed
- Fire-and-Forget Orchestrations Now Record History Events -
ctx.schedule_orchestration()(detached/chained orchestrations) now correctly createsOrchestrationChainedevents in history- Previously, these fire-and-forget calls were not recorded, breaking determinism detection on replay
- If an orchestration scheduled a detached orchestration followed by an activity, replay would fail with nondeterminism error
- Added proper action-to-event conversion and event matching in replay engine
Added
- Action-to-Event Recording Tests - New test module
tests/replay_engine/action_to_event.rsverifying all scheduling actions create corresponding history events - E2E Test for Detached + Activity Pattern -
sample_detached_then_activity_fsvalidates the fix end-to-end
[0.1.13] - 2026-01-24 [YANKED]
Release: https://crates.io/crates/duroxide/0.1.13
Proposal: System Calls as Real Activities
Changed
-
System Calls Reimplemented as Regular Activities -
ctx.new_guid()andctx.utc_now()now use normal activity infrastructure- Simplifies replay engine by removing special-case SystemCall handling
- Fixes determinism bugs where syscalls returned fresh values on replay
- Reserved activity prefix
__duroxide_syscall:prevents user collisions - Builtin activities injected automatically at runtime startup
-
API Rename:
utcnow()→utc_now()- Consistent with Rust naming conventions
Added
- Reserved Activity Prefix Validation -
ActivityRegistryrejects names starting with__duroxide_syscall: - Comprehensive Syscall Tests - Replay determinism, ordering, single-thread mode, cancellation
Removed
SystemCallvariants fromAction,EventKind,CompletionResult- SystemCall handling from replay engine (no more re-poll loop)
EVENT_TYPE_SYSTEM_CALLfrom sqlite provider
Documentation
- Updated ORCHESTRATION-GUIDE and durable-futures-internals for new syscall semantics
- Reorganized proposals: moved 11 implemented proposals to
docs/proposals-impl/ - Updated merge prompt to require squash-only merges
Breaking Changes
utcnow()renamed toutc_now()- update all call sites- Histories containing
SystemCallevents will not replay (pre-1.0, acceptable)
[0.1.12] - 2026-01-23
Release: https://crates.io/crates/duroxide/0.1.12
Added
-
Unobserved Future Cancellation - Futures that are scheduled but never awaited are now properly cancelled
- New
DurableFutureimplementation with proper drop semantics - Cancellation events recorded in history for deterministic replay
- Comprehensive test coverage in
tests/replay_engine/unobserved_futures.rs
- New
-
AI Skills System - New
docs/skills/folder for AI coding assistant context- Installation instructions for VS Code Copilot, Claude Code, and Cursor
duroxide-provider-implementationskill for provider developers
-
Provider Validation - New cancellation validation tests
test_activity_cancellation_via_lock_stealing- Additional lock stealing edge case tests
Changed
-
Major Documentation Refactor
- Rewrote
provider-implementation-guide.mdwith better structure and pedagogy - Rewrote
architecture.mdwith cleaner ASCII diagrams (removed mermaid) - Merged
replay-engine.mdintodurable-futures-internals.md - Added long-polling vs short-polling explanation
- Added Performance Considerations and ProviderAdmin sections
- Rewrote
-
Simplified ActivityRegistry API - Now takes value instead of Arc
-
Improved Dispatcher Backoff Logic - Better stale activity handling
Fixed
- Polling Model Documentation - Corrected to multi-poll (not single-poll) model
Code Statistics (vs v0.1.11)
| Area | Files | Insertions | Deletions | Net |
|---|---|---|---|---|
| Core (src/) | 15 | +2,355 | -1,999 | +356 |
| Tests (tests/) | 63 | +7,616 | -3,353 | +4,263 |
| Docs (docs/) | 43 | +17,477 | -4,004 | +13,473 |
| Total | 134 | +17,459 | -9,536 | +7,923 |
[0.1.11] - 2026-01-07
Release: https://crates.io/crates/duroxide/0.1.11
Fixed
-
Issue #49: WorkItemReader version extraction during completion-only replay
Fixed a bug where
WorkItemReaderdid not extract all fields from history during completion-only replay (when noStartorContinueAsNewitem is present in the work item batch). This caused nondeterminism errors when:- A versioned orchestration was started
- The runtime restarted (or lock expired) mid-execution
- Activity completion arrived without a start item
- The runtime incorrectly used the
Latestversion policy instead of the version recorded in history
The fix extracts all tuple fields (
orchestration_name,input,version,parent_instance,parent_id) fromHistoryManagerduring completion-only replay, ensuring deterministic handler resolution.
Added
- New scenario tests for issue #49 regression prevention:
e2e_replay_completion_only_must_use_version_from_history- First execution replaye2e_replay_completion_only_after_can_must_use_version_from_history- Nth execution (after CAN) replay- Unit tests verifying all
WorkItemReadertuple fields are correctly extracted
[0.1.10] - 2026-01-06
Release: https://crates.io/crates/duroxide/0.1.10
Added
-
Rolling Deployment Support - Exponential backoff for unregistered handlers
Unregistered orchestrations and activities now use exponential backoff instead of immediate failure, enabling graceful rolling deployments in multi-node clusters:
- Messages abandoned with backoff (1s → 2s → 4s → ... up to 60s max)
- Bounce between nodes until one with the handler registered picks it up
- Eventually fail as
ErrorDetails::Poisonif handler never becomes available - Configurable via
UnregisteredBackoffConfig(defaults: 1s base, 60s max, 6 exponent cap)
-
New scenario tests for rolling deployments
e2e_rolling_deployment_new_activity- Multi-node deployment with new activitye2e_rolling_deployment_version_upgrade- Version upgrade via continue-as-new
-
Consolidated unregistered handler tests in
tests/unregistered_backoff_tests.rsunknown_version_fails_with_poison- Version mismatch handlingcontinue_as_new_to_missing_version_fails_with_poison- CAN to missing versiondelete_poisoned_orchestration- Cleanup after poison- Plus existing backoff behavior tests
Changed
- BREAKING:
ConfigErrorKind::MissingVersionremoved - unregistered handlers now use backoff/poison path config_errormetric now only tracks nondeterminism (unregistered handlers result inpoison)- Updated
docs/metrics-specification.mdwith new error type behaviors - Updated
docs/ORCHESTRATION-GUIDE.mderror handling section
Removed
tests/unknown_activity_tests.rs- consolidated intounregistered_backoff_tests.rstests/unknown_orchestration_tests.rs- consolidated intounregistered_backoff_tests.rs
[0.1.9] - 2026-01-05
Release: https://crates.io/crates/duroxide/0.1.9
Added
-
Management API for Instance Deletion and Pruning - Comprehensive instance lifecycle management
Client API:
delete_instance(id, force)- Delete single instance with cascadingdelete_instance_bulk(filter)- Bulk delete with filters (IDs, timestamp, limit)prune_executions(id, options)- Prune old executions from long-running instancesprune_executions_bulk(filter, options)- Bulk prune across multiple instancesget_instance_tree(id)- Inspect instance hierarchy before deletion
Provider API (ProviderAdmin trait):
delete_instance(id, force)- Provider-level single deletiondelete_instance_bulk(filter)- Provider-level bulk deletiondelete_instances_atomic(ids)- Atomic batch deletion for cascadingprune_executions(id, options)- Provider-level pruningprune_executions_bulk(filter, options)- Provider-level bulk pruningget_instance_tree(id)- Provider-level tree traversallist_children(id)- List direct child sub-orchestrationsget_parent_id(id)- Get parent instance ID
Safety Guarantees:
- Running instances protected (skip or error based on API)
- Current execution never pruned
- Sub-orchestrations cannot be deleted directly (must delete root)
- Atomic cascading deletes (all-or-nothing)
- Force delete available for stuck instances
-
102 new provider validation tests - Deletion, bulk deletion, pruning, cascading deletes, filter combinations, safety tests
Changed
- Provider implementation guide with deletion/pruning contracts
- Provider testing guide updates
- Continue-as-new docs with pruning section
- README instance management section
- Enhanced management-api-deletion proposal with force delete semantics
[0.1.8] - 2026-01-02
Release: https://crates.io/crates/duroxide/0.1.8
Added
-
Lock-stealing activity cancellation - New mechanism for cancelling in-flight activities
- Activities are cancelled by deleting their worker queue entries ("lock stealing")
- Workers detect cancellation when lock renewal fails (entry missing)
- More efficient than polling execution state on every renewal
- Enables batch cancellation of multiple activities atomically
-
ScheduledActivityIdentifier- New struct for identifying activities in worker queue- Fields:
instance(String),execution_id(u64),activity_id(u64) - Used by
ack_orchestration_itemto specify activities to cancel - Exported from
duroxide::providers
- Fields:
-
Provider validation tests for lock-stealing - 5 new tests
test_cancelled_activities_deleted_from_worker_queue- Verify deletion during acktest_ack_work_item_fails_when_entry_deleted- Verify permanent error on stolen locktest_renew_fails_when_entry_deleted- Verify renewal fails on stolen locktest_cancelling_nonexistent_activities_is_idempotent- Verify no error for missing entriestest_batch_cancellation_deletes_multiple_activities- Verify batch deletion
-
Worker queue activity identity columns - Store activity identity for cancellation
- New migration:
20240104000000_add_worker_activity_identity.sql - SQLite provider stores
instance_id,execution_id,activity_idon ActivityExecute items
- New migration:
Changed
-
BREAKING:
Provider::ack_orchestration_itemsignature changed- Added 7th parameter:
cancelled_activities: Vec<ScheduledActivityIdentifier> - Provider must delete matching worker queue entries atomically in same transaction
- Added 7th parameter:
-
BREAKING:
Provider::fetch_work_itemreturn type simplified- Changed from
(WorkItem, String, u32, ExecutionState)to(WorkItem, String, u32) - Removed
ExecutionState- cancellation detected via lock renewal failure instead
- Changed from
-
BREAKING:
Provider::renew_work_item_lockreturn type changed- Changed from
Result<ExecutionState, ProviderError>toResult<(), ProviderError> - Failure indicates lock was stolen (activity cancelled) or expired
- Changed from
-
BREAKING:
Provider::ack_work_itemmust fail when entry missing- Returns permanent error if work item entry was deleted (lock stolen)
- Signals to worker that activity was cancelled
-
Provider validation test count: 80 tests (up from 75)
Removed
ExecutionStateenum removed from Provider API - No longer needed- Was used for state-polling cancellation approach
- Lock-stealing provides more efficient cancellation mechanism
- Provider validation tests for ExecutionState still exist (legacy support during migration)
Migration Guide
Provider implementers - Required changes:
- Update
ack_orchestration_itemsignature:
async fn ack_orchestration_item(
&self,
lock_token: &str,
execution_id: u64,
history_delta: Vec<Event>,
worker_items: Vec<WorkItem>,
orchestrator_items: Vec<WorkItem>,
metadata: ExecutionMetadata,
cancelled_activities: Vec<ScheduledActivityIdentifier>, // NEW
) -> Result<(), ProviderError>;
- Update
fetch_work_itemreturn type:
async fn fetch_work_item(...) -> Result<Option<(WorkItem, String, u32)>, ProviderError>;
// Removed ExecutionState from tuple
- Update
renew_work_item_lockreturn type:
async fn renew_work_item_lock(...) -> Result<(), ProviderError>;
// Returns () instead of ExecutionState
- Update
ack_work_itemto fail on missing entry:
// Return error if entry not found (lock was stolen)
if rows_affected == 0 {
return Err(ProviderError::permanent("ack_work_item", "Entry not found (lock stolen)"));
}
- Store activity identity on worker queue entries:
-- Add columns to worker_queue table
ALTER TABLE worker_queue ADD COLUMN instance_id TEXT;
ALTER TABLE worker_queue ADD COLUMN execution_id INTEGER;
ALTER TABLE worker_queue ADD COLUMN activity_id INTEGER;
-- Add index for efficient cancellation
CREATE INDEX idx_worker_queue_activity ON worker_queue(instance_id, execution_id, activity_id);
- Implement batch deletion in
ack_orchestration_item:
// Delete cancelled activities atomically within the ack transaction
for activity in cancelled_activities {
DELETE FROM worker_queue
WHERE instance_id = activity.instance
AND execution_id = activity.execution_id
AND activity_id = activity.activity_id;
}
[0.1.7] - 2025-12-28
Release: https://crates.io/crates/duroxide/0.1.7
Added
-
Cooperative activity cancellation - Activities can detect when their parent orchestration has been cancelled or completed
ActivityContextnow provides cancellation awareness viais_cancelled()andcancelled()methods- Activities can cooperatively respond to cancellation by checking the cancellation token
- Use
tokio::select!withctx.cancelled()for responsive cancellation in async activities - Configurable grace period before forced activity termination
-
ExecutionState enum - Providers now report orchestration state with activity work items
ExecutionState::Running- Orchestration is active, activity should proceedExecutionState::Terminal { status }- Orchestration completed/failed/continued, activity result won't be observedExecutionState::Missing- Orchestration instance deleted, activity should abort
-
Provider validation tests for cancellation - 13 new tests in
provider_validation::cancellation- Verifies
ExecutionStateis correctly returned byfetch_work_itemandrenew_work_item_lock - Tests for Running, Terminal (Completed/Failed/ContinuedAsNew), and Missing states
- Tests for state transitions during activity execution
- Verifies
-
Single-threaded runtime support - Full compatibility with
tokio::runtime::Builder::new_current_thread()- Essential for embedding in single-threaded environments (e.g., pgrx PostgreSQL extensions)
- New scenario tests in
tests/scenarios/single_thread.rs - Use
RuntimeOptions { orchestration_concurrency: 1, worker_concurrency: 1, .. }for 1x1 mode
-
Configurable wait timeout for stress tests -
StressTestConfig::wait_timeout_secsfield- Default: 60 seconds
- Increase for high-latency remote database providers
- Uses
#[serde(default)]for backward compatibility with existing configs
Changed
-
BREAKING:
Provider::fetch_work_itemnow returns 4-tuple:(WorkItem, String, u32, ExecutionState)- Added
ExecutionStateas fourth element to report parent orchestration state - Required for activity cancellation support
- Added
-
BREAKING:
Provider::renew_work_item_locknow returnsExecutionStateinstead of()- Allows runtime to detect orchestration state changes during long-running activities
- Triggers cancellation token when orchestration becomes terminal
-
Provider validation test count increased from 62 to 75
-
Documentation updates:
- Added "Runtime Polling Configuration" section to provider-implementation-guide
- Default polling interval (10ms) is aggressive; configure for remote/cloud providers
- Updated provider-testing-guide with new test count and wait_timeout_secs examples
Fixed
-
test_worker_lock_renewal_extends_timeout - Fixed timing sensitivity (GitHub #34)
- Test now creates proper orchestration with Running status before testing renewal
- Uses 0.6x pre-renewal wait + 0.4x post-renewal wait for reliable timing
-
test_multi_threaded_lock_expiration_recovery - Fixed race condition (GitHub #32)
- Uses
tokio::sync::Barrierto synchronize thread start times - Eliminates false failures from connection pool cold-start latency
- Uses
Migration Guide
Provider implementers:
// fetch_work_item now returns ExecutionState
async fn fetch_work_item(
&self,
lock_timeout: Duration,
poll_timeout: Duration,
) -> Result<Option<(WorkItem, String, u32, ExecutionState)>, ProviderError>;
// renew_work_item_lock now returns ExecutionState
async fn renew_work_item_lock(
&self,
token: &str,
extend_for: Duration,
) -> Result<ExecutionState, ProviderError>;
Determining ExecutionState:
// Query the execution status for the work item's instance/execution_id
let state = match (instance_exists, execution_status) {
(false, _) => ExecutionState::Missing,
(true, None) => ExecutionState::Missing,
(true, Some(status)) if status == "Running" => ExecutionState::Running,
(true, Some(status)) => ExecutionState::Terminal { status },
};
Activity authors (using cancellation):
activities.register("LongTask", |ctx: ActivityContext, input: String| async move {
for item in items {
// Check cancellation periodically
if ctx.is_cancelled() {
return Err("Cancelled".into());
}
process(item).await;
}
Ok("done".into())
});
// Or use select! for responsive cancellation
activities.register("AsyncTask", |ctx: ActivityContext, input: String| async move {
tokio::select! {
result = do_work(input) => result,
_ = ctx.cancelled() => Err("Cancelled".into()),
}
});
[0.1.6] - 2025-12-21
Release: https://crates.io/crates/duroxide/0.1.6
Added
-
Large payload stress test - New memory-intensive stress test scenario
- Tests large event payloads (10KB, 50KB, 100KB) and longer histories (~80-100 events)
- New binary:
large-payload-stressfor running the test standalone - Uses the same
ProviderStressFactorytrait as parallel orchestrations test - Configurable payload sizes and activity/sub-orchestration counts
- See
docs/provider-testing-guide.mdfor usage
-
Stress test monitoring - Resource usage tracking in
run-stress-tests.sh- Peak RSS (Resident Set Size) measurement
- Average CPU usage tracking
- Sampling every 500ms during test execution
- New documentation:
STRESS_TEST_MONITORING.md - Supports
--parallel-onlyand--large-payloadflags
Changed
-
Memory optimization - Reduced allocations in history processing
- Added
HistoryManager::full_history_len()- get count without allocation - Added
HistoryManager::is_full_history_empty()- check emptiness without allocation - Added
HistoryManager::full_history_iter()- iterate without allocation - Updated runtime to use efficient methods in hot paths
- Improved child cancellation to use iterator instead of collecting full history
- Added
-
Orchestration naming - Renamed "FanoutWorkflow" to "FanoutOrchestration" for consistency
Fixed
- Child sub-orchestration cancellation now uses iterator-based approach for better memory efficiency
[0.1.5] - 2025-12-18
Release: https://crates.io/crates/duroxide/0.1.5
Added
-
Provider identity API - Providers now expose
name()andversion()methodsProvider::name()returns provider name (e.g., "sqlite")Provider::version()returns provider version- Default implementations return "unknown" and "0.0.0"
- SQLite provider returns "sqlite" and the crate version
-
Runtime startup banner - Version information logged on startup
- Logs duroxide version and provider name/version
- Example:
duroxide runtime (0.1.4) starting with provider sqlite (0.1.4)
-
Worker queue visibility control - Worker queue now uses
visible_atfor delayed visibility- Added
visible_atcolumn to worker_queue (matches orchestrator queue pattern) abandon_work_itemwith delay now setsvisible_atinstead of keepinglocked_until- Cleaner semantics:
visible_atcontrols when item becomes visible,locked_untilonly for lock expiry - Migration file included for existing databases
- Added
-
New provider validation tests - 2 additional queue semantics tests
test_worker_item_immediate_visibility- Verify newly enqueued items are immediately visibletest_worker_delayed_visibility_skips_future_items- Verify items with future visible_at are skipped
Changed
- Reduced default
dispatcher_long_poll_timeoutfrom 5 minutes to 30 seconds- More responsive shutdown behavior
- Better suited for typical workloads
[0.1.3] - 2025-12-14
Added
- Provider validation tests - 4 new tests for abandon and poison handling
test_abandon_work_item_releases_lock- Verify abandon_work_item releases lock immediatelytest_abandon_work_item_with_delay- Verify abandon_work_item with delay defers refetchmax_attempt_count_across_message_batch- Verify MAX attempt_count returned for batched messages
Changed
- Provider validation test count increased from 58 to 62
Fixed
abandon_work_itemwith delay now correctly keeps lock_token to prevent immediate refetch
[0.1.2] - 2025-12-14
Added
-
Poison message handling - Automatic detection and failure of messages that exceed
max_attempts(default: 10)RuntimeOptions::max_attemptsconfiguration optionErrorDetails::Poisonvariant with detailed contextPoisonMessageTypeenum distinguishing orchestration vs activity poison- Dedicated metrics:
duroxide_orchestration_poison_total,duroxide_activity_poison_total
-
Lock renewal for orchestrations - Prevents lock expiration during long orchestration turns
Provider::renew_orchestration_item_lock()methodRuntimeOptions::orchestrator_lock_renewal_bufferconfiguration (default: 2s)- Automatic background renewal task in orchestration dispatcher
-
Work item abandon with retry - Explicit lock release for failed activities
Provider::abandon_work_item()method with optional delay- Called automatically when
ack_work_itemfails
-
Attempt count management -
ignore_attemptparameter for abandon methodsabandon_work_item(..., ignore_attempt: bool)- decrement count on transient failuresabandon_orchestration_item(..., ignore_attempt: bool)- same for orchestrations- Prevents false poison detection from infrastructure errors
-
Provider validation tests - 8 new poison message tests
orchestration_attempt_count_starts_at_oneorchestration_attempt_count_increments_on_refetchworker_attempt_count_starts_at_oneworker_attempt_count_increments_on_lock_expiryattempt_count_is_per_messageabandon_work_item_ignore_attempt_decrementsabandon_orchestration_item_ignore_attempt_decrementsignore_attempt_never_goes_negative
Changed
- BREAKING: SQLite provider is now optional - enable with
features = ["sqlite"] - BREAKING:
Provider::fetch_work_itemnow returns(WorkItem, String, u32)tuple (addedattempt_count) - BREAKING:
Provider::fetch_orchestration_itemnow returns(OrchestrationItem, String, u32)tuple (addedattempt_count) - BREAKING:
Provider::abandon_work_itemnow requiresignore_attempt: boolparameter - BREAKING:
Provider::abandon_orchestration_itemnow requiresignore_attempt: boolparameter OrchestrationItemstruct no longer containslock_token(moved to return tuple)- Provider validation test count increased from 50 to 58
Migration Guide
Cargo.toml (if using SQLite provider):
# Before
duroxide = "0.1.1"
# After - SQLite now requires explicit feature
duroxide = { version = "0.1.2", features = ["sqlite"] }
Provider implementers:
// fetch_work_item now returns attempt_count
async fn fetch_work_item(...) -> Result<Option<(WorkItem, String, u32)>, ProviderError>;
// fetch_orchestration_item now returns attempt_count
async fn fetch_orchestration_item(...) -> Result<Option<(OrchestrationItem, String, u32)>, ProviderError>;
// abandon methods now have ignore_attempt parameter
async fn abandon_work_item(&self, token: &str, delay: Option<Duration>, ignore_attempt: bool) -> Result<(), ProviderError>;
async fn abandon_orchestration_item(&self, token: &str, delay: Option<Duration>, ignore_attempt: bool) -> Result<(), ProviderError>;
// New method for orchestration lock renewal
async fn renew_orchestration_item_lock(&self, token: &str, extend_for: Duration) -> Result<(), ProviderError>;
Runtime users:
RuntimeOptions {
max_attempts: 10, // NEW - poison threshold
orchestrator_lock_renewal_buffer: Duration::from_secs(2), // NEW
..Default::default()
}
[0.1.1] - 2025-12-10
Added
- Long polling support - Providers can now block waiting for work, reducing CPU usage and latency
dispatcher_long_poll_timeoutconfiguration option (default: 5 minutes)poll_timeout: Durationparameter toProvider::fetch_orchestration_itemandProvider::fetch_work_item- Long polling validation tests in
duroxide::provider_validations::long_polling
Changed
- BREAKING:
Provider::fetch_orchestration_itemnow requirespoll_timeout: Durationparameter - BREAKING:
Provider::fetch_work_itemnow requirespoll_timeout: Durationparameter - BREAKING:
RuntimeOptions::dispatcher_idle_sleeprenamed todispatcher_min_poll_interval - BREAKING:
continue_as_new()now returns an awaitable future (usereturn ctx.continue_as_new(input).await)
Migration Guide
Provider implementers:
// Add poll_timeout parameter to both fetch methods
async fn fetch_orchestration_item(
&self,
lock_timeout: Duration,
poll_timeout: Duration, // NEW - ignore for short-polling, block for long-polling
) -> Result<Option<OrchestrationItem>, ProviderError>;
Runtime users:
// Rename dispatcher_idle_sleep to dispatcher_min_poll_interval
RuntimeOptions {
dispatcher_min_poll_interval: Duration::from_millis(100),
dispatcher_long_poll_timeout: Duration::from_secs(300), // NEW
..Default::default()
}
Orchestration authors using continue_as_new:
// Before: ctx.continue_as_new(input);
// After:
return ctx.continue_as_new(input).await;
[0.1.0] - 2025-12-01
Added
- Initial release
- Deterministic orchestration execution with replay
- Activity scheduling with automatic retries
- Timer support (create_timer)
- Sub-orchestration support
- External event handling
- Continue-as-new for long-running workflows
- SQLite provider implementation
- OpenTelemetry metrics and structured logging
- Provider validation test suite
- Comprehensive documentation