From e3532fc2c8b8156644a189b928a9108f8d3af685 Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Tue, 12 May 2026 15:44:54 +0800
Subject: [PATCH] =?UTF-8?q?feat(cqrs-benchmarks):=20=E8=A1=A5=E9=BD=90requ?=
=?UTF-8?q?est=E7=94=9F=E5=91=BD=E5=91=A8=E6=9C=9F=E7=9A=84Mediator?=
=?UTF-8?q?=E5=AF=B9=E7=85=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 RequestLifetimeBenchmarks 的 Mediator lifetime parity,并按编译期常量分支生成 Singleton、Scoped、Transient 宿主
- 更新 benchmark README 的 request lifetime coverage 与当前缺口说明,使其反映最新对照矩阵
- 修复 RequestBenchmarks 与 NotificationBenchmarks 的 XML 文档漂移,并同步 ai-plan 恢复点与验证结论
---
.../Messaging/NotificationBenchmarks.cs | 4 +-
.../Messaging/RequestBenchmarks.cs | 2 +-
.../Messaging/RequestLifetimeBenchmarks.cs | 115 +++++++++++++++++-
GFramework.Cqrs.Benchmarks/README.md | 3 +-
.../todos/cqrs-rewrite-migration-tracking.md | 39 +++---
.../traces/cqrs-rewrite-migration-trace.md | 37 ++++++
6 files changed, 174 insertions(+), 26 deletions(-)
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
index 80ce9b88..d7bbdfc5 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs
@@ -21,7 +21,7 @@ using GeneratedMediator = Mediator.Mediator;
namespace GFramework.Cqrs.Benchmarks.Messaging;
///
-/// 对比单处理器 notification 在 GFramework.CQRS 与 MediatR 之间的 publish 开销。
+/// 对比单处理器 notification 在 GFramework.CQRS、NuGet `Mediator` 与 MediatR 之间的 publish 开销。
///
[Config(typeof(Config))]
public class NotificationBenchmarks
@@ -132,7 +132,7 @@ public class NotificationBenchmarks
MediatR.INotification;
///
- /// 同时实现 GFramework.CQRS 与 MediatR 契约的最小 notification handler。
+ /// 同时实现 GFramework.CQRS、NuGet `Mediator` 与 MediatR 契约的最小 notification handler。
///
public sealed class BenchmarkNotificationHandler :
GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler,
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs
index 6e23cfe8..ff5ca2c4 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs
@@ -157,7 +157,7 @@ public class RequestBenchmarks
public sealed record BenchmarkResponse(Guid Id);
///
- /// 同时实现 GFramework.CQRS 与 MediatR 契约的最小 request handler。
+ /// 同时实现 GFramework.CQRS、NuGet `Mediator` 与 MediatR 契约的最小 request handler。
///
public sealed class BenchmarkRequestHandler :
GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler,
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs
index f96deeef..0e4affc2 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs
@@ -16,6 +16,7 @@ using GFramework.Core.Logging;
using GFramework.Cqrs.Abstractions.Cqrs;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
+using GeneratedMediator = Mediator.Mediator;
namespace GFramework.Cqrs.Benchmarks.Messaging;
@@ -35,7 +36,9 @@ public class RequestLifetimeBenchmarks
private ScopedBenchmarkContainer? _scopedContainer;
private ICqrsRuntime? _scopedRuntime;
private ServiceProvider _serviceProvider = null!;
+ private ServiceProvider _mediatorServiceProvider = null!;
private IMediator? _mediatr;
+ private GeneratedMediator? _mediator;
private BenchmarkRequestHandler _baselineHandler = null!;
private BenchmarkRequest _request = null!;
private ILogger _runtimeLogger = null!;
@@ -83,7 +86,7 @@ public class RequestLifetimeBenchmarks
}
///
- /// 构建当前生命周期下的 GFramework 与 MediatR request 对照宿主。
+ /// 构建当前生命周期下的 GFramework、NuGet `Mediator` 与 MediatR request 对照宿主。
///
[GlobalSetup]
public void Setup()
@@ -130,6 +133,12 @@ public class RequestLifetimeBenchmarks
{
_mediatr = _serviceProvider.GetRequiredService();
}
+
+ _mediatorServiceProvider = CreateMediatorServiceProvider(Lifetime);
+ if (Lifetime != HandlerLifetime.Scoped)
+ {
+ _mediator = _mediatorServiceProvider.GetRequiredService();
+ }
}
///
@@ -140,7 +149,7 @@ public class RequestLifetimeBenchmarks
{
try
{
- BenchmarkCleanupHelper.DisposeAll(_container, _serviceProvider);
+ BenchmarkCleanupHelper.DisposeAll(_container, _serviceProvider, _mediatorServiceProvider);
}
finally
{
@@ -196,6 +205,24 @@ public class RequestLifetimeBenchmarks
return _mediatr!.Send(_request, CancellationToken.None);
}
+ ///
+ /// 通过 `Mediator` source-generated concrete mediator 发送 request,作为 compile-time 对照。
+ ///
+ /// 代表当前 `Mediator` request dispatch 完成的值任务。
+ [Benchmark]
+ public ValueTask SendRequest_Mediator()
+ {
+ if (Lifetime == HandlerLifetime.Scoped)
+ {
+ return SendScopedMediatorRequestAsync(
+ _mediatorServiceProvider,
+ _request,
+ CancellationToken.None);
+ }
+
+ return _mediator!.Send(_request, CancellationToken.None);
+ }
+
///
/// 按生命周期把 benchmark request handler 注册到 GFramework 容器。
///
@@ -243,12 +270,83 @@ public class RequestLifetimeBenchmarks
};
}
+ ///
+ /// 构建只承载当前 benchmark request handler 的最小 `Mediator` 对照宿主,并按生命周期切换生成器注册形状。
+ ///
+ /// 待比较的 handler 生命周期。
+ /// 可直接解析 generated `Mediator.Mediator` 的 DI 宿主。
+ private static ServiceProvider CreateMediatorServiceProvider(HandlerLifetime lifetime)
+ {
+ return lifetime switch
+ {
+ HandlerLifetime.Singleton => CreateSingletonMediatorServiceProvider(),
+ HandlerLifetime.Scoped => CreateScopedMediatorServiceProvider(),
+ HandlerLifetime.Transient => CreateTransientMediatorServiceProvider(),
+ _ => throw new ArgumentOutOfRangeException(nameof(lifetime), lifetime, "Unsupported benchmark handler lifetime.")
+ };
+ }
+
+ ///
+ /// 在真实的 request 级作用域内执行一次 `Mediator` request 分发。
+ ///
+ /// 请求响应类型。
+ /// 当前 benchmark 的根 。
+ /// 要发送的 request。
+ /// 取消令牌。
+ /// 当前 request 的响应结果。
+ private static async ValueTask SendScopedMediatorRequestAsync(
+ ServiceProvider rootServiceProvider,
+ Mediator.IRequest request,
+ CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(rootServiceProvider);
+ ArgumentNullException.ThrowIfNull(request);
+
+ using var scope = rootServiceProvider.CreateScope();
+ var mediator = scope.ServiceProvider.GetRequiredService();
+ return await mediator.Send(request, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// 构建 singleton 生命周期的 `Mediator` 对照宿主。
+ ///
+ /// 按 singleton 形状生成 DI 注册的 `Mediator` 宿主。
+ private static ServiceProvider CreateSingletonMediatorServiceProvider()
+ {
+ var services = new ServiceCollection();
+ services.AddMediator(static options => options.ServiceLifetime = ServiceLifetime.Singleton);
+ return services.BuildServiceProvider();
+ }
+
+ ///
+ /// 构建 scoped 生命周期的 `Mediator` 对照宿主。
+ ///
+ /// 按 scoped 形状生成 DI 注册的 `Mediator` 宿主。
+ private static ServiceProvider CreateScopedMediatorServiceProvider()
+ {
+ var services = new ServiceCollection();
+ services.AddMediator(static options => options.ServiceLifetime = ServiceLifetime.Scoped);
+ return services.BuildServiceProvider();
+ }
+
+ ///
+ /// 构建 transient 生命周期的 `Mediator` 对照宿主。
+ ///
+ /// 按 transient 形状生成 DI 注册的 `Mediator` 宿主。
+ private static ServiceProvider CreateTransientMediatorServiceProvider()
+ {
+ var services = new ServiceCollection();
+ services.AddMediator(static options => options.ServiceLifetime = ServiceLifetime.Transient);
+ return services.BuildServiceProvider();
+ }
+
///
/// Benchmark request。
///
/// 请求标识。
public sealed record BenchmarkRequest(Guid Id) :
GFramework.Cqrs.Abstractions.Cqrs.IRequest,
+ Mediator.IRequest,
MediatR.IRequest;
///
@@ -258,10 +356,11 @@ public class RequestLifetimeBenchmarks
public sealed record BenchmarkResponse(Guid Id);
///
- /// 同时实现 GFramework.CQRS 与 MediatR 契约的最小 request handler。
+ /// 同时实现 GFramework.CQRS、NuGet `Mediator` 与 MediatR 契约的最小 request handler。
///
public sealed class BenchmarkRequestHandler :
GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler,
+ Mediator.IRequestHandler,
MediatR.IRequestHandler
{
///
@@ -272,6 +371,16 @@ public class RequestLifetimeBenchmarks
return ValueTask.FromResult(new BenchmarkResponse(request.Id));
}
+ ///
+ /// 处理 NuGet `Mediator` request。
+ ///
+ ValueTask Mediator.IRequestHandler.Handle(
+ BenchmarkRequest request,
+ CancellationToken cancellationToken)
+ {
+ return Handle(request, cancellationToken);
+ }
+
///
/// 处理 MediatR request。
///
diff --git a/GFramework.Cqrs.Benchmarks/README.md b/GFramework.Cqrs.Benchmarks/README.md
index dd943daa..c50f93b2 100644
--- a/GFramework.Cqrs.Benchmarks/README.md
+++ b/GFramework.Cqrs.Benchmarks/README.md
@@ -16,7 +16,7 @@
- `Messaging/RequestBenchmarks.cs`
- direct handler、默认 `GFramework.Cqrs` runtime、NuGet `Mediator` source-generated concrete path、`MediatR`
- `Messaging/RequestLifetimeBenchmarks.cs`
- - `Singleton / Scoped / Transient` 三类 handler 生命周期下,baseline、默认 generated-provider 宿主接线的 `GFramework.Cqrs` runtime 与 `MediatR`
+ - `Singleton / Scoped / Transient` 三类 handler 生命周期下,baseline、默认 generated-provider 宿主接线的 `GFramework.Cqrs` runtime、NuGet `Mediator` source-generated concrete path 与 `MediatR`
- `Messaging/RequestPipelineBenchmarks.cs`
- `0 / 1 / 4` 个 pipeline 行为下,baseline、默认 generated-provider 宿主接线的 `GFramework.Cqrs` runtime 与 `MediatR`
- `Messaging/RequestInvokerBenchmarks.cs`
@@ -104,5 +104,4 @@ dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.cspro
## 当前缺口
- 当前没有 stream 生命周期版的 NuGet `Mediator` source-generated concrete path 对照;`StreamLifetimeBenchmarks` 现在只覆盖 `GFramework.Cqrs` 与 `MediatR`
-- 当前没有 request 生命周期下的 NuGet `Mediator` compile-time lifetime 矩阵;`RequestLifetimeBenchmarks` 只覆盖 `GFramework.Cqrs` 与 `MediatR`
- 当前没有 notification fan-out 的生命周期矩阵;`NotificationFanOutBenchmarks` 只覆盖固定 `4 handler` 的已装配宿主
diff --git a/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md b/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
index 10260065..9ded9794 100644
--- a/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
+++ b/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
@@ -12,7 +12,7 @@ CQRS 迁移与收敛。
## 当前恢复点
-- 恢复点编号:`CQRS-REWRITE-RP-139`
+- 恢复点编号:`CQRS-REWRITE-RP-140`
- 当前阶段:`Phase 8`
- 当前 PR 锚点:`PR #349(已于 2026-05-12 合并到 origin/main)`
- 当前结论:
@@ -23,14 +23,14 @@ CQRS 迁移与收敛。
- README 一致性盘点成立,`GFramework.Cqrs.Benchmarks/README.md` 的 startup coverage / 解释边界仍可收紧
- benchmark XML 缺口盘点存在明显误报;代表文件中的 class / benchmark 方法 `` 与 `` 已实际存在
- 因此本轮不接受新的大范围 XML 收口波次,避免把上下文预算消耗在错误候选上
- - 本轮 accepted delegated scope 收敛为单文件 docs-only worker:
- - `GFramework.Cqrs.Benchmarks/README.md`
- - 明确 `StreamStartupBenchmarks` 现已覆盖 `MediatR`、`GFramework.Cqrs` reflection、
- `GFramework.Cqrs` generated、NuGet `Mediator` 四组 initialization / cold-start 对照
- - 补充 `RequestStartupBenchmarks` 与 `NotificationStartupBenchmarks` 的
- `GFramework.Cqrs` 路径是“单 handler 最小宿主 + 手工注册”的 startup / cold-start 模型,不外推到程序集扫描、
- 完整注册协调器、fan-out 或发布策略变体
- - 当前决定在该 docs-only 收口后停在自然边界:
+ - 本轮主线程将 `RequestLifetimeBenchmarks` 的 request lifetime 矩阵扩展为 baseline、`GFramework.Cqrs`、NuGet `Mediator` 与 `MediatR` 四组对照:
+ - `BenchmarkRequest` 与 `BenchmarkRequestHandler` 增补 `Mediator` 契约实现
+ - `Mediator` 宿主按 `Singleton / Scoped / Transient` 三种编译期常量分支生成,满足 source generator 的配置约束
+ - `Scoped` 路径通过显式 `CreateScope()` 解析 generated `Mediator.Mediator`,保持与现有 request scoped 对照相同的作用域边界
+ - 本轮同时收口两处 benchmark XML 文档漂移:
+ - `RequestBenchmarks` 的 handler 说明补齐 NuGet `Mediator` 契约事实
+ - `NotificationBenchmarks` 的类说明与 handler 说明补齐 NuGet `Mediator` 对照事实
+ - 当前决定在该 parity + docs 收口后停在自然边界:
- branch-size 仍低于 `50 files`
- 但下一批低风险候选已不再清晰;继续开波次的收益低于评审与上下文成本
- tests 侧此前已补齐并提交:
@@ -50,6 +50,9 @@ CQRS 迁移与收敛。
- 当前分支:`feat/cqrs-optimization`
- 当前 PR:`PR #349(已合并;当前分支暂无新的公开 PR)`
- 当前写面:
+ - `GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs`
+ - `GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs`
+ - `GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs`
- `GFramework.Cqrs.Benchmarks/README.md`
- `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md`
- `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
@@ -57,7 +60,7 @@ CQRS 迁移与收敛。
- `origin/main @ 2b2bec65 (2026-05-12 11:49:39 +0800)`
- 当前已提交 branch diff:`14 files`
- 当前分支比 `origin/main` 多 `5` 个提交:`f346110a`、`a016e3d4`、`ab422b05`、`555c7c07`、`c32a1ec4`
- - 当前未提交面由 benchmark README 的 startup 边界同步与 `ai-plan` 恢复点更新构成
+ - 当前工作面已收口为 request lifetime parity、两处 benchmark XML 文档修正与对应 `README` / `ai-plan` 同步
- 本轮提交:
- `f346110a` `feat(cqrs-benchmarks): 补齐 stream startup 的 Mediator 对照路径`
- `ab422b05` `docs(cqrs-benchmarks): 补齐 request benchmark 返回值注释`
@@ -67,28 +70,28 @@ CQRS 迁移与收敛。
## 当前风险
- `StreamStartupBenchmarks` 的 `Mediator` parity 目前只做了编译验证,尚未单独执行 benchmark 作业确认 startup 矩阵运行结果。
-- `StreamLifetimeBenchmarks` 仍缺 `Mediator` parity;该项涉及 `BenchmarkHostFactory` 与 compile-time lifetime 形状,不再是本轮低风险切片。
+- `StreamLifetimeBenchmarks` 仍缺 `Mediator` parity;虽然 `RequestLifetimeBenchmarks` 已证明 `Mediator` 的 compile-time lifetime 可通过常量分支接入,但 stream 侧仍需要额外处理 `IAsyncEnumerable` 与作用域覆盖整个枚举周期的组合边界。
- benchmark XML 盘点若再次依赖粗糙脚本或只读 inventory,仍有把已存在文档误记为缺口的风险;后续若再开 XML 波次,必须先用主线程抽样核对代表文件。
-- 本轮已在 README 精度同步后主动停批次;若后续恢复,优先先做 `StreamStartupBenchmarks` smoke run 或更明确的 parity / docs 候选,而不是继续机械扩张 XML 批次。
+- 本轮已在 request lifetime parity 与文档漂移收口后主动停批次;若后续恢复,优先先做 `StreamStartupBenchmarks` smoke run 或更明确的 parity / docs 候选,而不是继续机械扩张 XML 批次。
## 最近权威验证
- `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
+ - 备注:确认 `RequestLifetimeBenchmarks` 的 NuGet `Mediator` lifetime parity 可在当前生成器约束下编译通过
- `python3 scripts/license-header.py --check`
- 结果:通过
- `$gframework-pr-review`
- 结果:`PR #349` 已关闭;latest-head review open thread 经本地核对仅剩 `StreamingBenchmarks.Stream_MediatR()` 的 XML 文档缺口仍成立
-- `git --git-dir=/mnt/f/gewuyou/System/Documents/WorkSpace/GameDev/GFramework/.git/worktrees/GFramework-cqrs --work-tree=/mnt/f/gewuyou/System/Documents/WorkSpace/GameDev/GFramework-WorkTree/GFramework-cqrs diff -- GFramework.Cqrs.Benchmarks/README.md`
+- `git --git-dir=/mnt/f/gewuyou/System/Documents/WorkSpace/GameDev/GFramework/.git/worktrees/GFramework-cqrs --work-tree=/mnt/f/gewuyou/System/Documents/WorkSpace/GameDev/GFramework-WorkTree/GFramework-cqrs diff -- GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs`
- 结果:通过
- - 备注:确认本轮 worker 仅修改 README 的 startup coverage / 边界文案
+ - 备注:确认本轮代码面收敛在 request lifetime parity 与两处 XML 文档漂移修正
## 下一推荐步骤
-1. 串行运行 `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`、`python3 scripts/license-header.py --check --paths ...` 与 `git diff --check`,作为本轮 docs-only 收尾的权威验证。
-2. 提交当前 README 与 `ai-plan` 更新,回到干净工作树。
-3. 若后续继续 benchmark 波次,优先单独执行 `StreamStartupBenchmarks` 的最小 smoke run,验证新加 `Mediator` startup 路径可运行。
-4. 若后续再开文档批次,先用主线程核对代表文件,再决定是否存在真实 XML 缺口;不要直接沿用误报 inventory 扩批。
+1. 若后续继续 benchmark 波次,优先单独执行 `StreamStartupBenchmarks` 的最小 smoke run,验证新加 `Mediator` startup 路径可运行。
+2. 若后续评估 `StreamLifetimeBenchmarks` 的 `Mediator` parity,先复用本轮 request lifetime 的“编译期常量 lifetime 分支”经验,再判断是否值得引入新的 scoped stream helper。
+3. 若后续再开 XML / docs 批次,先由主线程逐文件核对代表样本,不要直接沿用误报 inventory 扩批。
## 活跃文档
diff --git a/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md b/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
index 30c89650..9848ad42 100644
--- a/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
+++ b/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
@@ -7,6 +7,43 @@ SPDX-License-Identifier: Apache-2.0
## 2026-05-12
+### 阶段:request lifetime 的 Mediator parity 与文档漂移收口(CQRS-REWRITE-RP-140)
+
+- 继续按 `$gframework-batch-boot 50` 推进,基线保持为 `origin/main @ 2b2bec65 (2026-05-12 11:49:39 +0800)`。
+- 本轮启动时重新测得当前已提交 branch diff 仍为 `14 files / 324 lines`,远低于 `50 files` 阈值;停止与否继续由 context-budget / reviewability 主导。
+- 主线程结合两个 explorer 子代理的只读盘点后,接受以下结论:
+ - 不再继续按 benchmark XML `` inventory 机械扩批;粗糙脚本会把“注释位于 `[Benchmark]` 之前”的现有文档误判为缺口
+ - `RequestLifetimeBenchmarks` 的 NuGet `Mediator` lifetime parity 是当前仍然真实、且能保持 reviewable 的实现候选
+ - `NotificationBenchmarks.cs` 与 `RequestBenchmarks.cs` 仍有两处低风险 XML 文档漂移,均只涉及 NuGet `Mediator` 事实同步
+- 本轮主线程实施:
+ - `RequestLifetimeBenchmarks.cs`
+ - 新增 `GeneratedMediator` 宿主字段、`SendRequest_Mediator()` benchmark 方法与 scoped `Mediator` request helper
+ - 将 `BenchmarkRequest` / `BenchmarkRequestHandler` 扩为同时实现 `Mediator` 契约
+ - 为 `Mediator` 宿主改用 `Singleton / Scoped / Transient` 三个编译期常量分支,规避 `MSG0007` 对运行时 lifetime 赋值的生成器限制
+ - `RequestBenchmarks.cs`
+ - 将 handler XML 文档说明补齐为同时实现 `GFramework.CQRS`、NuGet `Mediator` 与 `MediatR`
+ - `NotificationBenchmarks.cs`
+ - 将类说明与 handler XML 文档说明补齐为同时覆盖 `GFramework.CQRS`、NuGet `Mediator` 与 `MediatR`
+ - `GFramework.Cqrs.Benchmarks/README.md`
+ - 将 `RequestLifetimeBenchmarks` 的 coverage 更新为包含 NuGet `Mediator` source-generated concrete path
+ - 删除“当前没有 request 生命周期下的 NuGet `Mediator` compile-time lifetime 矩阵”这一已过时缺口
+- 验证里程碑:
+ - 第一次 `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
+ - 结果:失败
+ - 原因:`RequestLifetimeBenchmarks.cs` 中基于运行时变量写入 `MediatorOptions.ServiceLifetime`,触发 `MSG0007`
+ - 主线程修正:
+ - 将 `CreateMediatorServiceProvider(HandlerLifetime lifetime)` 收口为 3 个常量分支工厂:
+ `CreateSingletonMediatorServiceProvider()`、`CreateScopedMediatorServiceProvider()`、`CreateTransientMediatorServiceProvider()`
+ - 第二次 `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+- 当前 stop decision:
+ - 不继续开启新的实现波次
+ - 原因不是 branch-size 阈值耗尽;当前分支仍只有 `14 files`
+ - 停止原因是本轮已经完成一条真实 parity 收口和两处文档漂移修正,继续扩到 `StreamLifetimeBenchmarks` 会显著提高作用域与 review 成本
+- 当前下一步:
+ - 主线程补跑 `python3 scripts/license-header.py --check --paths ...` 与 `git diff --check`
+ - 更新 active tracking / trace 后提交当前 benchmark 代码、README 与 `ai-plan`
+
### 阶段:README startup coverage 精度同步并停在自然边界(CQRS-REWRITE-RP-139)
- 继续按 `$gframework-batch-boot 50` 推进,基线保持为 `origin/main @ 2b2bec65 (2026-05-12 11:49:39 +0800)`。