diff --git a/GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs b/GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs
index 34397dc1..1753f9aa 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs
@@ -275,15 +275,16 @@ internal static class BenchmarkHostFactory
/// 补充当前场景的显式服务注册。
/// 可直接解析 generated `Mediator.Mediator` 的 DI 宿主。
///
- /// 当前 benchmark 只把 `Mediator` 作为单例 steady-state 对照组接入,
- /// 因为它的 lifetime 由 source generator 在编译期塑形;若后续需要 `Transient` / `Scoped` 矩阵,
- /// 应按 `Mediator` 官方 benchmark 的做法拆成独立 build config,而不是在同一编译产物里混用多个 lifetime。
+ /// `Mediator` 的 DI lifetime 由 source generator 在编译期固定到整个位于当前项目中的生成产物上。
+ /// 因此 benchmark 工程必须统一使用一套 compile-time 常量配置;这里显式收敛为 `Singleton`,
+ /// 避免同一编译产物里混入多个 `AddMediator` lifetime 形状后,在 BenchmarkDotNet 自动生成宿主中触发
+ /// “generated code lifetime 与 runtime options 不一致”的启动失败。
///
internal static ServiceProvider CreateMediatorServiceProvider(Action? configure)
{
var services = new ServiceCollection();
configure?.Invoke(services);
- services.AddMediator();
+ services.AddMediator(static options => options.ServiceLifetime = ServiceLifetime.Singleton);
return services.BuildServiceProvider();
}
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs
index 0e4affc2..f12f83e6 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs
@@ -16,7 +16,6 @@ using GFramework.Core.Logging;
using GFramework.Cqrs.Abstractions.Cqrs;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
-using GeneratedMediator = Mediator.Mediator;
namespace GFramework.Cqrs.Benchmarks.Messaging;
@@ -27,6 +26,10 @@ namespace GFramework.Cqrs.Benchmarks.Messaging;
/// 当前矩阵覆盖 `Singleton`、`Scoped` 与 `Transient`。
/// 其中 `Scoped` 会在每次 request 分发时显式创建并释放真实的 DI 作用域,
/// 避免把 scoped handler 错误地压到根容器解析而扭曲生命周期对照。
+/// NuGet `Mediator` 的 DI lifetime 由 source generator 在编译期固定到整个 benchmark 项目,
+/// 不能在同一份生成产物里同时切换 `Singleton`、`Scoped` 与 `Transient`。
+/// 因此该矩阵当前只比较 `GFramework.Cqrs` 与 `MediatR` 的生命周期开销;`Mediator` 仍保留在其他
+/// steady-state / startup benchmark 中作为单一 compile-time 形状对照。
///
[Config(typeof(Config))]
public class RequestLifetimeBenchmarks
@@ -36,9 +39,7 @@ 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!;
@@ -86,7 +87,7 @@ public class RequestLifetimeBenchmarks
}
///
- /// 构建当前生命周期下的 GFramework、NuGet `Mediator` 与 MediatR request 对照宿主。
+ /// 构建当前生命周期下的 GFramework 与 MediatR request 对照宿主。
///
[GlobalSetup]
public void Setup()
@@ -133,12 +134,6 @@ public class RequestLifetimeBenchmarks
{
_mediatr = _serviceProvider.GetRequiredService();
}
-
- _mediatorServiceProvider = CreateMediatorServiceProvider(Lifetime);
- if (Lifetime != HandlerLifetime.Scoped)
- {
- _mediator = _mediatorServiceProvider.GetRequiredService();
- }
}
///
@@ -149,7 +144,7 @@ public class RequestLifetimeBenchmarks
{
try
{
- BenchmarkCleanupHelper.DisposeAll(_container, _serviceProvider, _mediatorServiceProvider);
+ BenchmarkCleanupHelper.DisposeAll(_container, _serviceProvider);
}
finally
{
@@ -206,25 +201,7 @@ public class RequestLifetimeBenchmarks
}
///
- /// 通过 `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 容器。
+ /// 按生命周期把 benchmark request handler 注册到 GFramework 容器。
///
/// 当前 benchmark 拥有并负责释放的容器。
/// 待比较的 handler 生命周期。
@@ -271,82 +248,11 @@ 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。
+ /// Benchmark request。
///
/// 请求标识。
public sealed record BenchmarkRequest(Guid Id) :
GFramework.Cqrs.Abstractions.Cqrs.IRequest,
- Mediator.IRequest,
MediatR.IRequest;
///
@@ -356,11 +262,10 @@ public class RequestLifetimeBenchmarks
public sealed record BenchmarkResponse(Guid Id);
///
- /// 同时实现 GFramework.CQRS、NuGet `Mediator` 与 MediatR 契约的最小 request handler。
+ /// 同时实现 GFramework.CQRS 与 MediatR 契约的最小 request handler。
///
public sealed class BenchmarkRequestHandler :
GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler,
- Mediator.IRequestHandler,
MediatR.IRequestHandler
{
///
@@ -371,16 +276,6 @@ 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 c50f93b2..c4ac3315 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、NuGet `Mediator` source-generated concrete path 与 `MediatR`
+ - `Singleton / Scoped / Transient` 三类 handler 生命周期下,baseline、默认 generated-provider 宿主接线的 `GFramework.Cqrs` runtime 与 `MediatR`
- `Messaging/RequestPipelineBenchmarks.cs`
- `0 / 1 / 4` 个 pipeline 行为下,baseline、默认 generated-provider 宿主接线的 `GFramework.Cqrs` runtime 与 `MediatR`
- `Messaging/RequestInvokerBenchmarks.cs`
@@ -104,4 +104,5 @@ dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.cspro
## 当前缺口
- 当前没有 stream 生命周期版的 NuGet `Mediator` source-generated concrete path 对照;`StreamLifetimeBenchmarks` 现在只覆盖 `GFramework.Cqrs` 与 `MediatR`
+- 当前没有 request 生命周期版的 NuGet `Mediator` source-generated concrete path 对照;`Mediator` 的 DI lifetime 由 source generator 在 benchmark 项目编译期固定,若要比较 `Singleton / Scoped / Transient`,需要拆成独立 build config 或独立 benchmark 工程,而不是在同一份生成产物里切换
- 当前没有 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 9ded9794..add78968 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
@@ -14,45 +14,33 @@ CQRS 迁移与收敛。
- 恢复点编号:`CQRS-REWRITE-RP-140`
- 当前阶段:`Phase 8`
-- 当前 PR 锚点:`PR #349(已于 2026-05-12 合并到 origin/main)`
+- 当前 PR 锚点:`PR #350(OPEN,2026-05-12)`
- 当前结论:
- - 本轮按 `$gframework-batch-boot 50` 恢复后,先重新确认基线仍为
- `origin/main @ 2b2bec65 (2026-05-12 11:49:39 +0800)`,当前已提交 branch diff 为 `14 files`,仍远低于 `50 files`
- 阈值;是否继续的主停止信号仍是 context-budget / reviewability,而不是 branch-size 预算。
- - 主线程结合本地抽样核对与两个 explorer 子代理的只读盘点后,确认当前不应再继续按“benchmark XML `` 批量缺口”扩批:
- - README 一致性盘点成立,`GFramework.Cqrs.Benchmarks/README.md` 的 startup coverage / 解释边界仍可收紧
- - benchmark XML 缺口盘点存在明显误报;代表文件中的 class / benchmark 方法 `` 与 `` 已实际存在
- - 因此本轮不接受新的大范围 XML 收口波次,避免把上下文预算消耗在错误候选上
- - 本轮主线程将 `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 侧此前已补齐并提交:
- - `CqrsRegistrationServiceTests`:补空输入、空项过滤、稳定键排序与跨调用跳过边界
- - `CqrsHandlerRegistrarTests` 与 `CqrsHandlerRegistrarFallbackFailureTests`:
- 补 abstract registry 与缺少无参构造器 registry 的回退 / 抛错覆盖
- - `CqrsNotificationPublisherTests`:补“零 publisher 回退到默认顺序发布器并缓存”回归
- - benchmark 侧此前已补齐并提交:
- - `StreamPipelineBenchmarks`
- - `StreamingBenchmarks` 的 steady-state `Mediator` 对照
- - `GFramework.Cqrs.Benchmarks/README.md` 的 stream coverage / gap 同步
- - `StreamStartupBenchmarks` 的 `Mediator` initialization / cold-start 对照
- - 本轮未修改 `GFramework.Cqrs` 运行时代码;notification fallback 与 generated registry 激活守卫均由新回归证明现有实现已满足预期。
+ - 本轮按 `$gframework-pr-review` 重新抓取 GitHub 真值后,确认当前公开 PR 不是已合并的 `PR #349`,而是仍处于 `OPEN` 状态的 `PR #350`。
+ - 最新 AI review 只有 1 条 Greptile open thread,关注点是:
+ - `StreamStartupBenchmarks.ColdStart_Mediator()` 与 `RequestLifetimeBenchmarks.SendRequest_Mediator()` 先前只做了编译验证,未实际 smoke-run
+ - 主线程按 review 提示执行最小 benchmark smoke run 后,确认 Greptile 线程不是误报,而是命中了真实运行时缺陷:
+ - `StreamStartupBenchmarks.ColdStart_Mediator()` 在 BenchmarkDotNet 自动生成宿主里抛出
+ `Invalid configuration detected for Mediator. Generated code for 'Transient' lifetime, but got 'Singleton' lifetime from options.`
+ - `RequestLifetimeBenchmarks.SendRequest_Mediator()` 的 `Singleton / Scoped` 也抛出同类异常;只有 `Transient` 变体能跑通
+ - 根因确认:
+ - NuGet `Mediator` 的 DI lifetime 由 source generator 在 benchmark 项目编译期固定
+ - 当前工程同时存在默认 `AddMediator()` 与 request lifetime 场景下的 `AddMediator(options => options.ServiceLifetime = ...)`
+ - 这会让同一份生成产物在 BenchmarkDotNet 自动生成宿主中出现 compile-time 形状与 runtime options 不一致
+ - 本轮收口策略:
+ - `BenchmarkHostFactory.CreateMediatorServiceProvider()` 统一显式固定为 `Singleton` compile-time lifetime
+ - `RequestLifetimeBenchmarks` 撤回当前无法真实运行的 `Mediator` 生命周期矩阵,只保留 `GFramework.Cqrs` 与 `MediatR`
+ - `GFramework.Cqrs.Benchmarks/README.md` 同步收窄 request lifetime coverage,并把 `Mediator` 生命周期矩阵改记为当前缺口
+ - 本轮未修改 `GFramework.Cqrs` 运行时代码;修复面限定在 benchmark 宿主装配与 reader-facing docs。
## 当前活跃事实
- 当前分支:`feat/cqrs-optimization`
- 当前 PR:`PR #349(已合并;当前分支暂无新的公开 PR)`
+- 当前 PR:`PR #350(OPEN)`
- 当前写面:
+ - `GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs`
- `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`
@@ -60,7 +48,7 @@ CQRS 迁移与收敛。
- `origin/main @ 2b2bec65 (2026-05-12 11:49:39 +0800)`
- 当前已提交 branch diff:`14 files`
- 当前分支比 `origin/main` 多 `5` 个提交:`f346110a`、`a016e3d4`、`ab422b05`、`555c7c07`、`c32a1ec4`
- - 当前工作面已收口为 request lifetime parity、两处 benchmark XML 文档修正与对应 `README` / `ai-plan` 同步
+- 当前工作面已收口为 `Mediator` benchmark runtime 配置修正、request lifetime coverage 收窄与对应 `README` / `ai-plan` 同步
- 本轮提交:
- `f346110a` `feat(cqrs-benchmarks): 补齐 stream startup 的 Mediator 对照路径`
- `ab422b05` `docs(cqrs-benchmarks): 补齐 request benchmark 返回值注释`
@@ -69,28 +57,29 @@ CQRS 迁移与收敛。
## 当前风险
-- `StreamStartupBenchmarks` 的 `Mediator` parity 目前只做了编译验证,尚未单独执行 benchmark 作业确认 startup 矩阵运行结果。
-- `StreamLifetimeBenchmarks` 仍缺 `Mediator` parity;虽然 `RequestLifetimeBenchmarks` 已证明 `Mediator` 的 compile-time lifetime 可通过常量分支接入,但 stream 侧仍需要额外处理 `IAsyncEnumerable` 与作用域覆盖整个枚举周期的组合边界。
+- `StreamLifetimeBenchmarks` 仍缺 `Mediator` parity;如果后续要补,必须采用独立 compile-time config 或独立 benchmark 工程,而不是在当前项目里切换 runtime `ServiceLifetime`。
+- `RequestLifetimeBenchmarks` 目前不再覆盖 `Mediator`;若后续要恢复该矩阵,也必须先解决 source-generated lifetime 与 BenchmarkDotNet 自动宿主的编译期塑形边界。
- benchmark XML 盘点若再次依赖粗糙脚本或只读 inventory,仍有把已存在文档误记为缺口的风险;后续若再开 XML 波次,必须先用主线程抽样核对代表文件。
-- 本轮已在 request lifetime parity 与文档漂移收口后主动停批次;若后续恢复,优先先做 `StreamStartupBenchmarks` smoke run 或更明确的 parity / docs 候选,而不是继续机械扩张 XML 批次。
+- 当前 PR 的 Greptile open thread 在代码修正后虽已有本地验证证据,但线程本身还未在 GitHub 上回复 / resolve。
## 最近权威验证
- `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`
+ - 备注:确认统一 `Mediator` compile-time lifetime 后 benchmark 工程仍可编译
+- `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix pr350-stream-startup-mediator-fixed --filter "*StreamStartupBenchmarks.ColdStart_Mediator*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
- 结果:通过
+ - 备注:`ColdStart_Mediator` 已在 BenchmarkDotNet 自动生成宿主中实际执行,约 `144.036 us / 69.3 KB`
+- `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix pr350-request-lifetime-fixed-rerun --filter "*RequestLifetimeBenchmarks*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
+ - 结果:通过
+ - 备注:当前矩阵为 `9` 项(baseline / `GFramework.Cqrs` / `MediatR` * `Singleton|Scoped|Transient`),不再包含伪 `Mediator` lifetime 条目
- `$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/Messaging/RequestLifetimeBenchmarks.cs GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs`
- - 结果:通过
- - 备注:确认本轮代码面收敛在 request lifetime parity 与两处 XML 文档漂移修正
+ - 结果:确认 `PR #350` open,CodeRabbit 已 `APPROVED`,Greptile 仍有 `1` 条 open thread 指向 `StreamStartupBenchmarks.cs`
## 下一推荐步骤
-1. 若后续继续 benchmark 波次,优先单独执行 `StreamStartupBenchmarks` 的最小 smoke run,验证新加 `Mediator` startup 路径可运行。
-2. 若后续评估 `StreamLifetimeBenchmarks` 的 `Mediator` parity,先复用本轮 request lifetime 的“编译期常量 lifetime 分支”经验,再判断是否值得引入新的 scoped stream helper。
+1. 在 GitHub `PR #350` 回应并 resolve 当前 Greptile 线程,说明 `ColdStart_Mediator` 已补 smoke-run,且 request lifetime 的 `Mediator` 矩阵已按 source-generator 真实约束撤回。
+2. 若后续评估 `StreamLifetimeBenchmarks` 或 request lifetime 的 `Mediator` parity,优先设计独立 compile-time config / 独立 benchmark 工程,而不是继续在同一项目里切换 runtime `ServiceLifetime`。
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 9848ad42..71c6705c 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,40 @@ SPDX-License-Identifier: Apache-2.0
## 2026-05-12
+### 阶段:PR #350 的 Mediator runtime 配置收口(CQRS-REWRITE-RP-141)
+
+- 使用 `$gframework-pr-review` 重新抓取当前分支 PR,确认 GitHub 真值已从 active tracking 中过期的 `PR #349` 切换为仍处于 `OPEN` 状态的 `PR #350`。
+- 最新 AI review 只剩 1 条 Greptile open thread:
+ - `GFramework.Cqrs.Benchmarks/Messaging/StreamStartupBenchmarks.cs:210`
+ - 质疑点不是文档,而是 `Mediator` startup / request lifetime 新路径仅编译通过、未实际 smoke-run
+- 主线程先按 review 建议做本地 smoke 验证,而不是直接回复线程:
+ - `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix pr350-stream-startup-mediator --filter "*StreamStartupBenchmarks.ColdStart_Mediator*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
+ - `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix pr350-request-lifetime-mediator --filter "*RequestLifetimeBenchmarks.SendRequest_Mediator*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
+- 第一轮 smoke 暴露出 Greptile 线程背后的真实运行时问题,而不是 review 噪音:
+ - `StreamStartupBenchmarks.ColdStart_Mediator()` 在 BenchmarkDotNet 自动生成宿主里抛出
+ `Invalid configuration detected for Mediator. Generated code for 'Transient' lifetime, but got 'Singleton' lifetime from options.`
+ - `RequestLifetimeBenchmarks.SendRequest_Mediator()` 的 `Singleton` / `Scoped` 同样抛出相同异常,只有 `Transient` 分支能实际执行
+- 根因判断:
+ - `Mediator` 的 DI lifetime 由 source generator 在 benchmark 项目编译期固定
+ - 当前项目同时包含默认 `AddMediator()` 和 request lifetime 场景里的 `AddMediator(options => options.ServiceLifetime = ...)`
+ - 同一份生成产物在 BenchmarkDotNet 自动生成宿主里因此出现 compile-time lifetime 与 runtime options 不一致
+- 主线程修复:
+ - `BenchmarkHostFactory.CreateMediatorServiceProvider()` 统一改为显式 `ServiceLifetime.Singleton`
+ - `RequestLifetimeBenchmarks` 删除当前无法真实运行的 `SendRequest_Mediator()` 与相关 `Mediator` 生命周期 helper / 契约实现
+ - `GFramework.Cqrs.Benchmarks/README.md` 将 request lifetime coverage 收窄为 `GFramework.Cqrs` + `MediatR`,并把 `Mediator` lifetime parity 改记为当前缺口
+- 串行验证结果:
+ - `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+ - `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix pr350-stream-startup-mediator-fixed --filter "*StreamStartupBenchmarks.ColdStart_Mediator*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
+ - 结果:通过
+ - 关键数值:`ColdStart_Mediator ≈ 144.036 us / 69.3 KB`
+ - `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix pr350-request-lifetime-fixed-rerun --filter "*RequestLifetimeBenchmarks*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
+ - 结果:通过
+ - 关键结论:当前 request lifetime 矩阵已收敛为 `9` 项(baseline / `GFramework.Cqrs` / `MediatR` * `Singleton|Scoped|Transient`),不再包含伪 `Mediator` lifetime 条目
+- 本轮 stop decision:
+ - 不继续把 `Mediator` lifetime parity 硬扩到 request 或 stream lifetime benchmark
+ - 原因不是 branch-size;而是 source-generator compile-time config 已明确构成真实边界,继续在同一项目里扩 runtime 切换只会制造新的伪覆盖
+
### 阶段:request lifetime 的 Mediator parity 与文档漂移收口(CQRS-REWRITE-RP-140)
- 继续按 `$gframework-batch-boot 50` 推进,基线保持为 `origin/main @ 2b2bec65 (2026-05-12 11:49:39 +0800)`。