diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
new file mode 100644
index 0000000..cad762a
--- /dev/null
+++ b/.github/workflows/documentation.yml
@@ -0,0 +1,73 @@
+name: Documentation
+
+on:
+ push:
+ branches: [ "main" ]
+ paths:
+ - "docs/**"
+ - "README.md"
+ - "TaskFlow/README.md"
+ - "TaskFlow.Extensions.Time/README.md"
+ - "TaskFlow.Extensions.Microsoft.DependencyInjection/README.md"
+ - "TaskFlow.Extensions.Microsoft.Logging/README.md"
+ - ".github/workflows/documentation.yml"
+ pull_request:
+ branches: [ "main" ]
+ paths:
+ - "docs/**"
+ - "README.md"
+ - "TaskFlow/README.md"
+ - "TaskFlow.Extensions.Time/README.md"
+ - "TaskFlow.Extensions.Microsoft.DependencyInjection/README.md"
+ - "TaskFlow.Extensions.Microsoft.Logging/README.md"
+ - ".github/workflows/documentation.yml"
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Configure GitHub Pages
+ if: github.event_name != 'pull_request'
+ uses: actions/configure-pages@v5
+
+ - name: Build the rendered site
+ uses: actions/jekyll-build-pages@v1
+ with:
+ source: ./docs
+ destination: ./_site
+
+ - name: Upload GitHub Pages artifact
+ if: github.event_name != 'pull_request'
+ uses: actions/upload-pages-artifact@v4
+ with:
+ path: ./_site
+
+ deploy:
+ if: github.event_name != 'pull_request'
+ needs: build
+ runs-on: ubuntu-latest
+
+ permissions:
+ pages: write
+ id-token: write
+
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/Directory.Build.props b/Directory.Build.props
index fb8bc8e..0cd321b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -12,7 +12,7 @@
- 1.0.0-rc4
+ 1.0.0-rc5Volodymyr DombrovskyiCopyright (c) 2023 Volodymyr Dombrovskyihttps://github.com/dombrovsky/TaskFlow.git
diff --git a/README.md b/README.md
index dae7e6a..1099c84 100644
--- a/README.md
+++ b/README.md
@@ -1,68 +1,87 @@
-# TaskFlow
+# TaskFlow for .NET
-**TaskFlow** is a robust, high-performance, extensible, and thread-safe library for orchestrating and controlling the execution of asynchronous tasks in .NET. It provides advanced patterns for sequential task execution, resource management, and cancellation, making it ideal for scenarios where you need more than just `SemaphoreSlim` or basic Task chaining.
+TaskFlow turns calls from many places into one owned FIFO lane of work. Every submission gets an awaitable result while the lane serializes execution and provides a clear lifetime boundary.
[](https://www.nuget.org/packages/TaskFlow/)
+[](https://github.com/dombrovsky/TaskFlow/actions/workflows/build.yml)
[](LICENSE)
----
+## Why TaskFlow?
-## Key Features
+Applications often need to accept work asynchronously while ensuring that only one operation touches a resource at a time and that operations retain their original order. Building that around a semaphore or task chain leaves ordering, per-call completion, cancellation, error observation, and shutdown ownership in application code.
-- **Sequential Task Execution:** Guarantee that tasks are executed in the order they are enqueued, with no concurrency unless explicitly configured.
-- **Thread Affinity:** Run tasks on a dedicated thread, the current thread, or the thread pool, with full control over execution context.
-- **Robust Disposal:** Dispose/DisposeAsync will only complete after all enqueued tasks have finished, ensuring clean shutdowns. This makes it ideal for managing fire-and-forget tasks by binding their lifetime to a specific scope.
-- **Cancellation Support:** All enqueued task functions are executed, even if canceled before execution, ensuring predictable execution order.
-- **SynchronizationContext Awareness:** Async/await inside enqueued delegates will execute continuations on the same `TaskFlow` if a `SynchronizationContext` is captured.
-- **Extensibility:** Extend `TaskFlowBase` to create custom task flow implementations or use extension methods and wrappers to enhance functionality, such as throttling, error handling, or scoped cancellation.
-- **Clean Task Pipeline Definition:** Define task pipelines separately from execution logic using extension methods from `System.Threading.Tasks.Flow.Extensions`, enabling better segregation of responsibilities and cleaner code.
-- **Dependency Injection Integration:** Extensions for `Microsoft.Extensions.DependencyInjection` for easy registration and scoping.
+TaskFlow packages those concerns into a reusable execution lane. It is useful when you need to:
----
+- expose an asynchronous API over a synchronous or non-thread-safe resource;
+- preserve event order when synchronous callbacks initiate work;
+- own background work within a component or dependency-injection scope;
+- cancel obsolete operations when a newer request arrives;
+- add timeouts, throttling, logging, annotations, or error observation without changing the work itself; or
+- run ordered work on the thread pool, a dedicated thread, a caller-owned thread, or a custom scheduler.
-## When Should You Use TaskFlow?
+One flow is one sequential lane. Create separate flows for work that should proceed independently.
-TaskFlow is ideal for scenarios where you need:
+## Features
-- **Serialized access to a resource** (e.g., database, file, hardware) from multiple async operations.
-- **Order-preserving task execution** (e.g., message processing, event handling).
-- **Thread affinity** (e.g., UI thread, dedicated worker thread).
-- **Graceful shutdown** with guaranteed completion of all in-flight work.
-- **Advanced error handling and cancellation patterns.**
-- **Fire-and-forget task lifetime management:** Bind fire-and-forget operations to a scope by disposing the `TaskFlow` instance, ensuring proper cleanup and resource management.
-- **Segregation of responsibilities:** Use extension methods to define task pipelines separately from execution logic, improving maintainability and readability.
+| Feature | What it provides |
+|---|---|
+| FIFO execution | Accepted operations start in submission order and do not overlap within one flow. |
+| Per-operation tasks | Every caller can await its own result, exception, or cancellation. |
+| Owned lifetime | A flow gives queued and running work an explicit component-level shutdown boundary. |
+| Composable policies | Add cancellation scopes, latest-request-wins behavior, timeouts, leading-edge throttling, operation names, interception, and error observation. |
+| Execution choices | Use the thread pool, a dedicated thread, the current thread, or another `TaskScheduler`. |
+| Application integration | Register scoped or named flows and emit structured lifecycle logs. |
+| Extensibility | Build scheduler decorators, adapters, interceptors, or custom `TaskFlowBase` implementations. |
----
+See the [extension reference](https://dombrovsky.github.io/TaskFlow/extensions/) and [execution models](https://dombrovsky.github.io/TaskFlow/execution-models/) for the available policies and implementations.
-## Getting Started
+## Serialize synchronous work for asynchronous callers
-### Installation
+```csharp
+using System.Threading.Tasks.Flow;
-Add the core package:
-`dotnet add package TaskFlow`
+public interface IDataStore
+{
+ void Save(Data data);
+}
-For dependency injection support:
-`dotnet add package TaskFlow.Microsoft.Extensions.DependencyInjection`
+public sealed class SerializedStore(IDataStore inner) : IAsyncDisposable
+{
+ private readonly TaskFlow _flow = new();
-For Microsoft.Extensions.Logging integration:
-`dotnet add package TaskFlow.Microsoft.Extensions.Logging`
+ public Task SaveAsync(Data data) =>
+ _flow.Enqueue(() => inner.Save(data));
-### Building from Source
+ public ValueTask DisposeAsync() => _flow.DisposeAsync();
+}
+```
-Use a .NET 10 SDK to restore, build, and test the repository. The test projects execute against both `net8.0` and `net10.0`, so both runtimes must be installed.
+Callers receive a task instead of blocking on `Save`. The wrapped synchronous method runs once at a time and in call order, regardless of how many callers submit work concurrently.
-### Basic Usage
-```csharp
-using var taskFlow = new TaskFlow();
+## Packages
-// Enqueue tasks for sequential execution
-var task1 = taskFlow.Enqueue(() => Console.WriteLine("Task 1"));
-var task2 = taskFlow.Enqueue(async () => await Task.Delay(100));
-```
----
+| Package | Purpose |
+|---|---|
+| [`TaskFlow`](https://www.nuget.org/packages/TaskFlow/) | FIFO execution lanes, built-in execution models, and core scheduler policies. |
+| [`TaskFlow.Extensions.Time`](https://www.nuget.org/packages/TaskFlow.Extensions.Time/) | Compatibility package for the `WithThrottle` time-based policy. |
+| [`TaskFlow.Microsoft.Extensions.DependencyInjection`](https://www.nuget.org/packages/TaskFlow.Microsoft.Extensions.DependencyInjection/) | Scoped, named, and customizable TaskFlow registrations. |
+| [`TaskFlow.Microsoft.Extensions.Logging`](https://www.nuget.org/packages/TaskFlow.Microsoft.Extensions.Logging/) | Structured operation-lifecycle logging through `Microsoft.Extensions.Logging`. |
+
+## Lifecycle essentials
+
+- Await returned tasks when their outcome belongs to the caller; intentionally discarded work remains bounded by the flow and can report failures inside the operation or through a decorator when needed.
+- Prefer `await using` so asynchronous disposal can wait for the lane to finish.
+- Cancellation is cooperative, and synchronous disposal has a timeout.
+- Scheduler decorators do not own the underlying flow; dispose the original `ITaskFlow`.
+
+Read [Concepts and lifecycle](https://dombrovsky.github.io/TaskFlow/concepts-and-lifecycle/) and [Semantics and pitfalls](https://dombrovsky.github.io/TaskFlow/semantics-and-pitfalls/) for the full behavior contract.
-## Extensions
+## Documentation
-## License
+- [Getting started](https://dombrovsky.github.io/TaskFlow/getting-started/)
+- [Recipes](https://dombrovsky.github.io/TaskFlow/recipes/)
+- [Extensions](https://dombrovsky.github.io/TaskFlow/extensions/)
+- [Dependency injection](https://dombrovsky.github.io/TaskFlow/dependency-injection/)
+- [Troubleshooting](https://dombrovsky.github.io/TaskFlow/troubleshooting/)
-This library is licensed under the [MIT License](LICENSE).
+TaskFlow is available under the [MIT License](LICENSE). Contributions and problem reports are welcome through [GitHub issues](https://github.com/dombrovsky/TaskFlow/issues).
diff --git a/TaskFlow.Extensions.Microsoft.DependencyInjection/README.md b/TaskFlow.Extensions.Microsoft.DependencyInjection/README.md
index 487d00b..0c47533 100644
--- a/TaskFlow.Extensions.Microsoft.DependencyInjection/README.md
+++ b/TaskFlow.Extensions.Microsoft.DependencyInjection/README.md
@@ -1,18 +1,14 @@
# TaskFlow.Microsoft.Extensions.DependencyInjection
-`TaskFlow.Microsoft.Extensions.DependencyInjection` integrates TaskFlow with the Microsoft dependency-injection container. It provides scoped schedulers, automatic lifetime management, named configurations, and factories for explicitly owned flows.
+This package integrates TaskFlow with `Microsoft.Extensions.DependencyInjection`, providing scoped FIFO execution lanes, named configurations, decorator chains, and factories for explicitly owned flows.
-## Installation
+## Install
```shell
dotnet add package TaskFlow.Microsoft.Extensions.DependencyInjection
```
-This package references the core `TaskFlow` package.
-
-## Basic usage
-
-Register a scoped TaskFlow execution lane:
+## Scoped flow
```csharp
using Microsoft.Extensions.DependencyInjection;
@@ -21,18 +17,16 @@ using System.Threading.Tasks.Flow;
services.AddTaskFlow();
```
-Inject `ITaskScheduler` into a scoped service and enqueue work through it:
+Inject `ITaskScheduler` into scoped consumers:
```csharp
public sealed class ReportWriter
{
private readonly ITaskScheduler _scheduler;
- private readonly IReportStore _store;
- public ReportWriter(ITaskScheduler scheduler, IReportStore store)
+ public ReportWriter(ITaskScheduler scheduler)
{
_scheduler = scheduler;
- _store = store;
}
public Task SaveAsync(
@@ -40,48 +34,24 @@ public sealed class ReportWriter
CancellationToken cancellationToken = default)
{
return _scheduler.Enqueue(
- token => _store.SaveAsync(report, token),
+ token => PersistAsync(report, token),
cancellationToken);
}
-}
-```
-
-The container creates one TaskFlow for each dependency-injection scope and disposes it when that scope ends.
-
-## Named flows and factories
-
-Register named options when different consumers need different flow configurations:
-
-```csharp
-services.AddTaskFlow();
-services.AddTaskFlow(
- "imports",
- new TaskFlowOptions
- {
- SynchronousDisposeTimeout = TimeSpan.FromSeconds(30)
- });
-```
-Create an explicitly owned named flow through `ITaskFlowFactory`:
-
-```csharp
-await using ITaskFlow importFlow = factory.CreateTaskFlow("imports");
-await importFlow.Enqueue(token => ImportAsync(token));
+ private static Task PersistAsync(
+ Report report,
+ CancellationToken token) => Task.CompletedTask;
+}
```
-The advanced `AddTaskFlow` overload can also provide a custom base-flow factory, dynamically resolved options, and a scheduler-decorator chain.
+The scope owns and disposes its underlying flow. Do not dispose an injected scheduler.
-## Lifetime notes
+Named registrations select independently configured sequential flows. When the caller needs ownership, create an `ITaskFlow` through `ITaskFlowFactory` and dispose it with `await using`.
-- `ITaskScheduler` and `ITaskFlowInfo` are registered as scoped services.
-- `ITaskFlowFactory` and the default factory are registered as singletons.
-- `ITaskFlow` is not registered directly. Use the scoped scheduler for container-owned work or `ITaskFlowFactory` when the caller needs to own and dispose a flow.
-- Do not dispose an injected scoped scheduler; the dependency-injection scope owns its underlying TaskFlow.
+## Documentation
-## Links
+- [Dependency injection guide](https://dombrovsky.github.io/TaskFlow/dependency-injection/)
+- [Concepts and lifecycle](https://dombrovsky.github.io/TaskFlow/concepts-and-lifecycle/)
+- [Extension composition](https://dombrovsky.github.io/TaskFlow/extensions/)
-- [TaskFlow repository](https://github.com/dombrovsky/TaskFlow)
-- [Dependency-injection source](https://github.com/dombrovsky/TaskFlow/tree/main/TaskFlow.Extensions.Microsoft.DependencyInjection)
-- [Core TaskFlow package documentation](https://github.com/dombrovsky/TaskFlow/blob/main/TaskFlow/README.md)
-- [License](https://github.com/dombrovsky/TaskFlow/blob/main/LICENSE)
-- [Issues and feedback](https://github.com/dombrovsky/TaskFlow/issues)
+Source, license, and feedback are available in the [TaskFlow repository](https://github.com/dombrovsky/TaskFlow).
diff --git a/TaskFlow.Extensions.Microsoft.DependencyInjection/TaskFlow.Extensions.Microsoft.DependencyInjection.csproj b/TaskFlow.Extensions.Microsoft.DependencyInjection/TaskFlow.Extensions.Microsoft.DependencyInjection.csproj
index 2354392..ba03501 100644
--- a/TaskFlow.Extensions.Microsoft.DependencyInjection/TaskFlow.Extensions.Microsoft.DependencyInjection.csproj
+++ b/TaskFlow.Extensions.Microsoft.DependencyInjection/TaskFlow.Extensions.Microsoft.DependencyInjection.csproj
@@ -3,6 +3,8 @@
netstandard2.0;net8.0;net10.0System.Threading.Tasks.FlowTaskFlow.Microsoft.Extensions.DependencyInjection
+ Microsoft dependency-injection integration for TaskFlow with scoped and named FIFO execution lanes and caller-owned factories.
+ async;task-scheduler;task-queue;concurrency;sequential;dependency-injection;microsoft-extensions
diff --git a/TaskFlow.Extensions.Microsoft.Logging/README.md b/TaskFlow.Extensions.Microsoft.Logging/README.md
index 6219aab..da26aa4 100644
--- a/TaskFlow.Extensions.Microsoft.Logging/README.md
+++ b/TaskFlow.Extensions.Microsoft.Logging/README.md
@@ -1,16 +1,14 @@
# TaskFlow.Microsoft.Extensions.Logging
-`TaskFlow.Microsoft.Extensions.Logging` adds structured lifecycle logging to any TaskFlow `ITaskScheduler` through `Microsoft.Extensions.Logging`.
+This package adds structured `Microsoft.Extensions.Logging` lifecycle events to any TaskFlow `ITaskScheduler`.
-## Installation
+## Install
```shell
dotnet add package TaskFlow.Microsoft.Extensions.Logging
```
-This package references the core `TaskFlow` package.
-
-## Basic usage
+## Log operation lifecycles
```csharp
using Microsoft.Extensions.Logging;
@@ -21,44 +19,25 @@ await using var flow = new TaskFlow();
ITaskScheduler operations = flow
.WithLogging(logger, options =>
{
- options.EnqueuedLogLevel = LogLevel.Debug;
options.StartedLogLevel = LogLevel.Information;
options.SucceededLogLevel = LogLevel.Information;
options.FailedLogLevel = LogLevel.Error;
- options.FinishedLogLevel = LogLevel.Debug;
})
.WithOperationName("orders.persist");
await operations.Enqueue(token => PersistOrdersAsync(token));
```
-Place `WithOperationName` outside `WithLogging`, as shown above, so the logging decorator can read the operation-name annotation.
-
-## Logged lifecycle
-
-The decorator can emit an event when an operation is:
-
-- enqueued;
-- starting;
-- requested to cancel through its enqueue token;
-- completed successfully;
-- failed or observed cancellation; and
-- finished, regardless of outcome.
+The decorator can emit enqueued, started, cancellation-requested, succeeded or failed, and finished events. Events include structured operation IDs, optional operation names, durations where applicable, and failure exceptions.
-All event levels default to `LogLevel.Trace`. Set any corresponding `TaskFlowLoggingOptions` property to `LogLevel.None` to disable that event.
+Place `WithOperationName` outside `WithLogging`, as shown, so logging can read the annotation. Cancellation logging reports a request rather than the final outcome. Failures remain observable through the task returned by `Enqueue`.
-## Behavior and lifetime
+The logging decorator does not own the underlying flow.
-- The cancellation-request event reports a request, not the final outcome. The operation can still complete successfully if it does not observe cancellation.
-- Failures are logged with their exception and still propagate through the task returned by `Enqueue`.
-- Each logging wrapper assigns increasing operation IDs to the operations it observes.
-- `WithLogging` returns a scheduler decorator and does not own the underlying flow. Dispose the original `ITaskFlow`.
-- Decorator order is observable; add operation names and other annotations outside the logging wrapper when the logger should include them.
+## Documentation
-## Links
+- [Observability extensions](https://dombrovsky.github.io/TaskFlow/extensions/observability/)
+- [Extension composition](https://dombrovsky.github.io/TaskFlow/extensions/)
+- [Semantics and pitfalls](https://dombrovsky.github.io/TaskFlow/semantics-and-pitfalls/)
-- [TaskFlow repository](https://github.com/dombrovsky/TaskFlow)
-- [Logging extension source](https://github.com/dombrovsky/TaskFlow/tree/main/TaskFlow.Extensions.Microsoft.Logging)
-- [Core TaskFlow package documentation](https://github.com/dombrovsky/TaskFlow/blob/main/TaskFlow/README.md)
-- [License](https://github.com/dombrovsky/TaskFlow/blob/main/LICENSE)
-- [Issues and feedback](https://github.com/dombrovsky/TaskFlow/issues)
+Source, license, and feedback are available in the [TaskFlow repository](https://github.com/dombrovsky/TaskFlow).
diff --git a/TaskFlow.Extensions.Microsoft.Logging/TaskFlow.Extensions.Microsoft.Logging.csproj b/TaskFlow.Extensions.Microsoft.Logging/TaskFlow.Extensions.Microsoft.Logging.csproj
index de125da..06ac637 100644
--- a/TaskFlow.Extensions.Microsoft.Logging/TaskFlow.Extensions.Microsoft.Logging.csproj
+++ b/TaskFlow.Extensions.Microsoft.Logging/TaskFlow.Extensions.Microsoft.Logging.csproj
@@ -3,6 +3,8 @@
netstandard2.0;net8.0;net10.0System.Threading.Tasks.FlowTaskFlow.Microsoft.Extensions.Logging
+ Structured Microsoft.Extensions.Logging lifecycle events for TaskFlow operations, including names, timing, cancellation, and failures.
+ async;task-scheduler;task-queue;logging;observability;microsoft-extensions
diff --git a/TaskFlow.Extensions.Time/README.md b/TaskFlow.Extensions.Time/README.md
index dd31012..c2ae5a3 100644
--- a/TaskFlow.Extensions.Time/README.md
+++ b/TaskFlow.Extensions.Time/README.md
@@ -1,18 +1,14 @@
# TaskFlow.Extensions.Time
-`TaskFlow.Extensions.Time` is only needed on older .NET runtimes when you want to use time-based extension methods such as `WithThrottle`; on newer runtimes, these extensions are included directly in the core `TaskFlow` package.
+`TaskFlow.Extensions.Time` supplies `WithThrottle` to consumers using TaskFlow's `netstandard2.0` asset through `Microsoft.Bcl.TimeProvider`. .NET 8 and .NET 10 consumers receive the same API directly from the core `TaskFlow` package.
-`WithThrottle` provides leading-edge throttling: it accepts the first operation and rejects later operations submitted during the configured interval.
-
-## Installation
+## Install
```shell
dotnet add package TaskFlow.Extensions.Time
```
-This package references the core `TaskFlow` package and uses `Microsoft.Bcl.TimeProvider`.
-
-## Basic usage
+## Leading-edge admission throttling
```csharp
using System.Threading.Tasks.Flow;
@@ -20,34 +16,28 @@ using System.Threading.Tasks.Flow;
await using var flow = new TaskFlow();
ITaskScheduler throttled = flow.WithThrottle(TimeSpan.FromSeconds(1));
-await throttled.Enqueue(() => SendUpdateAsync());
+await throttled.Enqueue(token => SendUpdateAsync(token));
try
{
- await throttled.Enqueue(() => SendUpdateAsync());
+ await throttled.Enqueue(token => SendUpdateAsync(token));
}
catch (OperationThrottledException)
{
- // The second operation was submitted inside the one-second interval.
+ // Rejected inside the one-second admission interval.
}
```
-After the interval elapses, the next submitted operation is accepted and starts a new interval.
+The first submission is admitted immediately. Later submissions inside the interval fail with `OperationThrottledException` without reaching the wrapped scheduler. Admission time is recorded before execution, so accepted operations consume the interval even if they later fail or observe cancellation.
+
+This is leading-edge throttling, not trailing-edge debounce: rejected work is not delayed, queued, or replaced. Pass a custom `TimeProvider` for deterministic tests.
-## Behavior
+The returned scheduler is a decorator and does not own the underlying flow.
-- The first operation is accepted immediately.
-- An operation submitted before the interval elapses fails with `OperationThrottledException` and is not forwarded to the wrapped scheduler.
-- The interval is checked when `Enqueue` is called, so time spent waiting in the wrapped scheduler does not delay the start of the interval.
-- The accepted timestamp is recorded before the operation executes. An accepted operation that later fails or observes cancellation still consumes the interval.
-- This is leading-edge throttling, not trailing-edge debouncing: rejected work is not delayed, queued, or replaced with the latest request.
-- Pass a custom `TimeProvider` to `WithThrottle` for deterministic time control in tests.
-- The returned scheduler is a decorator and does not own the underlying flow. Dispose the original `ITaskFlow`.
+## Documentation
-## Links
+- [Reliability extensions](https://dombrovsky.github.io/TaskFlow/extensions/reliability/)
+- [Semantics and pitfalls](https://dombrovsky.github.io/TaskFlow/semantics-and-pitfalls/)
+- [Compatibility](https://dombrovsky.github.io/TaskFlow/compatibility/)
-- [TaskFlow repository](https://github.com/dombrovsky/TaskFlow)
-- [Time extension source](https://github.com/dombrovsky/TaskFlow/tree/main/TaskFlow.Extensions.Time)
-- [Core TaskFlow package documentation](https://github.com/dombrovsky/TaskFlow/blob/main/TaskFlow/README.md)
-- [License](https://github.com/dombrovsky/TaskFlow/blob/main/LICENSE)
-- [Issues and feedback](https://github.com/dombrovsky/TaskFlow/issues)
+Source, license, and feedback are available in the [TaskFlow repository](https://github.com/dombrovsky/TaskFlow).
diff --git a/TaskFlow.Extensions.Time/TaskFlow.Extensions.Time.csproj b/TaskFlow.Extensions.Time/TaskFlow.Extensions.Time.csproj
index f3e8d02..d14de4d 100644
--- a/TaskFlow.Extensions.Time/TaskFlow.Extensions.Time.csproj
+++ b/TaskFlow.Extensions.Time/TaskFlow.Extensions.Time.csproj
@@ -3,6 +3,8 @@
netstandard2.0System.Threading.Tasks.FlowTaskFlow.Extensions.Time
+ Leading-edge admission throttling for TaskFlow on netstandard2.0 with TimeProvider-based deterministic timing.
+ async;task-scheduler;task-queue;concurrency;throttling;time-provider;netstandard
diff --git a/TaskFlow/README.md b/TaskFlow/README.md
index c0a19c7..d6e1a7b 100644
--- a/TaskFlow/README.md
+++ b/TaskFlow/README.md
@@ -1,8 +1,8 @@
-# TaskFlow
+# TaskFlow for .NET
-`TaskFlow` provides an owned FIFO execution lane for asynchronous .NET work. Use it when operations must run one at a time, in submission order, while each caller still receives a task for its own result or failure.
+`TaskFlow` provides owned FIFO execution lanes for asynchronous .NET work. Use it when operations must run one at a time in submission order while each caller retains a task for its own result, failure, or cancellation.
-## Installation
+## Install
```shell
dotnet add package TaskFlow
@@ -15,34 +15,33 @@ using System.Threading.Tasks.Flow;
await using var flow = new TaskFlow();
-Task first = flow.Enqueue(async cancellationToken =>
-{
- await SaveAsync("first", cancellationToken);
-});
-
-Task second = flow.Enqueue(async cancellationToken =>
-{
- await SaveAsync("second", cancellationToken);
-});
+Task first = flow.Enqueue(token => SaveAsync("first", token));
+Task second = flow.Enqueue(token => SaveAsync("second", token));
await Task.WhenAll(first, second);
```
-`second` does not begin until `first` finishes. Calls to `Enqueue` are thread-safe, so multiple callers can share the same flow to serialize access to a resource.
+`second` starts only after `first` finishes. Calls to `Enqueue` are thread-safe, and one failed operation does not stop later queued operations.
+
+## Important behavior
-## Behavior and lifetime
+- Cancellation is cooperative. Built-in flows invoke an accepted queued delegate even if its token was canceled while waiting.
+- `DisposeAsync` requests cancellation and waits for the lane to finish. Synchronous disposal is bounded by `TaskFlowOptions.SynchronousDisposeTimeout`.
+- Observe every returned operation task; disposal does not surface individual operation failures.
+- Timeout includes time spent waiting in the underlying queue.
+- Scheduler decorators do not own the flow they wrap. Dispose the original `ITaskFlow`.
-- Operations execute sequentially in FIFO order.
-- Each returned task reports the result, cancellation, or exception of its own operation. One failed operation does not stop later queued operations from running.
-- Cancellation is cooperative. A queued delegate is still invoked when its token has already been canceled, allowing queue progression while giving the delegate the canceled token.
-- `DisposeAsync` requests cancellation and waits for queued work to finish. Synchronous disposal waits up to `TaskFlowOptions.SynchronousDisposeTimeout`.
-- Scheduler decorators such as timeout, cancellation-scope, error-observation, interception, and cancel-previous wrappers do not own the underlying flow. Dispose the `ITaskFlow` that created the execution lane.
+TaskFlow also provides dedicated-thread and caller-owned-thread flows, cancellation and timeout policies, leading-edge throttling, error observation, annotations, and interception.
-TaskFlow also includes `DedicatedThreadTaskFlow` and `CurrentThreadTaskFlow` for work that requires thread affinity.
+## Documentation
-## Links
+- [Getting started](https://dombrovsky.github.io/TaskFlow/getting-started/)
+- [Concepts and lifecycle](https://dombrovsky.github.io/TaskFlow/concepts-and-lifecycle/)
+- [Semantics and pitfalls](https://dombrovsky.github.io/TaskFlow/semantics-and-pitfalls/)
+- [Execution models](https://dombrovsky.github.io/TaskFlow/execution-models/)
+- [Recipes](https://dombrovsky.github.io/TaskFlow/recipes/)
+- [Extensions](https://dombrovsky.github.io/TaskFlow/extensions/)
+- [Compatibility](https://dombrovsky.github.io/TaskFlow/compatibility/)
+- [Troubleshooting](https://dombrovsky.github.io/TaskFlow/troubleshooting/)
-- [TaskFlow repository](https://github.com/dombrovsky/TaskFlow)
-- [Source code](https://github.com/dombrovsky/TaskFlow/tree/main/TaskFlow)
-- [License](https://github.com/dombrovsky/TaskFlow/blob/main/LICENSE)
-- [Issues and feedback](https://github.com/dombrovsky/TaskFlow/issues)
+Source, license, and feedback are available in the [TaskFlow repository](https://github.com/dombrovsky/TaskFlow).
diff --git a/TaskFlow/TaskFlow.csproj b/TaskFlow/TaskFlow.csproj
index 95bef9e..8900c53 100644
--- a/TaskFlow/TaskFlow.csproj
+++ b/TaskFlow/TaskFlow.csproj
@@ -3,6 +3,8 @@
netstandard2.0;net8.0;net10.0System.Threading.Tasks.FlowTaskFlow
+ A composable .NET async scheduler for FIFO execution, lifetime-bound background work, cancellation, timeouts, logging, and thread affinity.
+ async;task-scheduler;task-queue;concurrency;sequential;serialization;cancellation;thread-affinity;background-tasks
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index 0f8cb67..0000000
--- a/_config.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-theme: minima
-plugins:
- - jekyll-relative-links
-relative_links:
- enabled: true
- collections: true
-include:
- - README.md
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000..ce39af1
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1,17 @@
+title: TaskFlow for .NET
+description: FIFO async execution lanes with owned lifetime, cancellation, diagnostics, and thread affinity.
+url: https://dombrovsky.github.io
+baseurl: /TaskFlow
+repository: dombrovsky/TaskFlow
+theme: minima
+plugins:
+ - jekyll-relative-links
+relative_links:
+ enabled: true
+ collections: true
+header_pages:
+ - getting-started.md
+ - recipes.md
+ - execution-models.md
+ - extensions/index.md
+ - dependency-injection.md
diff --git a/docs/compatibility.md b/docs/compatibility.md
new file mode 100644
index 0000000..e0bd686
--- /dev/null
+++ b/docs/compatibility.md
@@ -0,0 +1,48 @@
+---
+layout: page
+title: Compatibility
+permalink: /compatibility/
+---
+
+# Compatibility
+
+## Package and framework matrix
+
+| Package | Target frameworks | Purpose |
+|---|---|---|
+| `TaskFlow` | `netstandard2.0`, `net8.0`, `net10.0` | Core flows, scheduler contracts, cancellation, timeout, error observation, annotations, and interception |
+| `TaskFlow.Extensions.Time` | `netstandard2.0` | `WithThrottle` for older targets using `Microsoft.Bcl.TimeProvider` |
+| `TaskFlow.Microsoft.Extensions.DependencyInjection` | `netstandard2.0`, `net8.0`, `net10.0` | Scoped and named flow registration and factories |
+| `TaskFlow.Microsoft.Extensions.Logging` | `netstandard2.0`, `net8.0`, `net10.0` | Structured operation lifecycle logging |
+
+The core package includes `WithThrottle` when targeting .NET 8 or .NET 10. Its `netstandard2.0` asset omits that source because `TimeProvider` is not part of the target framework. Install `TaskFlow.Extensions.Time` in a `netstandard2.0` consumer to receive the same public API through `Microsoft.Bcl.TimeProvider`.
+
+## Language and runtime use
+
+The packages can be consumed from compatible target frameworks regardless of the repository's build SDK. Repository builds currently use the .NET 10 SDK, and the test projects execute against .NET 8 and .NET 10.
+
+`IAsyncDisposable` support for the `netstandard2.0` asset is supplied through `Microsoft.Bcl.AsyncInterfaces`. Prefer `await using` where the consuming language and runtime support it.
+
+## Package selection examples
+
+For a .NET 8 or .NET 10 application:
+
+```shell
+dotnet add package TaskFlow
+```
+
+For a library targeting `netstandard2.0` that needs throttling:
+
+```shell
+dotnet add package TaskFlow
+dotnet add package TaskFlow.Extensions.Time
+```
+
+Add integration packages only when needed:
+
+```shell
+dotnet add package TaskFlow.Microsoft.Extensions.DependencyInjection
+dotnet add package TaskFlow.Microsoft.Extensions.Logging
+```
+
+All TaskFlow APIs use the `System.Threading.Tasks.Flow` namespace. Logging configuration also uses `Microsoft.Extensions.Logging`, and registration uses `Microsoft.Extensions.DependencyInjection`.
diff --git a/docs/concepts-and-lifecycle.md b/docs/concepts-and-lifecycle.md
new file mode 100644
index 0000000..05266ab
--- /dev/null
+++ b/docs/concepts-and-lifecycle.md
@@ -0,0 +1,66 @@
+---
+layout: page
+title: Concepts and lifecycle
+permalink: /concepts-and-lifecycle/
+---
+
+# Concepts and lifecycle
+
+## Scheduler and flow
+
+`ITaskScheduler` is the minimal submission contract. It accepts a delegate, optional state, and cancellation token, then returns a task for that operation. A custom implementation may choose any scheduling policy.
+
+`ITaskFlow` adds ownership to that contract through `IAsyncDisposable`, `IDisposable`, `ITaskFlowInfo`, and `Dispose(TimeSpan)`. Built-in flows own their execution lane; scheduler decorators such as `WithTimeout` and `OnError` do not.
+
+Keep both references when composing policies:
+
+```csharp
+await using ITaskFlow flow = new TaskFlow();
+ITaskScheduler operations = flow
+ .WithTimeout(TimeSpan.FromSeconds(10))
+ .OnError(exception => Console.Error.WriteLine(exception));
+
+await operations.Enqueue(token => Task.Delay(25, token));
+```
+
+Dispose `flow`, not `operations`.
+
+## FIFO and operation completion
+
+`TaskFlow`, `DedicatedThreadTaskFlow`, and `CurrentThreadTaskFlow` execute accepted operations one at a time in FIFO order. An operation begins after its predecessor finishes, including failure or cancellation. The task returned by `Enqueue` completes with that operation's own outcome.
+
+FIFO describes invocation order within one flow. It does not impose ordering across multiple flows, and it is not a universal guarantee of every `ITaskScheduler` implementation.
+
+## Cancellation sources
+
+A built-in flow links the caller's token with its disposal token. Decorators can add more sources:
+
+- `CreateCancellationScope` adds a component or request lifetime token.
+- `CreateCancelPrevious` requests cancellation of older unfinished submissions.
+- `WithTimeout` adds a timeout cancellation signal and reports a `TimeoutException` if the timer wins.
+
+Cancellation is a request. Delegates must observe the supplied token. Built-in flows still invoke a queued delegate after cancellation so the lane can advance deterministically.
+
+## Failure continuity
+
+An operation failure is exposed through its returned task. Built-in flows wait for that task, suppress its failure only in the internal predecessor chain, and then invoke the next delegate. `OnError` can observe matching failures, but it rethrows them to the returned operation task.
+
+## Disposal sequence
+
+`DisposeAsync`:
+
+1. prevents new submissions;
+2. finishes pending initialization where applicable;
+3. cancels the flow lifetime token;
+4. waits for queued and active operations to finish; and
+5. releases owned resources.
+
+`Dispose()` performs the same shutdown attempt but waits no longer than `TaskFlowOptions.SynchronousDisposeTimeout`. `Dispose(TimeSpan)` reports whether completion occurred within its explicit timeout.
+
+After disposal begins, new submissions throw `ObjectDisposedException`. Retain operation tasks if callers need their individual shutdown or failure outcomes; disposal intentionally does not surface every operation exception.
+
+## Synchronization context
+
+Thread-backed flows install a synchronization context that marshals captured continuations back to their execution thread. The standard `TaskFlow` schedules each operation through its configured `TaskScheduler`; code inside a delegate follows normal `await` and synchronization-context rules.
+
+See [Execution models](execution-models.md) for the differences among the built-in flows and `TaskFlowSchedulerAdapter`.
diff --git a/docs/customization.md b/docs/customization.md
new file mode 100644
index 0000000..aba22dd
--- /dev/null
+++ b/docs/customization.md
@@ -0,0 +1,73 @@
+---
+layout: page
+title: Customization
+permalink: /customization/
+---
+
+# Customization
+
+Use customization when a built-in FIFO flow, decorator chain, or .NET `TaskScheduler` adapter cannot express the required policy. Custom implementations are responsible for defining their own ordering, cancellation, and ownership guarantees.
+
+## Implement ITaskScheduler
+
+`ITaskScheduler` is the smallest extension point. This example adapts an immediate execution policy:
+
+```csharp
+public sealed class InlineScheduler : ITaskScheduler
+{
+ public async Task Enqueue(
+ Func