Saga-Pattern Transactions (TypeScript)

SkillAI & models

Adding saga-pattern transactions with compensation to a TypeScript Golem agent. Use when the user asks about transactions, sagas, compensation, rollback, or multi-step operations that need undo logic.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Saga-Pattern Transactions (TypeScript) skill

What this skill tells your AI

The instructions your AI receives, as published by golemcloud/golem in golem-skills/skills/ts/golem-add-transactions-ts/SKILL.md and read by ahel’s review.

Overview

Golem supports the saga pattern for multi-step operations where each step has a compensation (undo) action. If a step fails, previously completed steps are automatically compensated in reverse order. The building blocks are compensable (a step) and the fallibleSaga / infallibleSaga runners, all imported from @golemcloud/golem-ts-sdk.

Defining Compensable Steps

A step is a Compensable with an async execute and an async compensate. Both return the SDK Result<T, E> type. compensable<In, Out, Err>(execute, compensate) builds one:

import { compensable, Result } from '@golemcloud/golem-ts-sdk';

const reserveInventory = compensable<string, string, string>(
    async (sku) => {
        // Execute: reserve the item, returning a reservation id (or a typed error).
        const reservationId = await callInventoryApi(sku);
        return Result.ok(reservationId);
    },
    async (sku, reservationId) => {
        // Compensate: cancel the reservation. Compensations should not throw.
        await cancelReservation(reservationId);
        return Result.ok(undefined);
    },
);

const chargePayment = compensable<number, string, string>(
    async (amount) => {
        const chargeId = await callPaymentApi(amount);
        return Result.ok(chargeId);
    },
    async (amount, chargeId) => {
        await refundPayment(chargeId);
        return Result.ok(undefined);
    },
);

Fallible Sagas

fallibleSaga runs steps and, if any step returns Result.err, compensates the already-completed steps in reverse order and reports the failure. saga.execute(step, input) returns the step's Result; return a Result from the saga body. The overall result is a SagaResult<Out, Err> (a Result whose error describes whether rollback completed fully or partially):

import { fallibleSaga, Result } from '@golemcloud/golem-ts-sdk';

const outcome = await fallibleSaga<{ reservation: string; charge: string }, string>(async (saga) => {
    const reservation = await saga.execute(reserveInventory, 'SKU-123');
    if (reservation.isErr()) return reservation;

    const charge = await saga.execute(chargePayment, 49.99);
    if (charge.isErr()) return charge;

    return Result.ok({ reservation: reservation.val, charge: charge.val });
});

// outcome.isOk()  → the saga committed
// outcome.isErr() → outcome.val is a SagaFailure describing the error + rollback status

Infallible Sagas

infallibleSaga runs a sequence whose steps are expected to succeed. If a step returns Result.err, the already-run steps' compensations run in reverse order and the entire saga is retried. Here saga.execute(step, input) returns the step's success value directly (an err triggers rollback + retry rather than being returned):

import { infallibleSaga } from '@golemcloud/golem-ts-sdk';

const result = await infallibleSaga(async (saga) => {
    const reservation = await saga.execute(reserveInventory, 'SKU-123');
    const charge = await saga.execute(chargePayment, 49.99);
    return { reservation, charge };
});
// Resolves once the whole sequence succeeds.

Using a Saga Inside an Agent Method

import { z } from 'zod';
import { defineAgent, method, compensable, fallibleSaga, Result } from '@golemcloud/golem-ts-sdk';

export const OrderAgent = defineAgent({
    name: 'OrderAgent',
    id: { name: z.string() },
    methods: {
        placeOrder: method({ input: { sku: z.string(), amount: z.number() }, returns: z.boolean() }),
    },
});

OrderAgent.implement({
    init: () => ({}),
    methods: {
        async placeOrder({ sku, amount }) {
            const outcome = await fallibleSaga<string, string>(async (saga) => {
                const reservation = await saga.execute(reserveInventory, sku);
                if (reservation.isErr()) return reservation;
                return await saga.execute(chargePayment, amount);
            });
            return outcome.isOk();
        },
    },
});

Guidelines

  • Keep compensation logic idempotent — it may be called more than once
  • Compensation runs in reverse order of execution
  • Steps signal expected failures with Result.err; a throw inside a saga is an unexpected defect and traps (the saga is retried)
  • Use fallibleSaga when failure is an acceptable outcome the caller should observe
  • Use infallibleSaga when the operation must eventually succeed (failures roll back and retry)

Signals

GitHub stars
2k
Forks
210
Last commit
Sep 2026
Advanced
Item type
skill
Key
golem-add-transactions-ts
Source
github.com/golemcloud/golem