diff --git a/ModularityKit.Mutator.slnx b/ModularityKit.Mutator.slnx
index f184391..f83eb84 100644
--- a/ModularityKit.Mutator.slnx
+++ b/ModularityKit.Mutator.slnx
@@ -15,6 +15,7 @@
+
diff --git a/README.md b/README.md
index 9093646..dd33e54 100644
--- a/README.md
+++ b/README.md
@@ -1,153 +1,27 @@
-# ModularityKit.Mutators
+
-[](LICENSE)
-[](https://dotnet.microsoft.com/)
-
-A deterministic, async safe mutation engine with built in policy enforcement, audit logging, and execution context.
-
----
-
-## Features
+
-- **Deterministic State Mutations** – Apply and simulate state changes safely
-- **Policy Enforcement** – Declarative, composable mutation policies
-- **Async Safe Execution** – Works across `async/await` boundaries
-- **Immutable State Models** – Encourages safe, concurrent operations
-- **Audit & Change Tracking** – `ChangeSet` captures granular modifications
-- **High Performance** – Minimal overhead per mutation execution
+# ModularityKit.Mutator
----
-
-## Quick Start (Example)
-
-```csharp
-using Microsoft.Extensions.DependencyInjection;
-using ModularityKit.Mutators.Abstractions;
-using ModularityKit.Mutators.Abstractions.Engine;
-using ModularityKit.Mutators.Runtime;
-using ModularityKit.Mutators.Runtime.Loggers;
-using Mutators.Examples.BillingQuotas.Policies;
-using Mutators.Examples.IamRoles.Policies;
-using Mutators.Examples.WorkflowApprovals.Policies;
-using IamTwoManApprovalPolicy = Mutators.Examples.IamRoles.Policies.RequireTwoManApprovalPolicy;
-using FeatureFlagsTwoManApprovalPolicy = Mutators.Examples.FeatureFlags.Policies.RequireTwoManApprovalPolicy;
+[](LICENSE)
+[](https://dotnet.microsoft.com/)
-var services = new ServiceCollection();
-// 1. Register Mutators engine with options
-services.AddMutators(MutationEngineOptions.Strict, addDefaultLoggingInterceptor: true);
+## Packages
-// 2. Build DI provider
-var provider = services.BuildServiceProvider();
-var engine = provider.GetRequiredService();
+- [`ModularityKit.Mutator`](src/README.md) - core mutation runtime
+- [`ModularityKit.Mutator.Governance`](src/Governance/README.md) - request lifecycle, approvals, and governed execution
-// 3. Register policies
-engine.RegisterPolicy(new MaxQuotaPolicy());
-engine.RegisterPolicy(new PreventNegativeQuotaPolicy());
-engine.RegisterPolicy(new IamTwoManApprovalPolicy());
-engine.RegisterPolicy(new PreventLastAdminRemovalPolicy());
-engine.RegisterPolicy(new FeatureFlagsTwoManApprovalPolicy());
-engine.RegisterPolicy(new EnforceOrderPolicy());
-engine.RegisterPolicy(new RequireManagerApprovalPolicy());
+## Repository
-// 4. Execute example scenarios
-await Examples.FeatureFlags.Scenarios.EnableNewCheckoutScenario.Run(engine);
-await Examples.BillingQuotas.Scenarios.EmergencyIncreaseScenario.Run(engine);
-await Examples.IamRoles.Scenarios.GrantAdminScenario.Run(engine);
+- [`Examples`](Examples/README.md)
+- [`Benchmarks`](Benchmarks/README.md)
+- [`Docs`](Docs/)
+- [`Tests`](Tests/)
-// 5. Inspect history
-var history = await engine.GetHistoryAsync(stateId: "EnableNewCheckout");
-MutationHistoryLogger.LogHistory(history);
+## Build
-// 6. Metrics & statistics
-var stats = await engine.GetStatisticsAsync();
-Console.WriteLine($"Total executed: {stats.TotalExecuted}");
-Console.WriteLine($"Average execution time: {stats.AverageExecutionTime.TotalMilliseconds:F2} ms");
+```bash
+dotnet build ModularityKit.Mutator.slnx -c Release
```
-
-___
-## Core Concepts
-
-### **Mutation**
-
-Represents single atomic change to specific state.
-
-- Implement `IMutation`
-- Define intent (`MutationIntent`)
-- Implement `Validate(TState)`
-- Implement `Apply(TState)` and optionally `Simulate(TState)`
-
-___
-### **State**
-
-Immutable representation of domain data.
-
-- use `record` types.
-- Concurrent safe
-- Represents the source of truth for mutations
-
-___
-### **Policy**
-
-Controls which mutations are allowed.
-
-- Implement `IMutationPolicy`
-- Evaluate mutations before application
-- Return `PolicyDecision.Allow()` or `PolicyDecision.Deny(reason)`
-
----
-## Best Practices
-
-1. **Immutable State** – Always use `record` types or read-only properties.
-2. **Explicit Context** – Pass `MutationContext` per mutation.
-3. **Validate Before Apply** – Call `Validate()` before applying a mutation.
-4. **Enforce Policies** – Never skip policy evaluation.
-5. **Scoped Execution** – Execute mutations inside a controlled engine.
-6. **Do Not Share Mutable State** – Each logical operation gets its own state snapshot.
-7. **Use Clear IDs for Tracking** – Helps with audit logs and debugging.
-8. **Centralized Registration** – Register all policies at engine startup.
-
----
-## API Reference
-
-### Core Interfaces
-
-- `IMutation` – Base interface for mutations
-- `IMutationPolicy` – Policy controlling allowed mutations
-- `IMutationEngine` – Engine for applying mutations
-- `MutationContext` – Context carrying metadata about execution
-- `MutationResult` – Wraps the new state and change set
-- `ChangeSet` – Captures state modifications
-
-## Package Layout
-
-- `ModularityKit.Mutator` - core mutation runtime, policies, audit, history, and side effects
-- [`ModularityKit.Mutator.Governance`](src/Governance/README.md) - governed mutation request lifecycle, pending execution, and approval-oriented contracts
-
----
-## Metrics & Logging
-
-The engine supports:
-- Mutation execution history (`GetHistoryAsync`)
-- Execution statistics (`GetStatisticsAsync`)
-- Logging via `MutationHistoryLogger`
-- Optional interceptors for audit and diagnostics
-
----
-## Architecture Decision Records (ADR)
-
-Key architectural decisions for **ModularityKit.Mutators** are tracked as ADRs. They document engine design, policy evaluation, context handling, change tracking, and DI registration.
-
-| ADR | Title | Summary |
-| ------- | --------------------------- | -------------------------------------------------------------------------------------------- |
-| ADR-001 | Mutation Engine Design | Defines `IMutationEngine`, engine options, strict vs lenient execution modes |
-| ADR-002 | Policy Evaluation | Centralized policy evaluation for validation, risk assessment, and allow/deny decisions |
-| ADR-003 | Context & MutationContext | Explicit, per execution flow context for audit, tracing, and tenant isolation |
-| ADR-004 | ChangeSet Model | Immutable, granular state changes for audit, rollback, and history inspection |
-| ADR-005 | Mutation Audit Abstractions | Structured, immutable audit entries capturing intent, context, changes, and policy decisions |
-
-See full ADR documentation in [`Docs/Decision/Adr`](Docs/Decision/listadr) for details on each architectural decision.
-
-## Roadmap
-
-The planned evolution of the engine is documented in [`Docs/Roadmap.md`](Docs/Roadmap.md).
diff --git a/Tests/ModularityKit.Mutator.Tests/ModularityKit.Mutator.Tests.csproj b/Tests/ModularityKit.Mutator.Tests/ModularityKit.Mutator.Tests.csproj
new file mode 100644
index 0000000..0f16982
--- /dev/null
+++ b/Tests/ModularityKit.Mutator.Tests/ModularityKit.Mutator.Tests.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/Tests/ModularityKit.Mutator.Tests/Runtime/MutationEngineConcurrencyTests.cs b/Tests/ModularityKit.Mutator.Tests/Runtime/MutationEngineConcurrencyTests.cs
new file mode 100644
index 0000000..b7705da
--- /dev/null
+++ b/Tests/ModularityKit.Mutator.Tests/Runtime/MutationEngineConcurrencyTests.cs
@@ -0,0 +1,221 @@
+using Microsoft.Extensions.DependencyInjection;
+using System.Collections.Concurrent;
+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.Results;
+using ModularityKit.Mutator.Runtime;
+using Xunit;
+
+namespace ModularityKit.Mutator.Tests.Runtime;
+
+public sealed class MutationEngineConcurrencyTests
+{
+ [Fact]
+ public async Task ExecuteAsync_serializes_mutations_that_target_the_same_state_id()
+ {
+ var services = new ServiceCollection();
+ services.AddMutators(configure: options =>
+ {
+ options.MaxConcurrentMutations = 4;
+ options.EnableDetailedMetrics = false;
+ });
+
+ await using var provider = services.BuildServiceProvider();
+ var engine = provider.GetRequiredService();
+ using var gate = new BlockingMutationGate();
+ var state = new OrderedState("initial");
+
+ var first = new BlockingMutation(gate, "shared-state", "first");
+ var second = new BlockingMutation(gate, "shared-state", "second");
+
+ var firstTask = Task.Run(() => engine.ExecuteAsync(first, state));
+ var secondTask = Task.Run(() => engine.ExecuteAsync(second, state));
+
+ Assert.True(await gate.WaitForEntriesAsync(1, TimeSpan.FromSeconds(5)));
+ Assert.Equal(1, gate.PeakConcurrency);
+
+ gate.Release();
+
+ var results = await Task.WhenAll(firstTask, secondTask);
+
+ Assert.All(results, result => Assert.True(result.IsSuccess));
+ Assert.Equal(1, gate.PeakConcurrency);
+ }
+
+ [Fact]
+ public async Task ExecuteAsync_honors_max_concurrent_mutations_for_different_states()
+ {
+ var services = new ServiceCollection();
+ services.AddMutators(configure: options =>
+ {
+ options.MaxConcurrentMutations = 2;
+ options.EnableDetailedMetrics = false;
+ });
+
+ await using var provider = services.BuildServiceProvider();
+ var engine = provider.GetRequiredService();
+ using var gate = new BlockingMutationGate();
+ var states = new[]
+ {
+ new OrderedState("one"),
+ new OrderedState("two"),
+ new OrderedState("three"),
+ new OrderedState("four")
+ };
+
+ var tasks = new[]
+ {
+ Task.Run(() => engine.ExecuteAsync(new BlockingMutation(gate, "state-1", "one"), states[0])),
+ Task.Run(() => engine.ExecuteAsync(new BlockingMutation(gate, "state-2", "two"), states[1])),
+ Task.Run(() => engine.ExecuteAsync(new BlockingMutation(gate, "state-3", "three"), states[2])),
+ Task.Run(() => engine.ExecuteAsync(new BlockingMutation(gate, "state-4", "four"), states[3]))
+ };
+
+ Assert.True(await gate.WaitForEntriesAsync(2, TimeSpan.FromSeconds(5)));
+ Assert.Equal(2, gate.PeakConcurrency);
+
+ gate.Release();
+
+ var results = await Task.WhenAll(tasks);
+
+ Assert.All(results, result => Assert.True(result.IsSuccess));
+ Assert.Equal(2, gate.PeakConcurrency);
+ }
+
+ [Fact]
+ public async Task ExecuteBatchAsync_remains_ordered_while_respecting_runtime_concurrency_gates()
+ {
+ var services = new ServiceCollection();
+ services.AddMutators(configure: options =>
+ {
+ options.MaxConcurrentMutations = 2;
+ options.EnableDetailedMetrics = false;
+ });
+
+ await using var provider = services.BuildServiceProvider();
+ var engine = provider.GetRequiredService();
+ var observed = new ConcurrentQueue();
+
+ var batch = new[]
+ {
+ new OrderedMutation("state-1", "first", observed),
+ new OrderedMutation("state-2", "second", observed),
+ new OrderedMutation("state-1", "third", observed)
+ };
+
+ var result = await engine.ExecuteBatchAsync(batch, new OrderedState("initial"));
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(3, result.Results.Count);
+ Assert.Equal(new[] { "first", "second", "third" }, observed);
+ }
+
+ [Fact]
+ public void AddMutators_rejects_non_positive_max_concurrent_mutations()
+ {
+ var services = new ServiceCollection();
+ services.AddMutators(configure: options => options.MaxConcurrentMutations = 0);
+
+ Assert.Throws(() => services.BuildServiceProvider().GetRequiredService());
+ }
+
+ private sealed record OrderedState(string Value);
+
+ private sealed class OrderedMutation(string stateId, string value, ConcurrentQueue observed)
+ : IMutation
+ {
+ public MutationIntent Intent { get; } = new()
+ {
+ OperationName = "Order",
+ Category = "Test",
+ Description = "Observe execution order"
+ };
+
+ public MutationContext Context { get; } = MutationContext.User("tester", "Tester", "Order test")
+ with { StateId = stateId };
+
+ public MutationResult Apply(OrderedState state)
+ {
+ observed.Enqueue(value);
+ return MutationResult.Success(state with { Value = value }, ChangeSet.Empty);
+ }
+
+ public ValidationResult Validate(OrderedState state) => ValidationResult.Success();
+
+ public MutationResult Simulate(OrderedState state) => Apply(state);
+ }
+
+ private sealed class BlockingMutationGate : IDisposable
+ {
+ private readonly ManualResetEventSlim _release = new(false);
+ private int _entered;
+ private int _active;
+ private int _peak;
+
+ public int PeakConcurrency => Volatile.Read(ref _peak);
+
+ public async Task WaitForEntriesAsync(int expectedEntries, TimeSpan timeout)
+ {
+ var started = DateTimeOffset.UtcNow;
+
+ while (Volatile.Read(ref _entered) < expectedEntries)
+ {
+ if (DateTimeOffset.UtcNow - started > timeout)
+ return false;
+
+ await Task.Delay(10);
+ }
+
+ return true;
+ }
+
+ public void Enter()
+ {
+ Interlocked.Increment(ref _entered);
+ var active = Interlocked.Increment(ref _active);
+
+ while (true)
+ {
+ var peak = Volatile.Read(ref _peak);
+ if (active <= peak || Interlocked.CompareExchange(ref _peak, active, peak) == peak)
+ break;
+ }
+
+ _release.Wait();
+ Interlocked.Decrement(ref _active);
+ }
+
+ public void Release() => _release.Set();
+
+ public void Dispose() => _release.Dispose();
+ }
+
+ private sealed class BlockingMutation(
+ BlockingMutationGate gate,
+ string stateId,
+ string value) : IMutation
+ {
+ public MutationIntent Intent { get; } = new()
+ {
+ OperationName = "Block",
+ Category = "Test",
+ Description = "Block until released"
+ };
+
+ public MutationContext Context { get; } = MutationContext.User($"{stateId}-actor", $"{stateId}-actor", "Concurrency test")
+ with { StateId = stateId };
+
+ public MutationResult Apply(OrderedState state)
+ {
+ gate.Enter();
+ return MutationResult.Success(state with { Value = value }, ChangeSet.Empty);
+ }
+
+ public ValidationResult Validate(OrderedState state) => ValidationResult.Success();
+
+ public MutationResult Simulate(OrderedState state) => Apply(state);
+ }
+}
diff --git a/assets/brand/logo.png b/assets/brand/logo.png
new file mode 100644
index 0000000..77c54e9
Binary files /dev/null and b/assets/brand/logo.png differ
diff --git a/assets/brand/logotype.png b/assets/brand/logotype.png
new file mode 100644
index 0000000..94da0fb
Binary files /dev/null and b/assets/brand/logotype.png differ
diff --git a/assets/brand/modularitykit-mutator-logo-128.png b/assets/brand/modularitykit-mutator-logo-128.png
new file mode 100644
index 0000000..44c912f
Binary files /dev/null and b/assets/brand/modularitykit-mutator-logo-128.png differ
diff --git a/assets/brand/mutator-landing-banner.png b/assets/brand/mutator-landing-banner.png
new file mode 100644
index 0000000..5f294ec
Binary files /dev/null and b/assets/brand/mutator-landing-banner.png differ
diff --git a/assets/brand/mutator-tagline.png b/assets/brand/mutator-tagline.png
new file mode 100644
index 0000000..b80530e
Binary files /dev/null and b/assets/brand/mutator-tagline.png differ
diff --git a/assets/core/mutator-core-what-it-provides.png b/assets/core/mutator-core-what-it-provides.png
new file mode 100644
index 0000000..f1e4de1
Binary files /dev/null and b/assets/core/mutator-core-what-it-provides.png differ
diff --git a/assets/core/mutator-overview.png b/assets/core/mutator-overview.png
new file mode 100644
index 0000000..c6b4bf1
Binary files /dev/null and b/assets/core/mutator-overview.png differ
diff --git a/assets/governance/goverwhatadded.png b/assets/governance/goverwhatadded.png
new file mode 100644
index 0000000..e4ebb08
Binary files /dev/null and b/assets/governance/goverwhatadded.png differ
diff --git a/assets/governance/logogorver.png b/assets/governance/logogorver.png
new file mode 100644
index 0000000..2fdb59e
Binary files /dev/null and b/assets/governance/logogorver.png differ
diff --git a/assets/governance/logotype_governance.png b/assets/governance/logotype_governance.png
new file mode 100644
index 0000000..f329f44
Binary files /dev/null and b/assets/governance/logotype_governance.png differ
diff --git a/assets/governance/modularitykit-mutator-governance-logo-128.png b/assets/governance/modularitykit-mutator-governance-logo-128.png
new file mode 100644
index 0000000..9ea4bef
Binary files /dev/null and b/assets/governance/modularitykit-mutator-governance-logo-128.png differ
diff --git a/assets/governance/mutator-governance-overview.png b/assets/governance/mutator-governance-overview.png
new file mode 100644
index 0000000..18e21e7
Binary files /dev/null and b/assets/governance/mutator-governance-overview.png differ
diff --git a/src/Abstractions/Engine/IMutationEngine.cs b/src/Abstractions/Engine/IMutationEngine.cs
index 7e67548..13315c6 100644
--- a/src/Abstractions/Engine/IMutationEngine.cs
+++ b/src/Abstractions/Engine/IMutationEngine.cs
@@ -25,6 +25,12 @@ namespace ModularityKit.Mutator.Abstractions.Engine;
/// - History persistence
///
///
+/// Core runtime concurrency is governed by .
+/// Mutations that target the same are serialized by the runtime so shared-state workloads
+/// remain deterministic. This is separate from governance request storage concurrency, which protects request lifecycle writes
+/// in the governance package.
+///
+///
/// The engine acts as the primary governance boundary for all state mutations.
///
///
@@ -57,8 +63,9 @@ Task> ExecuteAsync(
/// A describing the outcome of the batch execution.
///
///
- /// Batch execution semantics (e.g. fail-fast vs best-effort) are controlled
- /// by .
+ /// Batch execution is ordered and sequential. Each batch step passes through the same core concurrency controls as a
+ /// single execution, including the maximum concurrent execution limit and any state-specific serialization.
+ /// Fail-fast vs best-effort behavior is controlled by .
///
Task> ExecuteBatchAsync(
IEnumerable> mutations,
diff --git a/src/Abstractions/MutationEngineOptions.cs b/src/Abstractions/MutationEngineOptions.cs
index f3c888d..f2ff384 100644
--- a/src/Abstractions/MutationEngineOptions.cs
+++ b/src/Abstractions/MutationEngineOptions.cs
@@ -55,11 +55,14 @@ public sealed class MutationEngineOptions
public bool EnableDetailedMetrics { get; set; } = false;
///
- /// The maximum number of mutations that may be executed concurrently.
+ /// The maximum number of mutations that may be executed concurrently by the core runtime.
///
///
- /// This setting controls parallelism and can be used to limit resource usage
- /// or avoid contention.
+ /// This setting limits concurrent core execution across the engine.
+ /// Mutations that carry the same
+ /// are serialized so shared-state workloads remain deterministic.
+ /// Batch execution remains ordered; the limit applies to each batch step as it
+ /// passes through the runtime.
///
public int MaxConcurrentMutations { get; set; } = 10;
diff --git a/src/Governance/README.md b/src/Governance/README.md
index 76e43ef..6b15921 100644
--- a/src/Governance/README.md
+++ b/src/Governance/README.md
@@ -1,38 +1,42 @@
-# ModularityKit.Mutator.Governance
+
+
+
+
+## Quick start
+
+```csharp
+using ModularityKit.Mutator.Abstractions.Core;
+using ModularityKit.Mutator.Abstractions.Requests;
+using ModularityKit.Mutator.Governance.Abstractions.Storage;
+using ModularityKit.Mutator.Governance.Runtime.Storage;
+
+var store = new InMemoryMutationRequestStore();
+
+var request = MutationRequestFactory.PendingApproval(
+ stateId: "tenant-42:roles",
+ stateType: "IamRoleState",
+ mutationType: "GrantRoleMutation",
+ intent: new MutationIntent
+ {
+ OperationName = "GrantRole",
+ Category = "Security",
+ Description = "Grant elevated role to tenant operator"
+ },
+ context: MutationContext.User("requester-1", "Requester One", "Incident escalation"),
+ expectedStateVersion: "v10",
+ approvalRequirements:
+ [
+ MutationApprovalRequirement.SingleActorStep("security-lead"),
+ MutationApprovalRequirement.SingleActorStep("platform-owner")
+ ]);
+
+var persisted = await store.Create(request);
+Console.WriteLine($"{persisted.RequestId} -> {persisted.Status}");
+```
+
+## Primary APIs
-`ModularityKit.Mutator.Governance` is the governance focused extension layer for `ModularityKit.Mutator`.
-
-The core package stays responsible for direct mutation execution. Governance builds on top of that runtime with request based lifecycle concepts such as deferred execution, approvals, and request storage.
-
-## Features
-
-- **Mutation Requests** - model governed mutation submission as a durable request
-- **Pending Lifecycle** - represent requests that cannot execute immediately
-- **Decision History** - record approvals, rejections, cancellations, and other lifecycle transitions
-- **Approval Workflow** - model request-level approval requirements and explicit approver actions
-- **Governed Execution** - execute approved requests through resolution and the core mutation engine
-- **Request Storage Contracts** - define a persistence seam for governance-oriented stores
-- **Runtime Lifecycle Management** - move requests through pending, approval, expiration, and execution transitions
-- **In-Memory Runtime Support** - provide lightweight request runtime services for development and tests
-
-## Governance Flow
-
-The package is built around a request-driven governance loop:
-
-1. create `MutationRequest`
-2. move it through pending lifecycle states when direct execution is not allowed
-3. collect approval decisions when approval is required
-4. resolve the request against the current state version before execution
-5. execute the underlying mutation through the core engine
-6. persist the terminal governance outcome and execution metadata
-
-The important point is that governance owns the request lifecycle around execution. The base `ModularityKit.Mutator` package still owns the mutation engine itself.
-
-## Main Entry Points
-
-Most consumers only need a small set of types.
-
-### Request Model
+### Requests
- `MutationRequest`
- `MutationRequestFactory`
@@ -40,31 +44,23 @@ Most consumers only need a small set of types.
- `MutationRequestStatus`
- `PendingMutationReason`
-Use these to create and inspect governed requests.
-
### Storage
- `IMutationRequestStore`
- `InMemoryMutationRequestStore`
-Use the store to persist requests and load them back into governance runtime services.
-
### Lifecycle
- `IMutationRequestLifecycleManager`
- `MutationRequestLifecycleManager`
-Use lifecycle services to submit, pend, approve, reject, expire, supersede, cancel, and mark requests as executed.
-
### Approval
- `IMutationRequestApprovalWorkflowManager`
- `MutationRequestApprovalWorkflowManager`
- `MutationApprovalRequirement`
-Use approval workflow services when a request must be explicitly approved by one or more actors before execution.
-
-### Version Resolution
+### Resolution
- `IMutationRequestVersionResolver`
- `IMutationRequestVersionResolutionManager`
@@ -72,128 +68,53 @@ Use approval workflow services when a request must be explicitly approved by one
- `MutationRequestVersionResolutionOutcome`
- `VersionedRequestResolutionStrategy`
-Use resolution services to decide what happens when deferred request no longer matches the state version it was created against.
-
-### Governed Execution
+### Execution
- `IGovernanceExecutionManager`
- `GovernanceExecutionManager`
- `GovernedExecutionResult`
-Use governed execution to close the loop from approved request to core mutation execution and terminal governance state.
+## Package structure
-## Package Areas
+The project is organized by governance concern:
-The codebase is organized by governance concern rather than by framework layer alone.
+- `Abstractions/Requests` for request models, decisions, and factory methods
+- `Abstractions/Storage` for persistence contracts
+- `Abstractions/Approval` for approval requirements and workflow contracts
+- `Abstractions/Resolution` for stale-version handling and resolution outcomes
+- `Abstractions/Execution` for governed execution contracts and results
+- `Runtime` for lifecycle, approval, resolution, execution, and in-memory storage services
+- `Abstractions/Exceptions` for governance-specific failures
-### Requests
-
-`Abstractions/Requests` contains the durable request model, decision taxonomy, and request factory methods.
-
-- `Requests/Model`
-- `Requests/Decisions`
-- `Requests/Factory`
+## Examples
-### Lifecycle
+Runnable examples live under [`Examples/Governance`](../../Examples/Governance):
-`Lifecycle` owns generic request movement between governance states such as pending, approved, rejected, expired, superseded, and executed.
+- [`RequestLifecycle`](../../Examples/Governance/RequestLifecycle/README.md)
+- [`ApprovalWorkflow`](../../Examples/Governance/ApprovalWorkflow/README.md)
+- [`VersionedResolution`](../../Examples/Governance/VersionedResolution/README.md)
+- [`GovernedExecution`](../../Examples/Governance/GovernedExecution/README.md)
-- `Lifecycle/Contracts`
-- `Lifecycle/Model`
-- `Runtime/Lifecycle/Execution`
-- `Runtime/Lifecycle/Validation`
-- `Runtime/Lifecycle/State`
+## Relationship to the core package
-### Approval
+`ModularityKit.Mutator` owns mutation execution, policy evaluation, audit, history, side effects, and metrics.
-`Approval` builds request-level approval workflow on top of the generic lifecycle model.
+`ModularityKit.Mutator.Governance` owns the request lifecycle around that execution: approvals, pending states, request storage, stale-version resolution, and terminal governance decisions.
-- `Approval/Contracts`
-- `Approval/Model`
-- `Approval/Mapping`
-- `Runtime/Approval/Execution`
-- `Runtime/Approval/State`
+## Current scope
-### Resolution
+Included today:
-`Resolution` owns version-aware request handling before governed execution.
-
-- `Resolution/Contracts`
-- `Resolution/Model`
-- `Resolution/Strategies`
-- `Runtime/Resolution/Evaluation`
-- `Runtime/Resolution/Execution`
-
-### Execution
-
-`Execution` owns the bridge from governance request semantics into the core mutation engine.
-
-- `Execution/Contracts`
-- `Execution/Model`
-- `Runtime/Execution/Mutation`
-- `Runtime/Execution/Orchestration`
-- `Runtime/Execution/Outcome`
-- `Runtime/Execution/Persistence`
-
-### Storage and Exceptions
-
-`Storage` defines persistence seams. `Exceptions` contains governance-specific failures grouped by concern.
-
-- `Abstractions/Storage`
-- `Abstractions/Exceptions/Approval`
-- `Abstractions/Exceptions/Lifecycle`
-- `Abstractions/Exceptions/Storage`
-
-## What Exists Today
-
-Today the package already provides:
-
-- durable `MutationRequest` modeling
-- request-level approval requirements
+- request modeling and decision history
+- approval requirements and workflow execution
- optimistic concurrency in request storage
-- explicit lifecycle transitions
-- version-aware request resolution
-- governed execution through the core mutation engine
-- in-memory runtime support for examples and tests
-
-What it does not try to do yet:
-
-- persistence providers such as EF Core or PostgreSQL
-- query stores for operational governance reporting
-- compensation and retry orchestration
-- external async approval or policy integrations
-
-## Relationship to Core
-
-### `ModularityKit.Mutator`
-
-Responsible for:
-
-- mutation execution
-- policy evaluation
-- audit and history basics
-- side effects
-- metrics and interception
-
-### `ModularityKit.Mutator.Governance`
-
-Responsible for:
-
-- mutation request lifecycle
-- pending execution modeling
-- approval oriented governance contracts
-- request decision history
-- governance specific storage and future query seams
-
-## Direction
-
-This package is the place where broader governance behavior should grow without turning the core mutation engine into a workflow framework.
-
-The near-term direction is:
+- version-aware resolution before execution
+- governed execution orchestration
+- in-memory support for local runtime scenarios
-- harden governed execution semantics
-- add governance persistence and query providers
-- expose governance metadata operationally
-- support richer approval and integration scenarios
+Not included yet:
-The goal is to keep the core runtime small and execution focused while letting governance evolve as an opt-in extension.
+- production persistence providers such as EF Core or PostgreSQL
+- reporting/query stores for operational governance views
+- compensation or retry orchestration
+- external approval system integrations
diff --git a/src/ModularityKit.Mutator.Governance.csproj b/src/ModularityKit.Mutator.Governance.csproj
index 94484ae..fd73c51 100644
--- a/src/ModularityKit.Mutator.Governance.csproj
+++ b/src/ModularityKit.Mutator.Governance.csproj
@@ -17,6 +17,7 @@
MIT
governance;approvals;requests;workflow;audit
README.md
+ modularitykit-mutator-governance-logo-128.png
true
true
@@ -31,6 +32,11 @@
+
+
+
+
+
diff --git a/src/ModularityKit.Mutator.csproj b/src/ModularityKit.Mutator.csproj
index 2bcef58..1ed350b 100644
--- a/src/ModularityKit.Mutator.csproj
+++ b/src/ModularityKit.Mutator.csproj
@@ -15,6 +15,7 @@
MIT
mutations;policy;audit;history;governance
README.md
+ modularitykit-mutator-logo-128.png
true
true
@@ -28,7 +29,11 @@
-
+
+
+
+
+
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..0ccf497
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,166 @@
+
+
+
+
+## Quick start
+
+```csharp
+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();
+
+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 : IMutation
+{
+ public IncreaseQuotaMutation(string stateId, int amount)
+ {
+ Amount = amount;
+ Intent = new MutationIntent
+ {
+ OperationName = "IncreaseQuota",
+ Category = "Quota",
+ Description = "Increase tenant quota"
+ };
+ Context = MutationContext.System("Initial quota setup") with { StateId = stateId };
+ }
+
+ public int Amount { get; }
+
+ public MutationIntent Intent { get; }
+
+ public MutationContext Context { get; }
+
+ public MutationResult Apply(QuotaState state)
+ => MutationResult.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 MutationResult Simulate(QuotaState state) => Apply(state);
+}
+
+public sealed class PreventNegativeQuotaPolicy : IMutationPolicy
+{
+ public string Name => "PreventNegativeQuota";
+ public int Priority => 100;
+ public string? Description => "Rejects quota changes that would go negative.";
+
+ public PolicyDecision Evaluate(IMutation mutation, QuotaState state)
+ => PolicyDecision.Allow();
+}
+```
+
+## Execution model
+
+`IMutationEngine` runs a consistent pipeline around every mutation:
+
+1. evaluate registered policies
+2. validate the mutation against current state
+3. run interceptors
+4. apply the mutation
+5. record audit/history data
+6. update runtime metrics
+
+Core runtime concurrency is controlled by `MutationEngineOptions.MaxConcurrentMutations`.
+
+- mutations targeting the same `MutationContext.StateId` are serialized
+- batch execution remains ordered and sequential
+- this is separate from request-storage concurrency in `ModularityKit.Mutator.Governance`
+
+## Primary APIs
+
+### Engine
+
+- `IMutation`
+- `IMutationEngine`
+- `IMutationExecutor`
+- `MutationEngineOptions`
+
+### Policies
+
+- `IMutationPolicy`
+- `IPolicyRegistry`
+- `PolicyDecision`
+- `PolicyRequirement`
+
+### Results and changes
+
+- `MutationResult`
+- `BatchMutationResult`
+- `ValidationResult`
+- `ChangeSet`
+- `StateChange`
+
+### Context and intent
+
+- `MutationContext`
+- `MutationIntent`
+- `BlastRadius`
+
+### Runtime observability
+
+- `IMutationAuditor`
+- `IMutationHistoryStore`
+- `MutationHistory`
+- `IMetricsCollector`
+- `MutationStatistics`
+- `IMutationInterceptor`
+
+## Examples
+
+Runnable examples for the core engine live under [`Examples/Core`](../Examples/Core):
+
+- [`BillingQuotas`](../Examples/Core/BillingQuotas/README.md)
+- [`FeatureFlags`](../Examples/Core/FeatureFlags/README.md)
+- [`IamRoles`](../Examples/Core/IamRoles/README.md)
+- [`WorkflowApprovals`](../Examples/Core/WorkflowApprovals/README.md)
+
+## Relationship to governance
+
+`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`](Governance/README.md) on top of the core package.
+
+## Current scope
+
+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
diff --git a/src/Runtime/Internal/MutationExecutionConcurrencyGate.cs b/src/Runtime/Internal/MutationExecutionConcurrencyGate.cs
new file mode 100644
index 0000000..1bfb7fe
--- /dev/null
+++ b/src/Runtime/Internal/MutationExecutionConcurrencyGate.cs
@@ -0,0 +1,57 @@
+using System.Collections.Concurrent;
+
+namespace ModularityKit.Mutator.Runtime.Internal;
+
+///
+/// Coordinates core mutation execution concurrency across the engine.
+///
+internal sealed class MutationExecutionConcurrencyGate(int maxConcurrentMutations)
+{
+ private readonly SemaphoreSlim _globalGate = new(maxConcurrentMutations, maxConcurrentMutations);
+ private readonly ConcurrentDictionary _stateGates = new(StringComparer.Ordinal);
+
+ public async ValueTask EnterAsync(string? stateId, CancellationToken cancellationToken)
+ {
+ await _globalGate.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ var stateGate = default(SemaphoreSlim);
+
+ try
+ {
+ if (!string.IsNullOrWhiteSpace(stateId))
+ {
+ stateGate = _stateGates.GetOrAdd(stateId, static _ => new SemaphoreSlim(1, 1));
+ await stateGate.WaitAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ return new Lease(_globalGate, stateGate);
+ }
+ catch
+ {
+ _globalGate.Release();
+ throw;
+ }
+ }
+
+ ///
+ /// Represents an acquired execution slot.
+ ///
+ internal readonly struct Lease : IAsyncDisposable
+ {
+ private readonly SemaphoreSlim _globalGate;
+ private readonly SemaphoreSlim? _stateGate;
+
+ public Lease(SemaphoreSlim globalGate, SemaphoreSlim? stateGate)
+ {
+ _globalGate = globalGate;
+ _stateGate = stateGate;
+ }
+
+ public ValueTask DisposeAsync()
+ {
+ _stateGate?.Release();
+ _globalGate.Release();
+ return ValueTask.CompletedTask;
+ }
+ }
+}
diff --git a/src/Runtime/MutationEngine.cs b/src/Runtime/MutationEngine.cs
index 83ab80a..42ab9ff 100644
--- a/src/Runtime/MutationEngine.cs
+++ b/src/Runtime/MutationEngine.cs
@@ -32,6 +32,7 @@ internal sealed class MutationEngine(
private readonly IMutationHistoryStore _historyStore = historyStore ?? throw new ArgumentNullException(nameof(historyStore));
private readonly IMetricsCollector _metricsCollector = metricsCollector ?? throw new ArgumentNullException(nameof(metricsCollector));
private readonly MutationEngineOptions _options = options ?? throw new ArgumentNullException(nameof(options));
+ private readonly MutationExecutionConcurrencyGate _concurrencyGate = CreateConcurrencyGate(options);
public async Task> ExecuteAsync(
IMutation mutation,
@@ -42,9 +43,13 @@ public async Task> ExecuteAsync(
var stopwatch = Stopwatch.StartNew();
IMetricsScope? metricsScope = null;
+ await using var executionLease = await _concurrencyGate
+ .EnterAsync(mutation.Context.StateId, cancellationToken)
+ .ConfigureAwait(false);
+
if (_options.EnableDetailedMetrics)
metricsScope = _metricsCollector.BeginScope(executionId);
-
+
try
{
return await ExecutePipelineAsync(
@@ -374,4 +379,17 @@ private async Task StoreInHistoryAsync(
await _historyStore.StoreAsync(entry, cancellationToken);
}
+
+ private static MutationExecutionConcurrencyGate CreateConcurrencyGate(MutationEngineOptions options)
+ {
+ ArgumentNullException.ThrowIfNull(options);
+
+ if (options.MaxConcurrentMutations < 1)
+ throw new ArgumentOutOfRangeException(
+ nameof(options),
+ options.MaxConcurrentMutations,
+ "MaxConcurrentMutations must be greater than zero.");
+
+ return new MutationExecutionConcurrencyGate(options.MaxConcurrentMutations);
+ }
}