GFramework/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
gewuyou 255a6a152e fix(cqrs): 收敛 PR 304 review 跟进
- 修复 CqrsDispatcher 的 pipeline invoker 重复创建,并补齐缓存线程模型文档

- 优化 CQRS 与 generator 回归测试的并发保护和稳定语义断言

- 更新 cqrs-rewrite 跟踪与 trace,记录 RP-062 的 PR review follow-up 验证结果
2026-04-30 07:43:42 +08:00

354 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CQRS 重写迁移追踪
## 2026-04-30
### 阶段PR #304 review follow-up 收敛CQRS-REWRITE-RP-062
- 本轮使用 `$gframework-pr-review` 重新抓取当前分支 PR
- 当前分支 `feat/cqrs-optimization` 对应 `PR #304`
- latest review 信号主要由 `7` 条 CodeRabbit nitpick 与 `2` 条 Greptile open threads 组成
- MegaLinter 仍只给出 `dotnet-format``Restore operation failed`未附带当前仍成立的文件级格式问题CTRF 汇总为 `2203/2203` passed
- 本地复核后接受并收敛的 review follow-up
- `GFramework.Cqrs/Internal/CqrsDispatcher.cs`
-`_pipelineExecutors``RequestPipelineInvocation.GetContinuation(...)` 补齐线程模型与失败模式说明
- 将 request pipeline invoker 从“按 `behaviorCount` 重复创建”收敛为“binding 内创建一次、executor 缓存复用”
- `GFramework.Cqrs.Tests/Cqrs/*.cs`
-`DispatcherPipelineContextRefreshState``DispatcherNotificationContextRefreshState``DispatcherStreamContextRefreshState``DispatcherPipelineOrderState` 切换为 `System.Threading.Lock` 保护的共享状态
- 将 pipeline 顺序记录从公开可变 `List<string>` 收敛为 `Record(...)` + 快照只读访问
-`CqrsDispatcherCacheTests` 添加 `[NonParallelizable]`,并补齐反射辅助方法的 XML `param` / `returns`
-`CqrsRegistrationServiceTests` 的 debug 日志断言改为锁定语义片段而非整句文本
-`CqrsHandlerRegistrarFallbackFailureTests` 的缓存字段诊断改为显式指出 `CqrsHandlerRegistrar` 耦合点
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs`
- 将 precise runtime type lookup 的数组 / 外部泛型回归断言从局部变量名绑定改为稳定的语义片段断言
- 验证过程与结果:
- 首次把多个 `dotnet` restore / test 并发跑在同一 worktree 时,`GFramework.Cqrs.Tests` 出现 `*.nuget.g.props already exists` 竞争;该失败属于本地并发 restore 冲突,不代表代码问题
- 串行重跑后确认:
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests|FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsRegistrationServiceTests|FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarFallbackFailureTests"`
- 在第一次串行测试中暴露 `MA0158 Use System.Threading.Lock` warning 后,已同轮切换同步原语并准备重跑无 warning 验证
- 结果:
- 本轮把仍成立的 PR review 评论全部收敛到本地代码或测试基础设施
- 下一步应以“重跑无 warning 验证 + 提交本轮 follow-up”为恢复入口而不是继续扩写新的 CQRS 优化切片
## 2026-04-29
### 阶段registrar fallback 失败分支回归CQRS-REWRITE-RP-061
- 本轮继续按 `gframework-batch-boot 50` 的并行约束,把一个与主线程写集独立的新测试文件交给 worker
- delegated scope`GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarFallbackFailureTests.cs`
- delegated objective锁定 registrar 在 fallback 元数据失效时的 warning 语义,而不扩张到 runtime 实现修改
- 主线程接受结果前的复核结论:
- 该文件只复用现有 generated-registry 测试替身与捕获型日志工厂,不修改 `CqrsHandlerRegistrarTests.cs` 与生产代码
- 三个用例分别覆盖 named fallback 无法解析、named fallback 解析抛异常、direct fallback 类型跨程序集三条失败分支
- 主线程已复核并重新执行定向验证:
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarFallbackFailureTests"`
- `3/3` passed
- 结果:
- 当前 registrar 仍保持“跳过无效 fallback 条目 + 记录 warning”的既有语义
- 若连同当前工作区一起计算,当前分支相对 `origin/main` 的累计 diff 将达到 `32 files`
### 阶段dispatcher 上下文前置条件失败语义回归CQRS-REWRITE-RP-060
- 延续 `gframework-batch-boot 50``Phase 8` 主线,本轮选择一个新的单文件测试切片:锁定默认 dispatcher 对“仅实现 `ICqrsContext`、但未实现 `IArchitectureContext` 的上下文”会如何失败
- 主线程先复核当前公开契约与实现后确认:
- `GFramework.Cqrs.Abstractions.Cqrs.ICqrsRuntime` 的 XML 文档已经把这类失败语义写成公开契约
- `CqrsDispatcher.PrepareHandler(...)` 当前正是唯一的上下文前置条件检查点,因此本轮最稳妥的切片仍是测试补强,而不是继续改 runtime
- 已完成的测试补强:
- 新增 `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs`
- 通过 `CqrsRuntimeFactory.CreateRuntime(...)` + `Mock<IIocContainer>` 构造最小 runtime分别锁定 request、notification、stream 三条路径的失败语义
- 三个测试都只在需要上下文注入的 handler 已解析出来时触发,避免把“找不到 handler”与“上下文不满足注入前置条件”混淆成同一种异常
- 定向验证已通过:
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherContextValidationTests"`
- `3/3` passed
- 结果:
- 本轮只补测试,不改 `GFramework.Cqrs/Internal/CqrsDispatcher.cs`
- 若连同当前工作区一起计算,当前分支相对 `origin/main` 的累计 diff 将达到 `31 files`
### 阶段notification / stream binding 上下文刷新回归CQRS-REWRITE-RP-059
- 延续 `gframework-batch-boot 50``Phase 8` 主线,本轮继续沿着上一批 dispatcher cached executor 上下文回归往外扩一圈,但只覆盖 notification / stream 两条非 request 路径
- 主线程先复核 `CqrsDispatcher` 当前实现后确认:
- `PublishAsync(...)``CreateStream(...)` 都会在命中缓存 binding 后重新解析 handler并在调用前执行 `PrepareHandler(...)`
- 因此本轮最稳妥的切片仍是测试补强,而不是继续改 runtime
- 已完成的测试补强:
-`GFramework.Cqrs.Tests/Cqrs/` 新增 `DispatcherNotificationContextRefresh*``DispatcherStreamContextRefresh*` 测试替身,记录重复分发时 handler 实例身份与 `ArchitectureContext`
- `CqrsDispatcherCacheTests` 新增 `Dispatcher_Should_Reinject_Current_Context_When_Reusing_Cached_Notification_Dispatch_Binding`
- `CqrsDispatcherCacheTests` 新增 `Dispatcher_Should_Reinject_Current_Context_When_Reusing_Cached_Stream_Dispatch_Binding`
- 定向验证已通过:
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
- `7/7` passed
- 结果:
- 本轮未暴露新的 runtime 实现缺口,因此没有改动 `GFramework.Cqrs/Internal/CqrsDispatcher.cs`
- 若连同当前工作区一起计算,当前分支相对 `origin/main` 的累计 diff 将达到 `29 files`,继续低于 `gframework-batch-boot 50` 的主要 stop condition
### 阶段delegated fallback attribute 合同测试CQRS-REWRITE-RP-058
- 本轮按 `gframework-batch-boot 50` 的并行约束,把一个与主线程写集完全独立的叶子级测试文件交给 worker
- delegated scope`GFramework.Cqrs.Tests/Cqrs/CqrsReflectionFallbackAttributeTests.cs`
- delegated objective锁定 `CqrsReflectionFallbackAttribute` 的公开归一化合同,而不扩张到 registrar / generator / dispatcher 实现
- 已接受的 worker 结果:
- 新增 `CqrsReflectionFallbackAttributeTests`,覆盖空 marker、字符串 fallback 名称的去空/去重/排序、直接 `Type` fallback 的去空/去重/排序,以及两个重载对空参数数组的防御行为
- worker 已独立验证 `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsReflectionFallbackAttributeTests"`,结果为 `5/5` passed
- 该叶子级测试批次已作为独立提交落地:`86a24e00` `test(cqrs): 新增 ReflectionFallbackAttribute 合同测试`
### 阶段cached executor 上下文刷新回归CQRS-REWRITE-RP-057
- 延续 `gframework-batch-boot 50``Phase 8` 主线,本轮只处理一个窄写集测试批次:为 cached request pipeline executor 增加“重复分发仍重新注入上下文”的回归
- 先复核上一轮 request pipeline executor 形状缓存实现与测试边界后确认:
- 当前 runtime 只允许本轮写集落在 `GFramework.Cqrs.Tests/Cqrs/`,除非测试直接打出 `CqrsDispatcher` 的真实缺陷
- 目标是锁定 executor 缓存不会跨分发保留旧 `ArchitectureContext`,且不扩张到 notification / stream 路径
- 已完成的测试补强:
-`GFramework.Cqrs.Tests/Cqrs/` 新增 `DispatcherPipelineContextRefreshRequest``DispatcherPipelineContextRefreshBehavior``DispatcherPipelineContextRefreshRequestHandler``DispatcherPipelineContextRefreshState``DispatcherPipelineContextSnapshot`
- `DispatcherPipelineContextRefreshBehavior``DispatcherPipelineContextRefreshRequestHandler` 都基于 `CqrsContextAwareHandlerBase` 记录当次看到的 `ArchitectureContext`
- `CqrsDispatcherCacheTests` 新增 `Dispatcher_Should_Reinject_Current_Context_When_Reusing_Cached_Request_Pipeline_Executor`,断言同一个 cached executor 在两次分发间保持 executor 形状复用,但 handler 不会被 executor 黏住,且 handler / behavior 都会观察到本次分发的新上下文
- 调试过程中的结论:
- 初版断言曾要求 behavior 实例编号跨分发变化,随后确认这是错误假设
- `MicrosoftDiContainer.RegisterCqrsPipelineBehavior<TBehavior>()` 对已闭合的 pipeline behavior 使用的是 `AddSingleton(...)`
- 因此本轮最终锁定的是“singleton behavior 也必须重新注入上下文”,而不是强行要求 behavior 生命周期为 transient
- 定向验证已通过:
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
- `5/5` passed
- 结果:
- 本轮未暴露新的 runtime 实现缺口,因此没有改动 `GFramework.Cqrs/Internal/CqrsDispatcher.cs`
- 当前分支相对 `origin/main` 的累计提交 diff 仍为 `14 files`,继续低于 `gframework-batch-boot 50` 的主要 stop condition
### 阶段pointer runtime-reconstruction 残留清理CQRS-REWRITE-RP-056
- 延续 `gframework-batch-boot 50``Phase 8` 主线,本轮只处理一个写集很窄的 generator 清理切片:删除 `CqrsHandlerRegistryGenerator` 里已经不可达的 pointer runtime-reconstruction 残留
- 先复核当前实现后确认:
- `TryCreateRuntimeTypeReference` 已在入口直接拒绝 `IPointerTypeSymbol``IFunctionPointerTypeSymbol`
- `CanReferenceFromGeneratedRegistry` 也已统一把 pointer / function pointer 判定为不可直接引用
-`RuntimeTypeReferenceSpec``AppendRuntimeTypeReferenceResolution(...)``ContainsExternalAssemblyTypeLookup(...)` 仍残留 pointer 子结构与 `MakePointerType()` 分支,属于已失效的死代码
- 已完成的清理:
- `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.Models.cs` 已移除 `PointerElementTypeReference`
- `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.SourceEmission.cs` 已移除 pointer 运行时重建分支与 `AppendPointerRuntimeTypeReferenceResolution(...)`
- `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.RuntimeTypeReferences.cs` 已移除 pointer 外部程序集查找递归
- direct / named / mixed fallback 逻辑未改动pointer / function pointer 拒绝语义保持不变
- 定向验证已通过:
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
- `0 warning / 0 error`
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `22/22` passed
### 阶段缓存工厂闭包收敛CQRS-REWRITE-RP-055
- 延续 `gframework-batch-boot 50``Phase 8` 主线,本轮在不扩大语义面的前提下继续做一个更窄的 runtime 微切片:把弱缓存 / 并发缓存入口剩余的捕获型工厂收敛为 `static lambda + state`
- 先复核当前 runtime 热点后确认:
- `CqrsDispatcher` 的 notification / stream / request binding 与 pipeline executor 缓存仍存在少量可消除的捕获型工厂
- `CqrsHandlerRegistrar` 的程序集元数据缓存与可加载类型缓存也仍通过捕获 `logger` 的 lambda 建值
- 这些入口都只影响内部缓存建值,不触碰 handler / behavior 生命周期和 fallback 合同
- 已完成的收敛:
- `CqrsDispatcher` 现为 notification / stream / request binding 命中路径改用无捕获工厂pipeline executor 缓存改为显式状态对象承载 `requestType`
- `CqrsHandlerRegistrar` 现为 `AssemblyMetadataCache``LoadableTypesCache` 改用 `static` 工厂 + `logger` 显式状态参数
- 该批次没有改动 `RequestPipelineInvocation``next` 语义,也没有缓存 handler / behavior 实例
- 同轮继续补了一个独立 generator 覆盖缺口:
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 新增“外部程序集隐藏泛型定义 + 可见类型实参”的 precise registration 回归
- 该回归锁定生成器会输出 `ResolveReferencedAssemblyType("...ProtectedEnvelope\`1")` 与 `MakeGenericType(typeof(string))` 的组合,而不是退回程序集级字符串 fallback
- 定向验证已通过:
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~RegisterHandlers_Should_Cache_Assembly_Metadata_Across_Containers|FullyQualifiedName~RegisterHandlers_Should_Cache_Loadable_Types_Across_Containers|FullyQualifiedName~Dispatcher_Should_Cache_Request_Pipeline_Executors_Per_Behavior_Count"`
- `3/3` passed
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `22/22` passed
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
- `0 warning / 0 error`
### 阶段低风险并行批次收口CQRS-REWRITE-RP-054
- 继续按 `gframework-batch-boot 50` 推进 `Phase 8`,本轮先完成批次评估后再并行拆分写集,避免把 generator、runtime 与 docs 改动揉进同一片上下文
- 先复核当前 worktree、active tracking 与 `origin/main` 基线后确认:
- 当前分支头最初与 `origin/main` 对齐,批次阈值从 `0 files / 0 lines` 起算
- 本轮可以安全拆成三个互不冲突的切片request pipeline executor 形状缓存、precise runtime type lookup 数组回归补强、CQRS 入口文档对齐
- 主线程保留集成与验证职责subagent 只负责各自写集
- 本轮继续收口一个更窄的 generator 覆盖缺口:
-`GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 新增“外部程序集隐藏泛型定义 + 可见类型实参”的 precise registration 回归
- 该回归锁定生成器会输出 `ResolveReferencedAssemblyType("...ProtectedEnvelope\`1")` 与 `MakeGenericType(typeof(string))` 的组合,而不是退回程序集级字符串 fallback
- 定向测试 `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"` 通过,结果为 `22/22` passed因此本轮未触发 `RuntimeTypeReferences` / `SourceEmission` 的实现修正
- 已接受并整合的并行写集:
- docs 切片:更新 `GFramework.Cqrs/README.md``docs/zh-CN/core/cqrs.md``docs/zh-CN/api-reference/index.md`,明确 generated registry 优先、targeted fallback 只补剩余 handler
- generator 切片:在 `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 新增多维数组、交错数组、外部程序集隐藏元素类型三组 precise lookup 回归
- dispatcher 切片:在 `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 中将 request pipeline 从“每次分发重建 next 链”收敛为“binding 内按 behaviorCount 缓存 executor 形状”,并补充 dispatcher cache / 顺序回归
- docs 切片已作为独立提交落地:
- `66830ba2` `docs(cqrs): 更新入口与回退语义说明`
- 本轮定向验证已通过:
- `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~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
- `4/4` passed
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `21/21` passed
- 本轮停止时,当前工作区相对 `origin/main` 的累计 diff 为 `13 files / 709 lines`
- 结论:
- primary stop condition `50 files` 尚未触发,本轮停止是因为三条低风险切片已收口完毕
- 下一批更适合重新做一轮热点筛选,而不是在同一轮继续扩写集
### 阶段precise runtime type lookup 数组回归补强CQRS-REWRITE-RP-053
- 延续 `gframework-batch-boot 50``Phase 8` 主线,本轮选择一个更窄的 generator 覆盖缺口:锁定 precise runtime type lookup 下数组类型形态的回归
- 先复核当前实现后确认:
- `TryCreateRuntimeTypeReference` 已会把 `IArrayTypeSymbol` 递归建模为 `RuntimeTypeReferenceSpec.FromArray(element, rank)`
- `AppendArrayRuntimeTypeReferenceResolution` 已按 `ArrayRank == 1` 发射 `MakeArrayType()`,按 `rank > 1` 发射 `MakeArrayType(rank)`
- 当前缺口主要是测试面不足,尚未显式覆盖多维数组、交错数组、外部程序集隐藏元素类型这三类 precise lookup 场景
- 已在 `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 补充三组回归:
- 隐藏元素类型的多维数组响应,锁定 `MakeArrayType(2)` 发射
- 隐藏元素类型的交错数组响应,锁定递归 `MakeArrayType().MakeArrayType()` 发射
- 外部程序集隐藏元素类型的多维数组响应,锁定 `ResolveReferencedAssemblyType(...)``MakeArrayType(2)` 的组合
- 本轮定向测试全部通过,未暴露数组发射缺陷:
- 因此没有修改 `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.SourceEmission.cs`
- 也没有改动 `CqrsHandlerRegistryGenerator.RuntimeTypeReferences.cs`
- fallback 合同选择逻辑与 direct / named / mixed fallback 排版路径保持不变
- 定向验证已通过:
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `21/21` passed
### 阶段mixed fallback 元数据拆分CQRS-REWRITE-RP-052
- 延续 `gframework-batch-boot 50``Phase 8` 主线,本轮把上一批的“全部可直接引用 fallback handlers 走 `Type[]`”继续推进到 mixed 场景
- 先复核现状后确认:
- `CqrsHandlerRegistrar` 已天然支持读取多个 `CqrsReflectionFallbackAttribute` 实例
- 上一批真正阻止 mixed 场景继续收敛的点,是 runtime attribute 本身尚未开放多实例,以及 generator 只能二选一发射单个 fallback 特性
- 已在 `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 中将特性约束改为 `AllowMultiple = true`,并补充注释说明多个实例的用途
- 已在 `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 中扩展 fallback 合同探测:
- 探测 runtime 是否支持 `params string[]`
- 探测 runtime 是否支持 `params Type[]`
- 探测 runtime 是否允许多个 `CqrsReflectionFallbackAttribute` 实例
- 已在 `CqrsHandlerRegistryGenerator.Models.cs``CqrsHandlerRegistryGenerator.SourceEmission.cs` 中重构 fallback 发射模型:
- fallback 元数据现在可表示为一个或多个程序集级特性实例
- 当 fallback handlers 全部可直接引用时,继续优先输出单个 `Type[]` 特性
- 当 fallback 同时包含可直接引用与仅能按名称恢复的 handlers且 runtime 支持多实例时,拆分输出一条 `Type[]` 特性和一条字符串特性
- 若 runtime 不支持多实例或缺少相应构造函数,仍整体回退到字符串元数据,避免 mixed 场景漏注册
- 已补充 runtime 与 generator 双侧回归:
- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 新增 mixed fallback metadata 用例,锁定 registrar 只对字符串条目调用一次 `Assembly.GetType(...)`
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 新增 mixed fallback emission 用例,锁定 generator 会输出两个程序集级 fallback 特性实例
- 同步更新:
- `GFramework.Cqrs.SourceGenerators/README.md`
- `docs/zh-CN/source-generators/cqrs-handler-registry-generator.md`
- 说明 mixed 场景现在会拆分 `Type` 元数据与字符串元数据
- 定向验证已通过:
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- `13/13` passed
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `18/18` passed
- 随后按 `$gframework-pr-review` 重新拉取当前分支 PR 审查数据:
- 当前 worktree `feat/cqrs-optimization` 已对应 `PR #302`
- latest head commit 仍有 `3` 条 open AI review threadsGreptile 指向 generator preamble 的死参数与多实例 fallback 特性空行CodeRabbit 指向 mixed/direct fallback 测试断言过宽
- MegaLinter 仍只暴露 `dotnet-format``Restore operation failed`,未给出本地仍成立的格式文件线索,因此按环境噪音处理
- 本轮已继续收口 `RP-052` 的 follow-up
-`GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.SourceEmission.cs` 中移除已不再参与判断的 `generationEnvironment` 透传参数
- 调整多实例 fallback 特性发射时的换行策略,避免最后一个 fallback 特性与 `CqrsHandlerRegistryAttribute` 之间保留多余空行
-`GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 中补强 direct/mixed fallback 发射断言,锁定特性实例个数、拒绝空 marker并确保 mixed 场景的程序集级 preamble 排版稳定
-`GFramework.Cqrs.Tests/Cqrs/ReflectionFallbackNotificationContainer.cs` 中为 `DirectFallbackHandlerType` 补齐 `<returns>` XML 文档
- 本轮 review follow-up 验证已通过:
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
- `0 warning / 0 error`
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `18/18` passed
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- `13/13` passed
## 2026-04-20
### 阶段direct fallback 元数据优先级收敛CQRS-REWRITE-RP-051
- 重新按 `gframework-batch-boot 50` 恢复 `Phase 8` 后,先复核当前 worktree 的恢复入口、`origin/main` 基线与分支规模:
- worktree 仍映射到 `cqrs-rewrite`
- 基线按批处理约定固定为 `origin/main`
- 本轮开始前分支累计 diff 为 `0 files / 0 lines`
- 结合当前代码热点与历史归档后,选择本轮批次目标为“继续收敛 generator fallback 元数据,进一步减少 runtime 按字符串类型名回查 handler 的场景”
- 已在 `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 中新增 runtime fallback 合同探测:
- 识别 `CqrsReflectionFallbackAttribute` 是否支持 `params string[]`
- 识别 `CqrsReflectionFallbackAttribute` 是否支持 `params Type[]`
- 已在 `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.Models.cs`
`CqrsHandlerRegistryGenerator.SourceEmission.cs` 中收敛 fallback 发射策略:
- 当本轮所有 fallback handlers 都可被生成代码直接引用,且 runtime 支持 `params Type[]` 时,生成器现优先发射 `typeof(...)` 形式的程序集级 fallback 元数据
- 当 fallback handlers 中仍存在不能直接引用的实现类型时,生成器继续整体回退到字符串元数据,避免 mixed 场景下部分 handler 走 `Type[]`、其余 handler 丢失恢复入口
- 已在 `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 补充回归:
- 锁定 runtime 同时暴露字符串与 `Type` 两类 fallback 构造函数时,生成器优先选择直接 `Type` 元数据
- 保留现有字符串 fallback 合同测试,确保旧 contract 兼容路径不回退
- 同步更新:
- `GFramework.Cqrs.SourceGenerators/README.md`
- `docs/zh-CN/source-generators/cqrs-handler-registry-generator.md`
- 说明“可直接引用的 fallback handlers 会优先走 `typeof(...)` 元数据,减少运行时字符串回查”
- 定向验证已通过:
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `17/17` passed
- 额外修正:
- active tracking 中原先引用的 `ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md` 在当前 worktree 已不存在;本轮已移除该失效路径,后续以 active tracking / trace 作为默认恢复入口
### 阶段pointer / function pointer 泛型合同拒绝CQRS-REWRITE-RP-050
- 重新执行 `$gframework-pr-review` 后,确认当前分支对应 `PR #261`,状态仍为 `OPEN`
- latest reviewed commit 当前剩余 `1` 条 open CodeRabbit thread指向 `RP-047` 历史记录仍把 `MakePointerType()` precise registration 写成现行路径
- 本地核对后确认该评论有效:当前 pointer / function pointer 语义已由 `RP-050` 收敛为 fallback / diagnostic 路径,历史追踪必须显式标注 `RP-047` 已废弃,避免后续恢复时误回滚到旧方案
- 已在 `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 中收紧 `TryCreateRuntimeTypeReference``CanReferenceFromGeneratedRegistry`
- pointer / function pointer 现统一视为不可精确生成的 CQRS 泛型合同,生成器会保守回退到既有 fallback / diagnostic 路径,而不再发射运行时 `MakeGenericType(...)` 风险代码
- 已在 `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 中补充输入源诊断分离,并将相关测试改为显式断言 `CS0306` 与 fallback / diagnostic 结果
- 已同步修正 `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md``RP-047` 段落,明确其已被 `RP-050` 覆盖,且不得恢复 `MakePointerType()` precise registration
- 定向验证已通过:
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~Reports_Compilation_Error_And_Skips_Precise_Registration_For_Hidden_Pointer_Response|FullyQualifiedName~Reports_Diagnostic_And_Skips_Registry_When_Fallback_Metadata_Is_Required_But_Runtime_Contract_Lacks_Fallback_Attribute|FullyQualifiedName~Emits_Assembly_Level_Fallback_Metadata_When_Fallback_Is_Required_And_Runtime_Contract_Is_Available"`
- `3/3` passed
- 扩展验证已通过:
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `14/14` passed
### 阶段registrar duplicate mapping 索引收敛CQRS-REWRITE-RP-049
- 已将 `CqrsHandlerRegistrar` 的重复 handler mapping 判定从逐条线性扫描 `IServiceCollection` 收敛为单次构建的本地映射索引
- reflection fallback 或重复类型输入场景下,后续 duplicate mapping 判定改为 `HashSet` 命中,不再重复遍历已有服务描述符
- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已补充“程序集枚举返回重复 handler 类型时仍只注册一份映射”的回归
- 定向验证已通过:
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- `11/11` passed
- 当前沙箱限制 MSBuild named pipe因此验证在提权环境下执行
### 阶段registrar handler-interface 反射缓存CQRS-REWRITE-RP-048
- 已在 `CqrsHandlerRegistrar` 中新增按 `Type` 弱键缓存的 supported handler interface 元数据reflection 注册路径现会复用已筛选且排序好的接口列表
- 同一 handler 类型跨容器重复注册时,不再重复执行 `GetInterfaces()` 与支持接口筛选;缓存仍保持卸载安全,不会长期钉住 collectible 类型
- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已补充 registrar 静态缓存清理与 supported interface 缓存复用回归
- 定向验证已通过:
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- `10/10` passed
- 当前沙箱限制 MSBuild named pipe因此验证在提权环境下执行
### 阶段pointer precise runtime type 覆盖扩展CQRS-REWRITE-RP-047已由 RP-050 覆盖)
- 曾在 `CqrsHandlerRegistryGenerator` 中尝试补充 pointer 类型的 runtime type 递归建模与源码发射,计划通过 `MakePointerType()` 还原隐藏 pointer 响应类型
- 该方案后续已被 `RP-050` 明确废弃pointer / function pointer 不能作为 CQRS 泛型合同的 precise registration 输入,当前实现统一回到 fallback / diagnostic 路径,不能恢复到 `MakePointerType()` 精确注册
- 已同步收紧 function pointer 签名的可直接生成判定,只有当签名中的返回值与参数类型均可从 generated registry 安全引用时才走静态注册
- 已保留含隐藏类型 function pointer handler 的 fallback / 诊断回归覆盖,确保 pointer 支持扩展不会误删原有程序集级 fallback 契约边界
- 后续若需恢复当前 pointer / function pointer 行为,应以 `RP-050` 为权威记录,而不是继续沿用本阶段的旧设计假设
- 定向验证与 `CqrsHandlerRegistryGeneratorTests` 全组验证均已通过:
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~Generates_Precise_Service_Type_For_Hidden_Pointer_Response|FullyQualifiedName~Reports_Diagnostic_And_Skips_Registry_When_Fallback_Metadata_Is_Required_But_Runtime_Contract_Lacks_Fallback_Attribute|FullyQualifiedName~Emits_Assembly_Level_Fallback_Metadata_When_Fallback_Is_Required_And_Runtime_Contract_Is_Available"`
- `3/3` passed
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
- `14/14` passed
- 当前沙箱限制 MSBuild named pipe因此验证在提权环境下执行
### 阶段generated registry 激活反射收敛CQRS-REWRITE-RP-046
- 已在 `CqrsHandlerRegistrar` 中将 generated registry 的无参构造激活改为类型级缓存工厂
- 默认路径优先使用一次性动态方法直接创建 registry避免后续每次命中缓存仍走 `ConstructorInfo.Invoke`
- 若运行环境不允许动态方法,则保留原有反射激活回退,确保 generated registry 路径不因运行时限制失效
- 已补充“私有无参构造 generated registry 仍可激活”的回归测试,覆盖现有生成器产物兼容性
- 定向验证已通过:
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false`
- `63/63` passed
- 当前沙箱限制 MSBuild named pipe因此验证在提权环境下执行
### Archive Context
- 历史跟踪归档:
- `ai-plan/public/cqrs-rewrite/archive/todos/cqrs-rewrite-history-through-rp043.md`
- 历史 trace 归档:
- `ai-plan/public/cqrs-rewrite/archive/traces/cqrs-rewrite-history-through-rp043.md`
### 当前下一步
1. 回到 `Phase 8` 主线,优先再找一个写集独立的 generator 或 runtime 热点pointer runtime-reconstruction 残留已清空,后续不要恢复任何 `MakePointerType()` 发射路径
2. 若继续文档主线,优先补齐 `docs/zh-CN/api-reference` 与教程入口页中仍过时的 CQRS API / 命名空间表述
3. 若后续 review thread 或 PR 状态再次变化,再重新执行 `$gframework-pr-review` 复核远端信号