[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M719Q415KAXTPXBCB5RHFY0R":3,"skill-same:01M719Q415KAXTPXBCB5RHFY0R":65},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":33,"linkOnly":34,"markdownContent":35,"markdownBody":36,"frontMatter":37,"refName":39,"commitSha":40,"skillDirectory":41,"forksCount":14,"repositoryHtmlUrl":42,"repositoryHomepageUrl":6,"ownerHtmlUrl":43,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":44,"install":45,"resources":58},"01M719Q415KAXTPXBCB5RHFY0R","saga-orchestration",null,"Patterns for managing distributed transactions and long-running business processes.","sapsapshen\u002Fai-memory-router","sapsapshen","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F20347959?v=4","01M6XDQEF1DP50DKF37H81J3J5","shenmian","01M6XDQEJPE41Y3DT2763X8ZJN",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fglobal\u002Fsaga-orchestration\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32],"orchestration","saga orchestration","saga","distributed","transactions","business","community","patterns","long","processes","for","unknown","running","2026","2026-11-10 10:12:30",false,"---\nname: saga-orchestration\ndescription: \"Patterns for managing distributed transactions and long-running business processes.\"\nrisk: unknown\nsource: community\ndate_added: \"2026-02-27\"\n---\n\n# Saga Orchestration\n\nPatterns for managing distributed transactions and long-running business processes.\n\n## Do not use this skill when\n\n- The task is unrelated to saga orchestration\n- You need a different domain or tool outside this scope\n\n## Instructions\n\n- Clarify goals, constraints, and required inputs.\n- Apply relevant best practices and validate outcomes.\n- Provide actionable steps and verification.\n- If detailed examples are required, open `resources\u002Fimplementation-playbook.md`.\n\n## Use this skill when\n\n- Coordinating multi-service transactions\n- Implementing compensating transactions\n- Managing long-running business workflows\n- Handling failures in distributed systems\n- Building order fulfillment processes\n- Implementing approval workflows\n\n## Core Concepts\n\n### 1. Saga Types\n\n```\nChoreography                    Orchestration\n┌─────┐  ┌─────┐  ┌─────┐     ┌─────────────┐\n│Svc A│─►│Svc B│─►│Svc C│     │ Orchestrator│\n└─────┘  └─────┘  └─────┘     └──────┬──────┘\n   │        │        │               │\n   ▼        ▼        ▼         ┌─────┼─────┐\n Event    Event    Event       ▼     ▼     ▼\n                            ┌────┐┌────┐┌────┐\n                            │Svc1││Svc2││Svc3│\n                            └────┘└────┘└────┘\n```\n\n### 2. Saga Execution States\n\n| State            | Description                    |\n| ---------------- | ------------------------------ |\n| **Started**      | Saga initiated                 |\n| **Pending**      | Waiting for step completion    |\n| **Compensating** | Rolling back due to failure    |\n| **Completed**    | All steps succeeded            |\n| **Failed**       | Saga failed after compensation |\n\n## Templates\n\n### Template 1: Saga Orchestrator Base\n\n```python\nfrom abc import ABC, abstractmethod\nfrom dataclasses import dataclass, field\nfrom enum import Enum\nfrom typing import List, Dict, Any, Optional\nfrom datetime import datetime\nimport uuid\n\nclass SagaState(Enum):\n    STARTED = \"started\"\n    PENDING = \"pending\"\n    COMPENSATING = \"compensating\"\n    COMPLETED = \"completed\"\n    FAILED = \"failed\"\n\n\n@dataclass\nclass SagaStep:\n    name: str\n    action: str\n    compensation: str\n    status: str = \"pending\"\n    result: Optional[Dict] = None\n    error: Optional[str] = None\n    executed_at: Optional[datetime] = None\n    compensated_at: Optional[datetime] = None\n\n\n@dataclass\nclass Saga:\n    saga_id: str\n    saga_type: str\n    state: SagaState\n    data: Dict[str, Any]\n    steps: List[SagaStep]\n    current_step: int = 0\n    created_at: datetime = field(default_factory=datetime.utcnow)\n    updated_at: datetime = field(default_factory=datetime.utcnow)\n\n\nclass SagaOrchestrator(ABC):\n    \"\"\"Base class for saga orchestrators.\"\"\"\n\n    def __init__(self, saga_store, event_publisher):\n        self.saga_store = saga_store\n        self.event_publisher = event_publisher\n\n    @abstractmethod\n    def define_steps(self, data: Dict) -> List[SagaStep]:\n        \"\"\"Define the saga steps.\"\"\"\n        pass\n\n    @property\n    @abstractmethod\n    def saga_type(self) -> str:\n        \"\"\"Unique saga type identifier.\"\"\"\n        pass\n\n    async def start(self, data: Dict) -> Saga:\n        \"\"\"Start a new saga.\"\"\"\n        saga = Saga(\n            saga_id=str(uuid.uuid4()),\n            saga_type=self.saga_type,\n            state=SagaState.STARTED,\n            data=data,\n            steps=self.define_steps(data)\n        )\n        await self.saga_store.save(saga)\n        await self._execute_next_step(saga)\n        return saga\n\n    async def handle_step_completed(self, saga_id: str, step_name: str, result: Dict):\n        \"\"\"Handle successful step completion.\"\"\"\n        saga = await self.saga_store.get(saga_id)\n\n        # Update step\n        for step in saga.steps:\n            if step.name == step_name:\n                step.status = \"completed\"\n                step.result = result\n                step.executed_at = datetime.utcnow()\n                break\n\n        saga.current_step += 1\n        saga.updated_at = datetime.utcnow()\n\n        # Check if saga is complete\n        if saga.current_step >= len(saga.steps):\n            saga.state = SagaState.COMPLETED\n            await self.saga_store.save(saga)\n            await self._on_saga_completed(saga)\n        else:\n            saga.state = SagaState.PENDING\n            await self.saga_store.save(saga)\n            await self._execute_next_step(saga)\n\n    async def handle_step_failed(self, saga_id: str, step_name: str, error: str):\n        \"\"\"Handle step failure - start compensation.\"\"\"\n        saga = await self.saga_store.get(saga_id)\n\n        # Mark step as failed\n        for step in saga.steps:\n            if step.name == step_name:\n                step.status = \"failed\"\n                step.error = error\n                break\n\n        saga.state = SagaState.COMPENSATING\n        saga.updated_at = datetime.utcnow()\n        await self.saga_store.save(saga)\n\n        # Start compensation from current step backwards\n        await self._compensate(saga)\n\n    async def _execute_next_step(self, saga: Saga):\n        \"\"\"Execute the next step in the saga.\"\"\"\n        if saga.current_step >= len(saga.steps):\n            return\n\n        step = saga.steps[saga.current_step]\n        step.status = \"executing\"\n        await self.saga_store.save(saga)\n\n        # Publish command to execute step\n        await self.event_publisher.publish(\n            step.action,\n            {\n                \"saga_id\": saga.saga_id,\n                \"step_name\": step.name,\n                **saga.data\n            }\n        )\n\n    async def _compensate(self, saga: Saga):\n        \"\"\"Execute compensation for completed steps.\"\"\"\n        # Compensate in reverse order\n        for i in range(saga.current_step - 1, -1, -1):\n            step = saga.steps[i]\n            if step.status == \"completed\":\n                step.status = \"compensating\"\n                await self.saga_store.save(saga)\n\n                await self.event_publisher.publish(\n                    step.compensation,\n                    {\n                        \"saga_id\": saga.saga_id,\n                        \"step_name\": step.name,\n                        \"original_result\": step.result,\n                        **saga.data\n                    }\n                )\n\n    async def handle_compensation_completed(self, saga_id: str, step_name: str):\n        \"\"\"Handle compensation completion.\"\"\"\n        saga = await self.saga_store.get(saga_id)\n\n        for step in saga.steps:\n            if step.name == step_name:\n                step.status = \"compensated\"\n                step.compensated_at = datetime.utcnow()\n                break\n\n        # Check if all compensations complete\n        all_compensated = all(\n            s.status in (\"compensated\", \"pending\", \"failed\")\n            for s in saga.steps\n        )\n\n        if all_compensated:\n            saga.state = SagaState.FAILED\n            await self._on_saga_failed(saga)\n\n        await self.saga_store.save(saga)\n\n    async def _on_saga_completed(self, saga: Saga):\n        \"\"\"Called when saga completes successfully.\"\"\"\n        await self.event_publisher.publish(\n            f\"{self.saga_type}Completed\",\n            {\"saga_id\": saga.saga_id, **saga.data}\n        )\n\n    async def _on_saga_failed(self, saga: Saga):\n        \"\"\"Called when saga fails after compensation.\"\"\"\n        await self.event_publisher.publish(\n            f\"{self.saga_type}Failed\",\n            {\"saga_id\": saga.saga_id, \"error\": \"Saga failed\", **saga.data}\n        )\n```\n\n### Template 2: Order Fulfillment Saga\n\n```python\nclass OrderFulfillmentSaga(SagaOrchestrator):\n    \"\"\"Orchestrates order fulfillment across services.\"\"\"\n\n    @property\n    def saga_type(self) -> str:\n        return \"OrderFulfillment\"\n\n    def define_steps(self, data: Dict) -> List[SagaStep]:\n        return [\n            SagaStep(\n                name=\"reserve_inventory\",\n                action=\"InventoryService.ReserveItems\",\n                compensation=\"InventoryService.ReleaseReservation\"\n            ),\n            SagaStep(\n                name=\"process_payment\",\n                action=\"PaymentService.ProcessPayment\",\n                compensation=\"PaymentService.RefundPayment\"\n            ),\n            SagaStep(\n                name=\"create_shipment\",\n                action=\"ShippingService.CreateShipment\",\n                compensation=\"ShippingService.CancelShipment\"\n            ),\n            SagaStep(\n                name=\"send_confirmation\",\n                action=\"NotificationService.SendOrderConfirmation\",\n                compensation=\"NotificationService.SendCancellationNotice\"\n            )\n        ]\n\n\n# Usage\nasync def create_order(order_data: Dict):\n    saga = OrderFulfillmentSaga(saga_store, event_publisher)\n    return await saga.start({\n        \"order_id\": order_data[\"order_id\"],\n        \"customer_id\": order_data[\"customer_id\"],\n        \"items\": order_data[\"items\"],\n        \"payment_method\": order_data[\"payment_method\"],\n        \"shipping_address\": order_data[\"shipping_address\"]\n    })\n\n\n# Event handlers in each service\nclass InventoryService:\n    async def handle_reserve_items(self, command: Dict):\n        try:\n            # Reserve inventory\n            reservation = await self.reserve(\n                command[\"items\"],\n                command[\"order_id\"]\n            )\n            # Report success\n            await self.event_publisher.publish(\n                \"SagaStepCompleted\",\n                {\n                    \"saga_id\": command[\"saga_id\"],\n                    \"step_name\": \"reserve_inventory\",\n                    \"result\": {\"reservation_id\": reservation.id}\n                }\n            )\n        except InsufficientInventoryError as e:\n            await self.event_publisher.publish(\n                \"SagaStepFailed\",\n                {\n                    \"saga_id\": command[\"saga_id\"],\n                    \"step_name\": \"reserve_inventory\",\n                    \"error\": str(e)\n                }\n            )\n\n    async def handle_release_reservation(self, command: Dict):\n        # Compensating action\n        await self.release_reservation(\n            command[\"original_result\"][\"reservation_id\"]\n        )\n        await self.event_publisher.publish(\n            \"SagaCompensationCompleted\",\n            {\n                \"saga_id\": command[\"saga_id\"],\n                \"step_name\": \"reserve_inventory\"\n            }\n        )\n```\n\n### Template 3: Choreography-Based Saga\n\n```python\nfrom dataclasses import dataclass\nfrom typing import Dict, Any\nimport asyncio\n\n@dataclass\nclass SagaContext:\n    \"\"\"Passed through choreographed saga events.\"\"\"\n    saga_id: str\n    step: int\n    data: Dict[str, Any]\n    completed_steps: list\n\n\nclass OrderChoreographySaga:\n    \"\"\"Choreography-based saga using events.\"\"\"\n\n    def __init__(self, event_bus):\n        self.event_bus = event_bus\n        self._register_handlers()\n\n    def _register_handlers(self):\n        self.event_bus.subscribe(\"OrderCreated\", self._on_order_created)\n        self.event_bus.subscribe(\"InventoryReserved\", self._on_inventory_reserved)\n        self.event_bus.subscribe(\"PaymentProcessed\", self._on_payment_processed)\n        self.event_bus.subscribe(\"ShipmentCreated\", self._on_shipment_created)\n\n        # Compensation handlers\n        self.event_bus.subscribe(\"PaymentFailed\", self._on_payment_failed)\n        self.event_bus.subscribe(\"ShipmentFailed\", self._on_shipment_failed)\n\n    async def _on_order_created(self, event: Dict):\n        \"\"\"Step 1: Order created, reserve inventory.\"\"\"\n        await self.event_bus.publish(\"ReserveInventory\", {\n            \"saga_id\": event[\"order_id\"],\n            \"order_id\": event[\"order_id\"],\n            \"items\": event[\"items\"]\n        })\n\n    async def _on_inventory_reserved(self, event: Dict):\n        \"\"\"Step 2: Inventory reserved, process payment.\"\"\"\n        await self.event_bus.publish(\"ProcessPayment\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"order_id\": event[\"order_id\"],\n            \"amount\": event[\"total_amount\"],\n            \"reservation_id\": event[\"reservation_id\"]\n        })\n\n    async def _on_payment_processed(self, event: Dict):\n        \"\"\"Step 3: Payment done, create shipment.\"\"\"\n        await self.event_bus.publish(\"CreateShipment\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"order_id\": event[\"order_id\"],\n            \"payment_id\": event[\"payment_id\"]\n        })\n\n    async def _on_shipment_created(self, event: Dict):\n        \"\"\"Step 4: Complete - send confirmation.\"\"\"\n        await self.event_bus.publish(\"OrderFulfilled\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"order_id\": event[\"order_id\"],\n            \"tracking_number\": event[\"tracking_number\"]\n        })\n\n    # Compensation handlers\n    async def _on_payment_failed(self, event: Dict):\n        \"\"\"Payment failed - release inventory.\"\"\"\n        await self.event_bus.publish(\"ReleaseInventory\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"reservation_id\": event[\"reservation_id\"]\n        })\n        await self.event_bus.publish(\"OrderFailed\", {\n            \"order_id\": event[\"order_id\"],\n            \"reason\": \"Payment failed\"\n        })\n\n    async def _on_shipment_failed(self, event: Dict):\n        \"\"\"Shipment failed - refund payment and release inventory.\"\"\"\n        await self.event_bus.publish(\"RefundPayment\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"payment_id\": event[\"payment_id\"]\n        })\n        await self.event_bus.publish(\"ReleaseInventory\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"reservation_id\": event[\"reservation_id\"]\n        })\n```\n\n### Template 4: Saga with Timeouts\n\n```python\nclass TimeoutSagaOrchestrator(SagaOrchestrator):\n    \"\"\"Saga orchestrator with step timeouts.\"\"\"\n\n    def __init__(self, saga_store, event_publisher, scheduler):\n        super().__init__(saga_store, event_publisher)\n        self.scheduler = scheduler\n\n    async def _execute_next_step(self, saga: Saga):\n        if saga.current_step >= len(saga.steps):\n            return\n\n        step = saga.steps[saga.current_step]\n        step.status = \"executing\"\n        step.timeout_at = datetime.utcnow() + timedelta(minutes=5)\n        await self.saga_store.save(saga)\n\n        # Schedule timeout check\n        await self.scheduler.schedule(\n            f\"saga_timeout_{saga.saga_id}_{step.name}\",\n            self._check_timeout,\n            {\"saga_id\": saga.saga_id, \"step_name\": step.name},\n            run_at=step.timeout_at\n        )\n\n        await self.event_publisher.publish(\n            step.action,\n            {\"saga_id\": saga.saga_id, \"step_name\": step.name, **saga.data}\n        )\n\n    async def _check_timeout(self, data: Dict):\n        \"\"\"Check if step has timed out.\"\"\"\n        saga = await self.saga_store.get(data[\"saga_id\"])\n        step = next(s for s in saga.steps if s.name == data[\"step_name\"])\n\n        if step.status == \"executing\":\n            # Step timed out - fail it\n            await self.handle_step_failed(\n                data[\"saga_id\"],\n                data[\"step_name\"],\n                \"Step timed out\"\n            )\n```\n\n## Durable Execution Alternative\n\nThe templates above build saga infrastructure from scratch — saga stores, event publishers, compensation tracking. **Durable execution frameworks** (like DBOS) eliminate much of this boilerplate: the workflow runtime automatically persists state to a database, retries failed steps, and resumes from the last checkpoint after crashes. Instead of building a `SagaOrchestrator` base class, you write a workflow function with steps — the framework handles persistence, crash recovery, and exactly-once execution semantics. Consider durable execution when you want saga-like reliability without managing the coordination infrastructure yourself.\n\n## Best Practices\n\n### Do's\n\n- **Make steps idempotent** - Safe to retry\n- **Design compensations carefully** - They must work\n- **Use correlation IDs** - For tracing across services\n- **Implement timeouts** - Don't wait forever\n- **Log everything** - For debugging failures\n\n### Don'ts\n\n- **Don't assume instant completion** - Sagas take time\n- **Don't skip compensation testing** - Most critical part\n- **Don't couple services** - Use async messaging\n- **Don't ignore partial failures** - Handle gracefully\n\n## Related Skills\n\nWorks well with: `event-sourcing-architect`, `workflow-automation`, `dbos-*`\n\n## Resources\n\n- [Saga Pattern](https:\u002F\u002Fmicroservices.io\u002Fpatterns\u002Fdata\u002Fsaga.html)\n- [Designing Data-Intensive Applications](https:\u002F\u002Fdataintensive.net\u002F)\n","# Saga Orchestration\n\nPatterns for managing distributed transactions and long-running business processes.\n\n## Do not use this skill when\n\n- The task is unrelated to saga orchestration\n- You need a different domain or tool outside this scope\n\n## Instructions\n\n- Clarify goals, constraints, and required inputs.\n- Apply relevant best practices and validate outcomes.\n- Provide actionable steps and verification.\n- If detailed examples are required, open `resources\u002Fimplementation-playbook.md`.\n\n## Use this skill when\n\n- Coordinating multi-service transactions\n- Implementing compensating transactions\n- Managing long-running business workflows\n- Handling failures in distributed systems\n- Building order fulfillment processes\n- Implementing approval workflows\n\n## Core Concepts\n\n### 1. Saga Types\n\n```\nChoreography                    Orchestration\n┌─────┐  ┌─────┐  ┌─────┐     ┌─────────────┐\n│Svc A│─►│Svc B│─►│Svc C│     │ Orchestrator│\n└─────┘  └─────┘  └─────┘     └──────┬──────┘\n   │        │        │               │\n   ▼        ▼        ▼         ┌─────┼─────┐\n Event    Event    Event       ▼     ▼     ▼\n                            ┌────┐┌────┐┌────┐\n                            │Svc1││Svc2││Svc3│\n                            └────┘└────┘└────┘\n```\n\n### 2. Saga Execution States\n\n| State            | Description                    |\n| ---------------- | ------------------------------ |\n| **Started**      | Saga initiated                 |\n| **Pending**      | Waiting for step completion    |\n| **Compensating** | Rolling back due to failure    |\n| **Completed**    | All steps succeeded            |\n| **Failed**       | Saga failed after compensation |\n\n## Templates\n\n### Template 1: Saga Orchestrator Base\n\n```python\nfrom abc import ABC, abstractmethod\nfrom dataclasses import dataclass, field\nfrom enum import Enum\nfrom typing import List, Dict, Any, Optional\nfrom datetime import datetime\nimport uuid\n\nclass SagaState(Enum):\n    STARTED = \"started\"\n    PENDING = \"pending\"\n    COMPENSATING = \"compensating\"\n    COMPLETED = \"completed\"\n    FAILED = \"failed\"\n\n\n@dataclass\nclass SagaStep:\n    name: str\n    action: str\n    compensation: str\n    status: str = \"pending\"\n    result: Optional[Dict] = None\n    error: Optional[str] = None\n    executed_at: Optional[datetime] = None\n    compensated_at: Optional[datetime] = None\n\n\n@dataclass\nclass Saga:\n    saga_id: str\n    saga_type: str\n    state: SagaState\n    data: Dict[str, Any]\n    steps: List[SagaStep]\n    current_step: int = 0\n    created_at: datetime = field(default_factory=datetime.utcnow)\n    updated_at: datetime = field(default_factory=datetime.utcnow)\n\n\nclass SagaOrchestrator(ABC):\n    \"\"\"Base class for saga orchestrators.\"\"\"\n\n    def __init__(self, saga_store, event_publisher):\n        self.saga_store = saga_store\n        self.event_publisher = event_publisher\n\n    @abstractmethod\n    def define_steps(self, data: Dict) -> List[SagaStep]:\n        \"\"\"Define the saga steps.\"\"\"\n        pass\n\n    @property\n    @abstractmethod\n    def saga_type(self) -> str:\n        \"\"\"Unique saga type identifier.\"\"\"\n        pass\n\n    async def start(self, data: Dict) -> Saga:\n        \"\"\"Start a new saga.\"\"\"\n        saga = Saga(\n            saga_id=str(uuid.uuid4()),\n            saga_type=self.saga_type,\n            state=SagaState.STARTED,\n            data=data,\n            steps=self.define_steps(data)\n        )\n        await self.saga_store.save(saga)\n        await self._execute_next_step(saga)\n        return saga\n\n    async def handle_step_completed(self, saga_id: str, step_name: str, result: Dict):\n        \"\"\"Handle successful step completion.\"\"\"\n        saga = await self.saga_store.get(saga_id)\n\n        # Update step\n        for step in saga.steps:\n            if step.name == step_name:\n                step.status = \"completed\"\n                step.result = result\n                step.executed_at = datetime.utcnow()\n                break\n\n        saga.current_step += 1\n        saga.updated_at = datetime.utcnow()\n\n        # Check if saga is complete\n        if saga.current_step >= len(saga.steps):\n            saga.state = SagaState.COMPLETED\n            await self.saga_store.save(saga)\n            await self._on_saga_completed(saga)\n        else:\n            saga.state = SagaState.PENDING\n            await self.saga_store.save(saga)\n            await self._execute_next_step(saga)\n\n    async def handle_step_failed(self, saga_id: str, step_name: str, error: str):\n        \"\"\"Handle step failure - start compensation.\"\"\"\n        saga = await self.saga_store.get(saga_id)\n\n        # Mark step as failed\n        for step in saga.steps:\n            if step.name == step_name:\n                step.status = \"failed\"\n                step.error = error\n                break\n\n        saga.state = SagaState.COMPENSATING\n        saga.updated_at = datetime.utcnow()\n        await self.saga_store.save(saga)\n\n        # Start compensation from current step backwards\n        await self._compensate(saga)\n\n    async def _execute_next_step(self, saga: Saga):\n        \"\"\"Execute the next step in the saga.\"\"\"\n        if saga.current_step >= len(saga.steps):\n            return\n\n        step = saga.steps[saga.current_step]\n        step.status = \"executing\"\n        await self.saga_store.save(saga)\n\n        # Publish command to execute step\n        await self.event_publisher.publish(\n            step.action,\n            {\n                \"saga_id\": saga.saga_id,\n                \"step_name\": step.name,\n                **saga.data\n            }\n        )\n\n    async def _compensate(self, saga: Saga):\n        \"\"\"Execute compensation for completed steps.\"\"\"\n        # Compensate in reverse order\n        for i in range(saga.current_step - 1, -1, -1):\n            step = saga.steps[i]\n            if step.status == \"completed\":\n                step.status = \"compensating\"\n                await self.saga_store.save(saga)\n\n                await self.event_publisher.publish(\n                    step.compensation,\n                    {\n                        \"saga_id\": saga.saga_id,\n                        \"step_name\": step.name,\n                        \"original_result\": step.result,\n                        **saga.data\n                    }\n                )\n\n    async def handle_compensation_completed(self, saga_id: str, step_name: str):\n        \"\"\"Handle compensation completion.\"\"\"\n        saga = await self.saga_store.get(saga_id)\n\n        for step in saga.steps:\n            if step.name == step_name:\n                step.status = \"compensated\"\n                step.compensated_at = datetime.utcnow()\n                break\n\n        # Check if all compensations complete\n        all_compensated = all(\n            s.status in (\"compensated\", \"pending\", \"failed\")\n            for s in saga.steps\n        )\n\n        if all_compensated:\n            saga.state = SagaState.FAILED\n            await self._on_saga_failed(saga)\n\n        await self.saga_store.save(saga)\n\n    async def _on_saga_completed(self, saga: Saga):\n        \"\"\"Called when saga completes successfully.\"\"\"\n        await self.event_publisher.publish(\n            f\"{self.saga_type}Completed\",\n            {\"saga_id\": saga.saga_id, **saga.data}\n        )\n\n    async def _on_saga_failed(self, saga: Saga):\n        \"\"\"Called when saga fails after compensation.\"\"\"\n        await self.event_publisher.publish(\n            f\"{self.saga_type}Failed\",\n            {\"saga_id\": saga.saga_id, \"error\": \"Saga failed\", **saga.data}\n        )\n```\n\n### Template 2: Order Fulfillment Saga\n\n```python\nclass OrderFulfillmentSaga(SagaOrchestrator):\n    \"\"\"Orchestrates order fulfillment across services.\"\"\"\n\n    @property\n    def saga_type(self) -> str:\n        return \"OrderFulfillment\"\n\n    def define_steps(self, data: Dict) -> List[SagaStep]:\n        return [\n            SagaStep(\n                name=\"reserve_inventory\",\n                action=\"InventoryService.ReserveItems\",\n                compensation=\"InventoryService.ReleaseReservation\"\n            ),\n            SagaStep(\n                name=\"process_payment\",\n                action=\"PaymentService.ProcessPayment\",\n                compensation=\"PaymentService.RefundPayment\"\n            ),\n            SagaStep(\n                name=\"create_shipment\",\n                action=\"ShippingService.CreateShipment\",\n                compensation=\"ShippingService.CancelShipment\"\n            ),\n            SagaStep(\n                name=\"send_confirmation\",\n                action=\"NotificationService.SendOrderConfirmation\",\n                compensation=\"NotificationService.SendCancellationNotice\"\n            )\n        ]\n\n\n# Usage\nasync def create_order(order_data: Dict):\n    saga = OrderFulfillmentSaga(saga_store, event_publisher)\n    return await saga.start({\n        \"order_id\": order_data[\"order_id\"],\n        \"customer_id\": order_data[\"customer_id\"],\n        \"items\": order_data[\"items\"],\n        \"payment_method\": order_data[\"payment_method\"],\n        \"shipping_address\": order_data[\"shipping_address\"]\n    })\n\n\n# Event handlers in each service\nclass InventoryService:\n    async def handle_reserve_items(self, command: Dict):\n        try:\n            # Reserve inventory\n            reservation = await self.reserve(\n                command[\"items\"],\n                command[\"order_id\"]\n            )\n            # Report success\n            await self.event_publisher.publish(\n                \"SagaStepCompleted\",\n                {\n                    \"saga_id\": command[\"saga_id\"],\n                    \"step_name\": \"reserve_inventory\",\n                    \"result\": {\"reservation_id\": reservation.id}\n                }\n            )\n        except InsufficientInventoryError as e:\n            await self.event_publisher.publish(\n                \"SagaStepFailed\",\n                {\n                    \"saga_id\": command[\"saga_id\"],\n                    \"step_name\": \"reserve_inventory\",\n                    \"error\": str(e)\n                }\n            )\n\n    async def handle_release_reservation(self, command: Dict):\n        # Compensating action\n        await self.release_reservation(\n            command[\"original_result\"][\"reservation_id\"]\n        )\n        await self.event_publisher.publish(\n            \"SagaCompensationCompleted\",\n            {\n                \"saga_id\": command[\"saga_id\"],\n                \"step_name\": \"reserve_inventory\"\n            }\n        )\n```\n\n### Template 3: Choreography-Based Saga\n\n```python\nfrom dataclasses import dataclass\nfrom typing import Dict, Any\nimport asyncio\n\n@dataclass\nclass SagaContext:\n    \"\"\"Passed through choreographed saga events.\"\"\"\n    saga_id: str\n    step: int\n    data: Dict[str, Any]\n    completed_steps: list\n\n\nclass OrderChoreographySaga:\n    \"\"\"Choreography-based saga using events.\"\"\"\n\n    def __init__(self, event_bus):\n        self.event_bus = event_bus\n        self._register_handlers()\n\n    def _register_handlers(self):\n        self.event_bus.subscribe(\"OrderCreated\", self._on_order_created)\n        self.event_bus.subscribe(\"InventoryReserved\", self._on_inventory_reserved)\n        self.event_bus.subscribe(\"PaymentProcessed\", self._on_payment_processed)\n        self.event_bus.subscribe(\"ShipmentCreated\", self._on_shipment_created)\n\n        # Compensation handlers\n        self.event_bus.subscribe(\"PaymentFailed\", self._on_payment_failed)\n        self.event_bus.subscribe(\"ShipmentFailed\", self._on_shipment_failed)\n\n    async def _on_order_created(self, event: Dict):\n        \"\"\"Step 1: Order created, reserve inventory.\"\"\"\n        await self.event_bus.publish(\"ReserveInventory\", {\n            \"saga_id\": event[\"order_id\"],\n            \"order_id\": event[\"order_id\"],\n            \"items\": event[\"items\"]\n        })\n\n    async def _on_inventory_reserved(self, event: Dict):\n        \"\"\"Step 2: Inventory reserved, process payment.\"\"\"\n        await self.event_bus.publish(\"ProcessPayment\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"order_id\": event[\"order_id\"],\n            \"amount\": event[\"total_amount\"],\n            \"reservation_id\": event[\"reservation_id\"]\n        })\n\n    async def _on_payment_processed(self, event: Dict):\n        \"\"\"Step 3: Payment done, create shipment.\"\"\"\n        await self.event_bus.publish(\"CreateShipment\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"order_id\": event[\"order_id\"],\n            \"payment_id\": event[\"payment_id\"]\n        })\n\n    async def _on_shipment_created(self, event: Dict):\n        \"\"\"Step 4: Complete - send confirmation.\"\"\"\n        await self.event_bus.publish(\"OrderFulfilled\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"order_id\": event[\"order_id\"],\n            \"tracking_number\": event[\"tracking_number\"]\n        })\n\n    # Compensation handlers\n    async def _on_payment_failed(self, event: Dict):\n        \"\"\"Payment failed - release inventory.\"\"\"\n        await self.event_bus.publish(\"ReleaseInventory\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"reservation_id\": event[\"reservation_id\"]\n        })\n        await self.event_bus.publish(\"OrderFailed\", {\n            \"order_id\": event[\"order_id\"],\n            \"reason\": \"Payment failed\"\n        })\n\n    async def _on_shipment_failed(self, event: Dict):\n        \"\"\"Shipment failed - refund payment and release inventory.\"\"\"\n        await self.event_bus.publish(\"RefundPayment\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"payment_id\": event[\"payment_id\"]\n        })\n        await self.event_bus.publish(\"ReleaseInventory\", {\n            \"saga_id\": event[\"saga_id\"],\n            \"reservation_id\": event[\"reservation_id\"]\n        })\n```\n\n### Template 4: Saga with Timeouts\n\n```python\nclass TimeoutSagaOrchestrator(SagaOrchestrator):\n    \"\"\"Saga orchestrator with step timeouts.\"\"\"\n\n    def __init__(self, saga_store, event_publisher, scheduler):\n        super().__init__(saga_store, event_publisher)\n        self.scheduler = scheduler\n\n    async def _execute_next_step(self, saga: Saga):\n        if saga.current_step >= len(saga.steps):\n            return\n\n        step = saga.steps[saga.current_step]\n        step.status = \"executing\"\n        step.timeout_at = datetime.utcnow() + timedelta(minutes=5)\n        await self.saga_store.save(saga)\n\n        # Schedule timeout check\n        await self.scheduler.schedule(\n            f\"saga_timeout_{saga.saga_id}_{step.name}\",\n            self._check_timeout,\n            {\"saga_id\": saga.saga_id, \"step_name\": step.name},\n            run_at=step.timeout_at\n        )\n\n        await self.event_publisher.publish(\n            step.action,\n            {\"saga_id\": saga.saga_id, \"step_name\": step.name, **saga.data}\n        )\n\n    async def _check_timeout(self, data: Dict):\n        \"\"\"Check if step has timed out.\"\"\"\n        saga = await self.saga_store.get(data[\"saga_id\"])\n        step = next(s for s in saga.steps if s.name == data[\"step_name\"])\n\n        if step.status == \"executing\":\n            # Step timed out - fail it\n            await self.handle_step_failed(\n                data[\"saga_id\"],\n                data[\"step_name\"],\n                \"Step timed out\"\n            )\n```\n\n## Durable Execution Alternative\n\nThe templates above build saga infrastructure from scratch — saga stores, event publishers, compensation tracking. **Durable execution frameworks** (like DBOS) eliminate much of this boilerplate: the workflow runtime automatically persists state to a database, retries failed steps, and resumes from the last checkpoint after crashes. Instead of building a `SagaOrchestrator` base class, you write a workflow function with steps — the framework handles persistence, crash recovery, and exactly-once execution semantics. Consider durable execution when you want saga-like reliability without managing the coordination infrastructure yourself.\n\n## Best Practices\n\n### Do's\n\n- **Make steps idempotent** - Safe to retry\n- **Design compensations carefully** - They must work\n- **Use correlation IDs** - For tracing across services\n- **Implement timeouts** - Don't wait forever\n- **Log everything** - For debugging failures\n\n### Don'ts\n\n- **Don't assume instant completion** - Sagas take time\n- **Don't skip compensation testing** - Most critical part\n- **Don't couple services** - Use async messaging\n- **Don't ignore partial failures** - Handle gracefully\n\n## Related Skills\n\nWorks well with: `event-sourcing-architect`, `workflow-automation`, `dbos-*`\n\n## Resources\n\n- [Saga Pattern](https:\u002F\u002Fmicroservices.io\u002Fpatterns\u002Fdata\u002Fsaga.html)\n- [Designing Data-Intensive Applications](https:\u002F\u002Fdataintensive.net\u002F)\n",{"name":5,"risk":30,"source":25,"date_added":38,"description":7},"2026-02-27","main","7a38d8e7073e61c16eda85681cb32fb68a8657a5","skills-store\u002Fglobal\u002Fsaga-orchestration","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router","https:\u002F\u002Fgithub.com\u002Fsapsapshen","2026-04-14 21:12:12",{"source":8,"skillName":5,"commands":46,"prompt":56,"zipAvailable":57},[47,50,53],{"tool":48,"command":49},"npx","npx skills add sapsapshen\u002Fai-memory-router --skill saga-orchestration",{"tool":51,"command":52},"bunx","bunx skills add sapsapshen\u002Fai-memory-router --skill saga-orchestration",{"tool":54,"command":55},"pnpm","pnpm dlx skills add sapsapshen\u002Fai-memory-router --skill saga-orchestration","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fglobal\u002Fsaga-orchestration\u002FSKILL.md",true,[59],{"path":60,"resourceType":61,"resourceTypeDesc":62,"sizeBytes":63,"sourceHtmlUrl":64},"resources\u002Fimplementation-playbook.md","REFERENCE","参考文档",859,"https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fglobal\u002Fsaga-orchestration\u002Fresources\u002Fimplementation-playbook.md",[66,90,116,126,139,158],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":77,"publishedAt":33},"01M71ATQRKG48SH06FQ697T5G9","wechat-search","Search WeChat Official Account articles using OpenClaw's web search and fetch capabilities with compliance-focused design.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fwechat-search-release\u002FSKILL.md",[72],{"categoryNo":73,"categoryName":74,"taxonomyType":75,"taxonomyTypeDesc":76},"01JBSEEDCAT000000000000037","律师与法务","PROFESSION","适用职业",[78,79,80,81,82,83,84,85,86,87,88,89],"wechat search","wechat","search","capabilities","openclaw","design","web search","compliance","web","official","fetch","with",{"skillNo":91,"name":92,"nameZh":6,"description":93,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":94,"categories":95,"keywords":96,"publishedAt":33},"01M71ATNAVGZB35PFYM2GC5N00","trend-tap","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend tap', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-tap\u002FSKILL.md",[],[97,98,99,100,101,102,80,103,104,105,106,107,108,109,110,111,112,113,114,115],"trend","trend tap","tap","api keys","Bash","time","trending","hacker","aggregator","do","when","brew","live","data","google","install","read","across","api",{"skillNo":117,"name":118,"nameZh":6,"description":119,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":120,"categories":121,"keywords":122,"publishedAt":33},"01M71ATJ9AM5KM15AA8PS302EB","trend-radar","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend pulse', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-radar\u002FSKILL.md",[],[123,124,97,108,109,110,111,101,80,113,125,114,115,107,105,104,103,112,102,100],"trend radar","radar","pulse",{"skillNo":127,"name":128,"nameZh":6,"description":129,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":130,"categories":131,"keywords":132,"publishedAt":33},"01M71ATGF095N3EJM9G8SD0BB5","tech-news-daily","每日科技新闻汇——从全球多个 RSS 源抓取最新科技新闻，翻译（英→中）、去重、排版成每日简报。当用户询问科技新闻、科技新鲜事、每日科技、技术动态、Daily Tech News 或类似表达时触发此技能。","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftech-news-daily\u002FSKILL.md",[],[133,134,135,136,137,138],"news","tech","tech news daily","daily","rss","翻译",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":150,"publishedAt":33},"01M71ATFAEAMQ17M70NQNY7SR8","summarize","Summarize URLs or files with the summarize CLI (web, PDFs, images, audio, YouTube).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsummarize\u002FSKILL.md",[145],{"categoryNo":146,"categoryName":147,"taxonomyType":148,"taxonomyTypeDesc":149},"01JBSEEDCAT000000000000021","资料检索与研究","SCENARIO","使用场景",[141,86,151,152,153,89,108,154,155,156,99,157,112],"Audio","cli","youtube","images","https","files","pdfs",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":164,"publishedAt":33},"01M71ATDNS3R0M4H936PACQAWZ","sonoscli","Control Sonos speakers (discover\u002Fstatus\u002Fplay\u002Fvolume\u002Fgroup).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsonoscli\u002FSKILL.md",[],[160,165,166,167,112,168,155,169,170,171,172,173],"status","play","go","latest","github","discover","control","cmd","volume"]