GFramework/ai-plan/public/traces/cqrs-rewrite-migration-trace.md
gewuyou 6ab32032bc refactor(repo): 重构ai-plan目录语义
- 重命名 local-plan 为 ai-plan,并收口 public 与 private 的目录语义
- 更新 AGENTS、README 与 boot skill 的恢复文档路径和安全约束
- 迁移共享 tracking 与 trace 文件到 ai-plan/public,并补充治理说明文档
2026-04-19 03:18:46 +08:00

1408 lines
99 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-14
### 阶段:初始化
- 建立 `CQRS-REWRITE-RP-001` 恢复点
- 已确认本次迁移目标:
- 彻底参考 `Mediator` 思路重写 GFramework 正式 CQRS
- 不保留对 `Mediator` 的兼容层
- 使用 `abstractions + runtime 可选模块` 边界
- 保留 `EventBus`,不与 CQRS notification 合并
### 已确认的实现前提
- `CoreGrid-Migration` 当前仍依赖 NuGet 版 `GeWuYou.GFramework*`
- `CoreGrid/scripts/core/GameArchitecture.cs``CoreGrid-Migration/scripts/core/GameArchitecture.cs` 通过 `AddMediator(...)` 启用基于生成器的 runtime
- `GFramework` 当前 `IArchitectureContext` 与一批 CQRS 基类直接引用 `Mediator.*`
- `CoreGrid/scripts/cqrs/**` 的 handler 很薄,主要迁移成本在框架 runtime 和注册机制,不在业务逻辑本身
### 当前动作
- 准备更新 `AGENTS.md`,补充恢复点 / trace / subagent 协作规范
- 准备将 `CoreGrid-Migration` 切换为本地项目引用,建立真实验证链路
### 下一步
1. 完成 `AGENTS.md` 规则补充
2. 改造 `CoreGrid-Migration/CoreGrid.csproj` 为本地项目与本地生成器引用
3. 进行第一次构建验证,确认本地链路可用
### 阶段CQRS 主路径迁移完成
- `CoreGrid-Migration/CoreGrid.csproj` 已切到本地 `ProjectReference` + 本地 source generators
- `CoreGrid-Migration/scripts/core/GameArchitecture.cs` 已删除 `AddMediator(...)` 配置钩子
- `GFramework.Core.Abstractions` 新增 GFramework 自有 CQRS 契约与 `Unit`
- `IArchitectureContext` / `ArchitectureContext` 已切到自有 CQRS 签名
- `ArchitectureBootstrapper` 已内建 handler 扫描注册,使用方无需再显式调用 `AddMediator(...)`
- `CqrsDispatcher` 已补齐 request/notification/stream dispatch 与 pipeline behavior 执行
- `GFramework.Core.Cqrs.*` 基类、`ContextAwareMediator*Extensions`、Godot 协程上下文扩展均已迁到新契约
- `GFramework.Core.Tests` 中原依赖旧 `Mediator` 注册入口的测试已迁移到 `CqrsTestRuntime` 反射注册路径
### 阶段:验证
- `dotnet build GFramework.Core/GFramework.Core.csproj`
- 结果:通过
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj`
- 结果:通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj --no-build`
- 结果:通过
- 明细:`1621` 个测试全部通过
- `dotnet build GFramework.sln`
- 结果:通过
- `dotnet build ../CoreGrid-Migration/CoreGrid.sln`
- 结果:通过
- 备注:仅存在既有 analyzer warnings无新增构建错误
### 阶段CQRS 收尾修正
- 建立 `CQRS-REWRITE-RP-003` 恢复点
- 修正 `IArchitectureContext``QueryBase``Abstract*Handler``MessageHandlerDelegate` 的 XML 文档,明确旧
Command/Query 总线与新 CQRS runtime 的兼容边界
- `CqrsHandlerRegistrar` 改为按程序集名、处理器类型名与处理器接口名稳定排序
- `CqrsHandlerRegistrar``ReflectionTypeLoadException` 场景下会记录告警并保留可加载类型继续注册
- 自动扫描到的 request / notification / stream handler 改为 transient避免 `ContextAwareBase` 派生处理器在并发请求间共享可变上下文
- `CqrsTestRuntime` 移除自建 pipeline 执行逻辑,测试改为走 `ArchitectureContext.SendRequestAsync(...)` 正式入口
- `MediatorAdvancedFeaturesTests` 为断路器静态状态补上统一重置,消除测试间污染
### 阶段:补充验证
-
`dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorAdvancedFeaturesTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorComprehensiveTests"`
- 结果:通过
- 明细:`49` 个测试全部通过
### 阶段review 收尾修正
- 建立 `CQRS-REWRITE-RP-004` 恢复点
- `AbstractStreamCommandHandler<TCommand, TResponse>` 已把上下文注入窗口、瞬态实例约束与流创建/枚举取消边界移入显式
`<remarks>`
- `CqrsHandlerRegistrarTests` 已改为从 `CqrsTestRuntime.RegisterHandlers(...)` 真实入口覆盖部分类型加载失败场景,不再反射
`RecoverLoadableTypes(...)`
- `CqrsTestRuntime` 已补齐 XML 文档,并改为按 `IIocContainer + IEnumerable<Assembly> + ILogger` 精确绑定
`RegisterHandlers(...)` 反射签名,避免未来重载漂移
- 定向验证期间发现 `CqrsHandlerRegistrar.cs` 缺少 `Microsoft.Extensions.DependencyInjection``using`,导致
`IServiceCollection` 无法编译;该编译阻塞已一并修复
### 阶段review 修正验证
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`47` 个测试全部通过
### 阶段:去外部依赖收尾
- 建立 `CQRS-REWRITE-RP-005` 恢复点
- `GFramework.Core.Abstractions` 已移除 `Mediator.Abstractions` 包引用,并显式改依赖
`Microsoft.Extensions.DependencyInjection.Abstractions` 以承接 `IServiceCollection` / `IServiceScope`
- `GFramework.Core.Tests` 已移除 `Mediator.Abstractions``Mediator.SourceGenerator` 包引用
- `MediatorCoroutineExtensions` 已改为直接走 `IArchitectureContext.SendAsync(...)` 内建 CQRS 入口,不再解析
`IMediator`
- `Architecture` / `ArchitectureModules` / `IIocContainer` / `MicrosoftDiContainer` / `ArchitectureBootstrapper`
与主文档已补充“历史命名保留,但 runtime 已为内建 CQRS”说明
- 并行 subagent 盘点确认:生产代码与主文档中剩余的 `Mediator` 主要是历史兼容命名,不再对应任何外部包依赖
### 阶段:去外部依赖验证
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build`
- 结果:通过
- 明细:`1624` 个测试全部通过
- `dotnet build ../CoreGrid-Migration/CoreGrid.sln`
- 结果:通过
- 备注:仅存在既有 analyzer warnings 与 `GodotProjectDir` 缺失导致的 generator warning无新增构建错误
- `dotnet build GFramework.sln -c Release`
- 结果:失败
- 原因:当前 WSL 环境下顶层 `GFramework.csproj` 仍引用 Windows NuGet fallback package folder
`D:\Tool\Development Tools\Microsoft Visual Studio\Shared\NuGetPackages`
- 结论:属于既有环境配置问题,与本轮 CQRS 去 `Mediator` 改动无关
### 阶段:历史命名中性化
- 建立 `CQRS-REWRITE-RP-006` 恢复点
- `IArchitecture` / `IIocContainer` / `Architecture` / `MicrosoftDiContainer` 已新增
`RegisterCqrsPipelineBehavior<TBehavior>()` 推荐入口
- 旧的 `RegisterMediatorBehavior<TBehavior>()` 已降级为 `[Obsolete]` 兼容包装层,并转发到新的 CQRS 中性命名
- 新增 `ContextAwareCqrsExtensions``ContextAwareCqrsCommandExtensions``ContextAwareCqrsQueryExtensions`
`CqrsCoroutineExtensions`
- 旧的 `ContextAwareMediator*Extensions``MediatorCoroutineExtensions` 已改为 `[Obsolete]` 兼容包装层
- 新的 `ContextAwareCqrs*` / `CqrsCoroutineExtensions` 已放入独立命名空间,避免与旧扩展同时导入时产生调用歧义
- `ArchitectureModulesBehaviorTests``CqrsCoroutineExtensionsTests``docs/zh-CN/core/cqrs.md`
`docs/zh-CN/core/index.md``CoreGrid-Migration` 命中的旧扩展调用点已迁到新的中性命名入口
### 阶段:历史命名中性化验证
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build`
- 结果:通过
- 明细:`1624` 个测试全部通过
- `dotnet build ../CoreGrid-Migration/CoreGrid.sln`
- 结果:通过
- 备注:消除了新增扩展类带来的调用歧义,仅剩既有 analyzer warnings
### 阶段:兼容层正式弃用
- 建立 `CQRS-REWRITE-RP-007` 恢复点
- `IArchitecture``IIocContainer``Architecture``ArchitectureModules``MicrosoftDiContainer`
上剩余的 `RegisterMediatorBehavior<TBehavior>()` 已统一补充 future-major 移除说明
- `ContextAwareMediatorExtensions``ContextAwareMediatorCommandExtensions`
`ContextAwareMediatorQueryExtensions``MediatorCoroutineExtensions`
已统一补充 future-major 移除说明
- 上述历史兼容入口已统一加上 `EditorBrowsable(EditorBrowsableState.Never)`,从 IntelliSense 主路径隐藏,
将迁移默认路径进一步收敛到新的 `Cqrs` 命名入口
- `docs/zh-CN/core/cqrs.md``CLAUDE.md` 已同步记录“兼容别名进入正式弃用周期”的事实
- `MediatorCompatibilityDeprecationTests` 已新增并通过反射断言锁定:
- 公开兼容方法保留行为兼容,但必须带有 `Obsolete`
- 公开兼容方法与兼容扩展类型必须带有 `EditorBrowsable(EditorBrowsableState.Never)`
- 弃用消息必须明确新的 CQRS 迁移目标与 future-major 移除预期
### 阶段:兼容层正式弃用验证
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~GFramework.Core.Tests.Cqrs.MediatorCompatibilityDeprecationTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Coroutine.CqrsCoroutineExtensionsTests"`
- 结果:通过
- 明细:`8` 个测试全部通过
### 阶段CQRS source-generator 注册 MVP
- 建立 `CQRS-REWRITE-RP-008` 恢复点
- `GFramework.Core.Abstractions` 已新增:
- `ICqrsHandlerRegistry`
- `CqrsHandlerRegistryAttribute`
- `CqrsHandlerRegistrar` 已优先尝试读取程序集级 `CqrsHandlerRegistryAttribute`
指向的生成注册器;当生成注册器缺失、类型无效或实例化失败时,会记录 warning 并自动回退到原有反射扫描路径
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已新增:
- 为当前消费端程序集中的 concrete request / notification / stream handlers 生成单一程序集级注册器
- 生成注册顺序与 runtime 反射口径对齐,按实现类型与处理器接口名稳定排序
- 当程序集包含生成代码无法合法引用的 concrete handler例如私有嵌套 handler直接放弃生成让 runtime 保持完整反射回退
- `docs/zh-CN/core/cqrs.md``CLAUDE.md` 已同步记录“生成注册器优先,反射扫描兜底”的当前行为
### 阶段CQRS source-generator 注册验证
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
- 结果:通过
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`2` 个测试全部通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~GFramework.Core.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorComprehensiveTests"`
- 结果:通过
- 明细:`41` 个测试全部通过
### 阶段review 收尾修正(并发 lazy 初始化与共享测试运行时)
- `ArchitectureContext``ICqrsRuntime` 惰性解析已从 `??=` 改为 `Lazy<ICqrsRuntime>`
并显式指定 `LazyThreadSafetyMode.ExecutionAndPublication`
- 新增 `ArchitectureContextTests.SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently`
以回归锁定并发首次访问行为
- 两个测试项目中的 `CqrsTestRuntime` 重复实现已收敛为单一共享源码文件 `tests/Shared/CqrsTestRuntime.cs`
- 共享 `CqrsTestRuntime` 已移除 `GetType(..., throwOnError: true)` 后不可达的 `?? throw` 分支
### 阶段review 收尾修正验证
- `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`49` 个测试全部通过
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureContextTests|FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests"`
- 结果:通过
- 明细:`57` 个测试全部通过
- 并行执行两条 `dotnet test` 时命中过一次 `project.assets.json` 文件竞争;
顺序重跑 `GFramework.Core.Tests --no-restore` 后通过,确认是 restore 并发噪音而非实现缺陷
### 阶段:测试公共基础设施模块化
- 已将临时共享源码目录 `tests/Shared` 收敛为正式项目 `GFramework.Tests.Common`
- `CqrsTestRuntime` 已迁入 `GFramework.Tests.Common` 并改为由测试项目通过 `ProjectReference` 访问
## 2026-04-16
### 阶段review 收尾修正(线程安全文档、未冻结态去重与 runtime 契约说明)
- 建立 `CQRS-REWRITE-RP-020` 恢复点
- `GFramework.Cqrs/Internal/DefaultCqrsRegistrationService.cs` 已在类级 `<remarks>` 中明确“该类型不是线程安全的,必须由外部同步边界串行访问”的设计约束
- `GFramework.Cqrs.Abstractions/Cqrs/ICqrsRuntime.cs` 已补齐三个公开方法的 XML 契约:
- `null` 参数对应 `ArgumentNullException`
- handler 缺失或上下文不满足 `IArchitectureContext` 注入前提时对应 `InvalidOperationException`
- `CreateStream(...)` 额外说明了上下文需覆盖整个异步枚举生命周期
- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已补充线性去重扫描的性能特征注释,并记录未来若出现大服务集合热点,可在更高层批处理中引入 `HashSet<(Type, Type)>`
- `GFramework.Core/Ioc/MicrosoftDiContainer.cs` 已将未冻结态 `GetAll<T>()` / `GetAll(Type)` 的引用去重逻辑收窄为:
- 仍会折叠“不同 `ServiceType` 指向同一实例”的兼容别名重复
- 不再吞掉“同一 `ServiceType` 对同一实例的重复显式注册”
- 当同一实例同时暴露多个服务类型时,优先保留请求类型对应分组,否则保留注册次数最多且首次出现最早的分组,以维持确定性
- `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 已新增两个回归测试,分别锁定泛型与非泛型 `GetAll` 在未冻结态下的上述语义
### 阶段RP-020 验证
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests"`
- 结果:通过
- 明细:`40` 个测试全部通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests"`
- 结果:通过
- 明细:`40` 个测试全部通过
### 下一步
1. 继续评估 `CqrsHandlerRegistrar` / generated registry 的命中率提升空间,优先减少需要 `GetTypes()` 全量扫描的回退场景
2. 若后续 `MicrosoftDiContainer` 继续保留“未冻结时支持按可赋值类型观察实例”的宽语义,可考虑为 mixed alias + duplicate registration 组合场景再补一条更细的回归测试
## 2026-04-16
### 阶段review 收尾修正CQRS 程序集枚举输入固定与前置校验)
- 建立 `CQRS-REWRITE-RP-021` 恢复点
- `GFramework.Core/Ioc/MicrosoftDiContainer.cs` 已将 `RegisterCqrsHandlersFromAssemblies(...)` 调整为:
- 在进入容器写锁前先 `ToArray()` 固定输入枚举
- 逐项执行 `ArgumentNullException.ThrowIfNull(...)`
- 再把固定后的数组交给 `ICqrsRegistrationService`
- 该调整把 `null` 元素和依赖外部可变状态的延迟枚举拦截在容器入口处,避免更深层的反射/注册路径才暴露输入问题,也减少在写锁内枚举外部序列的非确定性
- `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 已新增空程序集元素回归测试,锁定“先校验,再委托”的失败边界
### 阶段RP-021 验证
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests"`
- 结果:通过
- 明细:`41` 个测试全部通过
### 下一步
1. 若后续 review 继续聚焦容器显式注册入口,可考虑补一条“延迟枚举只在入口处物化一次”的行为测试
- `GFramework.sln` 已纳入 `GFramework.Tests.Common`,测试公共基础设施不再悬空在 solution 外
### 阶段:测试公共基础设施模块化验证
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureContextTests|FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests"`
- 结果:通过
- 明细:`57` 个测试全部通过
- `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`49` 个测试全部通过
### 阶段:日志入口下沉与剩余 runtime behaviors 迁移
- 建立 `CQRS-REWRITE-RP-016` 恢复点
- `LoggerFactoryResolver` 已从 `GFramework.Core` 下沉到 `GFramework.Core.Abstractions`,继续保留 `GFramework.Core.Logging` 命名空间
- 新的抽象层 `LoggerFactoryResolver` 默认会优先反射解析 `GFramework.Core.Logging.ConsoleLoggerFactoryProvider`
- 当宿主未加载 `GFramework.Core` 默认日志实现时,`LoggerFactoryResolver` 会退回静默 provider避免上层模块只为拿日志入口而重新依赖实现层
- `GFramework.Core` 已新增 type forward继续对外暴露 `LoggerFactoryResolver`,降低已编译消费端的运行时兼容风险
- `LoggingBehavior<TRequest, TResponse>``PerformanceBehavior<TRequest, TResponse>` 已迁移到 `GFramework.Cqrs`,同时继续保留 `GFramework.Core.Cqrs.Behaviors` 命名空间
- 迁移后 `GFramework.Core/Cqrs/*` 已全部搬空;当前 runtime 物理迁移残项只剩 `CqrsCoroutineExtensions`
- 并行 explorer 结论:
- `ICqrsRuntime` 的真实阻塞链路是 `ICqrsRuntime -> IArchitectureContext -> IContextAware / handler Context`
- 只要 CQRS handler 上下文模型仍直接依赖完整 `IArchitectureContext``ICqrsRuntime` 就不能无损迁到 `GFramework.Cqrs.Abstractions`
- `CqrsCoroutineExtensions` 依赖 `TaskCoroutineExtensions``WaitForTask*`,本质属于 `Core` 协程桥接层,不宜原样迁到 `GFramework.Cqrs`
- 若只追求下一步最小可行推进,应优先设计更窄的 CQRS 专用上下文 seam并继续把协程桥接保留在 `GFramework.Core`
### 阶段RP-016 验证
## 2026-04-16
### 阶段ICqrsRuntime 归属收敛与兼容别名落地
- 建立 `CQRS-REWRITE-RP-017` 恢复点
- `GFramework.Cqrs.Abstractions/Cqrs/ICqrsContext.cs` 已新增,作为 CQRS runtime 使用的最小上下文 marker seam
- 该 seam 仅用于切断 `ICqrsRuntime -> IArchitectureContext` 的编译期循环依赖
- 当前 runtime 仍允许在实现层识别更具体的 `IArchitectureContext`,以兼容现有 `IContextAware` handler 注入模型
- `GFramework.Cqrs.Abstractions/Cqrs/ICqrsRuntime.cs` 已新增并成为正式 runtime seam 归属
- `GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs` 已改为兼容别名:
- 旧接口现在继承新的 `GFramework.Cqrs.Abstractions.Cqrs.ICqrsRuntime`
- 通过 `EditorBrowsable(EditorBrowsableState.Never)` 隐藏历史路径,避免新代码继续绑定旧 namespace
- `IArchitectureContext` 已实现 `ICqrsContext`,继续作为默认架构内的 CQRS 分发上下文
- `CqrsDispatcher` 已改为按 `ICqrsContext` 接收分发上下文:
- 对现有 `IContextAware` handler 的注入保留运行时兼容检查
- 若未来引入非架构型 CQRS context实现层可显式阻止将其注入到仍要求 `IArchitectureContext` 的历史 handler
- `ArchitectureContext``CqrsRuntimeFactory` 与默认 runtime 注册路径已切到新的 `GFramework.Cqrs.Abstractions.Cqrs.ICqrsRuntime`
- `CqrsRuntimeModule``GFramework.Tests.Common/CqrsTestRuntime.cs` 已同时注册新旧 `ICqrsRuntime` 接口:
- 主代码路径默认解析新接口
- 历史公开路径仍可解析到同一个 runtime 实例,避免 consumer 或测试基础设施立即断裂
- 本轮结论同步收敛:
-`GFramework.Core.Abstractions.Cqrs*` 兼容面只保留 `ICqrsRuntime` 这一最小公共别名
- `GFramework.Core.Cqrs.Extensions` 不再视为需要继续正式维护的旧路径;仓库与 `CoreGrid-Migration` 已切到新的 `GFramework.Cqrs.Extensions` 入口
### 阶段迁移目标修正为“Core = App RuntimeCQRS = 集成子系统”
- 在重新评估 Phase 5/7 的边界目标后,确认此前“继续推动 `Core` 尽量不依赖 `Cqrs`”的方向已经偏离本任务的真正目标
- 迁移计划现正式修正为:
- `GFramework.Core.Abstractions -> GFramework.Cqrs.Abstractions`
- `GFramework.Core -> GFramework.Cqrs`
- `Core` 作为 App Runtime默认集成 CQRS 子系统
- `CQRS` 作为被集成的正式子系统继续承接抽象层、实现层、generator 层
- 本次修正特别明确两点:
- `Core` 强依赖 `Cqrs` 是允许的,不再以“彻底零依赖”为目标
-`Core` 不应依赖 CQRS 的细节结构,例如直接 `new` 具体 dispatcher、直接依赖 generator 生成类型、硬编码 handler/internal registry 细节
- 因此后续健康依赖标准改为:
- 单向依赖
- 通过 seam / 模块入口装配
- 默认集成,但理论上可替换 runtime
- `CqrsCoroutineExtensions` 的定位也一并修正:
- 它不再是“尚未迁移干净的 CQRS runtime 残项”
- 而是 `Core` 对 CQRS 的协程桥接层,保留在 `Core` 是合理结果
- 方向修正后的下一步主线:
1. 停止继续为了纯边界推进高成本的上下文/桥接迁移
2. 保持 `Core -> Cqrs` 的健康单向依赖,继续避免细节泄漏
3. 把精力转向 CQRS 子系统增强,尤其是 generator 覆盖面与低反射路径
- 同时补充一条实现约束:
- 后续 CQRS runtime、pipeline、generator 与低反射路径的设计与实现,应明确参考 `Mediator` 中已经成熟可用的实现
- 目标是吸收其已验证的结构与经验,减少重复踩坑,而不是把这些运行时细节完全从零发明
- `dotnet build GFramework/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release`
- 结果:通过
- `dotnet build GFramework/GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
- 结果:通过
- `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- `dotnet build GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~GFramework.Core.Tests.Logging.LoggerFactoryTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Cqrs.MediatorCompatibilityDeprecationTests"`
- 结果:通过
- 明细:`20` 个测试全部通过
- `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Cqrs.Tests.Coroutine.CqrsCoroutineExtensionsTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`54` 个测试全部通过
### 阶段Core 泄漏盘点与 handler 注册协调器下沉
- 建立 `CQRS-REWRITE-RP-018` 恢复点
- 并行 explorer 盘点结论已收敛:
- `ArchitectureContext -> ICqrsRuntime` 属于合理的 runtime seam不视为实现细节泄漏
- `Core` 当前未直接依赖任何 generator 生成类型名或生成注册器具体类型
- 主要值得继续收敛的实现细节泄漏点集中在:
- `MicrosoftDiContainer` 里原本维护的 handler 程序集去重状态与 registrar 调度
- `ArchitectureBootstrapper` 对默认 handler 程序集的硬编码
- 更长线的 `IIocContainer` / `IArchitecture` CQRS 装配 API 暴露面
- 本轮先完成最小落地收敛:
- `GFramework.Cqrs/ICqrsRegistrationService.cs` 已新增,作为 CQRS handler 程序集接入协调入口
- `GFramework.Cqrs/Internal/DefaultCqrsRegistrationService.cs` 已新增,将稳定程序集键去重与 `ICqrsHandlerRegistrar` 调度统一收敛到 CQRS runtime 内部
- `GFramework.Cqrs/CqrsRuntimeFactory.cs` 已新增 `CreateRegistrationService(...)`
- `GFramework.Core/Services/Modules/CqrsRuntimeModule.cs` 现会同时注册:
- `ICqrsRuntime`
-`GFramework.Core.Abstractions.Cqrs.ICqrsRuntime` 兼容别名
- `ICqrsHandlerRegistrar`
- `ICqrsRegistrationService`
- `GFramework.Core/Ioc/MicrosoftDiContainer.cs` 已移除本地 `_registeredCqrsHandlerAssemblyKeys` 状态与 registrar 直连逻辑,`RegisterCqrsHandlersFromAssemblies(...)` 现仅委托给 `ICqrsRegistrationService`
- `GFramework.Tests.Common/CqrsTestRuntime.cs` 已同步补齐 `ICqrsRegistrationService` 接线,保持裸测试容器与生产路径一致
- 这一轮结论是:
- `Core -> Cqrs` 的单向依赖可以保留
- 但 CQRS handler 注册细节应继续下沉到 CQRS 子系统,而不是分散在容器壳层中
- 下一步主线可开始转向 generator / 低反射增强,而不是继续为边界纯化推进高成本桥接迁移
### 阶段RP-018 验证
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureAdditionalCqrsHandlersTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests"`
- 结果:通过
- 明细:`43` 个测试全部通过
- `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`45` 个测试全部通过
- 在一次并行执行两条 `dotnet test` 的尝试中再次命中 `GFramework.Cqrs.deps.json` 文件锁冲突;
顺序重跑后稳定通过,确认属于本地并发构建输出竞争,而不是本轮实现缺陷
### 阶段generator 局部回退落地
- 建立 `CQRS-REWRITE-RP-019` 恢复点
- 已将 CQRS handler generator 从“整程序集回退”推进到“局部 generated + 局部 reflection fallback”
- `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 已新增,作为程序集级 fallback marker
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 现会:
- 继续为可由生成代码合法引用的 handlers 生成 registry
- 当程序集内同时存在不可见 handlers 且 runtime 合同支持 marker 时,额外生成 `CqrsReflectionFallbackAttribute`
- 若消费端仍缺少该 marker 合同,则保持旧的整程序集回退行为,避免静默漏掉不可见 handlers
- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已配套支持新的组合路径:
- 优先执行 generated registry
- 若程序集带有 `CqrsReflectionFallbackAttribute`,则继续补一次 reflection 扫描
- reflection 补扫会按 `ServiceType + ImplementationType` 去重,避免已由 generated registry 注册的映射重复写入
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已更新/新增:
- 正常生成快照继续覆盖
- “私有嵌套 handler” 场景改为断言“仍生成可见 handler + 输出 fallback marker”
- 新增“旧 runtime 合同缺少 marker 时仍整程序集回退”的兼容回归
- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已新增组合路径回归:
- 当 generated registry 与 reflection fallback 同时参与时,剩余 handlers 会被补齐
- 已由 generated registry 注册的 handler 映射不会重复写入服务集合
### 阶段RP-019 验证
- `dotnet test GFramework/GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`4` 个测试全部通过
- `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- 结果:通过
- 明细:`5` 个测试全部通过
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureAdditionalCqrsHandlersTests"`
- 结果:通过
- 明细:`2` 个测试全部通过
- 在并行执行三条 `dotnet test` 时,`SourceGenerators``Cqrs` 两条命中过一次本地 restore / deps 文件竞争;
顺序重跑后全部通过,确认属于本地并发构建输出竞争,不是本轮实现缺陷
### 阶段runtime 物理迁移(第一批)
- 建立 `CQRS-REWRITE-RP-014` 恢复点
- `GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs``CqrsHandlerRegistrar.cs`
`DefaultCqrsHandlerRegistrar.cs` 已迁移到 `GFramework.Cqrs` 项目,保留原 `GFramework.Core.Cqrs.Internal`
命名空间,降低消费端源码层面的 breaking change
- `GFramework.Core/Cqrs/Command/CommandBase.cs``Query/QueryBase.cs``Request/RequestBase.cs`
`Notification/NotificationBase.cs` 已迁移到 `GFramework.Cqrs`
- `GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs``ContextAwareCqrsCommandExtensions.cs`
`ContextAwareCqrsQueryExtensions.cs` 已迁移到 `GFramework.Cqrs`
- `ICqrsHandlerRegistry``CqrsHandlerRegistryAttribute` 已从 `GFramework.Core.Abstractions`
收敛到 `GFramework.Cqrs` 根命名空间,作为 runtime 专属契约:
- 这样避免了把依赖 `ILogger` / `IServiceCollection` 的类型继续塞进 `GFramework.Cqrs.Abstractions`
- 同时规避 `GFramework.Core.Abstractions <-> GFramework.Cqrs.Abstractions` 循环引用
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已同步迁移 metadata name
- handler interface 解析改为指向 `GFramework.Cqrs.Abstractions.Cqrs`
- generated registry contract / attribute 改为指向 `GFramework.Cqrs`
- `GFramework.Cqrs/CqrsRuntimeFactory.cs` 已新增,`GFramework.Core/Services/Modules/CqrsRuntimeModule.cs`
`GFramework.Tests.Common/CqrsTestRuntime.cs` 现通过公开工厂接线默认 runtime / registrar
不再跨程序集直接实例化内部实现
- 迁移过程中确认 `GFramework.Core/Coroutine/Extensions/CqrsCoroutineExtensions.cs`
仍依赖 `TaskCoroutineExtensions.AsCoroutineInstruction()``Core` 协程工具链,
因此本轮暂留 `GFramework.Core`,未强行迁移
### 阶段runtime 物理迁移(第一批)验证
- `dotnet build GFramework/GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework/GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`3` 个测试全部通过
- `dotnet build GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`49` 个测试全部通过
- `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureAdditionalCqrsHandlersTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureContextTests"`
- 结果:通过
- 明细:`63` 个测试全部通过
- 并行执行两个会触发编译输出写入同一 `GFramework.Tests.Common.dll` 的一次性 copy retry warning
单次顺序验证通过,确认属于本地并行构建噪音,不是实现缺陷
## 2026-04-15
### 阶段:非默认程序集显式 CQRS 接入
- 建立 `CQRS-REWRITE-RP-009` 恢复点
- `IArchitecture` / `IIocContainer` / `Architecture` / `ArchitectureModules` / `MicrosoftDiContainer`
已新增:
- `RegisterCqrsHandlersFromAssembly(Assembly assembly)`
- `RegisterCqrsHandlersFromAssemblies(IEnumerable<Assembly> assemblies)`
- `ArchitectureBootstrapper` 已改为复用容器公开入口完成默认程序集注册,
让“默认启动路径”和“额外程序集显式接入路径”统一走同一套 CQRS handler 注册逻辑
- `MicrosoftDiContainer` 已按稳定程序集键去重 CQRS handler 注册,
避免同一程序集被默认路径、模块安装或用户初始化阶段重复接入时写入重复 handler 映射
- `ArchitectureAdditionalCqrsHandlersTests` 已新增并锁定两类行为:
- 显式接入扩展程序集时,运行时会通过程序集级 `CqrsHandlerRegistryAttribute` 成功注册 handlers
- 同一额外程序集被重复声明时,不会重复注册 handlers
- `docs/zh-CN/core/cqrs.md``CLAUDE.md` 已同步补充新的显式接入入口与行为说明
### 阶段:非默认程序集显式 CQRS 接入验证
- `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- 备注:仅存在既有 `MA0048` warnings无新增构建错误
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureAdditionalCqrsHandlersTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.RegistryInitializationHookBaseTests"`
- 结果:通过
- 明细:`13` 个测试全部通过
### 下一步
1. 评估如何进一步降低“显式额外程序集接入”场景中的反射回退占比,让更多程序集直接命中生成注册器
2. 评估是否将 CQRS 正式拆分为独立 abstractions 模块与 runtime 实现模块,并明确 `GFramework.Core` 的最终依赖边界
3. 为 future-major 版本准备兼容层真正移除时的 API 清理清单与升级说明
### 当前残留
- 兼容 API、测试目录与部分历史文档仍保留 `Mediator` 前缀,但主入口已新增中性 CQRS 命名,且兼容 API 已进入正式弃用周期
- handler 自动注册当前已具备默认路径与显式扩展路径的统一入口,但仍有一部分程序集只能走反射回退
- solution 级 `dotnet build GFramework.sln -c Release` 仍受既有 Windows NuGet fallback package folder 配置影响
### 下一步建议
1. 优先补齐“非默认程序集”的 CQRS handler generator 接入点,例如显式模块程序集或扩展包程序集
2. 在 source-generator 覆盖范围更明确后,评估是否把现有 CQRS runtime 从 `GFramework.Core` 中继续外提为独立模块
3. 再规划 `RegisterMediatorBehavior``MediatorCoroutineExtensions``ContextAwareMediator*Extensions` 的最终删除窗口
4. 评估是否为兼容层移除提供 analyzer 或 upgrade note以降低 future-major 迁移成本
### 阶段review 跟进微调
- `ArchitectureAdditionalCqrsHandlersTests` 已改为复用现有 `SyncTestArchitecture` + `AddPostRegistrationHook(...)`
测试基建,不再维护仅用于注入初始化逻辑的专用 `AdditionalHandlersTestArchitecture`
- “显式额外程序集去重”回归用例已加强为两个不同 `Assembly` mock 实例,但共享相同 `FullName` 稳定键,
直接锁定 `MicrosoftDiContainer` 的程序集键去重语义,而不是只验证同一 mock 实例的重复注册
### 下一步
1. 运行 `ArchitectureAdditionalCqrsHandlersTests` 定向回归,确认基建复用后显式程序集接入与去重行为保持通过
2. 如需继续收敛测试样板,可再盘点 `Architecture` 相关测试里仍然只用于初始化注入的本地架构类型
### 阶段CQRS 模块边界评估
- 建立 `CQRS-REWRITE-RP-010` 恢复点
- 已完成 Phase 5 的模块边界再评估,并新增 `ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md`
- 本轮结论:
- 将 CQRS 拆为独立 abstractions/runtime 模块是成立的
- 但当前不能直接做“搬项目”式拆分,必须先完成 `GFramework.Core -> CQRS runtime abstraction` 的依赖倒置
- 已确认的硬耦合点:
- `ArchitectureContext` 直接实例化 `CqrsDispatcher`
- `MicrosoftDiContainer` 直接调用 `CqrsHandlerRegistrar`
- `ArchitectureBootstrapper` 在默认启动路径内隐含 CQRS runtime 已存在
- 已确认的消费端兼容压力:
- `CoreGrid-Migration/scripts/cqrs/**` 大量直接依赖 `CommandBase``Abstract*Handler``GFramework.Core.Cqrs.Extensions`
- `GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs` 直接依赖 `GFramework.Core.Cqrs.Extensions`
- 新拆分草案明确了推荐目标:
- `GFramework.Cqrs.Abstractions`:正式 CQRS 契约 + runtime seam
- `GFramework.Cqrs`dispatcher、handler registrar、base handlers、behaviors、extensions
- `GFramework.Core.Abstractions`:转为依赖 `GFramework.Cqrs.Abstractions`
- `GFramework.Core`:只保留架构集成与兼容壳层
### 下一步
1. 进入 Phase 6优先为 runtime 建立 `ICqrsRuntime` / `ICqrsHandlerRegistrar` seam
2.`ArchitectureContext``MicrosoftDiContainer` 改为依赖该 seam而不是直接依赖 `CqrsDispatcher` / `CqrsHandlerRegistrar`
3. 在默认启动行为与消费端源码保持兼容的前提下,补齐针对 seam 改造的定向测试
4. 待 seam 稳定后,再创建 `GFramework.Cqrs.Abstractions` / `GFramework.Cqrs` 项目骨架并推进源码迁移
### 阶段CQRS runtime seam 落地
- 建立 `CQRS-REWRITE-RP-011` 恢复点
- `GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs``ICqrsHandlerRegistrar.cs` 已新增,明确 runtime 调度与 handler 接入的依赖倒置 seam
- `CqrsDispatcher` 已改为实现 `ICqrsRuntime`,并改为在调用时接收 `IArchitectureContext`
- `ArchitectureContext` 已改为解析 `ICqrsRuntime`,不再直接 `new CqrsDispatcher(...)`
- `MicrosoftDiContainer.RegisterCqrsHandlersFromAssemblies(...)` 已改为解析 `ICqrsHandlerRegistrar`,不再直接调用 `CqrsHandlerRegistrar`
- `DefaultCqrsHandlerRegistrar``CqrsRuntimeModule` 已落地,使默认架构启动路径继续自动具备 CQRS runtime 与 handler 注册能力
- `CqrsTestRuntime.RegisterInfrastructure(...)` 已新增,用于为裸 `MicrosoftDiContainer` 测试容器补齐与生产路径一致的 seam 基础设施
- 针对 `Clear()` 后重新接入 handler 的回归已补上“先恢复测试基础设施再验证 dedup 状态重置”的显式步骤
### 阶段CQRS runtime seam 验证
- `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests|FullyQualifiedName~GFramework.Core.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureAdditionalCqrsHandlersTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.RegistryInitializationHookBaseTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`97` 个测试全部通过
### 下一步
1. 进入 Phase 7创建 `GFramework.Cqrs.Abstractions` / `GFramework.Cqrs` 项目骨架
2. 先迁移 `GFramework.Core.Abstractions/Cqrs/*`,并保持现有命名空间,避免同时引入 namespace breaking change
3. 再迁移 `GFramework.Core/Cqrs/*``GFramework.Core.Cqrs.Extensions` 与相关协程扩展
4. 最后处理 `GFramework.SourceGenerators`、顶层 meta package 与消费端 transitive 依赖图
### 阶段CQRS abstractions 项目骨架与纯契约迁移
- 建立 `CQRS-REWRITE-RP-012` 恢复点
- `GFramework.Cqrs.Abstractions``GFramework.Cqrs` 项目骨架已创建,并加入 `GFramework.sln`
- `GFramework.Core.Abstractions` 已改为引用 `GFramework.Cqrs.Abstractions`
- 以下纯 CQRS 契约已迁移到 `GFramework.Cqrs.Abstractions`,同时保持原命名空间 `GFramework.Core.Abstractions.Cqrs*` 不变:
- `IRequest*` / `IStreamRequest*` / `INotification*`
- `IPipelineBehavior`
- `MessageHandlerDelegate`
- `Unit`
- `Command/Query/Request/Notification` 输入与标记契约
- `ICqrsHandlerRegistrar`
- 在实际迁移中确认:
- `ICqrsRuntime -> IArchitectureContext`
- `ICqrsHandlerRegistry -> ILogger`
这两条依赖会导致 `GFramework.Core.Abstractions <-> GFramework.Cqrs.Abstractions` 循环引用风险,因此当前暂不迁出这三类类型:
- `ICqrsRuntime`
- `ICqrsHandlerRegistry`
- `CqrsHandlerRegistryAttribute`
### 阶段CQRS abstractions 迁移验证
- `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- 备注:仅存在既有 `MA0048` warnings`IStreamCommand` / `IStreamQuery` 文件命名)
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests|FullyQualifiedName~GFramework.Core.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureAdditionalCqrsHandlersTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.RegistryInitializationHookBaseTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Core.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`97` 个测试全部通过
### 下一步
1. 先为 `ICqrsRuntime` / `ICqrsHandlerRegistry` 收敛新的边界方案,避免 `Core.Abstractions``Cqrs.Abstractions` 形成循环引用
2. 再开始迁移 `GFramework.Core/Cqrs/*``GFramework.Core.Cqrs.Extensions` 与协程扩展到 `GFramework.Cqrs`
3. 迁移 runtime 后,再处理 `GFramework.SourceGenerators` 与 meta package 的依赖图更新
### 阶段CQRS 聚焦测试拆分与 CI 接入
- 建立 `CQRS-REWRITE-RP-013` 恢复点
- 已新增 `GFramework.Cqrs.Tests` 项目,并加入 `GFramework.sln`
- 以下 CQRS 聚焦测试已从 `GFramework.Core.Tests` 迁移到 `GFramework.Cqrs.Tests`
- `Cqrs/CqrsHandlerRegistrarTests.cs`
- `Coroutine/CqrsCoroutineExtensionsTests.cs`
- `Mediator/MediatorAdvancedFeaturesTests.cs`
- `Mediator/MediatorArchitectureIntegrationTests.cs`
- `Mediator/MediatorComprehensiveTests.cs`
- `GFramework.Cqrs.Tests/GlobalUsings.cs` 已补齐 `NUnit` / `Moq` / `System.*` / `Microsoft.Extensions.DependencyInjection` 等基础编译上下文
- `GFramework.Cqrs.Tests/Logging/TestLogger.cs` 已新增,使新测试项目不再依赖 `GFramework.Core.Tests/Logging/LoggerTests.cs` 中的内嵌测试辅助
- `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 已改为依赖本地 `ContainerRegistrationFixtures.cs`,避免继续编译期引用已迁出的 CQRS 测试类型
- `GFramework/.github/workflows/ci.yml` 已新增 `dotnet test GFramework.Cqrs.Tests ...` step确保 PR CI 覆盖新测试项目
### 阶段CQRS 聚焦测试拆分验证
- `dotnet build GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build`
- 结果:通过
- 明细:`54` 个测试全部通过
- `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 结果:通过
- `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~MicrosoftDiContainerTests|FullyQualifiedName~ArchitectureAdditionalCqrsHandlersTests|FullyQualifiedName~ArchitectureModulesBehaviorTests|FullyQualifiedName~MediatorCompatibilityDeprecationTests"`
- 结果:通过
- 明细:`44` 个测试全部通过
### 下一步
1. 开始迁移 `GFramework.Core/Cqrs/*``GFramework.Core.Cqrs.Extensions` 与相关协程扩展到 `GFramework.Cqrs`
2. 在 runtime 迁移过程中保持 `GFramework.Cqrs.Tests` 绿灯,持续作为 CQRS 模块的主回归项目
3. 等 runtime 物理迁移完成后,再处理 source-generator 引用图与顶层 package/transitive 依赖整理
### 阶段:轻量 handler 上下文基类与 CoreGrid 兼容性收敛
- 建立 `CQRS-REWRITE-RP-015` 恢复点
- `GFramework.Cqrs/Cqrs/CqrsContextAwareHandlerBase.cs` 已新增,作为只依赖 `IContextAware` / `IArchitectureContext` 的轻量 CQRS handler 基类
- 各类 `Abstract*Handler` 已改为继承该轻量基类,不再直接依赖 `GFramework.Core.Rule.ContextAwareBase`
- 新增 `GFramework.Cqrs.Tests/Cqrs/AbstractCqrsHandlerContextTests.cs`,回归锁定“未注入前 fail-fast、注入后可访问 Context、`OnContextReady()` 仍生效”的行为
- 真实消费端验证阶段,`CoreGrid-Migration` 暴露了旧 `GFramework.Core.Abstractions.Cqrs*` 与旧 `GFramework.Core.Cqrs.Extensions` namespace 已不再自动可用的问题
- 为了先恢复迁移验证链路,本轮先采取 consumer 侧最小修复:
- `scripts/GlobalUsings.cs` 已补齐新的 `GFramework.Cqrs.Abstractions.Cqrs*``GFramework.Cqrs.*``GFramework.Core.Cqrs.*` 导入
- `scripts/cqrs/**` 中显式写死旧 `Unit` / `INotification` / `IQuery` 路径的文件已切到新的 `GFramework.Cqrs.Abstractions.Cqrs*`
- `GlobalInputController.cs``PauseMenu.cs``OptionsMenu.cs` 与两个组合 handler 已切到 `GFramework.Cqrs.Extensions.ContextAwareCqrs*Extensions`
### 阶段RP-015 验证
- `dotnet build CoreGrid-Migration/CoreGrid.sln`
- 结果:通过
- 备注:仅存在 `CoreGrid-Migration` 既有 analyzer warnings无新增 CQRS 编译错误
### 下一步
1. 明确是否正式承诺旧 `GFramework.Core.Abstractions.Cqrs*` / `GFramework.Core.Cqrs.Extensions` public namespace 兼容
2. 若不承诺兼容,补充迁移文档并继续沿 `GFramework.Cqrs*` 命名空间推进
3. 若承诺兼容,再单独设计兼容层,而不是让 `CoreGrid-Migration` 这类消费端各自散落修补
4. 无论兼容策略如何,下一阶段都应继续收敛 `ICqrsRuntime -> IArchitectureContext``CqrsCoroutineExtensions -> TaskCoroutineExtensions` 的剩余边界
## 2026-04-16
### 阶段review 收尾修正partial reflection fallback 精确定向补扫)
- 建立 `CQRS-REWRITE-RP-022` 恢复点
- `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 已扩展为可携带精确 fallback handler 类型名清单:
- 生成器能够把“无法在生成代码中直接引用”的 concrete handler 记录为程序集级 metadata
- 当 metadata 为空时runtime 仍保持旧版“整程序集补扫”的兼容语义
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已改为:
- 对不可直接引用的 concrete handler 生成精确的 runtime type-name 清单
- 若消费端 runtime 仅支持旧版无参 fallback marker则自动退回旧语义避免破坏兼容
- 无 unsupported handler 时不再错误输出 fallback marker
- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已改为:
- 生成注册器命中后,优先读取 `CqrsReflectionFallbackAttribute` 中的精确 type-name 清单
- 若清单存在,则按稳定顺序定向 `Assembly.GetType(...)` 解析剩余 handlers而不是重新 `GetTypes()` 扫描整个程序集
- 只有在旧 marker 或手写 marker 未提供清单时,才继续回退到整程序集扫描路径
- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已新增/更新回归:
- 锁定“partial fallback 通过精确 type-name 补扫”
- 断言该路径不会调用 `Assembly.GetTypes()`
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已新增/更新回归:
- 锁定“私有嵌套 handler => 输出精确 type-name fallback marker”
- 锁定“旧版 runtime 仅支持无参 marker 时 => 生成器自动退回旧语义”
### 阶段RP-022 验证
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`5` 个测试全部通过
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- 结果:通过
- 明细:`5` 个测试全部通过
### 下一步
1. 继续扩大 generator 命中面,优先盘点仍会落到“旧 marker / 无精确清单”路径的实际场景
2. 评估是否需要为手写/第三方程序集提供更正式的精确 fallback metadata 入口,而不是只依赖 generator 自动产出
3. 在 handler 注册路径之外,继续评估 dispatch / invoker 链路可否进一步减少运行时反射
## 2026-04-16
### 阶段review 收尾修正(手写/第三方程序集的精确 fallback metadata 入口)
- 建立 `CQRS-REWRITE-RP-023` 恢复点
- `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 已扩展为同时支持:
- `params string[] fallbackHandlerTypeNames`
- `params Type[] fallbackHandlerTypes`
- 无参兼容 marker
- 这使手写或第三方程序集在“有 generated registry但仍需补少量 reflection-only handlers”时可以直接声明可引用的 handler `Type`,避免再走字符串名称回查。
- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已改为按以下优先级处理 fallback metadata
- 先消费显式 `Type` 集合
- 再消费字符串 type-name 清单
- 只有没有任何精确 metadata 时,才继续回退到整程序集 `GetTypes()` 扫描
- registrar 对 direct `Type` fallback 的程序集一致性校验已从对象引用比较收敛为稳定程序集键比较,避免代理/测试场景下把语义等价的程序集误判为不一致。
- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已新增回归:
- 锁定“direct `Type` fallback 不触发 `Assembly.GetType()`
- 锁定“direct `Type` fallback 也不触发 `Assembly.GetTypes()`
### 阶段RP-023 验证
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- 结果:通过
- 明细:`6` 个测试全部通过
### 下一步
1. handler 注册层的“手写 fallback 精确入口”已补齐,后续优先转向 `CqrsDispatcher` 热路径
2. 盘点 `PublishAsync` / `SendAsync` / `CreateStream` 中每次分发都会发生的 `MakeGenericType` / service-type 构造,评估是否用稳定缓存进一步减少反射
3. 若热路径缓存收益成立,再补一组聚焦 `CqrsDispatcher` 的回归或性能守护测试
## 2026-04-16
### 阶段review 收尾修正dispatcher 热路径 service-type 缓存)
- 建立 `CQRS-REWRITE-RP-024` 恢复点
- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已新增三组热路径 service-type 缓存:
- `NotificationHandlerServiceTypes`
- `RequestServiceTypes`
- `StreamHandlerServiceTypes`
- 这些缓存把以下重复工作收敛为“首次构造一次,后续复用”:
- `PublishAsync(...)``typeof(INotificationHandler<>).MakeGenericType(...)`
- `SendAsync(...)` 中 request handler / pipeline behavior 的服务类型构造
- `CreateStream(...)` 中 stream handler 的服务类型构造
- 现有 invoker delegate 缓存继续保留;这轮补的是容器 service lookup 前的泛型服务类型构造缓存,而不是替换已有 invoker cache。
- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 已新增回归:
- 用唯一的 request / notification / stream 测试类型命中三条 dispatcher 路径
- 通过反射读取 dispatcher 内部缓存字典,锁定“首次分发新增一条缓存,后续同类型分发不再增长”
### 阶段RP-024 验证
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
- 结果:通过
- 明细:`7` 个测试全部通过
### 下一步
1. 继续评估 request pipeline / invoker 路径是否还有值得继续缓存或收敛的动态构造
2. 若收益有限,再回到 generator 命中面扩张与 legacy fallback 面积压缩
3. 若后续要做更强性能守护,可考虑补一个更窄的 benchmark 或轻量性能回归,而不是只依赖功能性缓存测试
## 2026-04-16
### 阶段review 收尾修正dispatcher invoker method-definition 静态缓存与统一缓存回归)
- 建立 `CQRS-REWRITE-RP-025` 恢复点
- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已将以下泛型方法定义查找收敛为静态一次解析:
- `InvokeRequestHandlerAsync`
- `InvokeRequestPipelineAsync`
- `InvokeNotificationHandlerAsync`
- `InvokeStreamHandler`
- 这样每种新消息类型首次命中 invoker cache 时,只剩 `MakeGenericMethod(...) + CreateDelegate(...)`,不再重复执行 `GetMethod(...)` 查找。
- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 已扩展为同时覆盖:
- request/no-pipeline 的 `RequestInvokers`
- request/with-pipeline 的 `RequestPipelineInvokers`
- notification 的 `NotificationInvokers`
- stream 的 `StreamInvokers`
- 以及对应的 service-type cache
- 测试通过显式注册 `DispatcherPipelineCacheBehavior` 命中 pipeline 分支,避免当前缓存回归只覆盖无行为请求路径。
### 阶段RP-025 验证
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
- 结果:通过
- 明细:`7` 个测试全部通过
### 下一步
1. dispatcher 侧基础低反射缓存已覆盖 service-type 与 invoker 两层,后续优先判断 pipeline delegate 链的每次分发构造是否仍值得继续收敛
2. 若 pipeline 链构造收益不大,则回到 generator 命中面扩张与 legacy fallback 面积压缩
3. 若需要进一步证明收益,可考虑增加更窄的性能守护而不是继续堆功能性反射缓存测试
## 2026-04-16
### 阶段review 收尾修正generated registry 内部定向反射注册隐藏 handler
- 建立 `CQRS-REWRITE-RP-026` 恢复点
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已将“不可直接 `typeof(...)` 引用的 concrete handler”从程序集级 fallback marker 输出收敛为 generated registry 内部的定向反射注册:
- 对可见 handler 仍保持 `typeof(...)` 直注册路径
- 对私有/不可直接引用但仍可按元数据名从当前程序集重新定位的 handler改为生成 `Assembly.GetType(...)` + `GetInterfaces()` 的本地注册逻辑
- 这类场景不再额外要求 runtime registrar 读取 `CqrsReflectionFallbackAttribute`
- 生成器不再依赖 runtime 是否暴露 `CqrsReflectionFallbackAttribute` 才决定是否产出 registry隐藏 handler 已由生成代码自身覆盖,因此旧版“无 marker 就整程序集放弃生成”的分支已去除。
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已同步更新:
- 私有嵌套 handler 快照改为断言 generated registry 自行调用 `RegisterReflectedHandler(...)`
- 旧版无参 marker 合同场景改为断言“不再输出 legacy marker”
- 完全不存在 fallback marker 合同时,仍断言 generator 会继续产出 registry 并覆盖隐藏 handler
### 阶段RP-026 验证
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`5` 个测试全部通过
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`5` 个测试全部通过
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
- 结果:通过
- 明细:`7` 个测试全部通过
### 下一步
1. 继续盘点 generator 仍未覆盖的 handler 形态,确认是否还存在必须依赖 runtime fallback metadata 的真实残余场景
2. 若 generator 命中面已接近稳定,再回到 dispatcher pipeline delegate 链构造,判断是否值得继续做低反射/低分配收敛
3. 若后续继续参考 `Mediator`,优先找“生成期已知、运行期只做常量时间绑定”的模式,而不是新增另一层宽反射补扫
## 2026-04-16
### 阶段review 收尾修正(隐藏 implementation + 可见 handler interface 的直连接线)
- 建立 `CQRS-REWRITE-RP-027` 恢复点
- 并行/本地盘点结论收敛:
- `CqrsDispatcher` 当前剩余开销主要是容器解析、pipeline 链重建与 `object` 装箱,不再是优先级最高的“反射缺口”
- 因此本轮继续回到 generator 主线,而不是提前做 dispatcher 微优化
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已新增一条更窄的生成路径:
- 当 handler implementation 不能被生成代码直接 `typeof(...)` 引用,但 handler interface 仍可直接引用时
- generated registry 现在只会对 implementation 做一次 `Assembly.GetType(...)`
- 随后直接以 `typeof(IRequestHandler<...>)` / `typeof(INotificationHandler<...>)` / `typeof(IStreamRequestHandler<...>)` 完成注册
- 不再为这类场景额外生成 `GetInterfaces()``IsSupportedHandlerInterface(...)``GetRuntimeTypeDisplayName(...)` 辅助逻辑
- 该调整把“隐藏 handler 的 generated registry 内部处理”继续从“implementation + interface 运行时发现”收敛为“implementation-only lookup + direct interface binding”
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已新增快照回归:
- 锁定“隐藏 implementation + 可见 interface”场景只生成 implementation lookup + direct service registration
- 同时保持已有“隐藏 implementation + 隐藏 contract”场景仍走旧的本地 `RegisterReflectedHandler(...)` 辅助路径
- 本轮实现过程中命中过一次生成器运行时异常:
- 原因是新增分支后 `ImmutableArray` builder 不再总是满容量,`MoveToImmutable()` 触发 `Count equals Capacity` 约束
- 已收敛为 `ToImmutable()`,并同步修正 generated source 分支顺序与 helper 输出条件
### 阶段RP-027 验证
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`6` 个测试全部通过
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- 结果:通过
- 明细:`6` 个测试全部通过
### 下一步
1. 继续盘点 generator 仍会落到 `GetInterfaces()` 或 runtime fallback metadata 的 handler 形态,优先找还能压成“生成期已知 service type + implementation lookup”的场景
2. 若 generator 侧残余面积已经很小,再回到 dispatcher重点考虑是否真的值得为值类型响应装箱与 pipeline 链重建做更复杂的 typed-invoker 优化
3. 若要继续参考 `Mediator`,优先寻找“生成期把接口/handler 绑定静态化”的模式,而不是继续扩大运行时辅助反射工具面
## 2026-04-16
### 阶段review 收尾修正(隐藏 interface 的精确 service type 重建)
- 建立 `CQRS-REWRITE-RP-028` 恢复点
- 在继续盘点 generator 命中面后,本轮把一类仍落到 `GetInterfaces()` 的常见场景继续收窄:
- implementation 不可直接 `typeof(...)`
- handler interface 也不可直接 `typeof(...)`
- 但 open generic contract 与闭包 type arguments 仍然能在生成期被精确表达
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已新增更窄的精确重建路径:
-`IRequestHandler<,>` / `INotificationHandler<>` / `IStreamRequestHandler<,>` 记录 open generic contract
- 对不可直引、但属于当前编译程序集的 type arguments 输出定向 `Assembly.GetType(...)`
- 运行期通过 `MakeGenericType(...)` 重建精确 service type
- 再把该 service type 直接绑定到 implementation而不是先 `GetInterfaces()` 再做支持接口筛选
- 这一轮已覆盖的典型场景包括:
- 私有嵌套 request + 私有 handler + 可见 response
- 可见 implementation + 隐藏消息类型
- 隐藏 implementation + 隐藏消息类型
- 为避免误把“当前仍无法精确表达的 type 形态”静默漏掉,生成器仍保留旧的本地回退辅助分支:
- 当闭包 type arguments 含有当前 helper 还无法精确重建的形态(本轮用“隐藏元素类型数组”做回归)时
- 仍会退回 `RegisterReflectedHandler(...)` + `GetInterfaces()` 的安全路径
- 本轮顺手修复了两处实现细节问题:
- `ImmutableArray` builder 在分支化后不再总是满容量,`MoveToImmutable()` 已收敛为 `ToImmutable()`
- open generic contract 的生成文本已从 `IRequestHandler<TRequest, TResponse>` 修正为合法的 `IRequestHandler<,>` 形式
### 阶段RP-028 验证
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`7` 个测试全部通过
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- 结果:通过
- 明细:`6` 个测试全部通过
### 下一步
1. 继续评估 `RuntimeTypeReference` 是否可以递归覆盖数组/更复杂闭包类型,进一步缩小仍需 `GetInterfaces()` 的残余场景
2. 若 generator 侧只剩极少数复杂形态,再回到 dispatcher重新判断 typed-invoker 去装箱优化是否值得进入实现阶段
3. 若后续继续参考 `Mediator`,优先找“生成器输出精确 closed service type运行期只负责常量时间绑定”的对应模式
## 2026-04-16
### 阶段review 收尾修正(递归 generic type reconstruction
- 建立 `CQRS-REWRITE-RP-029` 恢复点
- 继续按“优先参考 Mediator 的生成期定结构思路,而不是新增运行时宽反射”推进 generator 主线:
- 本轮没有新增任何新的 runtime 接口发现分支
- 而是把 `RuntimeTypeReference` 从“直接类型 + 数组”扩成递归可组合结构
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 现支持递归重建:
- 直接可引用类型
- 当前编译程序集内的隐藏类型定向 `Assembly.GetType(...)`
- 数组类型的 `MakeArrayType(...)`
- 构造泛型类型的 `MakeGenericType(...)`
- 因此以下场景已能直接生成 closed service type而不再退回 `GetInterfaces()`
- `HiddenResponse[]`
- `List<HiddenResponse>` 这类“可引用泛型定义 + 隐藏实参”
- `HiddenEnvelope<string>` 这类“隐藏泛型定义 + 可重建实参”
- 实现过程中额外收敛了两处细节:
- open generic definition 的直接引用改为通过 `ConstructUnboundGenericType()` 输出合法 `typeof(List<>)` / `typeof(IRequestHandler<,>)` 形式
- `TryCreateRuntimeTypeReference(...)` 的失败分支改为显式 `null` 输出,避免新的递归结构继续引入可空警告
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已新增/更新快照回归:
- 隐藏数组响应场景现在锁定为 `MakeArrayType()` 路径
- 新增“隐藏泛型定义 + 常量实参”场景,锁定递归 `MakeGenericType()` 输出
### 阶段RP-029 验证
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`8` 个测试全部通过
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- 结果:通过
- 明细:`6` 个测试全部通过
### 下一步
1. 继续盘点仍必须落回 `GetInterfaces()` 的残余类型形态,优先判断是否只剩极少数低价值边角分支
2. 若 generator 命中面已足够高,再回到 dispatcher评估 typed-invoker 去装箱是否值得作为下一条独立优化线
3. 若继续参考 `Mediator`,优先找“生成器直接输出 closed contract 表”的更强静态化模式,而不是继续给 runtime 补更多推断能力
## 2026-04-16
### 阶段review 收尾修正dispatcher typed invoker cache 去装箱)
- 建立 `CQRS-REWRITE-RP-030` 恢复点
- 在完成 `RuntimeTypeReference` 递归扩展后,本轮先对 dispatcher 做一轮低风险热路径优化:
- 重新盘点后确认,当前 C# 可编译的常见 handler interface 形态里generator 剩余 `GetInterfaces()` 回退已接近低价值尾部
- 因此本轮不再继续堆新的 runtime 类型推断分支,而是转回 dispatcher落实此前待定的 typed-invoker 去装箱优化
- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已将 request 两条调用路径改为按 `TResponse` 分层的强类型委托缓存:
- `RequestInvokerCache<TResponse>` 负责无 pipeline 的 request handler 调用缓存
- `RequestPipelineInvokerCache<TResponse>` 负责带 pipeline 的 request 调用缓存
- 缓存键从 `(RequestType, ResponseType)` 收敛为当前 `TResponse` 层内的 `RequestType`
- `InvokeRequestHandlerAsync<TRequest, TResponse>(...)``InvokeRequestPipelineAsync<TRequest, TResponse>(...)` 现直接返回 `ValueTask<TResponse>`,不再通过 `ValueTask<object?>` 桥接 request 结果
- 这一轮的实现约束是:
- 不改变 notification / stream 现有缓存结构
- 不改变公开 runtime 契约与 observable dispatch 语义
- 只减少 request 热路径里 value-type 响应的装箱/拆箱成本
- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 已同步更新:
- 原有“一次建缓存,多次复用”的 service-type / invoker 回归已适配新的泛型嵌套缓存结构
- 新增 `Dispatcher_Should_Cache_Request_Invokers_Per_Response_Type()`,锁定 `int``string` 响应会分别命中各自的 request invoker cache
- `SetUp()` 现在会显式清空 dispatcher 静态缓存,避免跨测试共享进程级状态导致缓存计数断言漂移
### 阶段RP-030 验证
- `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`47` 个测试全部通过
### 下一步
1. 回到 generator 主线,确认当前剩余 `GetInterfaces()` 本地回退是否只服务于极少数几乎不可达的边角形态
2. 若该尾部分支确实价值有限,优先评估是否直接推进“生成器输出 closed contract 表”的更强静态化方案
3. 若后续还要继续做 dispatcher 微优化,再单独评估 notification / stream 路径是否存在同等级别、且有明确收益的可观测热点
## 2026-04-16
### 阶段review 收尾修正generator mixed registration composition
- 建立 `CQRS-REWRITE-RP-031` 恢复点
- 在回到 generator 主线后,确认当前更值得优先修正的问题不是继续扩 `RuntimeTypeReference`,而是生成器输出粒度过粗:
- `TransformHandlerCandidate(...)` 已能把同一 implementation 上的不同 handler interface 分流到 direct / reflected-implementation / precise-reflected 三条路径
- 但原 `GenerateSource(...)` 仍按 implementation 做互斥分支选择
- 因此一旦同一个 implementation 同时命中多种路径,后面的分支会吞掉前面本已可静态绑定的注册
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已将该逻辑收敛为“按 handler interface 组合输出”:
- direct-only implementation 仍保持原有直注册输出
- 只要同一 implementation 上存在 reflected-implementation 或 precise-reflected 注册,生成器现在会先把三类注册步骤合并到一个有序列表
- 合并后的步骤继续按 `HandlerInterfaceLogName` 做稳定排序,避免因为内部 bucket 化而偏离原先按接口名排序的生成顺序
- 对可见 implementation会复用 `typeof(ImplementationType)` 作为统一 implementation 变量
- 对隐藏 implementation则继续只做一次 `Assembly.GetType(...)`,再在同一块里完成可见接口直绑与精确 `MakeGenericType(...)` 注册
- 这一轮的收敛点是:
- 不新增新的 runtime 宽反射分支
- 也不改变“真正 unresolved 形态仍可整实现退回 `RegisterReflectedHandler(...)`”的安全边界
- 只修正“本可静态绑定的接口被同实现上的另一条更窄路径连带降级”的生成器粒度问题
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已新增两条快照回归:
- `Generates_Mixed_Direct_And_Precise_Registrations_For_Same_Implementation()`
- `Generates_Mixed_Reflected_Implementation_And_Precise_Registrations_For_Same_Implementation()`
- 这两条测试分别锁定“可见 implementation”与“隐藏 implementation”上的混合路径输出防止后续再退回 implementation 级互斥分支
### 阶段RP-031 验证
- `dotnet test GFramework/GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`10` 个测试全部通过
### 下一步
1. 继续盘点当前剩余必须整实现退回 `RegisterReflectedHandler(...)` 的类型形态,确认是否真的只剩低价值尾部分支
2. 若该判断成立,下一步优先评估“生成器直接输出 closed contract 表”的静态化方案,而不是继续给 runtime 增加推断辅助
3. 若要继续参考 `Mediator`,重点比较其生成代码是否已经把“多接口 implementation 的 contract 表”静态化到比当前更细的粒度
## 2026-04-16
### 阶段review 收尾修正partial full-fallback 与 generated-registry accessibility
- 建立 `CQRS-REWRITE-RP-032` 恢复点
- 在继续盘点 generator 剩余 full-fallback 后,确认这一轮真正需要同时收敛的是两件事:
- 先前“是否能在生成注册器里直接 `typeof(...)` 某个类型”的判断只看声明可见性,无法区分“当前语义上下文可见”和“生成注册器顶层上下文可直接书写”
- 即使某个 implementation 上只剩一条未知接口,旧逻辑也会直接清空此前已收集注册,整实现退回 `GetInterfaces()` 发现
- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已将可访问性判断改为:
- `Compilation.IsSymbolAccessibleWithin(symbol, compilation.Assembly, null)`
- 这使生成器现在按“顶层 generated registry 所在上下文”判断能否直接引用类型,而不再把 `protected internal` 之类的声明可见类型粗暴视为一定可直引
- 同程序集私有/隐藏类型仍继续走 metadata-name lookup不影响当前已落地的精确定向注册路径
- `TransformHandlerCandidate(...)` 已不再在遇到单个未知接口时直接 `return` 丢掉已收集注册:
- 改为设置 `RequiresRuntimeInterfaceDiscovery = true`
- 继续把同实现上仍可确定的 direct / reflected-implementation / precise-reflected 注册收集完整
- 生成代码模型已新增“局部补洞”路径:
- `ImplementationRegistrationSpec` 现携带 `RequiresRuntimeInterfaceDiscovery`
- 当同一 implementation 仍残留未知接口时,生成代码会先建立 `knownServiceTypes`
- 先完成所有已知 service type 的注册,并把这些 service type 加入 `knownServiceTypes`
- 再调用 `RegisterRemainingReflectedHandlerInterfaces(...)`,只对 `GetInterfaces()` 中尚未出现在 `knownServiceTypes` 的 supported handler interface 做补注册
- 这一轮的结果是:
- full-fallback 仍保留为最后的安全边界
- 但已从“整实现吞掉已知注册”收敛为“implementation 内部局部补洞”
- 同时修正了外部 `protected internal` 嵌套类型这类边界下,旧判断可能误生成不可编译 `typeof(...)` 的风险
- 为了覆盖这个真实边界,测试基础设施也做了最小扩展:
- `GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs` 新增带 `AdditionalReferences` 的重载
- `MetadataReferenceTestBuilder.cs` 已新增,可把内存源码编成元数据引用
### 阶段RP-032 验证
- `dotnet test GFramework/GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`11` 个测试全部通过
### 下一步
1. 继续盘点当前仍必须依赖 `RegisterRemainingReflectedHandlerInterfaces(...)` 的真实类型形态,确认是否已经收敛到少量低价值边角路径
2. 若该判断成立,下一步优先评估“生成器直接输出 closed contract 表”的更强静态化方案
3. 若继续参考 `Mediator`,重点比较其对“多接口 implementation + 少量未知 contract”的生成期建模是否还比当前更细
## 2026-04-16
### 阶段review 小修RP-033
- 接收四条 follow-up 建议后,先确认都命中当前仓库:
- dispatcher 缓存清理遗漏了 `RequestPipelineInvokerCache<string>`
- generator 可访问性判断默认分支缺少显式假设说明
- `MetadataReferenceTestBuilder` 每次都会重建运行时元数据引用集合
- `RunGenerator(...)` 的编译错误断言消息上下文偏少
- 已完成对应实现:
-`ClearDispatcherCaches()` 中补齐 `string` pipeline invoker cache 清理
-`CanReferenceFromGeneratedRegistry(...)` 默认分支补上“暂按可引用处理,后续可收紧”的注释
-`Lazy<ImmutableArray<MetadataReference>>` 缓存 `TRUSTED_PLATFORM_ASSEMBLIES` 解析结果
- 让生成器测试失败消息输出完整 `Diagnostic.ToString()` 列表
- 本轮属于 review follow-up 小修,没有改动公开 API 或运行时主路径语义;核心目标是减少测试间静态状态泄漏风险,并提升 source-generator 测试的调试效率
- 定向验证已完成:
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过,`11` 个测试全部通过
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
- 结果:通过,`2` 个测试全部通过
### 下一步
1. 回到 generator 主线,继续盘点当前仍必须依赖 `RegisterRemainingReflectedHandlerInterfaces(...)` 的真实类型形态
2. 若该尾部分支价值确实很低,优先评估“生成器直接输出 closed contract 表”的更强静态化方案
## 2026-04-16
### 阶段review 小修RP-034
- 建立 `CQRS-REWRITE-RP-034` 恢复点
- 为了把“继续盘点 residual runtime interface discovery 形态”从人工读代码改成可观察输出,
`GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 现在会在每个仍需
`RegisterRemainingReflectedHandlerInterfaces(...)` 的 implementation 注册块前,生成显式注释:
`// Remaining runtime interface discovery target: ...`
- 注释内容直接复用对应 closed handler interface 的日志显示名,因此不会引入额外 runtime 逻辑,也能和现有 debug 日志口径保持一致
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已补齐断言,锁定当前已知尾部分支案例
`IRequestHandler<Dep.VisibilityScope.ProtectedRequest, Dep.VisibilityScope.ProtectedResponse[]>`
会出现在生成注释中,避免后续又退回“只知道走了 fallback但不知道具体是哪条 contract”的状态
- 本轮没有改动公开 API也没有扩大 runtime 反射面;目标仅是为下一步 closed-contract 评估提供稳定盘点入口
- 补充验证已完成:
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过,`11` 个测试全部通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureAdditionalCqrsHandlersTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.RegistryInitializationHookBaseTests"`
- 结果:通过,`52` 个测试全部通过
### 下一步
1. 基于生成注释清单,继续归类当前 residual contract 是否都集中在“外部程序集不可直引类型”这类低频边角
2. 若判断成立,下一步直接设计 closed-contract 表最小原型,优先验证能否覆盖外部程序集 `protected internal` 嵌套类型,而不是继续扩大 `GetInterfaces()` 补洞
## 2026-04-16
### 阶段主线推进RP-035
- 建立 `CQRS-REWRITE-RP-035` 恢复点
- 已将原 `GFramework.SourceGenerators` 继续按目标模块拆分为:
- `GFramework.Core.SourceGenerators`
- `GFramework.Core.SourceGenerators.Abstractions`
- `GFramework.Cqrs.SourceGenerators`
- `GFramework.Game.SourceGenerators`
- `GFramework.SourceGenerators.Tests` 已改为同时引用新的 `Core/Cqrs/Game` 生成器项目;
`GFramework.Core.Tests``GFramework.Game.Tests` 也已切到新的本地 analyzer 引用链,
其中 `Game.Tests` 额外改为导入 `GFramework.Game.SourceGenerators.targets`
- `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已继续收敛 residual tail
- `RuntimeTypeReferenceSpec` 现可表达“外部程序集 + metadata name”的定向 lookup
- 对外部程序集中的不可直引 `protected nested` 类型,不再只能退回 `RegisterRemainingReflectedHandlerInterfaces(...)`
- 当前已验证 `IRequestHandler<Dep.VisibilityScope.ProtectedRequest, Dep.VisibilityScope.ProtectedResponse[]>`
这类案例会生成 `ResolveReferencedAssemblyType(...)` 精确查找,而不是保留 residual 注释 + `GetInterfaces()` 补洞
- 已删除不再保留的公开 `Mediator` 兼容层:
- `RegisterMediatorBehavior<TBehavior>()`
- `ContextAwareMediatorExtensions`
- `ContextAwareMediatorCommandExtensions`
- `ContextAwareMediatorQueryExtensions`
- `MediatorCoroutineExtensions`
- 相关兼容测试 `MediatorCompatibilityDeprecationTests` 已删除;
`ArchitectureModulesBehaviorTests``RegistryInitializationHookBaseTests` 已同步移除对旧别名的断言/测试替身实现
- 文档已做最小同步:
- `docs/zh-CN/core/cqrs.md` 不再声明旧 `Mediator` 兼容别名
- `CLAUDE.md` 已改为统一使用 `Cqrs` 命名入口
### 阶段RP-035 验证
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
- 结果:通过
- `dotnet build GFramework.Core.SourceGenerators/GFramework.Core.SourceGenerators.csproj -c Release`
- 结果:通过
- `dotnet build GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -c Release`
- 结果:通过
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`11` 个测试全部通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureModulesBehaviorTests|FullyQualifiedName~GFramework.Core.Tests.Architectures.RegistryInitializationHookBaseTests"`
- 结果:通过
- 明细:`8` 个测试全部通过
- `dotnet build GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release`
- 结果:通过
- 备注:仍有一条既有 `GF_ContextRegistration_003` warning属于测试项目里的静态注册分析提示不是本轮拆分或 CQRS 主线回归
### 下一步
1. 继续盘点当前 residual `RegisterRemainingReflectedHandlerInterfaces(...)` 是否还剩下“外部程序集定向 lookup”无法覆盖的真实类型形态
2. 若只剩极少数低价值边角,直接评估 closed-contract 表最小原型,优先彻底消掉 generator 里的 implementation 级 `GetInterfaces()` 尾分支
3. 视需要再决定是否把现仍保留旧 namespace 的 source-generator abstractions/public docs 一并统一改名到 `GFramework.Core.SourceGenerators*`
## 2026-04-17
### 阶段主线推进RP-036
- 建立 `CQRS-REWRITE-RP-036` 恢复点
- `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已删除 generated registry 内部的 implementation 级 `GetInterfaces()` 尾分支:
- 不再生成 `RegisterRemainingReflectedHandlerInterfaces(...)`
- 不再生成 `knownServiceTypes` / `Remaining runtime interface discovery target` 注释 / `GetRuntimeTypeDisplayName(...)` 等仅服务于该尾分支的辅助代码
- 当前合法 C# closed handler contract 统一收敛到 direct / reflected-implementation / precise-reflected 三类注册路径
- 为保持未来未知 Roslyn 类型形态下的保守正确性,生成器现改为:
- 若仍有无法编码进 `RuntimeTypeReferenceSpec` 的 handler contract则保留已知静态注册
- 同时通过程序集级 `CqrsReflectionFallbackAttribute` 对具体 handler implementation 输出 targeted fallback metadata
- 若当前 runtime 合同不提供该 marker则直接放弃生成 registry避免静默漏注册
- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已为多程序集 `protected internal` 嵌套类型场景补齐显式断言:
- 锁定不会重新生成 `RegisterRemainingReflectedHandlerInterfaces(...)`
- 锁定不会重新出现 `Remaining runtime interface discovery target` 注释
- 继续保持 `ResolveReferencedAssemblyType(...)` 精确 lookup 输出
### 阶段RP-036 验证
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.SourceGenerators.Tests.Cqrs.CqrsHandlerRegistryGeneratorTests"`
- 结果:通过
- 明细:`11` 个测试全部通过
### 下一步
1. 继续收敛 CQRS runtime 热路径里剩余的反射绑定点,优先盘点 dispatcher / registrar 是否还有可转成静态缓存或强类型委托的分支
2. 视需要补做 source-generator 命名空间 / 文档收口,决定是否把仍残留旧 `GFramework.SourceGenerators*` 表述的公开文档一并统一到新的模块名
## 2026-04-17
### 阶段主线推进RP-037
- 建立 `CQRS-REWRITE-RP-037` 恢复点
- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已继续收敛 dispatcher 热路径里的首次反射绑定与重复缓存命中:
- notification 路径由分散的 `NotificationHandlerServiceTypes + NotificationInvokers` 合并为 `NotificationDispatchBindings`
- stream 路径由分散的 `StreamHandlerServiceTypes + StreamInvokers` 合并为 `StreamDispatchBindings`
- request 路径由 `RequestServiceTypes + RequestInvokerCache<TResponse> + RequestPipelineInvokerCache<TResponse>` 收敛为 `RequestDispatchBindingCache<TResponse>`
- 新的 dispatch binding 会把服务类型与强类型 invoker 委托绑定到同一缓存项:
- 首次命中仍只做一次必要的 `MakeGenericType(...)` / `MakeGenericMethod(...)` / `CreateDelegate(...)`
- 后续 `SendAsync(...)` / `PublishAsync(...)` / `CreateStream(...)` 会减少热路径上的重复字典查询
- request 绑定继续按 `TResponse` 分层,不回退到 `object` 结果桥接
- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 已同步更新:
- 断言从旧的 service-type / invoker 分散缓存切换为新的 dispatch binding 缓存结构
- 继续锁定 request/no-pipeline、request/with-pipeline、notification、stream 四条路径都是“一次建绑定,多次复用”
- 继续锁定 request 绑定按 `int` / `string` 等不同响应类型分层缓存
### 阶段RP-037 验证
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
- 结果:通过
- 明细:`2` 个测试全部通过
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`47` 个测试全部通过
### 下一步
1. 继续盘点 `CqrsHandlerRegistrar` 初始化路径里剩余的 attribute 读取、registry 实例化与 fallback metadata 解析反射,评估是否值得再做进程级静态缓存
2. 若 registrar 冷路径收益有限,再回到 source-generator 命名空间 / 文档收口,把仍残留旧 `GFramework.SourceGenerators*` 表述的公开文档统一到新模块名
## 2026-04-17
### 阶段主线推进RP-038
- 建立 `CQRS-REWRITE-RP-038` 恢复点
- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已把 registrar 冷路径里重复出现的程序集反射前置解析收敛为进程级缓存:
- `AssemblyMetadataCache` 复用 generated registry attribute 与 reflection fallback metadata 的分析结果
- `RegistryActivationMetadataCache` 复用 registry 类型是否实现 `ICqrsHandlerRegistry`、是否抽象、是否存在可用无参构造等激活分析
- `LoadableTypesCache` 复用未命中 generated registry 时的 `GetTypes()` / `ReflectionTypeLoadException` 恢复结果
- 为避免 `Assembly` 在 mock/proxy 场景下的自定义相等语义干扰缓存命中,上述程序集级缓存统一改为引用相等键
- 运行时行为保持不变:
- generated registry 仍优先于整程序集扫描
- fallback metadata 仍先消费显式 `Type`,再消费 type-name lookup
- 没有 generated registry 时仍按稳定排序扫描可加载 handlers
- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已新增两条缓存回归:
- 同一程序集对象跨两个容器重复接入时,程序集级 registry/fallback metadata 与 `Assembly.GetType(...)` 只解析一次
- 同一程序集对象在 full-scan 路径下跨两个容器重复接入时,`GetTypes()` 只执行一次,且两个容器都能复用首次恢复出的可加载 handler 集合
### 阶段RP-038 验证
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
- 结果:通过
- 明细:`8` 个测试全部通过
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorComprehensiveTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorArchitectureIntegrationTests|FullyQualifiedName~GFramework.Cqrs.Tests.Mediator.MediatorAdvancedFeaturesTests"`
- 结果:通过
- 明细:`53` 个测试全部通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Architectures.ArchitectureAdditionalCqrsHandlersTests"`
- 结果:通过
- 明细:`2` 个测试全部通过
### 下一步
1. 评估 registrar 冷路径里剩余的 registry 实例创建本身是否值得继续压缩;若收益有限,主线可转回 source-generator 命名空间 / 文档收口
2. 盘点公开文档和说明文件中仍残留的旧 `GFramework.SourceGenerators*` / 旧 CQRS 表述,决定是否在下一恢复点统一收口
## 2026-04-17
### 阶段主线推进RP-039
- 建立 `CQRS-REWRITE-RP-039` 恢复点
- 结合 RP-038 后的冷路径现状,判断 registrar 继续下压的边际收益已经明显变小,主线转向公开入口文档收口
- 已完成以下公开入口文档的最小同步:
- `README.md`
- `CLAUDE.md`
- `docs/zh-CN/core/cqrs.md`
- `docs/zh-CN/source-generators/index.md`
- 本轮收口重点:
- 根 README 的模块表、仓库结构与包选择说明已补入 `GFramework.Cqrs` / `GFramework.Cqrs.Abstractions`
- 根 README 与 `CLAUDE.md` 已从旧的单体 `GFramework.SourceGenerators` 模块表述切到当前的 `Core/Game/Godot/Cqrs SourceGenerators` 家族
- `docs/zh-CN/core/cqrs.md` 的代码示例命名空间已切到当前 `GFramework.Cqrs*` 路径,不再继续展示旧 `GFramework.Core.CQRS*` / `GFramework.Core.Abstractions.CQRS*`
- `docs/zh-CN/core/cqrs.md` 的迁移说明已收敛为“直接使用 `RegisterCqrsPipelineBehavior<TBehavior>()`”,不再把已删除的 `RegisterMediatorBehavior<TBehavior>()` 写成仍可替换的并存入口
- `docs/zh-CN/source-generators/index.md` 已从“单一 `GFramework.SourceGenerators` 包”口径改写为“按模块拆分的 Source Generators 家族”口径,同时保留“旧聚合包不存在”的说明
- 期间尝试把 `README.md` / `CLAUDE.md` 分派给 worker 并行处理,但 subagent 上游接口报错,最终由主 agent 本地完成,不影响结果正确性
### 阶段RP-039 验证
- 对以下文件执行定向全文扫描,确认已不存在旧公开入口表述:
- `README.md`
- `CLAUDE.md`
- `docs/zh-CN/core/cqrs.md`
- `docs/zh-CN/source-generators/index.md`
- 扫描结果:
- 已无旧 `GFramework.Core.CQRS*` / `GFramework.Core.Abstractions.CQRS*` 示例命名空间残留
- 已无旧 `GFramework.SourceGenerators` 聚合模块图或聚合包定位残留
- 保留的旧名仅存在于迁移说明和“不存在旧聚合包”的显式说明中,属于有意保留
### 下一步
1. 若继续文档主线,扩大扫描范围到 `docs/zh-CN/**` 与根 README 之外的说明文件,逐步清理更多历史 `GFramework.SourceGenerators*` / `Mediator` 表述
2. 若回到实现主线,可再次评估 registrar 冷路径是否还值得继续压缩,重点看 registry 实例创建本身的收益是否足以覆盖复杂度
## 2026-04-18
### 阶段规则与参考源收口RP-040
- 建立 `CQRS-REWRITE-RP-040` 恢复点
- 已确认用户将当前 CQRS 主线对照用的 `Mediator` 源码放入仓库内 `ai-libs/Mediator`
- `AGENTS.md` 已新增仓库规则:
- `ai-libs/` 为第三方源码参考区
- `ai-libs/**` 默认只读,不允许作为常规实现改动目标
- 后续计划、trace、评审与设计说明引用第三方实现时应优先写明仓库内路径
- CQRS 迁移跟踪文档已同步把“参考 Mediator 成熟实现”的当前执行语义收口为“优先参考 `ai-libs/Mediator`
### 验证
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
- 结果:通过
- 备注:存在既有 `MA0051``MA0158` analyzer warnings无新增构建错误
- `rg -n "ai-libs/|ai-libs/Mediator|只读|第三方源码参考区|第三方项目源码副本" AGENTS.md ai-plan/public/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/traces/cqrs-rewrite-migration-trace.md`
- 结果:通过
- 备注:三处文档都已命中 `ai-libs` 只读规则与 `ai-libs/Mediator` 参考路径
### 下一步
1. 继续推进 CQRS 子系统增强时,把 `ai-libs/Mediator` 作为唯一仓库内参考源使用
2. 若后续需要变更 `ai-libs/Mediator` 内容,单独建“第三方快照同步”任务,不与 GFramework 实现改动共用一个提交
## 2026-04-18
### 阶段文档主线推进RP-041
- 建立 `CQRS-REWRITE-RP-041` 恢复点
- 结合 `feat/cqrs-optimization` 当前主线与 RP-039 的文档收口结果,继续扩大扫描范围到 `docs/zh-CN/**`
- 盘点确认:
- 旧公开示例主要残留在大量文档代码块中的 `using GFramework.SourceGenerators.Abstractions.*;`
- 当前真实公开命名空间已经是 `GFramework.Core.SourceGenerators.Abstractions.*`
- `GFramework.SourceGenerators.Common` 等命名仍对应仓库内部项目,不应作为本轮批量替换目标
- 已批量把文档示例命名空间收口到当前公开路径,并额外手工修正三处叙述性旧口径:
- `docs/zh-CN/source-generators/logging-generator.md`
- `docs/zh-CN/source-generators/context-get-generator.md`
- `docs/zh-CN/api-reference/index.md`
### 验证
- 执行:
- `rg -n "using GFramework\\.SourceGenerators\\.Abstractions\\.|### GFramework\\.SourceGenerators|GFramework\\.SourceGenerators 自动生成|`GFramework\\.SourceGenerators` 现在还会分析" docs/zh-CN`
- 结果:
- 通过
- `docs/zh-CN/**` 已不再残留上述旧公开命名空间与旧聚合说明
### 下一步
1. 若继续文档主线,优先再扫 `docs/zh-CN/api-reference` 与教程入口页,补齐仍过时的 API / 命名空间表述
2. 若切回实现主线,重新盘点 `GFramework.Cqrs` 当前剩余的反射绑定点,并只选择收益明确的优化项推进
## 2026-04-19
### 阶段PR review 技能接入与 PR-253 follow-upRP-042
- 建立 `CQRS-REWRITE-RP-042` 恢复点
- 新增项目级 skill `.codex/skills/gframework-pr-review/`
- 暗号为 `$gframework-pr-review`
- 使用 Windows Git 解析当前分支,并通过公开 GitHub PR 页面定位当前分支对应的 PR
- 直接从 PR HTML 中提取 `Summary by CodeRabbit``Actionable comments posted``Failed checks` 与 CTRF 测试结果
- 不依赖 `gh` CLI也不要求登录态脚本会显式绕过当前 shell 中失效的代理变量
- 用新脚本验证了 PR `#253` 的当前状态:
- `CodeRabbit actionable comments` 仍有 2 条真实待处理项,分别落在 `.codex/skills/gframework-boot/SKILL.md`
`AGENTS.md`
- PR 页面当前无 `Failed Tests`CTRF 测试报告显示 `2103 passed / 0 failed`
- `Failed checks` 仅剩 `Title check` warning属于 GitHub PR 标题元数据问题,不是本地代码缺陷
- 已按 PR `#253` 的公开建议完成本地修正:
- `gframework-boot` 的恢复 heuristics 改为“先检索 `ai-plan/`,再判定 `resume``recovery`
- `AGENTS.md``ai-libs/**` 观察写入 active plan/trace 的要求收窄到“多步/复杂任务或已有 active tracking document”
- `Godot` 模板与 `IController` 文档注释中的旧
`GFramework.SourceGenerators.Abstractions.Rule` 引用已收口到
`GFramework.Core.SourceGenerators.Abstractions.Rule`
### 验证
- `python3 - <<'PY' ... ast.parse(...) ... PY`
- 结果:通过
- 备注:`fetch_current_pr_review.py` 语法正确,且避免了只读文件系统下写 `__pycache__` 的问题
- `python3 .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --pr 253`
- 结果:通过
- 备注:成功解析当前 PR 元数据、2 条 CodeRabbit 待处理评论、1 条 `Title check` warning 和 1 组 CTRF 测试报告
- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release`
- 结果:通过
- 备注:`GFramework.Cqrs.Abstractions``GFramework.Core.Abstractions` 均成功构建0 warning / 0 error
- `rg -n "GFramework\\.SourceGenerators\\.Abstractions\\.Rule" Godot GFramework.Core.Abstractions docs/zh-CN -g '*.cs' -g '*.md'`
- 结果:通过
- 备注:本轮目标范围内已无旧 `Rule` 命名空间残留
### 下一步
1. 若继续沿用当前 PR 驱动修复流程,可直接用 `$gframework-pr-review` 复查后续 PR 的 CodeRabbit 评论与测试状态
2. 若要消除 PR `#253` 的最后一个 `Title check` warning需要在 GitHub 上手动修改 PR 标题;该项不属于仓库内代码修复范围