# Routing Slip Activity Foundation

## Goal

Build routing slip activities with clear execution behavior and reliable compensation.

## Agent enforcement rules

### Do

- Prefer `record` types for activity arguments and compensation logs. Classes and interfaces are supported when required by an existing contract or interoperability.
- Validate arguments before side effects in `Execute`.
- Implement deterministic compensation for non-idempotent execute steps.

### Do not

- Do not perform side effects before validation.
- Do not omit compensation data needed to reverse side effects.

## Build checklist

- Define activity arguments and compensation log types.
- Validate arguments at activity boundaries.
- Implement both execute and compensate behavior when side effects exist.
- Build itineraries with an explicit execute endpoint. The compensation endpoint is configured with the activity registration.
- Subscribe to routing slip events for monitoring and diagnostics.
- Test both success and rollback scenarios.

## Example: compensating activity

```csharp
public record DownloadImageArguments(string ImageUri, string WorkPath);

public record DownloadImageLog(string ImageSavePath);

public class DownloadImageActivity : IActivity<DownloadImageArguments, DownloadImageLog>
{
    public async Task<ExecutionResult> Execute(ExecuteContext<DownloadImageArguments> context)
    {
        if (!Uri.TryCreate(context.Arguments.ImageUri, UriKind.Absolute, out _))
            throw new ArgumentException("ImageUri must be an absolute URI", nameof(context.Arguments.ImageUri));

        if (string.IsNullOrWhiteSpace(context.Arguments.WorkPath))
            throw new ArgumentException("WorkPath is required", nameof(context.Arguments.WorkPath));

        Directory.CreateDirectory(context.Arguments.WorkPath);
        var filePath = Path.Combine(context.Arguments.WorkPath, context.TrackingNumber.ToString());

        // Replace with a download implementation that uses ImageUri and the cancellation token.
        await File.WriteAllTextAsync(filePath, "downloaded", context.CancellationToken);

        return context.Completed(new DownloadImageLog(filePath));
    }

    public Task<CompensationResult> Compensate(CompensateContext<DownloadImageLog> context)
    {
        File.Delete(context.Log.ImageSavePath);
        return Task.FromResult(context.Compensated());
    }
}
```

## Example: building and executing a routing slip

```csharp
var builder = new RoutingSlipBuilder(NewId.NextGuid());

builder.AddActivity("DownloadImage", new Uri("exchange:download-image_execute"), new
{
    ImageUri = "https://example.com/image.png",
    WorkPath = "/tmp/images"
});

builder.AddVariable("TraceId", Guid.NewGuid().ToString("N"));

var routingSlip = builder.Build();
await bus.Execute(routingSlip);
```

## Example: register routing slip activity

```csharp
services.AddMassTransit(x =>
{
    x.AddActivity<DownloadImageActivity, DownloadImageArguments, DownloadImageLog>();

    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.ConfigureEndpoints(context);
    });
});
```

## Guardrails

- Every non-idempotent execute step needs compensation when its side effect can be reversed. Model irreversible side effects explicitly instead of pretending they can be compensated.
- Compensation logs must contain enough data to reverse side effects.
- Avoid side effects before input validation.
- Keep compensate operations deterministic and replay-safe.

## Default implementation shape

1. `IActivity<TArguments, TLog>` implementation.
2. Execute logic returning completion variables/log.
3. Compensate logic using logged data.
4. Routing slip builder usage with activity addresses.
5. Tests for execute success and downstream-failure compensation.

## Verification

- `dotnet build`
- `dotnet test --filter RoutingSlip`
- `dotnet test --filter Compensation`

## References

- [Routing Slips (Concepts)](/concepts/routing-slips)
- [Routing Slip Activity Configuration](/configuration/routing-slip-activity)
- [Routing Slips Guide](/guides/routing-slips)
