Skip to content

feat: durable_workflow 0.2.0#1

Merged
brody-0125 merged 19 commits into
mainfrom
release/0.2.0
Apr 4, 2026
Merged

feat: durable_workflow 0.2.0#1
brody-0125 merged 19 commits into
mainfrom
release/0.2.0

Conversation

@brody-0125

Copy link
Copy Markdown
Owner

Summary

  • 15 DoD items completed for 0.2.0 production-ready release
  • All 4 packages published to pub.dev (0.2.0)
  • 47 files changed, +821 / -128 lines across 5 packages

Breaking Changes

  • Removed idempotencyKey from public API (stored but never used)
  • CheckpointStore requires 3 new methods: saveCheckpoints(), deleteOldTimers(), deleteOldSignals()
  • Exception types: StateErrorWorkflowExecutionNotFoundException, TimeoutExceptionWorkflowTimeoutException

New Features

  • DurableEngine.dispose() on abstract interface
  • Custom exception hierarchy (DurableWorkflowException base + 4 subtypes)
  • DurableEngineObserver lifecycle monitoring hooks (9 methods)
  • Input validation at API boundaries
  • Error message formatter hook (errorFormatter)
  • CheckpointStore cleanup operations

Improvements

  • Recovery scanner reentrance guard
  • Signal timeout race condition protection
  • Engine dispose safety (cancels executors, disposed guard)
  • CI expanded to include Flutter and Drift packages (coverage threshold 80%)
  • Dart pub workspace for monorepo dependency resolution
  • Security Considerations documentation

Stats

  • Tests: 299 (core) + 86 (sqlite) + 50 (drift) = 435 total, all passing
  • Coverage: 94% (core)
  • SDK: ^3.6.0 (workspace support)

Test plan

  • dart test passes in durable_workflow (299 tests)
  • dart test passes in durable_workflow_sqlite (86 tests)
  • dart test passes in durable_workflow_drift (50 tests)
  • dart analyze --fatal-warnings clean in all packages
  • dart pub publish --dry-run with 0 warnings
  • All 4 packages published to pub.dev 0.2.0

🤖 Generated with Claude Code

brody-0125 and others added 18 commits April 4, 2026 01:07
DurableEngine abstract class now declares void dispose(), allowing
users holding an interface reference to release resources without
casting to DurableEngineImpl.

Tasks:
- T01: DurableEngine 인터페이스에 dispose() 추가 + impl @OverRide
- T02: 통합 검증 — 290 tests passed, dart analyze clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce DurableWorkflowException base class with specific subtypes:
- WorkflowTimeoutException (replaces TimeoutException for signal waits)
- WorkflowExecutionNotFoundException (replaces StateError for missing executions)
- CompensationException (for saga compensation failures)
- WorkflowCancelledException now extends DurableWorkflowException

Resolves DOD-002 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BREAKING CHANGE: idempotencyKey parameter removed from WorkflowContext.step(),
StepExecutor.executeStep(), and StepCheckpoint model. The field was stored but
never used for actual deduplication. DB column preserved as null for schema
compatibility.

Resolves DOD-005 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Validate workflowType, step name, signalName, and executionId at API
boundaries: reject empty, whitespace-only, control chars, and strings
exceeding 256 chars with ArgumentError.

Resolves DOD-010 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add durable_workflow_drift to Dart test matrix with build_runner step
- Add separate test-flutter job using Flutter SDK for durable_workflow_flutter
- Raise minimum coverage threshold from 70% to 80%
- Coverage report now depends on both test jobs

Resolves DOD-014 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BREAKING CHANGE: CheckpointStore now requires saveCheckpoints(),
deleteOldTimers(), and deleteOldSignals() implementations.

- saveCheckpoints(): batch insert for multiple checkpoints
- deleteOldTimers(): remove FIRED/CANCELLED timers before cutoff
- deleteOldSignals(): remove DELIVERED/EXPIRED signals before cutoff
- All 3 implementations updated (InMemory, SQLite, Drift)
- Added dependency_overrides for local development

Resolves DOD-003 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ection

- RecoveryScanner: add _isScanning flag to prevent concurrent scan()
  calls from duplicating resume work (DOD-008)
- SignalManager: strengthen timeout callback guard — check both
  completer.isCompleted and _completers map presence before timeout
  handling, preventing edge case signal loss (DOD-007)

Resolves DOD-007, DOD-008 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…spose

- Add _disposed flag with _checkNotDisposed() guard on run()
- Prevent duplicate concurrent execution of same executionId
- dispose() now cancels all active executors before cleanup
- dispose() closes observer streams safely (isClosed check)
- Idempotent dispose (second call is no-op)

Resolves DOD-006, DOD-009 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DurableEngineImpl: add optional errorFormatter parameter to sanitize
  error messages before persistence (default: 1000-char truncation)
- README/README.ko: remove dead /docs/ links, add Flutter package link

Resolves DOD-012, DOD-015 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document data protection best practices: avoid storing secrets in step
results, use custom serializers for encryption, place DB in private
directory, use errorFormatter for error sanitization.

Resolves DOD-011 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce DurableEngineObserver abstract class with 9 hooks:
onExecutionStart, onExecutionComplete, onStepStart, onStepComplete,
onStepRetry, onCompensationStart, onRecoveryStart, onRecoveryComplete,
onError. Observer errors are caught internally — they never affect
engine execution. Register via DurableEngineImpl(observers: [...]).

Resolves DOD-004 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 10 new test cases:
- Concurrent workflow execution (3 and 5 simultaneous)
- Dispose safety (idempotent, post-dispose guard)
- Recovery scanner reentrance protection
- DurableEngineObserver lifecycle events
- Observer error isolation
- Input validation (empty, control chars, length)

Core coverage: 94%. Total: 299 tests.

Resolves DOD-013 (AGI-001).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Version bump 0.1.x → 0.2.0 for all 4 publishable packages.
Update inter-package dependency constraints to ^0.2.0.
Remove dependency_overrides (sqlite, drift).
Add detailed CHANGELOG entries with breaking changes and migration guide.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lidation

Code review findings (3-agent parallel review):

1. Fix malformed doc comment: _formatError doc was attached to
   _notifyEngineObservers instead
2. Add _checkNotDisposed guard to cancel(), observe(), sendSignal()
   for consistent use-after-dispose protection
3. Wrap Drift saveCheckpoints() in transaction for batch performance
4. Rethrow WorkflowCancelledException directly instead of converting
   to StateError (preserves typed exception for callers)
5. Reject leading/trailing whitespace in validateIdentifier()
6. Re-add dependency_overrides for local dev (TODO: remove before publish)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline dependency_overrides with pubspec_overrides.yaml files
(Dart standard mechanism, auto-recognized by dart pub get).
These files are gitignored so pubspec.yaml stays clean with pub.dev
version constraints. No more manual add/remove cycle for publishing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add root pubspec.yaml with workspace listing 4 Dart packages.
Each package gets `resolution: workspace` and SDK bumped to ^3.6.0.
Workspace auto-resolves inter-package dependencies to local versions
— no more dependency_overrides or pubspec_overrides.yaml needed.

durable_workflow_flutter is excluded from workspace (requires Flutter
SDK); uses pubspec_overrides.yaml locally instead (gitignored).

Removes pubspec_overrides.yaml from sqlite/drift packages.
Examples package switches from path dep to ^0.2.0 (workspace resolves).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Flutter SDK pins meta to 1.18.0, which is excluded by ^1.18.2.
Widen to ^1.18.0 so durable_workflow_sqlite can be used as a
dev_dependency in Flutter packages without version conflict.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Patch release to fix Flutter SDK compatibility.
meta ^1.18.2 excluded Flutter's pinned meta 1.18.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@brody-0125 brody-0125 changed the title feat: durable_workflow 0.2.0 — production-ready release feat: durable_workflow 0.2.0 Apr 4, 2026
@brody-0125 brody-0125 self-assigned this Apr 4, 2026
durable_workflow_sqlite is not used in Flutter tests (all tests use
InMemoryCheckpointStore from core). Removing it avoids the meta
version conflict between sqlite's meta ^1.18.0 and Flutter SDK's
pinned meta 1.17.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@brody-0125 brody-0125 merged commit 05520fc into main Apr 4, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant