Automaton 1.0.73

dotnet add package Automaton --version 1.0.73
                    
NuGet\Install-Package Automaton -Version 1.0.73
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Automaton" Version="1.0.73" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Automaton" Version="1.0.73" />
                    
Directory.Packages.props
<PackageReference Include="Automaton" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Automaton --version 1.0.73
                    
#r "nuget: Automaton, 1.0.73"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Automaton@1.0.73
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Automaton&version=1.0.73
                    
Install as a Cake Addin
#tool nuget:?package=Automaton&version=1.0.73
                    
Install as a Cake Tool

Automaton

Write once, run everywhere.

A minimal, production-hardened Mealy machine kernel for building state machines, MVU runtimes, event-sourced aggregates, and actor systems — based on the observation that all three are instances of the same mathematical structure: a Mealy machine (finite-state transducer with effects).

transition : (State × Event) → (State × Effect)

Define your pure domain logic once as a transition function. Then plug it into any runtime — a browser UI loop, an event-sourced aggregate, or a mailbox actor — without changing a single line.

Installation

dotnet add package Automaton

The Kernel

public interface Automaton<TState, TEvent, TEffect>
{
    static abstract (TState State, TEffect Effect) Initialize();
    static abstract (TState State, TEffect Effect) Transition(TState state, TEvent @event);
}

Two methods. Zero dependencies. The rest is runtime.

Example: Counter

public record CounterState(int Count);

public interface CounterEvent
{
    record struct Increment : CounterEvent;
    record struct Decrement : CounterEvent;
}

public interface CounterEffect
{
    record struct None : CounterEffect;
}

public class Counter : Automaton<CounterState, CounterEvent, CounterEffect>
{
    public static (CounterState, CounterEffect) Initialize() =>
        (new CounterState(0), new CounterEffect.None());

    public static (CounterState, CounterEffect) Transition(CounterState state, CounterEvent @event) =>
        @event switch
        {
            CounterEvent.Increment => (state with { Count = state.Count + 1 }, new CounterEffect.None()),
            CounterEvent.Decrement => (state with { Count = state.Count - 1 }, new CounterEffect.None()),
            _ => throw new UnreachableException()
        };
}

The Shared Runtime

var runtime = await AutomatonRuntime<Counter, CounterState, CounterEvent, CounterEffect>
    .Start(
        observer: (state, @event, effect) =>
        {
            Console.WriteLine($"{@event} → {state}");
            return ValueTask.CompletedTask;
        },
        interpreter: _ => new ValueTask<CounterEvent[]>([]));

await runtime.Dispatch(new CounterEvent.Increment());
// Prints: Increment → CounterState { Count = 1 }

Production Guarantees

Property Guarantee
Thread safety All public mutating methods are serialized via SemaphoreSlim. Concurrent callers are queued, never interleaved.
Cancellation All async methods accept CancellationToken.
Feedback depth Interpreter feedback loops are bounded (max 64 depth). Runaway cycles throw InvalidOperationException.
Null safety Observer and Interpreter are validated at construction.

Observer Composition

Observers compose sequentially with Then:

var combined = renderObserver.Then(logObserver).Then(metricsObserver);

Building Custom Runtimes

Runtime Pattern Observer Interpreter
MVU Render the new state Execute effects, return feedback events
Event Sourcing Append event to store + record effect No-op (empty)
Actor No-op (state is internal) Execute effect with self-reference

See the test project for complete reference implementations.

The Decider — Command Validation

public interface Decider<TState, TCommand, TEvent, TEffect, TError>
    : Automaton<TState, TEvent, TEffect>
{
    static abstract Result<TEvent[], TError> Decide(TState state, TCommand command);
    static virtual bool IsTerminal(TState state) => false;
}
var runtime = await DecidingRuntime<Counter, CounterState, CounterCommand,
    CounterEvent, CounterEffect, CounterError>.Start(observer, interpreter);

var result = await runtime.Handle(new CounterCommand.Add(5));
// result is Ok(CounterState { Count = 5 })

var overflow = await runtime.Handle(new CounterCommand.Add(200));
// overflow is Err(CounterError.Overflow { ... }) — state unchanged

Observability — OpenTelemetry Tracing

Zero-dependency tracing via System.Diagnostics.ActivitySource, compatible with any OpenTelemetry collector.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing.AddSource(AutomatonDiagnostics.SourceName));

When no listener is registered, instrumentation has near-zero overhead.

Span Name Tags
Automaton.Start automaton.type, automaton.state.type
Automaton.Dispatch automaton.type, automaton.event.type
Automaton.InterpretEffect automaton.type, automaton.effect.type
Automaton.Decider.Start automaton.type, automaton.state.type
Automaton.Decider.Handle automaton.type, automaton.command.type, automaton.result, automaton.error.type

What's in the Box

Type Purpose
Automaton<TState, TEvent, TEffect> Mealy machine interface (Initialize + Transition)
AutomatonRuntime<TAutomaton, TState, TEvent, TEffect> Thread-safe async runtime
Observer<TState, TEvent, TEffect> Transition observer delegate
Interpreter<TEffect, TEvent> Effect interpreter delegate
ObserverExtensions.Then Sequential observer composition
Decider<TState, TCommand, TEvent, TEffect, TError> Command validation interface
DecidingRuntime<...> Command-validating runtime wrapper
Result<TSuccess, TError> Discriminated union with Match, Map, Bind, MapError
AutomatonDiagnostics OpenTelemetry-compatible tracing (ActivitySource)

License

Apache 2.0

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.73 50 3/9/2026
1.0.59 42 3/8/2026
1.0.23 49 3/5/2026
1.0.20 43 3/4/2026
1.0.18 46 3/4/2026
1.0.16 43 3/3/2026
1.0.15 40 3/3/2026
1.0.14 46 3/3/2026
1.0.10 54 3/2/2026
1.0.7 47 3/2/2026
1.0.6 53 3/2/2026