From 01360bb4ae82e928670652150fad89580350f2c0 Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 13 May 2026 08:29:00 +0800
Subject: [PATCH 1/5] =?UTF-8?q?chore(cqrs-rewrite):=20=E5=88=B7=E6=96=B0PR?=
=?UTF-8?q?350=E5=90=88=E5=B9=B6=E5=90=8E=E7=9A=84=E6=81=A2=E5=A4=8D?=
=?UTF-8?q?=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 更新 cqrs-rewrite tracking 到 PR #350 已合并且 branch diff 归零的当前真值
- 补充 batch boot 恢复结论与下一轮 CQRS 工作应从新分支继续的建议
---
.../todos/cqrs-rewrite-migration-tracking.md | 81 ++++++++-----------
.../traces/cqrs-rewrite-migration-trace.md | 26 ++++++
2 files changed, 59 insertions(+), 48 deletions(-)
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 add78968..f366c5d1 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,75 +12,60 @@ CQRS 迁移与收敛。
## 当前恢复点
-- 恢复点编号:`CQRS-REWRITE-RP-140`
+- 恢复点编号:`CQRS-REWRITE-RP-142`
- 当前阶段:`Phase 8`
-- 当前 PR 锚点:`PR #350(OPEN,2026-05-12)`
+- 当前 PR 锚点:`PR #350(MERGED,2026-05-13)`
- 当前结论:
- - 本轮按 `$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。
+ - 本轮按 `$gframework-batch-boot 50` 恢复后,先核对本地仓库真值,确认 `feat/cqrs-optimization` 已与
+ `origin/main` 指向同一合并提交 `4837aa2a`,此前 tracking 中的 `PR #350(OPEN)`、`14 files` 等事实已过期。
+ - 当前 topic 的 benchmark runtime 修正、XML 文档补齐与 `README` 边界收口已经随 `PR #350` 合并进入
+ `origin/main`,不再存在可继续扩批的活动写面。
+ - 这轮收口不再继续新增 benchmark 或测试切片,而是把 public recovery 入口刷新为“已合并、无 branch diff、等待下一轮新任务”的状态,
+ 避免后续 `boot` 落回已完成的 PR 上下文。
+ - 当前 branch-wide 停止原因不是 `50 files` 阈值,而是语义边界已经完成:
+ - `origin/main...HEAD` 的累计 diff 为 `0 files / 0 lines`
+ - 当前工作树干净
+ - 继续在同一 topic 上机械扩批不会产生新的低风险、可验证切片
## 当前活跃事实
- 当前分支:`feat/cqrs-optimization`
-- 当前 PR:`PR #349(已合并;当前分支暂无新的公开 PR)`
-- 当前 PR:`PR #350(OPEN)`
+- 当前 HEAD / 基线:`origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`
+- 当前 PR:`PR #350(已合并到 origin/main)`
- 当前写面:
- - `GFramework.Cqrs.Benchmarks/Messaging/BenchmarkHostFactory.cs`
- - `GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.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`
- 当前基线:
- - `origin/main @ 2b2bec65 (2026-05-12 11:49:39 +0800)`
- - 当前已提交 branch diff:`14 files`
- - 当前分支比 `origin/main` 多 `5` 个提交:`f346110a`、`a016e3d4`、`ab422b05`、`555c7c07`、`c32a1ec4`
-- 当前工作面已收口为 `Mediator` benchmark runtime 配置修正、request lifetime coverage 收窄与对应 `README` / `ai-plan` 同步
-- 本轮提交:
- - `f346110a` `feat(cqrs-benchmarks): 补齐 stream startup 的 Mediator 对照路径`
- - `ab422b05` `docs(cqrs-benchmarks): 补齐 request benchmark 返回值注释`
- - `555c7c07` `docs(cqrs-benchmarks): 补齐 request benchmark 返回值文档`
- - `c32a1ec4` `docs(cqrs-benchmarks): 补齐stream与notification基准返回值文档`
+ - `origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`
+ - 当前已提交 branch diff:`0 files / 0 lines`
+ - `origin/main...HEAD` 提交差异:`0 behind / 0 ahead`
+- 当前工作面已收口为 public recovery 文档刷新;CQRS benchmark 代码不再处于活跃修改状态
+- 最近已合并提交:
+ - `2dd9435c` `fix(cqrs-benchmarks): 修正Mediator基准运行时配置`
+ - `e3532fc2` `feat(cqrs-benchmarks): 补齐request生命周期的Mediator对照`
+ - `092946e9` `docs(cqrs-benchmarks): 同步startup基准文档边界`
## 当前风险
-- `StreamLifetimeBenchmarks` 仍缺 `Mediator` parity;如果后续要补,必须采用独立 compile-time config 或独立 benchmark 工程,而不是在当前项目里切换 runtime `ServiceLifetime`。
-- `RequestLifetimeBenchmarks` 目前不再覆盖 `Mediator`;若后续要恢复该矩阵,也必须先解决 source-generated lifetime 与 BenchmarkDotNet 自动宿主的编译期塑形边界。
-- benchmark XML 盘点若再次依赖粗糙脚本或只读 inventory,仍有把已存在文档误记为缺口的风险;后续若再开 XML 波次,必须先用主线程抽样核对代表文件。
-- 当前 PR 的 Greptile open thread 在代码修正后虽已有本地验证证据,但线程本身还未在 GitHub 上回复 / resolve。
+- 后续若再次评估 `StreamLifetimeBenchmarks` 或 request lifetime 的 `Mediator` parity,仍必须采用独立 compile-time config
+ 或独立 benchmark 工程,而不是在同一份 source-generated 产物上切换 runtime `ServiceLifetime`。
+- 如果 `feat/cqrs-optimization` 继续承载新的 CQRS 任务而不先分支,public recovery 入口会把“已合并的 PR #350”与下一轮新工作混在一起。
+- 若未来再开 benchmark XML / docs 波次,仍需要主线程先抽样核对代表文件,避免重复接受误报 inventory。
## 最近权威验证
+- `git rev-list --left-right --count origin/main...HEAD`
+ - 结果:通过
+ - 备注:返回 `0 0`,确认当前分支与 `origin/main` 没有提交差异
- `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
- - 备注:确认统一 `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 #350` open,CodeRabbit 已 `APPROVED`,Greptile 仍有 `1` 条 open thread 指向 `StreamStartupBenchmarks.cs`
+ - 备注:作为当前 recovery 刷新任务的最小 build validation,继续确认 benchmark 工程在合并后保持可编译
## 下一推荐步骤
-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 扩批。
+1. 若要继续 CQRS 主题的新一轮实现,先从最新 `origin/main` 创建新的 topic branch,再补新的 recovery point,而不是继续复用已完成的 `PR #350` 上下文。
+2. 若后续重新打开 `Mediator` 生命周期 parity 工作,优先设计独立 compile-time config / 独立 benchmark 工程,并把该设计单独记录到新的 tracking phase。
+3. 若只是恢复本 worktree 继续其他 topic,可把 `cqrs-rewrite` 视为“当前已在自然停点完成”的历史入口,不再默认把 benchmark README / XML 清扫当作活跃批处理目标。
## 活跃文档
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 71c6705c..cc46473c 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
@@ -5,6 +5,32 @@ SPDX-License-Identifier: Apache-2.0
# CQRS 重写迁移追踪
+## 2026-05-13
+
+### 阶段:PR #350 合并后的 recovery 入口刷新(CQRS-REWRITE-RP-142)
+
+- 继续按 `$gframework-batch-boot 50` 恢复当前 topic,但启动时先核对分支真值,而不是沿用 active tracking 中的旧 PR 状态。
+- 本轮基线按 skill 规则固定为 `origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`。
+- 本地核对结果:
+ - `feat/cqrs-optimization` 当前 `HEAD` 与 `origin/main` 指向同一合并提交 `4837aa2a`
+ - `git rev-list --left-right --count origin/main...HEAD` 返回 `0 0`
+ - `git diff --name-only origin/main...HEAD | wc -l` 返回 `0`
+ - 当前工作树干净,不存在可继续扩批的 CQRS benchmark / test 写面
+- 批处理候选重判:
+ - 不再把当前任务视为新的 benchmark 批处理波次
+ - 原因不是 `50 files` 阈值耗尽,而是语义边界已经完成,继续在已合并 topic 上扩批只会制造恢复入口噪音
+- 本轮 accepted scope:
+ - `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md`
+ - `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
+- 本轮主线程收口目标:
+ - 把 active tracking 从 `PR #350(OPEN)` 刷新为 `PR #350(MERGED)`
+ - 把 branch metric 从过期的 `14 files` 刷新为 `0 files / 0 lines`
+ - 把“继续回 GitHub resolve thread”替换为“下一轮 CQRS 工作应从新分支 / 新 recovery point 开始”
+- 当前下一步:
+ - 串行执行一次 benchmark 工程 Release build,满足仓库对完成任务的 build validation 要求
+ - 运行 `python3 scripts/license-header.py --check --paths ...` 与 `git diff --check`
+ - 提交本轮 recovery 刷新
+
## 2026-05-12
### 阶段:PR #350 的 Mediator runtime 配置收口(CQRS-REWRITE-RP-141)
From 4564b3273b16d6bf5e3c8fd56c1255a5673d14fc Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 13 May 2026 08:30:46 +0800
Subject: [PATCH 2/5] =?UTF-8?q?chore(cqrs-rewrite):=20=E5=9B=9E=E5=A1=AB?=
=?UTF-8?q?=E6=81=A2=E5=A4=8D=E5=85=A5=E5=8F=A3=E7=9A=84=E6=8F=90=E4=BA=A4?=
=?UTF-8?q?=E5=90=8E=E6=8C=87=E6=A0=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 更新 cqrs-rewrite tracking 为 recovery 刷新提交后的 branch diff 与 ahead 状态
- 补充 trace 对应的最终停点,避免 ai-plan 与真实分支指标不一致
---
.../todos/cqrs-rewrite-migration-tracking.md | 20 ++++++++++++-------
.../traces/cqrs-rewrite-migration-trace.md | 9 ++++++---
2 files changed, 19 insertions(+), 10 deletions(-)
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 f366c5d1..75b8fe60 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
@@ -22,9 +22,10 @@ CQRS 迁移与收敛。
`origin/main`,不再存在可继续扩批的活动写面。
- 这轮收口不再继续新增 benchmark 或测试切片,而是把 public recovery 入口刷新为“已合并、无 branch diff、等待下一轮新任务”的状态,
避免后续 `boot` 落回已完成的 PR 上下文。
- - 当前 branch-wide 停止原因不是 `50 files` 阈值,而是语义边界已经完成:
- - `origin/main...HEAD` 的累计 diff 为 `0 files / 0 lines`
+ - 刷新提交 `01360bb4` 落地后,当前 branch-wide 停止原因仍不是 `50 files` 阈值,而是语义边界已经完成:
+ - `origin/main...HEAD` 的累计 diff 为 `2 files / 107 lines`
- 当前工作树干净
+ - 唯一新增 diff 只来自 `cqrs-rewrite` 的 public recovery 文档刷新
- 继续在同一 topic 上机械扩批不会产生新的低风险、可验证切片
## 当前活跃事实
@@ -37,13 +38,15 @@ CQRS 迁移与收敛。
- `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
- 当前基线:
- `origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`
- - 当前已提交 branch diff:`0 files / 0 lines`
- - `origin/main...HEAD` 提交差异:`0 behind / 0 ahead`
-- 当前工作面已收口为 public recovery 文档刷新;CQRS benchmark 代码不再处于活跃修改状态
+ - 当前已提交 branch diff:`2 files / 107 lines`
+ - `origin/main...HEAD` 提交差异:`0 behind / 1 ahead`
+- 当前工作面已收口为 public recovery 文档刷新;当前工作树干净,CQRS benchmark 代码不再处于活跃修改状态
- 最近已合并提交:
- `2dd9435c` `fix(cqrs-benchmarks): 修正Mediator基准运行时配置`
- `e3532fc2` `feat(cqrs-benchmarks): 补齐request生命周期的Mediator对照`
- `092946e9` `docs(cqrs-benchmarks): 同步startup基准文档边界`
+- 当前恢复提交:
+ - `01360bb4` `chore(cqrs-rewrite): 刷新PR350合并后的恢复入口`
## 当前风险
@@ -56,14 +59,17 @@ CQRS 迁移与收敛。
- `git rev-list --left-right --count origin/main...HEAD`
- 结果:通过
- - 备注:返回 `0 0`,确认当前分支与 `origin/main` 没有提交差异
+ - 备注:在恢复刷新提交前返回 `0 0`,确认 `PR #350` 已完整合入 `origin/main`
+- `git diff --name-only origin/main...HEAD | wc -l`
+ - 结果:通过
+ - 备注:刷新提交后返回 `2`,确认当前 branch diff 只剩两份 `ai-plan` recovery 文档
- `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
- 备注:作为当前 recovery 刷新任务的最小 build validation,继续确认 benchmark 工程在合并后保持可编译
## 下一推荐步骤
-1. 若要继续 CQRS 主题的新一轮实现,先从最新 `origin/main` 创建新的 topic branch,再补新的 recovery point,而不是继续复用已完成的 `PR #350` 上下文。
+1. 若要继续 CQRS 主题的新一轮实现,先把当前 recovery 刷新提交合并或重放到新的 topic branch,再从最新 `origin/main` 继续,而不是复用已完成的 `PR #350` 上下文。
2. 若后续重新打开 `Mediator` 生命周期 parity 工作,优先设计独立 compile-time config / 独立 benchmark 工程,并把该设计单独记录到新的 tracking phase。
3. 若只是恢复本 worktree 继续其他 topic,可把 `cqrs-rewrite` 视为“当前已在自然停点完成”的历史入口,不再默认把 benchmark README / XML 清扫当作活跃批处理目标。
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 cc46473c..eed33991 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
@@ -26,10 +26,13 @@ SPDX-License-Identifier: Apache-2.0
- 把 active tracking 从 `PR #350(OPEN)` 刷新为 `PR #350(MERGED)`
- 把 branch metric 从过期的 `14 files` 刷新为 `0 files / 0 lines`
- 把“继续回 GitHub resolve thread”替换为“下一轮 CQRS 工作应从新分支 / 新 recovery point 开始”
+- 提交 `01360bb4 chore(cqrs-rewrite): 刷新PR350合并后的恢复入口` 后,当前 branch metric 改写为:
+ - `origin/main...HEAD` 为 `2 files / 107 lines`
+ - 提交差异为 `0 behind / 1 ahead`
+ - diff 仅覆盖 `cqrs-rewrite` 的 public tracking / trace 文档
- 当前下一步:
- - 串行执行一次 benchmark 工程 Release build,满足仓库对完成任务的 build validation 要求
- - 运行 `python3 scripts/license-header.py --check --paths ...` 与 `git diff --check`
- - 提交本轮 recovery 刷新
+ - 运行一次轻量校验,确认回填后的 tracking / trace 仍可干净提交
+ - 提交这次“提交后指标回填”的最终收口
## 2026-05-12
From 2bc6502297a2b180cd37d3b01734174abfc0a07a Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 13 May 2026 08:32:32 +0800
Subject: [PATCH 3/5] =?UTF-8?q?chore(cqrs-rewrite):=20=E7=A8=B3=E5=AE=9A?=
=?UTF-8?q?=E6=81=A2=E5=A4=8D=E5=85=A5=E5=8F=A3=E7=9A=84=E5=81=9C=E6=AD=A2?=
=?UTF-8?q?=E6=9D=A1=E4=BB=B6=E6=8F=8F=E8=BF=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 更新 cqrs-rewrite tracking 为不会被后续收尾提交再次改坏的稳定事实
- 补充 trace 的自然停点说明,避免继续保留已完成提交的待办语义
---
.../todos/cqrs-rewrite-migration-tracking.md | 13 +++++--------
.../traces/cqrs-rewrite-migration-trace.md | 9 +++------
2 files changed, 8 insertions(+), 14 deletions(-)
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 75b8fe60..05e37c78 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
@@ -22,10 +22,10 @@ CQRS 迁移与收敛。
`origin/main`,不再存在可继续扩批的活动写面。
- 这轮收口不再继续新增 benchmark 或测试切片,而是把 public recovery 入口刷新为“已合并、无 branch diff、等待下一轮新任务”的状态,
避免后续 `boot` 落回已完成的 PR 上下文。
- - 刷新提交 `01360bb4` 落地后,当前 branch-wide 停止原因仍不是 `50 files` 阈值,而是语义边界已经完成:
- - `origin/main...HEAD` 的累计 diff 为 `2 files / 107 lines`
+ - recovery 刷新提交落地后,当前 branch-wide 停止原因仍不是 `50 files` 阈值,而是语义边界已经完成:
+ - 刷新开始前,`origin/main...HEAD` 的评估结果为 `0 files / 0 lines`
- 当前工作树干净
- - 唯一新增 diff 只来自 `cqrs-rewrite` 的 public recovery 文档刷新
+ - 当前唯一新增 diff 只来自 `cqrs-rewrite` 的 public recovery 文档刷新
- 继续在同一 topic 上机械扩批不会产生新的低风险、可验证切片
## 当前活跃事实
@@ -38,15 +38,12 @@ CQRS 迁移与收敛。
- `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
- 当前基线:
- `origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`
- - 当前已提交 branch diff:`2 files / 107 lines`
- - `origin/main...HEAD` 提交差异:`0 behind / 1 ahead`
+- 当前已提交 branch diff 只涉及 `2` 个 `ai-plan/public/cqrs-rewrite/**` recovery 文档
- 当前工作面已收口为 public recovery 文档刷新;当前工作树干净,CQRS benchmark 代码不再处于活跃修改状态
- 最近已合并提交:
- `2dd9435c` `fix(cqrs-benchmarks): 修正Mediator基准运行时配置`
- `e3532fc2` `feat(cqrs-benchmarks): 补齐request生命周期的Mediator对照`
- `092946e9` `docs(cqrs-benchmarks): 同步startup基准文档边界`
-- 当前恢复提交:
- - `01360bb4` `chore(cqrs-rewrite): 刷新PR350合并后的恢复入口`
## 当前风险
@@ -62,7 +59,7 @@ CQRS 迁移与收敛。
- 备注:在恢复刷新提交前返回 `0 0`,确认 `PR #350` 已完整合入 `origin/main`
- `git diff --name-only origin/main...HEAD | wc -l`
- 结果:通过
- - 备注:刷新提交后返回 `2`,确认当前 branch diff 只剩两份 `ai-plan` recovery 文档
+ - 备注:确认当前 branch diff 始终只覆盖两份 `ai-plan` recovery 文档,没有重新打开 CQRS benchmark 代码写面
- `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
- 备注:作为当前 recovery 刷新任务的最小 build validation,继续确认 benchmark 工程在合并后保持可编译
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 eed33991..d0f9070a 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
@@ -26,13 +26,10 @@ SPDX-License-Identifier: Apache-2.0
- 把 active tracking 从 `PR #350(OPEN)` 刷新为 `PR #350(MERGED)`
- 把 branch metric 从过期的 `14 files` 刷新为 `0 files / 0 lines`
- 把“继续回 GitHub resolve thread”替换为“下一轮 CQRS 工作应从新分支 / 新 recovery point 开始”
-- 提交 `01360bb4 chore(cqrs-rewrite): 刷新PR350合并后的恢复入口` 后,当前 branch metric 改写为:
- - `origin/main...HEAD` 为 `2 files / 107 lines`
- - 提交差异为 `0 behind / 1 ahead`
- - diff 仅覆盖 `cqrs-rewrite` 的 public tracking / trace 文档
+- recovery 刷新提交落地后,当前 branch diff 仍只覆盖 `cqrs-rewrite` 的 public tracking / trace 文档,没有重新打开 benchmark 代码写面
- 当前下一步:
- - 运行一次轻量校验,确认回填后的 tracking / trace 仍可干净提交
- - 提交这次“提交后指标回填”的最终收口
+ - 若后续继续 CQRS 主题,实现工作应从新的 topic branch 与新的 recovery point 开始
+ - 若只是恢复其他 topic,可把当前 `cqrs-rewrite` active 入口视为已停在自然边界
## 2026-05-12
From e5b173c29abb4ad2faf211bf8f20fd2075c1945c Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 13 May 2026 09:04:13 +0800
Subject: [PATCH 4/5] =?UTF-8?q?fix(cqrs):=20=E4=BF=AE=E6=AD=A3request?=
=?UTF-8?q?=E7=AE=A1=E9=81=93=E4=B8=AD=E7=9A=84generated=20invoker?=
=?UTF-8?q?=E5=9B=9E=E9=80=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 修正 request pipeline 末端继续复用缓存 RequestInvoker 的运行时语义
- 补充 generated request invoker 与 stream 缺失 handler 的回归测试覆盖
- 更新 benchmark reader-facing 注释与 cqrs-rewrite 恢复入口记录
---
.../Messaging/NotificationFanOutBenchmarks.cs | 4 +-
.../Messaging/RequestBenchmarks.cs | 2 +-
.../Messaging/RequestLifetimeBenchmarks.cs | 4 +-
.../Messaging/StreamingBenchmarks.cs | 2 +-
.../CqrsDispatcherContextValidationTests.cs | 23 +++++++
...qrsGeneratedRequestInvokerProviderTests.cs | 61 ++++++++++++++++++
GFramework.Cqrs/Internal/CqrsDispatcher.cs | 25 +++++---
.../todos/cqrs-rewrite-migration-tracking.md | 62 ++++++++++++++-----
.../traces/cqrs-rewrite-migration-trace.md | 45 ++++++++++++++
9 files changed, 198 insertions(+), 30 deletions(-)
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
index 0e3efb1a..29ee33ea 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs
@@ -22,8 +22,8 @@ using GeneratedMediator = Mediator.Mediator;
namespace GFramework.Cqrs.Benchmarks.Messaging;
///
-/// 对比固定 4 个处理器的 notification fan-out publish 在 baseline、GFramework.CQRS、NuGet `Mediator`
-/// 与 MediatR 之间的开销。
+/// 对比固定 4 个处理器的 notification fan-out publish 在 baseline、GFramework.CQRS 默认顺序发布器、
+/// GFramework.CQRS 内置 TaskWhenAllNotificationPublisher、NuGet `Mediator` 与 MediatR 之间的开销。
///
[Config(typeof(Config))]
public class NotificationFanOutBenchmarks
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs
index ff5ca2c4..f0dbf4b9 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs
@@ -132,7 +132,7 @@ public class RequestBenchmarks
}
///
- /// 通过 `ai-libs/Mediator` 的 source-generated concrete mediator 发送 request,作为高性能对照组。
+ /// 通过 NuGet `Mediator` 的 source-generated concrete mediator 发送 request,作为高性能对照组。
///
/// 代表当前 `Mediator` request dispatch 完成的值任务。
[Benchmark]
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs
index f12f83e6..6869b0ab 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs
@@ -201,7 +201,7 @@ public class RequestLifetimeBenchmarks
}
///
- /// 按生命周期把 benchmark request handler 注册到 GFramework 容器。
+ /// 按生命周期把 benchmark request handler 注册到 GFramework 容器。
///
/// 当前 benchmark 拥有并负责释放的容器。
/// 待比较的 handler 生命周期。
@@ -248,7 +248,7 @@ public class RequestLifetimeBenchmarks
}
///
- /// Benchmark request。
+ /// Benchmark request。
///
/// 请求标识。
public sealed record BenchmarkRequest(Guid Id) :
diff --git a/GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs b/GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs
index 449c1bba..a4dff680 100644
--- a/GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs
+++ b/GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs
@@ -168,7 +168,7 @@ public class StreamingBenchmarks
}
///
- /// 通过 `ai-libs/Mediator` 的 source-generated concrete mediator 创建 stream,并按当前观测模式消费。
+ /// 通过 NuGet `Mediator` 的 source-generated concrete mediator 创建 stream,并按当前观测模式消费。
///
/// 按当前观测模式完成 stream 消费后的等待句柄。
[Benchmark]
diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
index e3e0d337..c189eb53 100644
--- a/GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
+++ b/GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
@@ -147,6 +147,29 @@ internal sealed class CqrsDispatcherContextValidationTests
Throws.InvalidOperationException.With.Message.Contains("does not implement IArchitectureContext"));
}
+ ///
+ /// 验证 stream handler 缺失时,dispatcher 会在建流调用点同步抛出异常,
+ /// 而不是返回一个延迟到枚举阶段才失败的异步流。
+ ///
+ [Test]
+ public void CreateStream_Should_Throw_When_Handler_Is_Missing()
+ {
+ var runtime = CreateRuntime(
+ container =>
+ {
+ container
+ .Setup(currentContainer => currentContainer.Get(typeof(IStreamRequestHandler)))
+ .Returns((object?)null);
+ container
+ .Setup(currentContainer => currentContainer.HasRegistration(typeof(IStreamPipelineBehavior)))
+ .Returns(false);
+ });
+
+ Assert.That(
+ () => runtime.CreateStream(new FakeCqrsContext(), new ContextAwareStreamRequest()),
+ Throws.InvalidOperationException.With.Message.Contains("No CQRS stream handler registered"));
+ }
+
///
/// 验证当 stream pipeline behavior 需要上下文注入、但当前 CQRS 上下文不实现
/// 时,
diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsGeneratedRequestInvokerProviderTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsGeneratedRequestInvokerProviderTests.cs
index 227f101d..6c16b20e 100644
--- a/GFramework.Cqrs.Tests/Cqrs/CqrsGeneratedRequestInvokerProviderTests.cs
+++ b/GFramework.Cqrs.Tests/Cqrs/CqrsGeneratedRequestInvokerProviderTests.cs
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
using System.Reflection;
+using System.Threading;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
@@ -28,6 +29,7 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
{
_previousLoggerFactoryProvider = LoggerFactoryResolver.Provider;
LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
+ GeneratedRequestPipelineTrackingBehavior.InvocationCount = 0;
GeneratedStreamPipelineTrackingBehavior.InvocationCount = 0;
ClearRegistrarCaches();
ClearDispatcherCaches();
@@ -40,6 +42,7 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
public void TearDown()
{
LoggerFactoryResolver.Provider = _previousLoggerFactoryProvider ?? new ConsoleLoggerFactoryProvider();
+ GeneratedRequestPipelineTrackingBehavior.InvocationCount = 0;
GeneratedStreamPipelineTrackingBehavior.InvocationCount = 0;
ClearRegistrarCaches();
ClearDispatcherCaches();
@@ -181,6 +184,30 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
Assert.That(response, Is.EqualTo("generated-hidden:payload"));
}
+ ///
+ /// 验证 generated request invoker 与 request pipeline 行为同时存在时,
+ /// dispatcher 仍会保持 generated invoker 优先,并正确复用现有 request 执行链。
+ ///
+ [Test]
+ public async Task SendAsync_Should_Use_Generated_Request_Invoker_Inside_Request_Pipeline()
+ {
+ var generatedAssembly = CreateGeneratedRequestInvokerAssembly();
+ var container = new MicrosoftDiContainer();
+ container.RegisterCqrsPipelineBehavior();
+
+ CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object);
+ container.Freeze();
+
+ var context = new ArchitectureContext(container);
+ var response = await context.SendRequestAsync(new GeneratedRequestInvokerRequest("payload")).ConfigureAwait(false);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(response, Is.EqualTo("generated:payload"));
+ Assert.That(GeneratedRequestPipelineTrackingBehavior.InvocationCount, Is.EqualTo(1));
+ });
+ }
+
///
/// 验证 dispatcher 在首次创建 stream binding 时,会优先消费 generated stream invoker provider。
///
@@ -608,6 +635,40 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
Assert.That(results, Is.EqualTo([30, 31]));
}
+ ///
+ /// 记录 generated request invoker 与 request pipeline 行为组合时的命中次数。
+ ///
+ private sealed class GeneratedRequestPipelineTrackingBehavior
+ : IPipelineBehavior
+ {
+ private static int _invocationCount;
+
+ ///
+ /// 获取或重置当前测试进程中的行为触发次数。
+ ///
+ public static int InvocationCount
+ {
+ get => Volatile.Read(ref _invocationCount);
+ set => Volatile.Write(ref _invocationCount, value);
+ }
+
+ ///
+ /// 记录一次行为执行,然后继续执行 generated request invoker。
+ ///
+ /// 当前请求消息。
+ /// 下一个处理阶段。
+ /// 取消令牌。
+ /// 下游处理阶段返回的响应。
+ public ValueTask Handle(
+ GeneratedRequestInvokerRequest message,
+ MessageHandlerDelegate next,
+ CancellationToken cancellationToken)
+ {
+ Interlocked.Increment(ref _invocationCount);
+ return next(message, cancellationToken);
+ }
+ }
+
///
/// 模拟返回实例 request invoker 方法的 generated registry。
///
diff --git a/GFramework.Cqrs/Internal/CqrsDispatcher.cs b/GFramework.Cqrs/Internal/CqrsDispatcher.cs
index b9bab1db..3e039e3e 100644
--- a/GFramework.Cqrs/Internal/CqrsDispatcher.cs
+++ b/GFramework.Cqrs/Internal/CqrsDispatcher.cs
@@ -150,7 +150,7 @@ internal sealed class CqrsDispatcher(
}
return dispatchBinding.GetPipelineExecutor(behaviors.Count)
- .Invoke(handler, behaviors, request, cancellationToken);
+ .Invoke(handler, behaviors, dispatchBinding.RequestInvoker, request, cancellationToken);
}
catch (Exception exception)
{
@@ -604,12 +604,14 @@ internal sealed class CqrsDispatcher(
private static ValueTask InvokeRequestPipelineExecutorAsync(
object handler,
IReadOnlyList
private sealed class RequestPipelineInvocation(
- IRequestHandler handler,
+ object handler,
+ RequestInvoker requestInvoker,
IReadOnlyList behaviors)
where TRequest : IRequest
{
- private readonly IRequestHandler _handler = handler;
+ private readonly object _handler = handler;
+ private readonly RequestInvoker _requestInvoker = requestInvoker;
private readonly IReadOnlyList _behaviors = behaviors;
private readonly MessageHandlerDelegate?[] _continuations =
new MessageHandlerDelegate?[behaviors.Count + 1];
@@ -1198,11 +1204,16 @@ internal sealed class CqrsDispatcher(
}
///
- /// 调用最终请求处理器。
+ /// 调用最终请求处理入口。
///
+ ///
+ /// request pipeline 末端必须继续复用当前 binding 上缓存的 ,
+ /// 这样 generated request invoker provider 才能在接入 pipeline 后保持与无 pipeline 路径一致的调用语义,
+ /// 而不是退回到接口虚调用路径。
+ ///
private ValueTask InvokeHandlerAsync(TRequest request, CancellationToken cancellationToken)
{
- return _handler.Handle(request, cancellationToken);
+ return _requestInvoker(_handler, request, cancellationToken);
}
///
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 05e37c78..39c347f9 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,21 +12,32 @@ CQRS 迁移与收敛。
## 当前恢复点
-- 恢复点编号:`CQRS-REWRITE-RP-142`
+- 恢复点编号:`CQRS-REWRITE-RP-143`
- 当前阶段:`Phase 8`
- 当前 PR 锚点:`PR #350(MERGED,2026-05-13)`
- 当前结论:
- - 本轮按 `$gframework-batch-boot 50` 恢复后,先核对本地仓库真值,确认 `feat/cqrs-optimization` 已与
- `origin/main` 指向同一合并提交 `4837aa2a`,此前 tracking 中的 `PR #350(OPEN)`、`14 files` 等事实已过期。
- - 当前 topic 的 benchmark runtime 修正、XML 文档补齐与 `README` 边界收口已经随 `PR #350` 合并进入
- `origin/main`,不再存在可继续扩批的活动写面。
- - 这轮收口不再继续新增 benchmark 或测试切片,而是把 public recovery 入口刷新为“已合并、无 branch diff、等待下一轮新任务”的状态,
- 避免后续 `boot` 落回已完成的 PR 上下文。
- - recovery 刷新提交落地后,当前 branch-wide 停止原因仍不是 `50 files` 阈值,而是语义边界已经完成:
- - 刷新开始前,`origin/main...HEAD` 的评估结果为 `0 files / 0 lines`
- - 当前工作树干净
- - 当前唯一新增 diff 只来自 `cqrs-rewrite` 的 public recovery 文档刷新
- - 继续在同一 topic 上机械扩批不会产生新的低风险、可验证切片
+ - 在用户允许 subagent 后,本轮按 `context-budget 优先、reviewability 次之、50 files 仅作粗阈值` 重开了一波小型 multi-agent 批处理,
+ 只接受单文件或窄文件组的 docs/test 切片。
+ - 已接受的低风险切片:
+ - `GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs`
+ - 修正两处 XML 文档缩进异常
+ - `GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs`
+ - 类级摘要明确当前 `GFramework.CQRS` fan-out 对照包含默认顺序发布器与内置 `TaskWhenAllNotificationPublisher`
+ - `GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs`
+ - `GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs`
+ - 将 `Mediator` reader-facing 注释统一为 “NuGet `Mediator` 的 source-generated concrete mediator”
+ - `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs`
+ - 补一条 stream 缺失 handler 的失败语义回归,固定当前分支在调用点同步抛 `InvalidOperationException`
+ - worker 在 `CqrsGeneratedRequestInvokerProviderTests.cs` 补 request/generated + pipeline 对称测试后,主线程确认这不是环境噪音,而是命中了真实运行时缺口:
+ - request 路径在接入 `IPipelineBehavior<,>` 后,会退回 `_handler.Handle(...)`
+ - 因此 generated request invoker 无法像 stream 路径那样在 pipeline 末端继续保持优先
+ - 主线程已在 `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 收口该缺口:
+ - request pipeline executor 现在显式复用当前 binding 缓存的 `RequestInvoker`
+ - generated request invoker provider 在接入 pipeline 后保持与无 pipeline 路径一致的调用语义
+ - 当前 stop decision:
+ - 不再继续下一波
+ - 原因不是 `50 files` 阈值耗尽;当前 accepted scope 仍然很小
+ - 停止原因是当前上下文已接近本轮安全预算,而剩余候选只剩 descriptor / factory / publisher 类小测试,继续扩批收益明显下降
## 当前活跃事实
@@ -34,12 +45,19 @@ CQRS 迁移与收敛。
- 当前 HEAD / 基线:`origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`
- 当前 PR:`PR #350(已合并到 origin/main)`
- 当前写面:
+ - `GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs`
+ - `GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs`
+ - `GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs`
+ - `GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs`
+ - `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs`
+ - `GFramework.Cqrs.Tests/Cqrs/CqrsGeneratedRequestInvokerProviderTests.cs`
+ - `GFramework.Cqrs/Internal/CqrsDispatcher.cs`
- `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md`
- `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
- 当前基线:
- `origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`
-- 当前已提交 branch diff 只涉及 `2` 个 `ai-plan/public/cqrs-rewrite/**` recovery 文档
-- 当前工作面已收口为 public recovery 文档刷新;当前工作树干净,CQRS benchmark 代码不再处于活跃修改状态
+- 当前 batch working-tree diff:`7` 个源码 / 测试文件
+- 当前工作面已收口为 docs/test 小切片 + 1 处 request pipeline runtime 修正;没有重新打开 benchmark 工程设计级改造
- 最近已合并提交:
- `2dd9435c` `fix(cqrs-benchmarks): 修正Mediator基准运行时配置`
- `e3532fc2` `feat(cqrs-benchmarks): 补齐request生命周期的Mediator对照`
@@ -51,6 +69,7 @@ CQRS 迁移与收敛。
或独立 benchmark 工程,而不是在同一份 source-generated 产物上切换 runtime `ServiceLifetime`。
- 如果 `feat/cqrs-optimization` 继续承载新的 CQRS 任务而不先分支,public recovery 入口会把“已合并的 PR #350”与下一轮新工作混在一起。
- 若未来再开 benchmark XML / docs 波次,仍需要主线程先抽样核对代表文件,避免重复接受误报 inventory。
+- 剩余低风险候选主要是 `NotificationPublisher` / invoker descriptor / `CqrsRuntimeFactory` 的单文件测试;它们不是当前上下文预算下的高收益下一波。
## 最近权威验证
@@ -62,13 +81,22 @@ CQRS 迁移与收敛。
- 备注:确认当前 branch diff 始终只覆盖两份 `ai-plan` recovery 文档,没有重新打开 CQRS benchmark 代码写面
- `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
- - 备注:作为当前 recovery 刷新任务的最小 build validation,继续确认 benchmark 工程在合并后保持可编译
+ - 备注:确认 benchmark reader-facing 文档收口后工程仍保持 `Release` 可编译
+- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+ - 备注:确认 request pipeline 复用 generated invoker 的 runtime 修正未引入编译回归
+- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
+ - 结果:通过,`Passed: 28, Failed: 0`
+ - 备注:确认 generated request invoker 在接入 request pipeline 后仍保持优先,并通过新增回归测试锁定
+- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsDispatcherContextValidationTests"`
+ - 结果:通过,`Passed: 7, Failed: 0`
+ - 备注:确认 stream 缺失 handler 的失败语义回归与既有上下文校验测试仍全部通过
## 下一推荐步骤
-1. 若要继续 CQRS 主题的新一轮实现,先把当前 recovery 刷新提交合并或重放到新的 topic branch,再从最新 `origin/main` 继续,而不是复用已完成的 `PR #350` 上下文。
+1. 若继续 CQRS 主题的低风险硬化,优先从 `NotificationPublisher`、invoker descriptor、`CqrsRuntimeFactory` 三类单文件测试候选中再挑一条,不要重新打开 benchmark 设计级议题。
2. 若后续重新打开 `Mediator` 生命周期 parity 工作,优先设计独立 compile-time config / 独立 benchmark 工程,并把该设计单独记录到新的 tracking phase。
-3. 若只是恢复本 worktree 继续其他 topic,可把 `cqrs-rewrite` 视为“当前已在自然停点完成”的历史入口,不再默认把 benchmark README / XML 清扫当作活跃批处理目标。
+3. 若只是恢复其他 topic,可把当前 `cqrs-rewrite` active 入口视为“本轮已在上下文预算前的自然停点完成”。
## 活跃文档
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 d0f9070a..83756595 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,51 @@ SPDX-License-Identifier: Apache-2.0
## 2026-05-13
+### 阶段:允许 subagent 后的低风险多 Agent 收口(CQRS-REWRITE-RP-143)
+
+- 在用户允许 subagent 后,主线程切到小型 multi-agent 协调模式,但仍把 critical path 保留在本地:
+ - 主线程负责候选筛选、runtime 缺口判断、最终验证与 `ai-plan`
+ - explorer 只做只读盘点
+ - worker 只接单文件或窄文件组 ownership
+- explorer 结论汇总:
+ - 没有值得继续扩成“实现型 benchmark 波次”的大切片
+ - 低风险候选主要是 reader-facing 术语对齐与单文件回归测试
+ - `Mediator` 生命周期 parity、notification fan-out 生命周期矩阵、benchmark 工程级拆分仍判定为高风险
+- accepted worker / 主线程 scope:
+ - `RequestLifetimeBenchmarks.cs`
+ - 主线程修正 2 处 XML 文档缩进异常
+ - `NotificationFanOutBenchmarks.cs`
+ - `RequestBenchmarks.cs`
+ - `StreamingBenchmarks.cs`
+ - worker 收口 benchmark reader-facing 术语,build 通过
+ - `CqrsDispatcherContextValidationTests.cs`
+ - worker 补 stream 缺失 handler 的同步抛错回归,targeted test 通过
+ - `CqrsGeneratedRequestInvokerProviderTests.cs`
+ - worker 补 generated request + pipeline 对称测试
+ - 首次运行失败,暴露 request pipeline 路径在接入 behavior 后退回 `_handler.Handle(...)`
+ - `CqrsDispatcher.cs`
+ - 主线程将 request pipeline 末端改为继续复用当前 binding 的 `RequestInvoker`
+ - 使 generated request invoker provider 在 pipeline 存在时保持与无 pipeline 路径一致
+- 本轮权威验证:
+ - `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+ - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
+ - 结果:通过,`Passed: 28, Failed: 0`
+ - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsDispatcherContextValidationTests"`
+ - 结果:通过,`Passed: 7, Failed: 0`
+ - `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+- 当前 stop decision:
+ - 不再继续下一波
+ - 原因不是 branch-size;当前变更仍远低于 `$gframework-batch-boot 50``
+ - 停止原因是主线程上下文已接近本轮安全预算,而剩余候选只剩收益较低的单文件测试硬化
+- 当前下一步:
+ - 更新 `ai-plan/public/cqrs-rewrite/**`
+ - 运行 license-header / `git diff --check`
+ - 提交本轮多 Agent 小波次收口
+
+## 2026-05-13
+
### 阶段:PR #350 合并后的 recovery 入口刷新(CQRS-REWRITE-RP-142)
- 继续按 `$gframework-batch-boot 50` 恢复当前 topic,但启动时先核对分支真值,而不是沿用 active tracking 中的旧 PR 状态。
From 20f09f0b73fda9165d4875b784e095ed127293c2 Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Thu, 14 May 2026 10:40:31 +0800
Subject: [PATCH 5/5] =?UTF-8?q?docs(cqrs-rewrite):=20=E6=94=B6=E5=8F=A3PR3?=
=?UTF-8?q?51=E6=81=A2=E5=A4=8D=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 更新 active tracking 到 PR #351 的当前审查真值与最小恢复入口
- 修复 active tracking 中过期 PR 锚点与冲突事实
- 重构 active trace 为精简恢复摘要并保留 archive 指针
- 补充本轮 PR review、license header、diff check 与 Release build 验证结果
---
.../todos/cqrs-rewrite-migration-tracking.md | 116 ++--
.../traces/cqrs-rewrite-migration-trace.md | 522 ++----------------
2 files changed, 110 insertions(+), 528 deletions(-)
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 39c347f9..1499c06f 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,91 +12,74 @@ CQRS 迁移与收敛。
## 当前恢复点
-- 恢复点编号:`CQRS-REWRITE-RP-143`
+- 恢复点编号:`CQRS-REWRITE-RP-144`
- 当前阶段:`Phase 8`
-- 当前 PR 锚点:`PR #350(MERGED,2026-05-13)`
+- 当前 PR 锚点:`PR #351(OPEN,2026-05-14)`
- 当前结论:
- - 在用户允许 subagent 后,本轮按 `context-budget 优先、reviewability 次之、50 files 仅作粗阈值` 重开了一波小型 multi-agent 批处理,
- 只接受单文件或窄文件组的 docs/test 切片。
- - 已接受的低风险切片:
- - `GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs`
- - 修正两处 XML 文档缩进异常
- - `GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs`
- - 类级摘要明确当前 `GFramework.CQRS` fan-out 对照包含默认顺序发布器与内置 `TaskWhenAllNotificationPublisher`
- - `GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs`
- - `GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs`
- - 将 `Mediator` reader-facing 注释统一为 “NuGet `Mediator` 的 source-generated concrete mediator”
- - `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs`
- - 补一条 stream 缺失 handler 的失败语义回归,固定当前分支在调用点同步抛 `InvalidOperationException`
- - worker 在 `CqrsGeneratedRequestInvokerProviderTests.cs` 补 request/generated + pipeline 对称测试后,主线程确认这不是环境噪音,而是命中了真实运行时缺口:
- - request 路径在接入 `IPipelineBehavior<,>` 后,会退回 `_handler.Handle(...)`
- - 因此 generated request invoker 无法像 stream 路径那样在 pipeline 末端继续保持优先
- - 主线程已在 `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 收口该缺口:
- - request pipeline executor 现在显式复用当前 binding 缓存的 `RequestInvoker`
- - generated request invoker provider 在接入 pipeline 后保持与无 pipeline 路径一致的调用语义
- - 当前 stop decision:
- - 不再继续下一波
- - 原因不是 `50 files` 阈值耗尽;当前 accepted scope 仍然很小
- - 停止原因是当前上下文已接近本轮安全预算,而剩余候选只剩 descriptor / factory / publisher 类小测试,继续扩批收益明显下降
+ - 本轮先按 `$gframework-pr-review` 重新抓取当前分支 PR 真值,确认当前在审 PR 已从旧的 `PR #350(MERGED)` 切换为
+ `PR #351(OPEN)`。
+ - 当前 PR 没有 failed checks;GitHub Test Reporter 显示 `2379 passed / 0 failed`,当前 CI 没有测试阻塞信号。
+ - 最新 head review 只有 `coderabbitai[bot]` 的 `4` 条 open threads,且都落在
+ `ai-plan/public/cqrs-rewrite/todos/` 与 `ai-plan/public/cqrs-rewrite/traces/` 两份恢复文件上。
+ - 这些线程指向的都是有效治理问题,而不是运行时代码缺陷:
+ - active tracking 仍停留在 `PR #350`,与当前 `PR #351` 不一致
+ - active tracking 同时保留了“7 个源码/测试文件写面”与“branch diff 只剩两份 `ai-plan` 文档”两套互相冲突的事实
+ - active trace 重新膨胀为追加式长日志,且包含 Markdown 结构噪音
+ - 当前收口策略是把 `cqrs-rewrite` active tracking / trace 刷回“最小可恢复入口”,并把旧阶段流水继续留在
+ `archive/`,避免在 `boot` 与后续 PR review 中继续暴露失真的恢复入口。
## 当前活跃事实
- 当前分支:`feat/cqrs-optimization`
-- 当前 HEAD / 基线:`origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`
-- 当前 PR:`PR #350(已合并到 origin/main)`
-- 当前写面:
- - `GFramework.Cqrs.Benchmarks/Messaging/NotificationFanOutBenchmarks.cs`
- - `GFramework.Cqrs.Benchmarks/Messaging/RequestBenchmarks.cs`
- - `GFramework.Cqrs.Benchmarks/Messaging/RequestLifetimeBenchmarks.cs`
- - `GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs`
- - `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs`
- - `GFramework.Cqrs.Tests/Cqrs/CqrsGeneratedRequestInvokerProviderTests.cs`
- - `GFramework.Cqrs/Internal/CqrsDispatcher.cs`
+- 当前 HEAD:`e5b173c29abb4ad2faf211bf8f20fd2075c1945c`
+- 当前基线:`origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`
+- 当前 PR:`PR #351(OPEN)`
+- 当前 review 焦点:
- `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md`
- `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
-- 当前基线:
- - `origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`
-- 当前 batch working-tree diff:`7` 个源码 / 测试文件
-- 当前工作面已收口为 docs/test 小切片 + 1 处 request pipeline runtime 修正;没有重新打开 benchmark 工程设计级改造
-- 最近已合并提交:
- - `2dd9435c` `fix(cqrs-benchmarks): 修正Mediator基准运行时配置`
- - `e3532fc2` `feat(cqrs-benchmarks): 补齐request生命周期的Mediator对照`
- - `092946e9` `docs(cqrs-benchmarks): 同步startup基准文档边界`
+- 当前工作面:
+ - 只收口 `cqrs-rewrite` 的 public recovery 文档,不重新打开 CQRS benchmark、runtime 或测试工程写面。
+- 当前 AI review 真值:
+ - CodeRabbit latest review:`CHANGES_REQUESTED`(`2026-05-14T02:21:55Z`)
+ - open threads:`4`
+ - 已本地复核为有效的主题:
+ - PR 锚点过期
+ - active tracking 事实冲突
+ - active trace 结构噪音
+ - active trace 体积过大,应继续瘦身
## 当前风险
-- 后续若再次评估 `StreamLifetimeBenchmarks` 或 request lifetime 的 `Mediator` parity,仍必须采用独立 compile-time config
- 或独立 benchmark 工程,而不是在同一份 source-generated 产物上切换 runtime `ServiceLifetime`。
-- 如果 `feat/cqrs-optimization` 继续承载新的 CQRS 任务而不先分支,public recovery 入口会把“已合并的 PR #350”与下一轮新工作混在一起。
-- 若未来再开 benchmark XML / docs 波次,仍需要主线程先抽样核对代表文件,避免重复接受误报 inventory。
-- 剩余低风险候选主要是 `NotificationPublisher` / invoker descriptor / `CqrsRuntimeFactory` 的单文件测试;它们不是当前上下文预算下的高收益下一波。
+- 若继续沿用 `PR #350` 的恢复入口,后续 `boot` 与 PR triage 会把“已合并阶段”误当作当前审查上下文。
+- 若 active tracking 同时保留历史阶段事实与当前恢复事实,后续恢复会继续遭遇“当前写面”与“当前 branch diff”互相冲突的问题。
+- 若 active trace 再次回到追加式长日志,后续每一轮 PR review 都会优先命中文档治理噪音,而不是当前真正需要验证的 CQRS 代码问题。
## 最近权威验证
-- `git rev-list --left-right --count origin/main...HEAD`
+- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json`
- 结果:通过
- - 备注:在恢复刷新提交前返回 `0 0`,确认 `PR #350` 已完整合入 `origin/main`
-- `git diff --name-only origin/main...HEAD | wc -l`
+ - 备注:确认当前分支对应 `PR #351`,并抓到 `4` 条 CodeRabbit open threads、`0` failed checks
+- `python3 scripts/license-header.py --check --paths ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
- 结果:通过
- - 备注:确认当前 branch diff 始终只覆盖两份 `ai-plan` recovery 文档,没有重新打开 CQRS benchmark 代码写面
-- `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- - 结果:通过,`0 warning / 0 error`
- - 备注:确认 benchmark reader-facing 文档收口后工程仍保持 `Release` 可编译
+ - 备注:本轮改动的 public recovery 文档均保留 Apache-2.0 头
+- `git diff --check`
+ - 结果:通过
+ - 备注:本轮 `ai-plan` 收口未引入 patch 格式或 trailing whitespace 问题
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
- - 备注:确认 request pipeline 复用 generated invoker 的 runtime 修正未引入编译回归
-- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
- - 结果:通过,`Passed: 28, Failed: 0`
- - 备注:确认 generated request invoker 在接入 request pipeline 后仍保持优先,并通过新增回归测试锁定
-- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsDispatcherContextValidationTests"`
- - 结果:通过,`Passed: 7, Failed: 0`
- - 备注:确认 stream 缺失 handler 的失败语义回归与既有上下文校验测试仍全部通过
+ - 备注:满足仓库“完成任务前至少通过一条 build validation”的要求
+- GitHub Test Reporter(来自 `PR #351` 的最新 review 数据)
+ - 结果:通过
+ - 备注:`2379 passed / 0 failed / 38.4s`
+- MegaLinter(来自 `PR #351` 的最新 review 数据)
+ - 结果:`Success with warnings`
+ - 备注:当前高信号问题仍是 `ai-plan` 文档治理收口;未观察到新的 CQRS 运行时代码失败信号
## 下一推荐步骤
-1. 若继续 CQRS 主题的低风险硬化,优先从 `NotificationPublisher`、invoker descriptor、`CqrsRuntimeFactory` 三类单文件测试候选中再挑一条,不要重新打开 benchmark 设计级议题。
-2. 若后续重新打开 `Mediator` 生命周期 parity 工作,优先设计独立 compile-time config / 独立 benchmark 工程,并把该设计单独记录到新的 tracking phase。
-3. 若只是恢复其他 topic,可把当前 `cqrs-rewrite` active 入口视为“本轮已在上下文预算前的自然停点完成”。
+1. 先完成 `ai-plan/public/cqrs-rewrite/**` 的 active 入口瘦身与事实刷新,再重新运行最小验证。
+2. 若文档收口后 PR 仍有 open threads,再次执行 `$gframework-pr-review`,确认是否只剩 stale review 线程。
+3. 只有在文档治理线程清空后,才继续评估是否存在新的 CQRS benchmark / runtime / test 跟进项。
## 活跃文档
@@ -108,5 +91,6 @@ CQRS 迁移与收敛。
## 说明
-- `RP-131` 及之前的长历史验证、阶段流水与旧恢复点说明已迁移到新的 `archive/` 文件,不再继续堆叠在 active 入口。
-- active tracking 现在只保留当前恢复点所需的最小事实、风险、权威验证与下一步,供 `boot` 与后续 PR review 快速恢复。
+- `RP-131` 及更早长历史已保留在 `archive/`,active 入口不再重复堆叠旧阶段流水。
+- `RP-142` 与 `RP-143` 的细节已压缩为当前恢复点结论与 active trace 摘要,避免继续把 active 入口用作追加式日志。
+- 本地再次执行 `$gframework-pr-review` 时,GitHub 仍返回旧 head 上的 `4` 条 open threads;在本轮 `ai-plan` 修复提交推到新 head 前,这些线程仍会继续显示为未解决。
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 83756595..52328f5f 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
@@ -5,34 +5,67 @@ SPDX-License-Identifier: Apache-2.0
# CQRS 重写迁移追踪
+## 当前恢复摘要
+
+- 当前恢复点:`CQRS-REWRITE-RP-144`
+- 当前日期:`2026-05-14`
+- 当前分支:`feat/cqrs-optimization`
+- 当前 PR:`PR #351(OPEN)`
+- 当前目标:
+ - 用 `$gframework-pr-review` 重新对齐当前 PR 真值
+ - 修复 `cqrs-rewrite` active tracking / trace 的过期与膨胀问题
+ - 让 `boot` 与后续 PR triage 回到最小可恢复入口
+- 历史归档入口:
+ - `RP-131` 及之前:`ai-plan/public/cqrs-rewrite/archive/traces/cqrs-rewrite-migration-trace-history-through-rp131.md`
+ - `RP-131` 及之前对应 tracking:`ai-plan/public/cqrs-rewrite/archive/todos/cqrs-rewrite-migration-tracking-history-through-rp131.md`
+
+## 2026-05-14
+
+### 阶段:PR #351 的 active recovery 入口收口(CQRS-REWRITE-RP-144)
+
+- 先执行 `$gframework-pr-review` 抓取当前分支的 GitHub 真值,而不是沿用 active tracking 里过期的 `PR #350` 状态。
+- 当前抓取结果:
+ - 当前 PR:`#351`
+ - 状态:`OPEN`
+ - 最新 reviewed commit:`e5b173c29abb4ad2faf211bf8f20fd2075c1945c`
+ - failed checks:`0`
+ - 测试汇总:`2379 passed / 0 failed`
+ - latest-head open threads:`4`
+- 4 条 open threads 的本地复核结论:
+ - `todos/cqrs-rewrite-migration-tracking.md`
+ - `PR #350(MERGED)` 已不再代表当前审查上下文,必须刷新为 `PR #351(OPEN)`
+ - “当前 batch working-tree diff:7 个源码 / 测试文件”与“branch diff 只覆盖两份 `ai-plan` 文档”互相冲突,不能继续同时保留
+ - `traces/cqrs-rewrite-migration-trace.md`
+ - 出现重复日期标题与多余反引号,属于应当立即清掉的 Markdown 结构噪音
+ - active trace 已重新膨胀成追加式长日志,不适合继续作为默认恢复入口
+- 当前收口动作:
+ - 重写 active tracking,使其只保留 `PR #351` 的当前恢复真值、风险、验证与下一步
+ - 重写 active trace,使其只保留 `RP-144` 摘要与必要的最近阶段记录
+ - 继续把更早阶段细节留在 `archive/`,避免再次扩大 active 入口
+- 本轮本地验证:
+ - `python3 scripts/license-header.py --check --paths ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
+ - 结果:通过
+ - `git diff --check`
+ - 结果:通过
+ - `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+ - 再次执行 `$gframework-pr-review`
+ - 结果:仍显示旧 head 上的 `4` 条 CodeRabbit open threads
+ - 说明:这些线程要等本轮 `ai-plan` 修复提交形成新的 PR head 后,才有机会被 GitHub 标记为 stale 或 resolved
+- 当前下一步:
+ - 提交本轮 `ai-plan` recovery 入口收口
+ - 推送后重新执行 `$gframework-pr-review`,确认当前 open threads 是否随新 head 收敛
+
## 2026-05-13
-### 阶段:允许 subagent 后的低风险多 Agent 收口(CQRS-REWRITE-RP-143)
+### 阶段:request pipeline generated invoker 收口(CQRS-REWRITE-RP-143)
-- 在用户允许 subagent 后,主线程切到小型 multi-agent 协调模式,但仍把 critical path 保留在本地:
- - 主线程负责候选筛选、runtime 缺口判断、最终验证与 `ai-plan`
- - explorer 只做只读盘点
- - worker 只接单文件或窄文件组 ownership
-- explorer 结论汇总:
- - 没有值得继续扩成“实现型 benchmark 波次”的大切片
- - 低风险候选主要是 reader-facing 术语对齐与单文件回归测试
- - `Mediator` 生命周期 parity、notification fan-out 生命周期矩阵、benchmark 工程级拆分仍判定为高风险
-- accepted worker / 主线程 scope:
- - `RequestLifetimeBenchmarks.cs`
- - 主线程修正 2 处 XML 文档缩进异常
- - `NotificationFanOutBenchmarks.cs`
- - `RequestBenchmarks.cs`
- - `StreamingBenchmarks.cs`
- - worker 收口 benchmark reader-facing 术语,build 通过
- - `CqrsDispatcherContextValidationTests.cs`
- - worker 补 stream 缺失 handler 的同步抛错回归,targeted test 通过
- - `CqrsGeneratedRequestInvokerProviderTests.cs`
- - worker 补 generated request + pipeline 对称测试
- - 首次运行失败,暴露 request pipeline 路径在接入 behavior 后退回 `_handler.Handle(...)`
- - `CqrsDispatcher.cs`
- - 主线程将 request pipeline 末端改为继续复用当前 binding 的 `RequestInvoker`
- - 使 generated request invoker provider 在 pipeline 存在时保持与无 pipeline 路径一致
-- 本轮权威验证:
+- 在用户允许 subagent 后,本轮只接受 docs/test 小切片与 1 处 request pipeline runtime 修正,未重新打开 benchmark 工程设计级改造。
+- 本轮关键结论:
+ - `CqrsGeneratedRequestInvokerProviderTests.cs` 的新增对称测试证明 request 路径在接入 `IPipelineBehavior<,>` 后,会退回 `_handler.Handle(...)`
+ - `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已把 request pipeline 末端改为继续复用当前 binding 的 `RequestInvoker`
+ - generated request invoker provider 在 pipeline 存在时恢复与无 pipeline 路径一致的调用语义
+- 当轮验证:
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
@@ -41,440 +74,5 @@ SPDX-License-Identifier: Apache-2.0
- 结果:通过,`Passed: 7, Failed: 0`
- `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
-- 当前 stop decision:
- - 不再继续下一波
- - 原因不是 branch-size;当前变更仍远低于 `$gframework-batch-boot 50``
- - 停止原因是主线程上下文已接近本轮安全预算,而剩余候选只剩收益较低的单文件测试硬化
-- 当前下一步:
- - 更新 `ai-plan/public/cqrs-rewrite/**`
- - 运行 license-header / `git diff --check`
- - 提交本轮多 Agent 小波次收口
-
-## 2026-05-13
-
-### 阶段:PR #350 合并后的 recovery 入口刷新(CQRS-REWRITE-RP-142)
-
-- 继续按 `$gframework-batch-boot 50` 恢复当前 topic,但启动时先核对分支真值,而不是沿用 active tracking 中的旧 PR 状态。
-- 本轮基线按 skill 规则固定为 `origin/main @ 4837aa2a (2026-05-12 20:37:56 +0800)`。
-- 本地核对结果:
- - `feat/cqrs-optimization` 当前 `HEAD` 与 `origin/main` 指向同一合并提交 `4837aa2a`
- - `git rev-list --left-right --count origin/main...HEAD` 返回 `0 0`
- - `git diff --name-only origin/main...HEAD | wc -l` 返回 `0`
- - 当前工作树干净,不存在可继续扩批的 CQRS benchmark / test 写面
-- 批处理候选重判:
- - 不再把当前任务视为新的 benchmark 批处理波次
- - 原因不是 `50 files` 阈值耗尽,而是语义边界已经完成,继续在已合并 topic 上扩批只会制造恢复入口噪音
-- 本轮 accepted scope:
- - `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md`
- - `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
-- 本轮主线程收口目标:
- - 把 active tracking 从 `PR #350(OPEN)` 刷新为 `PR #350(MERGED)`
- - 把 branch metric 从过期的 `14 files` 刷新为 `0 files / 0 lines`
- - 把“继续回 GitHub resolve thread”替换为“下一轮 CQRS 工作应从新分支 / 新 recovery point 开始”
-- recovery 刷新提交落地后,当前 branch diff 仍只覆盖 `cqrs-rewrite` 的 public tracking / trace 文档,没有重新打开 benchmark 代码写面
-- 当前下一步:
- - 若后续继续 CQRS 主题,实现工作应从新的 topic branch 与新的 recovery point 开始
- - 若只是恢复其他 topic,可把当前 `cqrs-rewrite` active 入口视为已停在自然边界
-
-## 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)`。
-- 本轮启动时重新测得当前已提交 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)`。
-- 本轮启动时重新测得当前已提交 branch diff 为 `14 files`,仍远低于 `50 files` 阈值;继续与否的主停止信号仍是
- context-budget / reviewability。
-- 本轮主线程先做只读盘点与抽样核对:
- - README 一致性 explorer 结论成立:`GFramework.Cqrs.Benchmarks/README.md` 对 startup coverage / 边界的表述仍可更精确
- - benchmark XML 缺口 explorer 结论未直接接受;主线程抽样检查 `NotificationBenchmarks.cs`、
- `RequestBenchmarks.cs`、`StreamingBenchmarks.cs`、`NotificationStartupBenchmarks.cs` 后确认,
- 其 class / benchmark 方法的 `` 与 `` 实际已存在,不能继续按“14 个门面文件普遍缺 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` startup 路径是
- “单 handler 最小宿主 + 手工注册”模型,不外推到程序集扫描、完整注册协调器、fan-out 或发布策略变体
-- worker 回传验收结论:
- - 改动文件未越出 ownership 边界
- - README diff 与代码事实一致,且未引入无法从当前 benchmark 实现验证的表述
-- 当前 stop decision:
- - 不继续开启新的 XML 文档波次
- - 原因不是 branch-size 阈值耗尽;当前分支仍只有 `14 files`
- - 停止原因是候选清晰度下降:继续追逐 explorer 误报会降低 reviewability,并无谓增加当前上下文负担
-- 当前下一步:
- - 主线程更新 `ai-plan/public/cqrs-rewrite/**`
- - 串行运行 benchmark 工程 build、license-header 与 `git diff --check`
- - 提交 README 与 `ai-plan` 收尾
-
-### 阶段:benchmark XML 契约第 2 波收口(CQRS-REWRITE-RP-138)
-
-- 延续 `$gframework-batch-boot 50`,基线保持为 `origin/main @ 2b2bec65 (2026-05-12 11:49:39 +0800)`。
-- 第 2 波启动前,当前分支相对基线的已提交 branch diff 为 `5 files / 177 lines`,明显低于 `50 files` 阈值;本轮继续与否的主停止信号仍是 context-budget / reviewability。
-- 主线程本地盘点 `GFramework.Cqrs.Benchmarks/Messaging/*.cs` 的公开 `[Benchmark]` 方法后,确认当前仍有一批与既有收口模式一致的 XML `` 缺口:
- - `StreamingBenchmarks.Stream_GFrameworkCqrs()`
- - `NotificationBenchmarks` 的 3 个公开 benchmark 方法
- - `NotificationFanOutBenchmarks` 的 5 个公开 benchmark 方法
- - `StreamInvokerBenchmarks` 的 4 个公开 benchmark 方法
- - `StreamLifetimeBenchmarks` 的 4 个公开 benchmark 方法
-- 本波 accepted ownership:
- - 主线程
- - `GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs`
- - `GFramework.Cqrs.Benchmarks/Messaging/NotificationBenchmarks.cs`
- - `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md`
- - `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
- - worker
- - `StreamLifetimeBenchmarks.cs`
- - `StreamInvokerBenchmarks.cs`
- - `NotificationFanOutBenchmarks.cs`
-- worker 回传验收结论:
- - `StreamLifetimeBenchmarks.cs`
- - 只补 `Stream_Baseline`、`Stream_GFrameworkReflection`、`Stream_GFrameworkGenerated`、`Stream_MediatR` 的 ``
- - worker 自报 `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release` 通过
- - `StreamInvokerBenchmarks.cs`
- - 只补 4 个公开 benchmark 方法的 ``
- - worker 自报同一条 benchmark 工程 build 通过
- - `NotificationFanOutBenchmarks.cs`
- - 只补 5 个公开 benchmark 方法的 ``
- - worker 自报 build 遇到 `CS2012`:`obj/Release/net10.0/GFramework.Cqrs.Benchmarks.dll` 被并发进程占用;该失败被判定为并发构建噪音,而不是代码语义问题
-- 主线程局部实施:
- - `StreamingBenchmarks.cs`
- - 为 `Stream_GFrameworkCqrs()` 补 ``
- - `NotificationBenchmarks.cs`
- - 为 `PublishNotification_GFrameworkCqrs()`、`PublishNotification_MediatR()`、`PublishNotification_Mediator()` 补 ``
-- 当前下一步:
- - 主线程串行执行 benchmark 工程 Release build,消除 worker 并发写 `obj/Release` 带来的验证噪音
- - 若串行验证通过,决定是在当前自然停点提交收尾,还是继续 request 侧 XML 契约的下一波低风险批处理
-
-### 阶段:request benchmark XML 契约第 3 波收口后停在自然边界(CQRS-REWRITE-RP-138)
-
-- 第 2 波串行验证通过后,继续用 3 个 worker 扩展 request 系 benchmark 的同类 `` 收口:
- - `RequestStartupBenchmarks.cs`
- - `RequestBenchmarks.cs` + `RequestPipelineBenchmarks.cs`
- - `RequestInvokerBenchmarks.cs` + `RequestLifetimeBenchmarks.cs`
-- worker 回传与 acceptance:
- - `RequestStartupBenchmarks.cs`
- - 只补公开 benchmark 方法缺失的 ``
- - worker 自报 `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release` 通过
- - `RequestBenchmarks.cs` + `RequestPipelineBenchmarks.cs`
- - 只补公开 benchmark 方法缺失的 ``
- - worker 自报 build 通过,并已提交:`555c7c07 docs(cqrs-benchmarks): 补齐 request benchmark 返回值文档`
- - `RequestInvokerBenchmarks.cs` + `RequestLifetimeBenchmarks.cs`
- - 只补公开 benchmark 方法缺失的 ``
- - worker 自报 build 通过,并已提交:`ab422b05 docs(cqrs-benchmarks): 补齐 request benchmark 返回值注释`
-- 第 3 波后主线程 stop decision:
- - 不再开启第 4 波 XML 契约批处理
- - 原因不是 branch-size 阈值耗尽;当前分支相对 `origin/main` 仍只有 `9 files / 143 lines`
- - 停止原因是 context-budget / reviewability:剩余候选已不比当前波次更低风险,继续机械扩批收益下降
-- 当前下一步:
- - 只做主线程未提交面的串行验证与收尾提交
- - 将干净工作树作为下一次 `boot` 的默认恢复目标
-
-### 阶段:stream startup parity 与文档收尾(CQRS-REWRITE-RP-137)
-
-- 按 `$gframework-batch-boot 50` 恢复后,先重新执行 `$gframework-pr-review`。
-- 当前 GitHub 事实:
- - `PR #349` 已关闭并合并到 `origin/main`
- - 基线切换为 `origin/main @ 2b2bec65 (2026-05-12 11:49:39 +0800)`
- - 当前分支相对新基线的已提交 diff 初始为 `0 files / 0 lines`
-- latest-head open thread 本地复核:
- - stale
- - `StreamPipelineBenchmarks.Stream_Baseline` 的 `` 已存在
- - `CqrsNotificationPublisherTests` 的 fallback 缓存安全网已收口
- - trace 的当前 PR / 下一步已同步到 `PR #349`
- - valid
- - `StreamingBenchmarks.Stream_MediatR()` 仍缺 `` XML 文档
-- 第 1 波 accepted delegated scope:
- - `StreamingBenchmarks.cs`
- - worker 补 `Stream_Baseline()` 与 `Stream_MediatR()` 的 `` XML 契约
- - 主线程验收时确认其中 `Stream_Baseline()` 属于额外收口,不是 latest-head 必修项
- - `StreamStartupBenchmarks.cs`
- - worker 在单文件 ownership 内补 `GeneratedMediator` 宿主字段、setup/cleanup、`Initialization_Mediator()`、`ColdStart_Mediator()`
- - 同文件把 `BenchmarkStreamRequest` / `BenchmarkStreamHandler` 扩成同时支持 `Mediator` stream 合同
- - worker 自主完成并提交:`f346110a feat(cqrs-benchmarks): 补齐 stream startup 的 Mediator 对照路径`
- - `GFramework.Cqrs.Benchmarks/README.md`
- - worker 只收口 `StreamStartupBenchmarks` coverage 与当前 gap 描述,不假设 `StreamLifetimeBenchmarks` 已补 parity
-- 主线程验收结论:
- - `StreamLifetimeBenchmarks` 的 `Mediator` parity 被判定为 hard slice,需要 `BenchmarkHostFactory` 与 compile-time lifetime 配套,不再继续作为本 turn 的低风险并行切片
- - 当前自然停点应落在:
- - 已提交的 `StreamStartupBenchmarks` parity
- - 未提交但已验收的 `StreamingBenchmarks.cs` / `README.md` 收尾
- - `ai-plan` 同步到新基线与新恢复点
-- 本轮权威验证里程碑:
- - `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- - 结果:通过,`0 warning / 0 error`
- - `python3 scripts/license-header.py --check`
- - 结果:通过
- - `git diff --check`
- - 待当前未提交收尾切片与 `ai-plan` 一并提交前再次运行
-- 当前下一步:
- - 提交 `StreamingBenchmarks.cs`、`GFramework.Cqrs.Benchmarks/README.md` 与 `ai-plan/public/cqrs-rewrite/**` 收尾
- - 如需继续 benchmark 波次,优先做 `StreamStartupBenchmarks` 的最小 smoke run,而不是直接展开 `StreamLifetimeBenchmarks`
-
-### 阶段:PR #349 latest-head review 收口(CQRS-REWRITE-RP-136)
-
-- 重新执行 `$gframework-pr-review`,按 GitHub 当前分支状态确认 `feat/cqrs-optimization` 在 `2026-05-12` 对应的是 `PR #349`,不再沿用 active tracking 中的 `PR #348` 锚点。
-- 本轮 latest-head open AI thread 复核结论:
- - `StreamPipelineBenchmarks` 的 `Stream_Baseline`、`Stream_GFrameworkCqrs`、`Stream_MediatR` 缺少 `` XML 契约,接受修复
- - `StreamingBenchmarks.Stream_Mediator` 缺少 `` XML 契约,接受修复
- - `CqrsNotificationPublisherTests` 的 fallback publisher 缓存回归测试用“第二次解析返回另一个 publisher”充当安全网,和断言消息表达不一致,接受收口为“首次后任何再次解析都直接失败”
- - active tracking / trace 的当前 PR 锚点与下一步入口仍停留在 `PR #348`,接受同步到 `PR #349`
-- 本轮主线程实施:
- - `StreamPipelineBenchmarks`
- - 为 3 个公开 benchmark 方法补齐 `` XML 文档
- - `StreamingBenchmarks`
- - 为 `Stream_Mediator()` 补齐 `` XML 文档
- - `CqrsNotificationPublisherTests`
- - 把 fallback publisher 缓存回归测试改为“首次返回空数组,后续任何再次解析立即抛 `AssertionException`”,避免测试安全网与失败消息自相矛盾
- - `ai-plan/public/cqrs-rewrite/**`
- - 将 active tracking / trace 的当前 PR 锚点与下一步入口同步到 `PR #349`
-- 本轮权威验证:
- - `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- - 结果:通过,`0 warning / 0 error`
- - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsNotificationPublisherTests"`
- - 结果:通过,`Passed: 9, Failed: 0`
- - `python3 scripts/license-header.py --check --paths GFramework.Cqrs.Benchmarks/Messaging/StreamPipelineBenchmarks.cs GFramework.Cqrs.Benchmarks/Messaging/StreamingBenchmarks.cs GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md`
- - 结果:通过
-
-### 阶段:多波 batch 继续收口(CQRS-REWRITE-RP-135)
-
-- 按 `$gframework-batch-boot 50` 恢复当前 topic,并把基线固定为
- `origin/main @ ef4d3d5d (2026-05-11 17:33:43 +0800)`。
-- 启动时确认当前工作树干净,branch diff 为 `0 files / 0 lines`;旧 tracking 中“未提交收尾切片”已不再反映真实仓库状态。
-- 第 1 波 accepted delegated scope:
- - `CqrsRegistrationServiceTests`
- - 补空输入不触发 registrar、忽略空项后按稳定程序集键排序并去重、跨调用跳过已注册键时继续处理剩余新程序集
- - `CqrsHandlerRegistrarTests` + `CqrsHandlerRegistrarFallbackFailureTests`
- - 补 abstract registry 与缺少无参构造器 registry 在程序集级回退路径和 direct activation 入口的告警 / 抛错覆盖
- - `StreamPipelineBenchmarks` + `GFramework.Cqrs.Benchmarks/README.md`
- - 新增 `0 / 1 / 4` 个 stream pipeline 行为与 `FirstItem / DrainAll` 观测矩阵
- - README 补齐 stream pipeline coverage、运行示例与 gap 说明
-- 第 2 波 accepted delegated scope:
- - `CqrsNotificationPublisherTests`
- - 补“容器未注册 publisher 时回退到 `SequentialNotificationPublisher`,且首次解析后缓存结果”回归
- - `StreamingBenchmarks` + `GFramework.Cqrs.Benchmarks/README.md`
- - 补 steady-state stream 的 `Mediator` 对照
- - README 将 stream steady-state gap 收口为“lifetime / startup 仍缺 `Mediator` parity”
-- 主线程验收与修正:
- - 审核 5 个 worker 提交均未越出 ownership 边界
- - 在 `StreamPipelineBenchmarks.cs` 修掉 `git diff --check` 报出的 1 处 trailing whitespace
- - 更新 active tracking / trace 到当前 branch 事实,避免下次 `boot` 继续落到过期恢复点
-- 本轮权威验证:
- - `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsRegistrationServiceTests|FullyQualifiedName~CqrsHandlerRegistrarTests|FullyQualifiedName~CqrsHandlerRegistrarFallbackFailureTests|FullyQualifiedName~CqrsNotificationPublisherTests"`
- - `python3 scripts/license-header.py --check --paths ...`
- - `git diff --check origin/main...HEAD`
-- 当前停点判断:
- - 当前 branch diff 约为 `9 files / 1111 lines`
- - 明显低于 `50 files` 阈值
- - 本轮停止信号来自 `context-budget / reviewability`,不是文件预算耗尽
-- 当前下一步:
- - 先按需要运行 `$gframework-pr-review`,确认 `PR #349` latest-head open thread 是否已随当前修复提交收敛
- - 若继续扩 benchmark,优先补 `StreamLifetimeBenchmarks` 或 `StreamStartupBenchmarks` 的单文件 `Mediator` parity
- - 若切回文档收尾,把 `GFramework.Cqrs/README.md`、`docs/zh-CN/core/command.md`、`docs/zh-CN/core/query.md` 单独作为 docs-only 下一波
-
-## 2026-05-11
-
-### 阶段:PR #348 latest-head review 再收口(CQRS-REWRITE-RP-134)
-
-- 重新执行 `$gframework-pr-review` 抓取当前分支 `feat/cqrs-optimization` 对应的 `PR #348`
-- 本轮 latest-head open AI thread 复核结论:
- - `NotificationLifetimeBenchmarks.HandlerLifetime` 补 `[GenerateEnumExtensions]` 仍判定为泛化误报
- - 仓库没有“产品/benchmark 枚举默认都启用该特性”的现行约定
- - benchmark 项目也未接入 `GFramework.Core.SourceGenerators.Abstractions`,不应为局部对照枚举平白扩大 generator 依赖面
- - `NotificationLifetimeBenchmarks` 的 `_scopedContainer` 释放缺口与公开 benchmark API 的 XML 契约缺口仍成立,接受修复
- - `CqrsHandlerRegistrar` 中 generated descriptor 的“先去重后校验”缺陷仍成立,接受修复并补测试
- - `CqrsHandlerRegistrar` 对 `MethodInfo` 使用 `ReferenceEquals` 的过严比较仍成立,接受修复
- - active tracking / trace 的当前 PR 锚点仍停留在 `PR #347`,接受同步到 `PR #348`
-- 本轮主线程实施:
- - `NotificationLifetimeBenchmarks`
- - `Cleanup()` 将 `_scopedContainer` 一并交给 `BenchmarkCleanupHelper.DisposeAll(...)`
- - 为公开 benchmark 方法与公开 handler 方法补齐缺失的 `` / `` XML 契约
- - `CqrsHandlerRegistrar`
- - request / stream generated descriptor 预热路径改为“先 `TryValidate...`,后写入 `registeredKeys`”
- - descriptor 对齐判断从 `ReferenceEquals(resolvedDescriptor.InvokerMethod, ...)` 调整为 `resolvedDescriptor.InvokerMethod.Equals(...)`
- - `CqrsGeneratedRequestInvokerProviderTests`
- - 新增 request / stream 两个回归用例,锁定“首条同键 descriptor 无效、后条有效时,仍应接受后条有效 generated descriptor”
- - `ai-plan/public/cqrs-rewrite/**`
- - 将 active tracking / trace 的当前 PR 锚点同步到 `PR #348`
-
-### 阶段:PR #347 latest-head review 收口(CQRS-REWRITE-RP-132)
-
-- 使用 `$gframework-pr-review` 重新抓取当前分支 `feat/cqrs-optimization` 对应的 `PR #347`
-- 抓取结果显示当前 latest-head 仍有 `CodeRabbit 6` 与 `Greptile 2` open thread,但本地复核后只接受以下仍成立的结论:
- - `Program.cs` 只在命令行 `--artifacts-suffix` 下重启隔离宿主,未覆盖环境变量触发的隔离路径
- - `Program.cs` 缺少“目标隔离宿主目录位于当前宿主输出目录内”的防守式校验
- - `RequestLifetimeBenchmarks` / `StreamLifetimeBenchmarks` 的 `Scoped` 路径每次 benchmark 调用都会新建 runtime,污染生命周期矩阵公平性
- - `ScopedBenchmarkContainer` 的公共/关键成员 XML 契约说明不完整
- - active tracking / trace 已经偏离“快速恢复入口”,需要归档瘦身
-- 本轮实现选择:
- - `Program.cs`
- - 把“是否需要重启隔离宿主”统一收敛到 `ArtifactsPath != null`
- - 为隔离宿主目录补充“不得等于或嵌套在当前宿主输出目录内”的校验
- - `BenchmarkHostFactory.cs`
- - scoped request / stream helper 改为复用单个 runtime,只在调用边界进入和释放 scope
- - `ScopedBenchmarkContainer.cs`
- - 从“构造时绑定单次 scope”改为“可重复进入的作用域适配器 + `ScopeLease`”
- - 补齐只读适配器与作用域租约的 XML 合同说明
- - `RequestLifetimeBenchmarks.cs`
- - `Scoped` 路径改为 `GlobalSetup` 时创建单个 scoped runtime
- - `StreamLifetimeBenchmarks.cs`
- - `Scoped` reflection / generated 路径改为 `GlobalSetup` 时各自创建单个 scoped runtime
- - 按语义阶段拆分 `Setup()`,同时清掉 `MA0051`
- - `README.md`
- - 把 `RequestLifetimeBenchmarks` 的 scoped 语义更新为“复用 runtime,仅每次创建真实 scope”
- - `ai-plan/public/cqrs-rewrite/**`
- - 将旧 active tracking / trace 复制归档到
- `archive/todos/cqrs-rewrite-migration-tracking-history-through-rp131.md`
- 与
- `archive/traces/cqrs-rewrite-migration-trace-history-through-rp131.md`
- - 重写 active tracking / trace,只保留当前恢复点、关键结论、风险与验证
-
-### 本轮验证
-
-- `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 pr347-req-scoped --filter "*RequestLifetimeBenchmarks.SendRequest_GFrameworkCqrs*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
- - 结果:通过
- - 备注:独立宿主目录位于 `BenchmarkDotNet.Artifacts/pr347-req-scoped/host/...`
- - 结果摘要:`Singleton 52.69 ns / 32 B`、`Transient 57.88 ns / 56 B`、`Scoped 144.72 ns / 368 B`
-- `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix pr347-stream-scoped --filter "*StreamLifetimeBenchmarks.Stream_GFramework*" --job short --warmupCount 1 --iterationCount 1 --launchCount 1`
- - 结果:通过
- - 备注:独立宿主目录位于 `BenchmarkDotNet.Artifacts/pr347-stream-scoped/host/...`
- - 结果摘要:
- - `Singleton + FirstItem`:reflection `108.7 ns / 216 B`,generated `110.1 ns / 216 B`
- - `Scoped + FirstItem`:reflection `266.7 ns / 792 B`,generated `267.0 ns / 792 B`
- - `Scoped + DrainAll`:reflection `331.6 ns / 856 B`,generated `332.2 ns / 856 B`
-
-### 当前结论
-
-- `Program.cs` 已不再依赖“命令行后缀”这一单一来源决定宿主隔离,环境变量生效路径也会进入隔离宿主。
-- scoped benchmark 的公平性问题已经实质收口:runtime 构造成本不再按每次调用重复计入生命周期矩阵。
-- active tracking / trace 已恢复到可供 `boot` 和后续 PR review 快速进入的体量;历史细节仍可通过新的 archive 文件追溯。
-
-### 当前下一步
-
-- 推送本轮变更后,重新运行 `$gframework-pr-review`,确认 `PR #347` 的 latest-head open thread 是否已随着新 head 收敛。
-
-### 阶段:多波 batch 收口与 benchmark / docs 扩面(CQRS-REWRITE-RP-133)
-
-- 按 `$gframework-batch-boot` 启动多波 non-conflicting subagent,基线固定为
- `origin/main @ 3b2e6899d5ffdcfb634b28f3846f57528fbf9196 (2026-05-11T12:25:00+08:00)`。
-- 启动前分支累计 diff 为 `0 files / 0 lines`;自然停点时累计 branch diff 约为 `12 files`。
-- 主线程把 stop decision 明确交给 `reviewability / context-budget`,没有在仍有文件预算时继续机械追到 `50 files`。
-- 本轮 accepted delegated scope:
- - runtime / tests
- - `CqrsNotificationPublisherTests`:补“多 publisher 报错”与“publisher 缓存复用”回归
- - `CqrsGeneratedRequestInvokerProviderTests` + `CqrsHandlerRegistrar`:补 generated descriptor 坏元数据、异常枚举、重复 pair 回退契约
- - `CqrsDispatcherCacheTests`:补 request / stream pipeline presence、executor cache 与上下文重新注入组合分支
- - `CqrsRegistrationServiceTests`:补稳定程序集键 fallback 到 `AssemblyName.Name` / `ToString()` 的回归
- - benchmarks
- - `RequestStartupBenchmarks`:补 `Mediator` startup 对照
- - `StreamStartupBenchmarks`
- - `NotificationStartupBenchmarks`
- - `NotificationLifetimeBenchmarks`
- - `GFramework.Cqrs.Benchmarks/README.md`:收口当前 coverage / gap / smoke 解释边界
- - docs / recovery
- - `GFramework.Cqrs/README.md`
- - `docs/zh-CN/core/cqrs.md`
- - `docs/zh-CN/source-generators/cqrs-handler-registry-generator.md`
- - `docs/zh-CN/core/command.md`
- - `docs/zh-CN/core/query.md`
- - `ai-plan/public/cqrs-rewrite/archive/**` 顶部导航与跳转约定
-- 本轮权威验证:
- - `dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release`
- - `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
- - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release`
- - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsRegistrationServiceTests"`
- - `dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- --artifacts-suffix notif-lifetime --filter "*NotificationLifetimeBenchmarks*"`
-- 本轮 benchmark 结果摘要:
- - `RequestStartupBenchmarks`
- - `ColdStart_GFrameworkCqrs 61.648 us / 25336 B`
- - `ColdStart_Mediator 110.867 us / 57872 B`
- - `ColdStart_MediatR 679.103 us / 606256 B`
- - `StreamStartupBenchmarks`
- - `ColdStart_GFrameworkReflection 71.13 us / 25504 B`
- - `ColdStart_GFrameworkGenerated 82.12 us / 28280 B`
- - `ColdStart_MediatR 933.87 us / 678992 B`
- - `NotificationStartupBenchmarks`
- - `ColdStart_GFrameworkCqrs 85.09 us / 24752 B`
- - `ColdStart_Mediator 136.08 us / 62512 B`
- - `ColdStart_MediatR 1.379 ms / 719056 B`
- - `NotificationLifetimeBenchmarks`
- - `Singleton`:`GFramework 295.48 ns / 360 B`,`MediatR 77.99 ns / 288 B`
- - `Scoped`:`GFramework 410.92 ns / 640 B`,`MediatR 213.49 ns / 632 B`
- - `Transient`:`GFramework 311.21 ns / 416 B`,`MediatR 74.36 ns / 288 B`
-- 当前收尾判断:
- - branch diff 仍远低于 `50` 文件阈值,但 active 未提交面与 benchmark 运行输出已经足够构成自然 stop boundary
- - 下一步不继续扩 batch,先提交当前收尾切片并回到干净工作树,再按 PR review 结果决定后续波次
+- 历史说明:
+ - `RP-143` 的完整执行流水不再保留在 active trace;若后续需要 worker 边界或更细的阶段材料,应把该阶段补入 archive,而不是继续膨胀 active 入口。