using Microsoft.Extensions.DependencyInjection;
using ModularityKit.Mutator.Abstractions;
using ModularityKit.Mutator.Abstractions.Changes;
using ModularityKit.Mutator.Abstractions.Context;
using ModularityKit.Mutator.Abstractions.Engine;
using ModularityKit.Mutator.Abstractions.Intent;
using ModularityKit.Mutator.Abstractions.Policies;
using ModularityKit.Mutator.Abstractions.Results;
using ModularityKit.Mutator.Runtime;
var services = new ServiceCollection();
services.AddMutators(MutationEngineOptions.Strict);
var provider = services.BuildServiceProvider();
var engine = provider.GetRequiredService<IMutationEngine>();
engine.RegisterPolicy(new PreventNegativeQuotaPolicy());
var state = new QuotaState("tenant-42", 10);
var mutation = new IncreaseQuotaMutation("tenant-42", 5);
var result = await engine.ExecuteAsync(mutation, state);
Console.WriteLine(result.NewState!.Quota);
public sealed record QuotaState(string StateId, int Quota);
public sealed class IncreaseQuotaMutation : MutationBase<QuotaState>
{
public IncreaseQuotaMutation(string stateId, int amount)
: base(
CreateIntent(
operationName: "IncreaseQuota",
category: "Quota",
description: "Increase tenant quota"),
MutationContext.System("Initial quota setup") with { StateId = stateId })
{
Amount = amount;
}
public int Amount { get; }
public MutationResult<QuotaState> Apply(QuotaState state)
=> MutationResult<QuotaState>.Success(
state with { Quota = state.Quota + Amount },
ChangeSet.Single(StateChange.Modified("Quota", state.Quota, state.Quota + Amount)));
public ValidationResult Validate(QuotaState state)
=> Amount > 0
? ValidationResult.Success()
: ValidationResult.WithError("Amount", "Amount must be positive.");
}
public sealed class PreventNegativeQuotaPolicy : IMutationPolicy<QuotaState>
{
public string Name => "PreventNegativeQuota";
public int Priority => 100;
public string? Description => "Rejects quota changes that would go negative.";
public PolicyDecision Evaluate(IMutation<QuotaState> mutation, QuotaState state)
=> PolicyDecision.Allow();
}IMutationEngine runs a consistent pipeline around every mutation:
- evaluate registered policies
- validate the mutation against current state
- run interceptors
- apply the mutation
- record audit/history data
- update runtime metrics
Core runtime concurrency is controlled by MutationEngineOptions.MaxConcurrentMutations.
- mutations targeting the same
MutationContext.StateIdare serialized - batch execution remains ordered and sequential
- this is separate from request-storage concurrency in
ModularityKit.Mutator.Governance
IMutation<TState>MutationBase<TState>IMutationEngineIMutationExecutorMutationEngineOptions
IMutationPolicy<TState>IPolicyRegistryPolicyDecisionPolicyRequirement
IMutationPolicy<TState> supports both synchronous and asynchronous evaluation.
- implement
Evaluate(...)for lightweight in-process rules - implement
EvaluateAsync(..., CancellationToken)for external identity, ticketing, quota, or compliance checks - the runtime evaluates policies in descending
Priorityorder - sync and async policies can be registered together; they participate in the same priority-ordered pipeline
- when
MutationEngineOptions.PolicyEvaluationTimeoutis set, the timeout is applied per policy evaluation - caller cancellation still flows through unchanged
- policy failures are surfaced as
PolicyEvaluationException - policy timeouts are surfaced as
PolicyEvaluationTimeoutException
Typical integration cases include:
- external approval evidence checks
- actor/identity resolution against IAM or directory systems
- ticket status validation before execution
- quota lookups in remote control planes
- compliance or risk verification before commit
public sealed class RequireApprovedTicketPolicy : IMutationPolicy<QuotaState>
{
private readonly ITicketGateway _tickets;
public RequireApprovedTicketPolicy(ITicketGateway tickets)
{
_tickets = tickets;
}
public string Name => "RequireApprovedTicket";
public int Priority => 200;
public string? Description => "Checks ticket approval status before quota changes execute.";
public async Task<PolicyDecision> EvaluateAsync(
IMutation<QuotaState> mutation,
QuotaState state,
CancellationToken cancellationToken = default)
{
var ticketId = mutation.Context.CorrelationId;
var approved = await _tickets.IsApprovedAsync(ticketId!, cancellationToken);
return approved
? PolicyDecision.Allow(Name, "External ticket is approved.")
: PolicyDecision.Deny("External ticket is not approved.", Name);
}
}MutationResult<TState>BatchMutationResult<TState>ValidationResultChangeSetStateChangeSideEffectSideEffectDataContractAttributeSideEffectDataContractRegistry
Use typed side effect payloads when the emitted data is meant to survive serialization, audit, or downstream integration:
[SideEffectDataContract("workflow.started", 1)]
public sealed record WorkflowStartedSideEffectData
{
public required string Initiator { get; init; }
public required int StepCount { get; init; }
public required string WorkflowId { get; init; }
}
SideEffectDataContractRegistry.Register<WorkflowStartedSideEffectData>();
var effect = SideEffect.Create(
type: "WorkflowStarted",
description: "Approval workflow started",
data: new WorkflowStartedSideEffectData
{
Initiator = "alice",
StepCount = 2,
WorkflowId = "wf-42"
});The side effect keeps DataContractType and DataContractVersion alongside Data, so persistence and integration layers do not have to guess the payload shape.
MutationContextMutationIntentBlastRadius
IMutationAuditorIMutationHistoryStoreMutationHistoryIMetricsCollectorMutationStatisticsIMutationInterceptor
Runnable examples for the core engine live under Examples/Core:
ModularityKit.Mutator is the direct execution runtime.
If your workflow needs deferred execution, request approval, pending states, or stale-version resolution before the mutation can run, use ModularityKit.Mutator.Governance on top of the core package.
Included today:
- direct mutation execution
- batch execution
- policy evaluation
- validation and failure modeling
- audit/history capture
- metrics and interception
- in-memory runtime components for local and test scenarios
Not included in the core package:
- request lifecycle management
- approval workflow orchestration
- versioned request resolution
- governed request persistence contracts

