diff --git a/.codex/skills/gframework-boot/SKILL.md b/.codex/skills/gframework-boot/SKILL.md index a1c5eea5..55e9b55f 100644 --- a/.codex/skills/gframework-boot/SKILL.md +++ b/.codex/skills/gframework-boot/SKILL.md @@ -19,29 +19,31 @@ Treat `AGENTS.md` as the source of truth. Use this skill to enforce a startup se `todos/` and `traces/` directories in listed priority order. 5. If no mapping exists, scan `ai-plan/public//todos/` and `ai-plan/public//traces/` across active topics, and ignore `ai-plan/public/archive/` unless the user explicitly asks for historical context. -6. If `ai-plan/private//` exists and is relevant, treat it as private recovery context for the +6. Treat `ai-plan/public//archive/` as secondary context even for active topics; only read it when the active + todo/trace files point there or when the user explicitly asks for historical detail. +7. If `ai-plan/private//` exists and is relevant, treat it as private recovery context for the current worktree only and do not assume it should be committed. -7. Classify the task state: +8. Classify the task state: - `new`: no matching recovery document exists, or the user is clearly starting fresh work - `resume`: a matching todo or trace exists and the user is continuing that thread - `recovery`: prior work looks partial, interrupted, or ambiguous and the next safe recovery point must be reconstructed -8. Choose the best matching `ai-plan` artifacts: +9. Choose the best matching `ai-plan` artifacts: - Prefer topics explicitly mapped from `ai-plan/public/README.md` - Prefer path names or headings that match the user's task wording - Break ties by most recently updated trace or todo - If ambiguity would materially change implementation, summarize the candidates and ask one concise question -9. Classify the task complexity before deciding on subagents: +10. Classify the task complexity before deciding on subagents: - `simple`: one concern, one file or module, no parallel discovery required - `medium`: a small number of modules, some read-only exploration helpful, critical path still easy to keep local - `complex`: cross-module design, migration, large refactor, or work likely to exceed one context window -10. Apply the delegation policy from `AGENTS.md`: +11. Apply the delegation policy from `AGENTS.md`: - Keep the critical path local - Use `explorer` with `gpt-5.1-codex-mini` for narrow read-only questions, tracing, inventory, and comparisons - Use `worker` with `gpt-5.4` only for bounded implementation tasks with explicit ownership - Do not delegate purely for ceremony; delegate only when it materially shortens the task or controls context growth -11. Before editing files, tell the user what you read, how you classified the task, whether subagents will be used, +12. Before editing files, tell the user what you read, how you classified the task, whether subagents will be used, and the first implementation step. -12. Proceed with execution, validation, and documentation updates required by `AGENTS.md`. +13. Proceed with execution, validation, and documentation updates required by `AGENTS.md`. ## Task Tracking @@ -52,6 +54,9 @@ For multi-step, cross-module, or interruption-prone work, maintain the repositor risks, and the next recovery point. - Update the matching public trace under `ai-plan/public//traces/` with key decisions, delegated scope, and the immediate next step. +- Keep the active todo/trace files concise enough for `boot` to use as default entrypoints. When completed, validated + stages start piling up, move their detailed history into `ai-plan/public//archive/` and leave archive + pointers in the active files. - Move stage-complete artifacts into `ai-plan/public//archive/`, and move completed topics into `ai-plan/public/archive//` so `boot` does not keep reloading stale context. - Keep worktree-private scratch recovery files under `ai-plan/private/` and do not treat them as commit targets. diff --git a/.codex/skills/gframework-boot/references/startup-artifacts.md b/.codex/skills/gframework-boot/references/startup-artifacts.md index 2ffb67d8..8707b26d 100644 --- a/.codex/skills/gframework-boot/references/startup-artifacts.md +++ b/.codex/skills/gframework-boot/references/startup-artifacts.md @@ -16,6 +16,8 @@ - Prefer the newest matching trace when several candidates describe the same feature area. - If one file records a clearer recovery point than a newer but vague file, prefer the clearer recovery point. - Ignore `ai-plan/public/archive/**` unless the user explicitly requests historical recovery context. +- Even inside an active topic, prefer the root `todos/` and `traces/` entry files first; only read `archive/` when the + active files point there or when the user asks for historical detail. - If a matching `ai-plan/private//` directory exists, use it only as private context for the current worktree. ## Complexity Defaults diff --git a/AGENTS.md b/AGENTS.md index 058e2d2a..00311302 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -334,7 +334,8 @@ bash scripts/validate-csharp-naming.sh entry points - `ai-plan/public//todos/`: repository-safe recovery documents for an active topic - `ai-plan/public//traces/`: repository-safe execution traces for an active topic - - `ai-plan/public//archive/`: archived stage-level artifacts that still belong to an active topic + - `ai-plan/public//archive/`: archived stage-level artifacts that still belong to an active topic; prefer + `archive/todos/` and `archive/traces/` when archiving content cut out of the active entry files - `ai-plan/public/archive//`: completed-topic archives that should not be treated as default boot context - `ai-plan/private/`: worktree-private recovery artifacts; keep these untracked and scoped to the current worktree - Contributors MUST keep committed `ai-plan/public/**` content safe to publish in Git history. @@ -352,6 +353,8 @@ bash scripts/validate-csharp-naming.sh `ai-plan/public//todos/` in the same change. - Tracking updates MUST reflect completed work, newly discovered issues, validation results, and the next recommended recovery point. +- Active tracking and trace files are recovery entrypoints, not append-only changelogs. They MUST stay concise enough + for `boot` to locate the current recovery point quickly. - Completing code changes without updating the active tracking document is considered incomplete work. - For any multi-step refactor, migration, or cross-module task, contributors MUST create or adopt a dedicated recovery document under `ai-plan/public//todos/` before making substantive code changes. @@ -361,6 +364,9 @@ bash scripts/validate-csharp-naming.sh trace should record the current date, key decisions, validation milestones, and the immediate next step. - When a stage inside an active topic is fully complete, move the finished artifacts into that topic's `archive/` directory instead of leaving every completed step in the default boot path. +- When completed and validated stages begin to accumulate, contributors MUST archive their detailed history out of the + active `todos/` and `traces/` entry files in the same change. Keep only the current recovery point, active facts, + active risks, immediate next step, and pointers to the relevant archive files in the default boot path. - When a topic is fully complete, move the entire topic directory under `ai-plan/public/archive//` and remove it from `ai-plan/public/README.md` in the same change. - When a task spans multiple commits or is likely to exceed a single agent context window, update both the recovery diff --git a/ai-plan/README.md b/ai-plan/README.md index 5fcab16b..eec9137c 100644 --- a/ai-plan/README.md +++ b/ai-plan/README.md @@ -18,6 +18,7 @@ bootstrapping purpose. - `public//archive/` - Stage-level archive for completed artifacts that still belong to an active topic. - Use this when a topic remains active, but some prior phase no longer belongs in the default boot path. + - Prefer `archive/todos/` and `archive/traces/` when archiving content cut from the active recovery entry files. - `public/archive//` - Completed-topic archive. - Move the entire topic directory here when that work direction is fully complete. @@ -34,6 +35,9 @@ bootstrapping purpose. the user explicitly asks for historical context. - When a worktree changes its active topic set, update `public/README.md` in the same change. - When a stage is complete, move the finished artifacts into `public//archive/`. +- Keep the active `todos/` and `traces/` entry files concise. Once a stage is complete and validated, move its detailed + history into `public//archive/` and leave only the current recovery point, active facts, active risks, + immediate next step, and archive pointers in the default boot path. - When a topic is complete, move the whole topic directory into `public/archive//` and remove it from the shared startup index. diff --git a/ai-plan/public/README.md b/ai-plan/public/README.md index a56d8aa9..86b3db70 100644 --- a/ai-plan/public/README.md +++ b/ai-plan/public/README.md @@ -16,6 +16,10 @@ help the current worktree land on the right recovery documents without scanning - Purpose: govern the `ai-plan/` directory model, startup index, and archive policy. - Tracking: `ai-plan/public/ai-plan-governance/todos/ai-plan-governance-tracking.md` - Trace: `ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md` +- `ai-first-config-system` + - Purpose: continue the AI-First config runtime, generator, and consumer DX work for `GFramework.Game`. + - Tracking: `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md` + - Trace: `ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md` - `cqrs-rewrite` - Purpose: continue the CQRS migration, registry hardening, and related PR follow-up. - Tracking: `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` @@ -23,6 +27,9 @@ help the current worktree land on the right recovery documents without scanning ## Worktree To Active Topic Map +- Branch: `feat/ai-first-config` + - Worktree hint: `GFramework-Ai-First-Config` + - Priority 1: `ai-first-config-system` - Branch: `feat/cqrs-optimization` - Worktree hint: `GFramework-cqrs` - Priority 1: `ai-plan-governance` diff --git a/ai-plan/public/ai-first-config-system/archive/todos/ai-first-config-system-history-through-2026-04-17.md b/ai-plan/public/ai-first-config-system/archive/todos/ai-first-config-system-history-through-2026-04-17.md new file mode 100644 index 00000000..7025f0b0 --- /dev/null +++ b/ai-plan/public/ai-first-config-system/archive/todos/ai-first-config-system-history-through-2026-04-17.md @@ -0,0 +1,601 @@ +# AI-First Config System 执行追踪 + +## 0. 迁移说明 + +### 2026-04-19 + +- 已按 `ai-plan` 治理规范把当前 worktree 的旧本地恢复文档迁移到 `ai-plan/public/ai-first-config-system/` +- 当前分支 `feat/ai-first-config` 已登记到 `ai-plan/public/README.md`,后续 `boot` 可直接命中本主题 +- 迁移后的公共文档已清洗绝对路径与机器相关信息,避免把本地环境细节继续带入可提交恢复文档 + +## 1. 目标 + +基于当前 `GFramework` 设计结论,逐步落地 AI-First 游戏配置系统。 + +当前已确认的产品与架构决策: + +- 配置系统定位为游戏静态内容配置,不是 `GFramework.Core.Configuration` 的扩展 +- 配置系统位于 `GFramework.Game` 侧 +- 运行时、生成器、工具层分离 +- 当前主线优先级调整为 `C# Runtime + Source Generator + Consumer DX` +- VS Code Extension 保持可选工具位,不再阻塞 C# 首发可用版本 +- 独立 `Config Studio` 桌面版暂不进入首发范围 + +## 2. 阶段拆分 + +### Phase 1: Runtime MVP + +目标: + +- 建立只读配置表抽象 +- 建立配置注册表抽象 +- 提供内存实现 +- 补齐基础测试 + +状态:已完成 + +### Phase 2: YAML Loader MVP + +目标: + +- 支持从配置目录加载文本配置 +- 建立最小加载流程 +- 明确主键与错误处理策略 + +状态:已完成 + +### Phase 3: Source Generator MVP + +目标: + +- 从 schema 生成配置类型 +- 生成表访问包装 +- 建立快照测试 + +状态:已完成 + +### Phase 4: VS Code Extension MVP + +目标: + +- 配置树浏览 +- schema 校验 +- 表单编辑入口 +- Raw 编辑入口 + +状态:已完成(首版骨架,后续作为可选增强项维护) + +## 3. 本轮执行项 + +- [x] 重写设计文档,收敛为当前仓库边界可执行版本 +- [x] 将工具层默认方案切换为 VS Code Extension +- [x] 创建执行追踪文件 +- [x] 新增配置抽象接口 +- [x] 新增内存表与注册表实现 +- [x] 增加基础行为测试 +- [x] 运行定向测试并记录结果 +- [x] 新增 YAML 配置目录加载器 +- [x] 验证目录扫描、失败回滚与反序列化错误路径 +- [x] 新增 schema 到配置类型/表包装的 Source Generator +- [x] 增加生成快照测试与基础错误诊断测试 +- [x] 新增 VS Code 插件最小骨架 +- [x] 提供配置树、raw/schema 打开、基础校验和轻量表单入口 +- [x] 将追踪主线从“工具优先”切回 `C# Runtime + Generator + Consumer DX` +- [x] 为生成器新增项目级聚合注册入口 `RegisterAllGeneratedConfigTables()` +- [x] 为生成器新增 `GeneratedConfigCatalog`,统一暴露当前消费者项目内的生成表目录 +- [x] 将消费者端到端测试与 `Architecture` 集成测试切换到聚合注册入口 + +## 4. 面向正式可用的 TODO + +### P0: 必须完成后才建议用于正式游戏项目 + +- [x] Runtime 接入 JSON Schema 驱动校验 +- [x] 运行时拒绝缺失必填字段、未知字段和类型不匹配数据 +- [x] 为运行时 schema 校验补齐回归测试 +- [x] 让 Source Generator 在消费项目中自动拾取 `schemas/**/*.schema.json` +- [x] 提供最小可复制的消费者接入示例与说明 +- [x] 增加一个端到端消费者集成测试 + +### P1: 强烈建议尽快补齐 + +- [x] 扩展最有价值的一批 schema 关键字到 Runtime + Generator + Tooling 的共享子集 +- [x] 开发期热重载:文件监听、局部重载、诊断通知 +- [x] VS Code 插件增加基本自动化测试 +- [x] VS Code 插件支持比“顶层标量字段”更完整的 schema 表单能力 +- [x] VS Code 插件校验逻辑与运行时校验逻辑对齐 + +### P2: 后续增强 + +- [x] 跨表引用校验 +- [x] 批量编辑能力 +- [x] 更丰富的 schema 元数据支持 +- [x] 评估是否需要独立 `Config Studio` + +### 当前已知剩余缺口(按当前 C# 优先级排序) + +- [x] 继续降低多表项目启动样板,例如支持按配置域过滤或分组注册,而不只是“全部注册” +- [x] 提供比文档模板更正式的 C# 启动帮助器,收敛 `ConfigRegistry + YamlConfigLoader + 热重载` 生命周期 +- [ ] 扩展 JSON Schema 支持范围,在已支持 `const`、`not`、`pattern`、`minimum`、`maximum`、`minLength`、`maxLength`、`minItems`、`maxItems`、`exclusiveMinimum`、`exclusiveMaximum`、`multipleOf`、`uniqueItems`、`minProperties`、`maxProperties`、`dependentRequired`、`dependentSchemas`、`allOf` 的基础上补齐下一批关键字与约束映射 +- [x] 评估是否需要为高频查询字段生成可选只读索引,而不破坏当前轻量线性扫描契约 +- [ ] VS Code 表单继续支持更深层对象数组嵌套,减少 raw YAML 回退 +- [ ] 为复杂结构提供比“顶层标量 / 标量数组”更强的批量编辑能力 +- [ ] 在真实 VS Code 宿主中完成对象数组编辑与复杂 schema 的交互式手工验证 + +## 5. 当前实现范围约束 + +当前阶段额外暂不做: + +- 为了工具侧继续扩深层编辑器能力而阻塞 C# 首发可用版本 +- 运行时可写配置或在线编辑工作流 +- 独立桌面 `Config Studio` + +## 6. 最近更新 + +### 2026-04-17 + +- Runtime / Generator / Tooling 共享新增 object-focused `allOf` 支持;当前只接受 object 节点上的 object-typed inline schema 数组,并按 focused constraint block 语义叠加约束,不做属性合并,也不改变生成类型形状 +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 新增 `allOf` 解析、运行时匹配与引用递归采集;非 object 节点声明 `allOf` 会在 schema 解析阶段直接拒绝,`allOf` 匹配成功分支的 ref-table 也继续走结构化去重 +- `GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs` 覆盖 `allOf` 满足 / 不满足、非数组、非 object 值、非 object-typed 条目与非 object 节点声明回归;`GFramework.Game.Tests/Config/YamlConfigSchemaValidatorTests.cs` 新增 `allOf` 引用去重回归 +- `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs`、`ConfigSchemaDiagnostics` 与 `AnalyzerReleases.Unshipped.md` 新增 `GF_ConfigSchema_012`,递归校验 `allOf` 形状并将约束摘要写入 XML 文档 +- `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 新增 `allOf` 文档输出、非 object 节点、非数组与非 object-typed 条目诊断回归 +- `tools/gframework-config-tool/src/configValidation.js`、`extension.js`、`localization.js` 与 `localizationKeys.js` 现支持 `allOf` 解析、校验、本地化与对象 section hint;`configValidation.test.js` 与 `localization.test.js` 已补齐对应回归 +- `docs/zh-CN/game/config-system.md` 与 `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md` 已更新共享关键字清单、`allOf` 语义和工具提示范围;`ai-plan/public/ai-first-config-system/traces/` 已补建本轮执行 trace +- 根据 review 继续收紧 `allOf`:Runtime / Generator / Tooling 现都会拒绝在 `allOf` focused block 中引用父对象未声明字段的不可满足 schema,避免“主链路拒绝 unknown property、allOf 又要求该字段”的死锁形状 +- 根据 review 继续收紧 `allOf` 关键字形状校验:Runtime / Generator 不再静默放过 `allOf.properties` 非对象或 `allOf.required` 非数组的坏 schema,而是直接给出编译期 / 运行时失败 +- 根据 review 继续收紧 `allOf.required` 条目校验:Runtime / Generator 不再静默跳过非字符串或空白项,而是直接报 schema 元数据错误 +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 的对象关键字解析/校验已拆到新的 partial 文件 `YamlConfigSchemaValidator.ObjectKeywords.cs`,把 `dependentRequired`、`dependentSchemas`、`allOf` 与对象属性数量约束从近 5k 行主文件中移出 +- `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 修正 `TryTraverseSchemaRecursively()` 注释缩进,并把深层 `allOf` 递归路径统一为运行时约定的 `reward[allOf[0]]` 形式;`tools/gframework-config-tool/src/configValidation.js` 同步改为相同路径格式 +- `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 新增 `allOf.properties` / `allOf.required` 形状错误回归;`GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs` 同步补齐运行时坏 schema 回归 +- `docs/zh-CN/game/config-system.md` 补充 `allOf` 最小 schema/YAML 示例与兼容性说明,明确“父对象先声明字段,再用 allOf 叠加 required/约束”,且 `allOf` 不做属性合并 +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderAllOfTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_AllOf_Constraint_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_NonObject_Schema_Declares_AllOf|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Is_Not_An_Array|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Is_Not_Object_Valued|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Is_Not_Object_Typed|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Targets_Undeclared_Parent_Property|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_With_Runtime_Aligned_Path_When_AllOf_Inner_Schema_Is_Invalid"` +- 结果:review 补丁后的 JS / Runtime / Generator 定向回归均已通过;`GFramework.Game.Tests` 仍保留既有 `GF_ContextRegistration_003` 告警,但与本轮 `allOf` 修正无关 +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 已执行:`node --check tools/gframework-config-tool/src/extension.js` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --test tools/gframework-config-tool/test/localization.test.js` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderAllOfTests|FullyQualifiedName~YamlConfigSchemaValidatorTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_AllOf_Constraint_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_NonObject_Schema_Declares_AllOf|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Is_Not_An_Array|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Is_Not_Object_Typed"` +- 结果:本轮 JS 与 C# 定向回归均已通过;`GFramework.Game.Tests` 仍保留既有 `GF_ContextRegistration_003` 告警,但与本轮 `allOf` 改动无关 + +- 根据 review 修正 `tools/gframework-config-tool/src/configValidation.js` 的 `dependentSchemas` 触发判断复用路径,`validateParsedConfig()` 与 `matchesSchemaNodeInternal()` 现在共享同一 helper,避免对象条件匹配语义后续漂移 +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 的 `ValidateObjectConstraints()` XML 注释已同步到“数量约束 + dependentRequired + dependentSchemas”新职责,并把条件子 schema 成功匹配时的跨表引用回写改为结构化去重,避免同一字段被重复记录 +- `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 现在会拒绝非 object 节点上的 `dependentSchemas`,补齐 `TryBuildInlineSchemaSummary(..., includeRequiredProperties)` 的 XML 参数注释,并把误登记在 `GFramework.Core.SourceGenerators/AnalyzerReleases.Unshipped.md` 的 `GF_ConfigSchema_001` 到 `GF_ConfigSchema_011` 清理回 `GFramework.Game.SourceGenerators/AnalyzerReleases.Unshipped.md` 所属项目 +- `GFramework.Game.Tests/Config/YamlConfigSchemaValidatorTests.cs` 新增条件子 schema 引用去重回归;`GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 新增“非 object 节点声明 dependentSchemas”诊断回归;`GFramework.Game.Tests/Config/YamlConfigLoaderDependentSchemasTests.cs` 现补齐“trigger 未在同级 properties 中声明时拒绝”分支,并与 `GFramework.Game.Tests/Config/YamlConfigSchemaValidatorTests.cs` 同步去掉 `null!` 测试根目录初始化 +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderDependentSchemasTests|FullyQualifiedName~YamlConfigSchemaValidatorTests"` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderDependentSchemasTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~Run_Should_Write_DependentSchemas_Constraint_Into_Generated_Documentation|FullyQualifiedName~Run_Should_Report_Diagnostic_When_DependentSchemas_Schema_Is_Not_Object_Typed|FullyQualifiedName~Run_Should_Report_Diagnostic_When_NonObject_Schema_Declares_DependentSchemas|FullyQualifiedName~Run_Should_Report_Diagnostic_When_DependentSchemas_Schema_Uses_Format_On_Non_String_Node"` +- 结果:本轮 JS 与 C# 定向回归均已通过;`GF_ConfigSchema_*` 误登记导致的 `RS2002` 警告已消失,当前仅保留既有的 `GF_ContextRegistration_003` 测试项目告警 +- Runtime / Generator / Tooling 共享新增 `dependentSchemas` 关键字支持,用于表达“当对象内某个字段出现时,当前对象还必须额外满足哪个 object 子 schema”,且不改变生成类型形状 +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 现会解析对象级 `dependentSchemas` 映射,并在运行时复用现有递归 matcher 执行条件校验;当前条件子 schema 按 focused constraint block 语义允许未声明的额外同级字段继续存在 +- 新增 `GFramework.Game.Tests/Config/YamlConfigLoaderDependentSchemasTests.cs`,覆盖条件 schema 未满足拒绝、触发字段缺席通过、条件满足且保留额外 sibling 通过,以及坏 schema 拒绝路径 +- `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` 与 `ConfigSchemaDiagnostics` 新增 `dependentSchemas` 递归元数据校验和 XML 文档输出;`GF_ConfigSchema_011` 会拒绝非 object-typed 或坏形状的 `dependentSchemas` +- `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 新增 `dependentSchemas` 文档输出、坏 schema 诊断,以及子 schema 内非法 `format` 递归诊断回归;`GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md` 已登记新规则 +- `tools/gframework-config-tool/src/configValidation.js`、`extension.js`、`localization.js` 与 `localizationKeys.js` 现会解析 `dependentSchemas`、给出中英文校验诊断,并在对象 section hint 中展示条件子 schema 摘要 +- `tools/gframework-config-tool/test/configValidation.test.js` 与 `localization.test.js` 已新增 `dependentSchemas` 的解析 / 校验 / 本地化回归 +- `docs/zh-CN/game/config-system.md` 与 `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md` 已更新共享关键字清单,补充 `dependentSchemas` 的语义与工具提示范围 +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 已执行:`node --check tools/gframework-config-tool/src/extension.js` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --test tools/gframework-config-tool/test/localization.test.js` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderDependentSchemasTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~Run_Should_Write_DependentSchemas_Constraint_Into_Generated_Documentation|FullyQualifiedName~Run_Should_Report_Diagnostic_When_DependentSchemas_Schema_Is_Not_Object_Typed|FullyQualifiedName~Run_Should_Report_Diagnostic_When_DependentSchemas_Schema_Uses_Format_On_Non_String_Node"` +- 结果:JS 与 C# 定向回归均已通过;本轮继续串行执行 `.NET` 验证,避免同一 worktree 的 `obj` 文件竞争 +### 2026-04-16 + +- Runtime / Generator / Tooling 共享新增 `dependentRequired` 关键字支持,用于表达“当对象内某个字段出现时,还必须同时声明哪些同级字段”,且不改变生成类型形状 +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 现会解析对象级 `dependentRequired` 映射,并在运行时与 `contains` / `not` 试匹配路径上统一复用同一套 sibling 依赖语义 +- 新增 `GFramework.Game.Tests/Config/YamlConfigLoaderDependentRequiredTests.cs`,覆盖依赖字段缺失拒绝、触发字段缺席通过、依赖满足通过,以及坏 schema 拒绝路径 +- `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` 与 `ConfigSchemaDiagnostics` 新增 `dependentRequired` 递归元数据校验和 XML 文档输出;`GF_ConfigSchema_010` 会拒绝引用未声明 sibling 字段的坏 schema +- `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 新增 `dependentRequired` 文档输出与坏 schema 诊断回归;`GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md` 已登记新规则 +- `tools/gframework-config-tool/src/configValidation.js`、`localization.js`、`localizationKeys.js` 与 `extension.js` 现会解析 `dependentRequired`、给出中英文校验诊断,并在对象 section hint 中展示 sibling 依赖关系 +- `tools/gframework-config-tool/test/configValidation.test.js` 与 `localization.test.js` 已新增 `dependentRequired` 的解析 / 校验 / 本地化回归 +- `docs/zh-CN/game/config-system.md` 与 `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md` 已更新共享关键字清单,补充 `dependentRequired` 的语义与工具提示范围 +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 已执行:`node --check tools/gframework-config-tool/src/extension.js` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --test tools/gframework-config-tool/test/localization.test.js` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderDependentRequiredTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_DependentRequired_Constraint_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_DependentRequired_Target_Is_Not_Declared"` +- 结果:JS 与 C# 定向回归均已通过;中途确认并避开了并行 `dotnet test` 导致的同一 worktree `obj` 文件竞争问题,后续应继续串行执行相关 .NET 验证 + +- Runtime / Generator / Tooling 共享把 `enum` 从“标量与标量数组元素”扩到“标量、对象、数组节点”;当前对象 `enum` 会忽略字段顺序比较,数组 `enum` 保留元素顺序 +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 现会把 `enum` 候选值预归一化为与 `const` 相同的稳定比较键,并在对象 / 数组 / 标量主校验链上统一执行匹配 +- 新增 `GFramework.Game.Tests/Config/YamlConfigLoaderEnumTests.cs`,覆盖对象 `enum` 的顺序无关匹配、对象值未命中拒绝,以及数组 `enum` 的顺序敏感拒绝 +- `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` 现会把对象 / 数组 `enum` 以原始 JSON 文本写入生成代码 XML 文档;新增 `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorEnumTests.cs` 覆盖对象 / 数组文档输出 +- `tools/gframework-config-tool/src/configValidation.js` 现会为对象 / 数组 `enum` 保存显示文本与 comparable key,并在 VS Code 校验中复用与运行时一致的比较语义;新增 `tools/gframework-config-tool/test/configValidation.enum.test.js` 覆盖元数据解析与顺序语义 +- `docs/zh-CN/game/config-system.md` 与 `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md` 已更新共享关键字清单,明确对象 / 数组 `enum` 当前主要参与校验与 XML 文档输出 +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.enum.test.js` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderEnumTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~SchemaConfigGeneratorEnumTests|FullyQualifiedName~SchemaConfigGeneratorTests"` +- 结果:JS 与 C# 定向回归均已通过;本轮继续沿用 `-p:RestoreFallbackFolders=` 规避旧的 Windows NuGet fallback 目录干扰 + +- 根据 review 修正 `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` 的 `not` 递归 `format` 校验顺序,`not` 子 schema 不再被非数组节点的早退逻辑跳过 +- `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 里的 `GF_ConfigSchema_009` 回归现在对应到真实接线路径,目标是稳定覆盖 `hp[not]` 这类非数组节点诊断 +- 新增 `GFramework.Game.Tests/Config/YamlConfigLoaderNegationTests.cs`,把 `not` 运行时回归从 3800 行主 fixture 中独立出来,并补齐“未命中 not 时允许通过”“对象完整命中时拒绝”“对象仅命中属性子集时允许通过”的对照覆盖 +- `tools/gframework-config-tool/test/configValidation.test.js` 新增对象 `not` 的完整命中失败用例,避免对象分支整体失效时仍被现有子集匹配回归漏过 +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~SchemaConfigGeneratorTests"` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderTests|FullyQualifiedName~YamlConfigLoaderNegationTests"` +- 结果:两条 C# 定向测试均已通过;本轮在命令行显式传入 `-p:RestoreFallbackFolders=` 后,`ResolvePackageAssets` 不再指向旧的 Windows fallback 目录 + +- Runtime / Generator / Tooling 共享新增 `not` 关键字支持,可在不改变生成类型形状的前提下表达“当前值不得匹配某个内联子 schema”的负约束 +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 现会解析 `not` 子 schema、在运行时按主校验链的严格对象语义执行 negated match,并在命中禁用分支时抛出结构化 `ConstraintViolation` +- `GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs` 新增 `not` 命中拒绝与坏 schema(`not` 不是对象)回归,覆盖运行时行为与 schema 解析失败路径 +- `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` 现会把 `not` 写入生成 XML 文档,并把 `not` 子树纳入递归 `format` 元数据校验,避免生成器比运行时 / tooling 更宽松 +- `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 新增 `not` 文档输出与 `not` 子 schema 非法 `format` 诊断回归 +- `tools/gframework-config-tool/src/configValidation.js` 新增 `not` 子 schema 解析、严格对象匹配语义与校验诊断;`localization.js` / `localizationKeys.js` 新增中英文 `not` 诊断文本 +- `tools/gframework-config-tool/test/configValidation.test.js` 与 `localization.test.js` 已新增 `not` 解析 / 校验 / 本地化回归,并覆盖“对象 not 不采用 contains 式子集匹配”的边界 +- `docs/zh-CN/game/config-system.md` 与 `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md` 已更新共享关键字清单,明确 `not` 的当前语义边界 +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --test tools/gframework-config-tool/test/localization.test.js` +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 尝试执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_Value_Matches_Not_Schema|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_Not_Is_Not_An_Object"` +- 尝试执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_Not_Constraint_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_Not_Schema_Uses_Format_On_Non_String_Node"` +- 结果:两条 `dotnet test` 在当前 WSL 环境下都被同一 NuGet fallback 目录错误阻塞,`ResolvePackageAssets` 继续指向某个不存在的宿主 Windows NuGet fallback 目录 + +- Runtime / Generator / Tooling 共享新增稳定字符串 `format: duration` 支持;当前统一支持 `date`、`date-time`、`duration`、`email`、`time`、`uri` 与 `uuid` +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 新增 `duration` 格式映射、day-time duration 正则与运行时校验;当前只接受 `P[n]D`、`PT[n]H[n]M[n]S` 及其组合,秒允许小数,并明确拒绝 `Y` / `M(月)` / `W` 等日历语义片段 +- `GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs` 将 `duration` 纳入共享 format 成功 / 失败参数化回归,并校验未支持格式诊断文本同步包含新白名单成员 +- `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` 将 `duration` 纳入生成阶段共享白名单;`SchemaConfigGeneratorTests` 现在验证 `format = 'duration'` XML 文档输出与未支持格式诊断文本更新 +- `tools/gframework-config-tool/src/configValidation.js` 新增 `duration` 解析白名单与 day-time duration 校验;`configValidation.test.js` 同步覆盖接受 / 拒绝、schema 元数据提取和未支持格式提示文本 +- `docs/zh-CN/game/config-system.md` 与 `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md` 已更新稳定 `format` 子集说明,并明确 `duration` 只支持 day-time 子集 +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 尝试执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Accept_Supported_String_Format|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_String_Does_Not_Match_Supported_Format|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_String_Format_Is_Not_Supported"` +- 尝试执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_Supported_Duration_Format_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_String_Format_Is_Not_Supported"` +- 结果:两条 `dotnet test` 在当前 WSL 环境下仍被同一 NuGet fallback 目录错误阻塞,`ResolvePackageAssets` 解析到某个不存在的宿主 Windows NuGet fallback 目录;本轮依旧无法完成 C# 定向测试 + +- Runtime / Generator / Tooling 共享新增稳定字符串 `format: time` 支持;当前统一支持 `date`、`date-time`、`email`、`time`、`uri` 与 `uuid` +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 新增 `time` 格式映射、显式时区偏移正则与运行时校验;当前只接受 `HH:mm:ss[.fraction](Z|±HH:mm)`,避免 time-only 文本在不同宿主上隐式补默认日期或本地时区 +- `GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs` 将 `time` 纳入共享 format 成功 / 失败参数化回归,并校验未支持格式诊断文本同步包含新白名单成员 +- `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` 将 `time` 纳入生成阶段共享白名单;`SchemaConfigGeneratorTests` 现在验证 `format = 'time'` XML 文档输出与未支持格式诊断文本更新 +- `tools/gframework-config-tool/src/configValidation.js` 新增 `time` 解析白名单与 RFC 3339 full-time 校验;`configValidation.test.js` 同步覆盖接受 / 拒绝、schema 元数据提取和未支持格式提示文本 +- `docs/zh-CN/game/config-system.md` 与 `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md` 已更新稳定 `format` 子集说明,并明确 `time` 需要显式时区偏移 +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 尝试执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Accept_Supported_String_Format|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_String_Does_Not_Match_Supported_Format|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_String_Format_Is_Not_Supported"` +- 尝试执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_Supported_Time_Format_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_String_Format_Is_Not_Supported"` +- 结果:两条 `dotnet test` 在当前 WSL 环境下都被同一 NuGet fallback 目录错误阻塞,`ResolvePackageAssets` 解析到某个不存在的宿主 Windows NuGet fallback 目录;即使额外尝试 `-p:RestoreFallbackFolders=` 也未绕过该环境问题,本轮无法完成 C# 定向测试 + +### 2026-04-11 + +- Runtime / Generator / Tooling 共享新增稳定字符串 `format` 子集支持:当前统一支持 `date`、`date-time`、`email`、`uri` 与 `uuid` +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 现在会在 schema 解析阶段拒绝不支持的 `format`,并在运行时对字符串值执行跨端一致的格式校验;`uri` 额外要求显式 scheme,避免把普通路径误判成绝对 URI +- `GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs` 新增 `format` 成功加载、格式违规拒绝与坏 schema 拒绝回归,覆盖当前共享子集的接受 / 失败路径 +- `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` 与 `ConfigSchemaDiagnostics` 新增 `format` 元数据校验和 XML 文档输出;`GF_ConfigSchema_009` 会在生成阶段拒绝未纳入共享子集的字符串格式 +- `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 新增 `format` 文档输出与坏 schema 诊断回归;`AnalyzerReleases.Unshipped.md` 已登记新规则,避免 Roslyn release tracking 警告 +- `tools/gframework-config-tool/src/configValidation.js`、`extension.js`、`localization.js` 与 `localizationKeys.js` 同步补齐 `format` 解析、诊断、本地化文案和表单 hint;`configValidation.test.js` 新增校验 / 元数据回归 +- `docs/zh-CN/game/config-system.md` 已更新共享关键字清单、运行时拒绝路径和表单元数据说明,明确当前 `format` 只支持稳定子集 +- 补齐 `format` 剩余 schema 级分支回归:`YamlConfigLoaderTests` 新增“非字符串节点声明 format”与“format 不是字符串值”的 `SchemaUnsupported` 测试,避免后续重构时静默放宽 schema 约束 +- `SchemaConfigGenerator` 现补上根节点与数组 `contains` 子 schema 的 `format` 校验,防止同一份 schema 在生成器与运行时/工具侧出现接受范围漂移;`SchemaConfigGeneratorTests` 新增对应的 `GF_ConfigSchema_009` 诊断回归 +- `tools/gframework-config-tool/src/configValidation.js` 现在会在任意非字符串 schema 节点声明 `format` 时直接抛错,不再静默忽略;`configValidation.test.js` 同步新增“非字符串节点”和“非字符串 format 值”回归 +- `tools/gframework-config-tool/src/configValidation.js` 的日期校验不再依赖 `Date.UTC(...)`,改为显式年份边界、闰年和每月天数判断,修复 `0000-xx-xx` 与低位年份在 JavaScript 里的特殊年份归一化偏差;`configValidation.test.js` 同步补上 `0000-01-01` 的 `date` 回归 +- `docs/zh-CN/game/config-system.md` 补充 `x-gframework-ref-table` 与 UI 展示名 `ref-table` 的对应说明,消除文档关键字命名歧义 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Accept_Supported_String_Format|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_String_Does_Not_Match_Supported_Format|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_String_Format_Is_Not_Supported"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_Supported_String_Format_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_String_Format_Is_Not_Supported"` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --test tools/gframework-config-tool/test/localization.test.js` +- 已执行:`node --check tools/gframework-config-tool/src/extension.js` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_Format_Is_Used_On_Non_String_Property|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_Format_Is_Not_A_String"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_Root_Node_Uses_Format|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_Contains_Schema_Uses_Format_On_Non_String_Node|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_String_Format_Is_Not_Supported"` +- 已执行:`node --check tools/gframework-config-tool/src/configValidation.js` +- 尝试执行:`bash scripts/validate-csharp-naming.sh` +- 结果:脚本在当前 WSL + Windows worktree 环境下仍命中 Git 路径翻译错误;本轮未完成该项校验,后续如需跑该脚本,应继续按仓库约定通过宿主 Windows Git 显式绑定脚本内的 `git` 调用 + +### 2026-04-10 + +- Runtime / Generator / Tooling 共享新增 `const` 关键字支持 +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 现在会在 schema 解析阶段预归一化 `const`,并对标量、对象、数组统一复用稳定比较键做运行时校验 +- `YamlConfigLoaderTests` 新增标量、数组与嵌套对象 `const` 回归用例,覆盖固定值、固定序列和固定对象结构的拒绝路径 +- `SchemaConfigGenerator` 现在会把 `const` 写入生成 XML 文档约束说明,`MonsterConfig.g.txt` 快照已更新 +- `tools/gframework-config-tool/src/configValidation.js` 现在会解析 `const` 元数据,并在 VS Code 校验中按与运行时一致的对象 / 数组 / 标量比较语义给出诊断 +- `tools/gframework-config-tool/src/extension.js` 与本地化文本已补齐 `const` hint 展示;标量字段在 YAML 缺值时会优先回填 schema 固定值 +- `docs/zh-CN/game/config-system.md` 已更新共享关键字清单、运行时拒绝路径和表单元数据说明 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGeneratorSnapshotTests"` +- 已执行:`cd tools/gframework-config-tool && bun run test` +- 下一恢复点:在 `const` 收敛后继续评估下一批共享关键字时,优先比较 `contains / minContains / maxContains` 和 `format` 的投入产出,而不是回到工具侧深层 UI 扩展 +- Runtime / Generator / Tooling 对 `const` 的边界行为进一步对齐:运行时允许空对象 `const: {}`,生成器不再忽略空字符串 `const: ""`,VS Code 工具的字符串 `const` 诊断与 hint 改为保留 JSON 风格展示值 +- `tools/gframework-config-tool/src/configValidation.js` 新增 ordinal 字符串比较辅助,替换对象 `const`、对象值 comparable key 以及批量字段列表中的 `localeCompare(...)`,避免与运行时 `string.CompareOrdinal(...)` 在非 ASCII 键名上出现排序语义漂移 +- `tools/gframework-config-tool/src/extension.js` 改为对 `constValue` 使用 `??` / `!== undefined` 分支,避免空字符串固定值被错误回退到 `defaultValue` 或被 hint 渲染逻辑跳过 +- `YamlConfigLoaderTests` 新增空对象 `const` 成功加载回归测试;`SchemaConfigGeneratorTests` 新增空字符串 `const` XML 文档回归测试;`tools/gframework-config-tool/test/configValidation.test.js` 新增 ordinal 排序、空字符串 `const` 原始值/展示值与示例 YAML 回归测试 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Accept_Empty_Object_Schema_Const|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_Nested_Object_Does_Not_Match_Schema_Const|FullyQualifiedName~YamlConfigLoaderTests.LoadAsync_Should_Throw_When_Scalar_Value_Does_Not_Match_Schema_Const"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Preserve_Empty_String_Const_In_Generated_Documentation"` +- 已执行:`cd tools/gframework-config-tool && bun run test` +- 已执行:`node --check tools/gframework-config-tool/src/extension.js` +- 补齐 `contains` 剩余运行时契约分支:`YamlConfigLoaderTests` 新增 `contains` 非对象、嵌套数组 `contains` 的 `SchemaUnsupported` 回归,以及 `matchingCount == minContains == maxContains` 的成功边界回归 +- 修正 `YamlConfigSchemaValidator` 在 `contains` 试匹配路径中遗漏引用收集的问题:匹配成功的 `contains` 子树现在会把 `x-gframework-ref-table` 使用量写回 collector,同时 `CollectReferencedTableNames(...)` 也会递归到 `ArrayConstraints.ContainsConstraints.ContainsNode` +- 新增 `contains + ref-table` 运行时回归:初次加载会拒绝仅声明在 `contains` 子 schema 里的缺失目标引用;热重载也会把这类目标表纳入依赖闭包并在依赖破坏时整体回滚 +- `tools/gframework-config-tool/src/extension.js` 将 `contains` 摘要抽到 `containsSummary.js`,改为复用现有本地化 hint 文案,避免中文界面出现 `const / enum / pattern / ref / item` 的英文硬编码 +- `tools/gframework-config-tool/test/containsSummary.test.js` 新增摘要本地化回归,覆盖中文摘要与空摘要回退文案 +- `YamlConfigSchemaValidator` 的 `contains` 试匹配现在会在对象节点上允许当前 `contains` 子树未声明的额外字段,避免对象数组使用“声明属性子集”匹配时被 `UnknownProperty` 误判;主加载链仍保持未知字段即失败 +- `YamlConfigLoaderTests` 新增对象数组 `contains` 子集匹配成功回归,覆盖 `{ id: 1, weight: 2 }` 这类对象在 `contains` 只声明 `id` 时仍会计入匹配数 +- `tools/gframework-config-tool/src/containsSummary.js` 新增 `buildContainsHintLines(...)`,让表单 hint 在仅声明 `contains` 时也显式展示运行时默认语义 `minContains = 1` +- `tools/gframework-config-tool/src/configValidation.js` 补齐与 C# runtime 对齐的 schema 级拒绝:拒绝 nested-array `contains`,并在 `contains` 存在时按 `effectiveMinContains` 校验反转的 `minContains` / `maxContains` +- `tools/gframework-config-tool/test/configValidation.test.js` 与 `containsSummary.test.js` 新增默认 `minContains = 1`、nested-array `contains`、`maxContains: 0` 与显式反转边界的 Node 回归 + +### 2026-03-30 + +- 完成设计文档重写并收敛为 `Runtime / Generator / Tooling` 三层 +- 确认工具层 MVP 采用 VS Code Extension +- 开始 Runtime MVP 实现 +- 在 `GFramework.Game.Abstractions/Config` 中新增 `IConfigTable`、`IConfigRegistry`、`IConfigLoader` +- 在 `GFramework.Game/Config` 中新增 `InMemoryConfigTable` 与 `ConfigRegistry` +- 在 `GFramework.Game/Config` 中新增 `YamlConfigLoader`,支持按目录注册 YAML 配置表 +- 在 `GFramework.Game/GFramework.Game.csproj` 中新增 `YamlDotNet` 依赖 +- 在 `GFramework.Game.Tests/Config` 中新增基础行为测试与 YAML loader 测试,共通过 11 个测试 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~Config"` +- 在 `GFramework.SourceGenerators/Config` 中新增 `SchemaConfigGenerator`,支持从 `*.schema.json` 生成 `Config` 类型与 `Table` 包装 +- 在 `GFramework.SourceGenerators/Diagnostics` 中新增 `ConfigSchemaDiagnostics` +- 在 `GFramework.SourceGenerators/GFramework.SourceGenerators.csproj` 中新增 `System.Text.Json` 依赖 +- 在 `GFramework.SourceGenerators.Tests/Config` 中新增 schema 生成器测试驱动、快照测试和诊断测试,共通过 2 个测试 +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfig"` +- 在 `tools/gframework-config-tool` 中新增 VS Code 插件骨架 +- 在 `tools/gframework-config-tool/src/extension.js` 中实现配置树、打开 raw/schema、基础 schema 校验与顶层标量表单预览 +- 已执行静态校验:`node --check tools/gframework-config-tool/src/extension.js` +- 已执行静态校验:`jq empty tools/gframework-config-tool/package.json` +- 尚未在真实 VS Code 宿主中做交互式手工验证 + +### 2026-03-31 + +- 开始补齐面向正式可用的第一批缺口 +- 已将 P0 项拆分为运行时 schema 校验、生成器接入自动化和消费者接入说明三个方向 +- 在 `GFramework.Game/Config` 中新增 `YamlConfigSchemaValidator`,为 `YamlConfigLoader` 提供最小运行时 schema 校验 +- `YamlConfigLoader` 新增绑定 schema 的 `RegisterTable` 重载 +- 补充缺失必填字段、未知字段、标量类型和数组元素类型的回归测试 +- `GeWuYou.GFramework.SourceGenerators.targets` 默认收集 `schemas/**/*.schema.json` +- 新增 `docs/zh-CN/game/config-system.md` 作为最小接入说明 +- `YamlConfigLoader` 新增 `EnableHotReload(...)` 开发期热重载入口 +- 热重载支持监听配置目录与绑定 schema 文件,并按表粒度刷新注册表 +- 补充配置变更成功重载与 schema 变更失败保留旧表的回归测试 +- 将 VS Code 插件中的纯校验逻辑拆分到独立模块,便于在 Node 环境中直接回归测试 +- 新增 `tools/gframework-config-tool/test/configValidation.test.js` +- VS Code 插件校验补齐未知字段、数组元素类型和顶层 YAML 结构检查 +- VS Code 表单编辑补齐顶层标量数组支持,复杂对象仍回退到 raw YAML + +### 2026-04-01 + +- 在 `GFramework.Game/Config` 中扩展运行时 schema 校验,支持通过 `x-gframework-ref-table` 声明跨表引用 +- `YamlConfigLoader` 在初次加载与热重载时都会执行跨表引用校验,并在依赖表变更导致引用失效时整体回滚受影响表 +- `IConfigRegistry` / `ConfigRegistry` 新增弱类型 `TryGetTable` 入口,供运行时跨表校验读取目标表元数据 +- 在 `GFramework.Game.Tests/Config` 中补充跨表引用成功、缺失目标、数组引用与热重载回滚回归测试 +- 更新 `docs/zh-CN/game/config-system.md`,补充跨表引用 schema 扩展和热重载行为说明 +- `tools/gframework-config-tool` 新增按配置域批量编辑入口,可对多份 YAML 统一写入顶层标量字段和标量数组 +- 在 `tools/gframework-config-tool/test` 中补充批量编辑辅助逻辑测试 +- 运行时 schema 校验新增标量 `enum` 与数组元素 `enum` 约束 +- VS Code 插件新增对 `title`、`description`、`default`、`enum` 与 `x-gframework-ref-table` 的元数据展示 +- Source Generator 将 schema 元数据写入生成类型 XML 文档,并在可安全映射时把 `default` 转成属性初始值 +- 完成独立 `Config Studio` 评估:当前阶段维持 `VS Code Extension` 为主,不建议额外启动桌面工具项目 +- 运行时 schema 校验升级为递归模型,支持嵌套对象、对象数组和深层必填 / 未知字段 / enum 校验 +- `SchemaConfigGenerator` 升级为生成嵌套配置类型,支持对象属性和对象数组项的强类型代码生成 +- VS Code 插件校验升级为递归模型,支持嵌套对象和对象数组结构诊断 +- VS Code 表单入口支持嵌套对象字段编辑 +- VS Code 表单入口补齐对象数组编辑,支持新增 / 删除对象项,以及对象项中的标量、标量数组和嵌套对象字段写回 +- 对象数组编辑新增结构化写回测试;更深层对象数组嵌套仍继续保持 raw YAML 回退 +- 在 `GFramework.Game.Tests`、`GFramework.SourceGenerators.Tests` 和 `tools/gframework-config-tool/test` 中补充递归 schema 子集回归测试 + +### 2026-04-02 + +- 收敛 VS Code 插件内部的逻辑路径辅助,统一 `joinPropertyPath / joinArrayIndexPath / joinArrayTemplatePath` 的使用 +- 为校验消息引入共享 `ValidationMessageKeys`,减少本地化 key 漂移 +- 放宽 YAML 注释提取与最小 YAML 解析对复杂 key 的支持,覆盖带引号、短横线和空格的 key +- 中文本地化调整为:简体中文使用 `zh-CN` 字典,繁体中文语言环境暂回退英文,避免错误显示简体文本 +- “从 schema 初始化” 改为覆盖前弹出确认,避免静默丢失尚未保存的表单修改 +- 在 `tools/gframework-config-tool/test` 中补充复杂 key 与繁中回退回归测试 +- 新增 `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md`,作为下一阶段 C# 体验主清单 + +### 2026-04-03 + +- `SchemaConfigGenerator` 新增生成注册/访问辅助代码:为每个 schema 产出 `YamlConfigLoader` 注册扩展、`IConfigRegistry` 强类型访问扩展,以及表名 / 配置目录 / schema 路径常量 +- `docs/zh-CN/game/config-system.md` 更新为优先使用生成辅助的接入方式,减少消费端手写字符串和 key selector +- `GFramework.SourceGenerators.Tests/Config` 快照测试新增 `MonsterConfigBindings.g.cs` 校验,并补充最小运行时 stub +- 已执行:`dotnet build GFramework.SourceGenerators/GFramework.SourceGenerators.csproj -c Release` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfig"` +- `GFramework.Game.Abstractions/Config` 新增 `ConfigLoadFailureKind`、`ConfigLoadDiagnostic` 与 `ConfigLoadException`,为配置加载失败提供稳定的结构化诊断字段 +- `YamlConfigLoader` 与 `YamlConfigSchemaValidator` 统一改为抛出 `ConfigLoadException`,覆盖目录缺失、schema 读取/解析、字段级 schema 校验、反序列化和跨表引用失败 +- `YamlConfigLoaderTests` 新增对 `FailureKind / TableName / YamlPath / SchemaPath / DisplayPath / ReferencedTableName / RawValue` 的断言,热重载失败回调也改为验证结构化诊断 +- `docs/zh-CN/game/config-system.md` 补充 `ConfigLoadException.Diagnostic` 的使用方式,说明热重载失败回调如何读取结构化字段 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests"` +- `GFramework.Game.Tests` 现在通过仓库内 `.targets` 自动拾取 `schemas/**/*.schema.json`,并新增真实消费者端到端测试,覆盖生成代码编译、`YamlConfigLoader` 注册辅助、运行时加载和 `IConfigRegistry` 强类型访问 +- Runtime / Generator / Tooling 共享新增 `minimum`、`maximum`、`minLength`、`maxLength` 子集支持:运行时与 VS Code 校验会拒绝违反范围/长度约束的值,生成代码 XML 文档会同步暴露这些约束 +- `SchemaConfigGenerator` 进一步将 `*ConfigBindings` 中的元数据收敛为 `Metadata` 容器,并补充 `ConfigDomain` 常量,同时保留顶层 `TableName / ConfigRelativePath / SchemaRelativePath` 兼容别名 +- `GeneratedConfigConsumerIntegrationTests` 与 `SchemaConfigGenerator` 快照测试新增对 `Metadata.*` 与 `ConfigDomain` 的回归覆盖 +- `docs/zh-CN/game/config-system.md` 补充统一读取 `MonsterConfigBindings.Metadata` 的推荐写法,减少后续消费者继续分散引用裸常量 +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGeneratorSnapshotTests"` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~GeneratedConfigConsumerIntegrationTests"` +- `SchemaConfigGenerator` 新增生成期跨表引用辅助:`*ConfigBindings` 现在会生成 `ReferenceMetadata`、`References.All`、按字段路径命名的引用元数据成员,以及 `TryGetByDisplayPath(...)` +- `SchemaConfigGenerator` 快照测试补充对象数组内引用字段覆盖,验证生成代码会暴露 `dropItems` 与 `phases[].monsterId` 这类引用路径 +- `GeneratedConfigConsumerIntegrationTests` 增加对空引用集合与 `TryGetByDisplayPath(...)` 的编译期回归,确保没有 ref-table 的普通 schema 也具备稳定 API +- `docs/zh-CN/game/config-system.md` 补充如何通过 `MonsterConfigBindings.References` 读取引用目标表、值类型和是否为集合 +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGeneratorSnapshotTests"` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~GeneratedConfigConsumerIntegrationTests"` + +### 2026-04-09 + +- 为 `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 做 SonarQube 可维护性清理,拆分 `BuildComparableNodeValue(...)` 与 `ValidateScalarConstraints(...)` 的递归 / 约束校验分支,降低主方法认知复杂度 +- `YamlConfigScalarConstraints` 改为聚合 `YamlConfigNumericConstraints` 与 `YamlConfigStringConstraints`,避免继续使用 9 参数构造函数 +- `YamlConfigSchemaNode` 改为通过 `CreateObject(...)`、`CreateArray(...)` 与 `CreateScalar(...)` 命名工厂创建,避免继续通过多语义长参数构造函数拼装不同节点模式 +- 更新 `AGENTS.md`:补充 SonarQube 复杂度 / 长参数处理规范,并记录在当前 WSL + Windows worktree 环境下优先使用 Windows `git`(如 `git.exe`)的仓库约定 +- 修正 `multipleOf` 的判定实现:运行时与 JS 工具优先按十进制字面量做精确整倍数判断,仅在无法精确归一化时才退回浮点容差兜底,避免同时出现大数十进制步进误拒和大数量级非整倍数误放 +- `YamlConfigLoaderTests` 新增大数十进制步进回归用例,覆盖 `10000000.2` 配合 `multipleOf: 0.1` 时运行时会接受应当合法的十进制整倍数 +- `YamlConfigLoaderTests` 新增大数量级非整倍数回归用例,覆盖 `1000000000000.4` 配合 `multipleOf: 1` 时运行时仍会拒绝明显非法输入的场景 +- `tools/gframework-config-tool/test/configValidation.test.js` 新增对应的 VS Code 工具回归用例,确保编辑器侧与运行时共享同一组大数 `multipleOf` 边界行为 +- 更新 `AGENTS.md`:新增单文件默认应控制在约 800-1000 行内的规则;超过该范围时必须先检查是否应按职责拆分 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests"` +- 下一恢复点:继续沿清理后的 validator 结构扩展下一批 JSON Schema 关键字时,优先复用现有“按语义拆分 helper / 分组约束对象 / 命名工厂”的模式,避免再次累积 SonarQube 复杂度与长参数问题 +- `docs/zh-CN/game/config-system.md` 新增“推荐接入模板”,补齐项目目录布局、仓库内 `csproj` 引用模板、`GFrameworkConfigSchemaDirectory` 覆盖方式、初始化入口、强类型读取入口和开发期热重载模板 +- 文档中的热重载示例改为优先使用 `RegisterMonsterTable()`,避免推荐方案与示例重新回到手写字符串注册 +- `GFramework.Game/Config` 新增 `YamlConfigTableRegistrationOptions` 与 `YamlConfigHotReloadOptions`,为 `RegisterTable(...)` 和 `EnableHotReload(...)` 提供稳定的 options 入口 +- `YamlConfigLoader` 现有重载全部委托到新的 options API,保留兼容调用方式,同时为未来新增加载开关预留统一扩展点 +- `YamlConfigLoaderTests` 新增对 `RegisterTable(options)`、`EnableHotReload(options)` 和空 options 参数的回归覆盖 +- `docs/zh-CN/game/config-system.md` 补充 `YamlConfigTableRegistrationOptions` 与 `YamlConfigHotReloadOptions` 的推荐用法 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests"` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~GeneratedConfigConsumerIntegrationTests"` + +### 2026-04-06 + +- `SchemaConfigGenerator` 为顶层非主键标量字段新增轻量查询辅助,生成 `FindBy*` 与 `TryFindFirstBy*` 入口,保持运行时仍基于 `All()` 线性扫描,不引入新索引契约 +- `GFramework.SourceGenerators.Tests/Config` 补充查询辅助生成规则回归,明确断言主键、数组、对象和 ref-table 字段都不会生成 `FindBy*` / `TryFindFirstBy*` +- `GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterTable.g.txt` 更新为包含查询辅助的最新快照 +- `GFramework.Game.Tests` 的消费者端到端 schema 新增 `faction` 字段,并补充生成查询辅助的运行时断言 +- 新增 `ArchitectureConfigIntegrationTests`,验证在 `Architecture.OnInitialize()` 中注册 `ConfigRegistry`、执行 `YamlConfigLoader.LoadAsync(...)`,并通过生成的 `GetMonsterTable()` 读取配置 +- `docs/zh-CN/game/config-system.md` 补充生成查询辅助说明,以及将 `ConfigRegistry + YamlConfigLoader + Register*Table()` 收敛为 `Architecture` 推荐接入模板 +- 修正 `SchemaConfigGenerator` 中无参数字符串插值导致的分析器告警,避免生成器实现继续留下无意义的插值调用 +- `ArchitectureConfigIntegrationTests` 的临时目录清理改为同时兜底 `IOException` 与 `UnauthorizedAccessException`,降低测试在不同文件系统环境下的偶发失败 +- `SchemaConfigGenerator` 新增项目级聚合输出 `GeneratedConfigCatalog` 与 `RegisterAllGeneratedConfigTables()`,让消费者项目可以一行注册当前编译中全部生成表 + +### 2026-04-09(生命周期与同步桥接) + +- `GameConfigModule` 现在会在安装前显式拒绝已离开 `ArchitecturePhase.None` 的架构,避免错过 `BeforeUtilityInit` 首载窗口 +- `GameConfigModule.Install(...)` 现在会先完成无副作用阶段校验,再先注册 `IConfigRegistry` / 生命周期 utility、最后注册生命周期钩子;一旦进入注册阶段即把模块实例视为已消耗,避免任何不可回滚的部分安装失败后重复暴露 utility 或重复挂钩子 +- `BootstrapInitializationHook` 的同步桥接改为 `InitializeAsync().ConfigureAwait(false).GetAwaiter().GetResult()`,并为 `GameConfigBootstrap.InitializeAsync()` 补充线程契约说明 +- `GameConfigBootstrap`、`YamlConfigLoader` 与 `YamlConfigSchemaValidator` 的初始化异步链统一补充 `ConfigureAwait(false)`,降低在 Unity 主线程、UI 线程或自定义 `SynchronizationContext` 上的死锁风险 +- `ArchitectureConfigIntegrationTests` 统一为 PascalCase 命名,并新增“阻塞同步上下文下通过真实架构生命周期桥接完成初始化”和“迟到安装失败不消耗模块实例”的回归测试 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~ArchitectureConfigIntegrationTests"` + +### 2026-04-08(索引构建逻辑修复与聚合注册能力) + +- 修复 `SchemaConfigGenerator` 生成的索引构建逻辑:`BuildLookupIndex` 现在会跳过运行时空 key,并在生成代码 XML 注释中说明这是为了避免 `Lazy` 因格式错误配置而永久缓存异常 +- 收紧生成器内部的索引查询空值守卫分类逻辑:不再依赖“只有 `string` 是引用类型”的静默假设,而是显式枚举当前支持的标量映射;未来新增标量若未同步分类,将在生成期直接失败 +- 在 `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 中新增定向回归,验证生成代码包含运行时空 key 防御逻辑与说明性注释 +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGenerator"` +- 下一恢复点:如果后续扩展新的 schema 标量类型,需要同步更新 `RequiresIndexedLookupNullGuard(...)` 的分类分支,并补充对应生成回归测试 +- `GeneratedConfigCatalog` 暴露 `Tables` 与 `TryGetByTableName(...)`,为启动、诊断和后续按域收敛提供稳定元数据目录 +- 修正聚合注册入口遗漏自定义 comparer 的问题:新增 `GeneratedConfigRegistrationOptions`,让 `RegisterAllGeneratedConfigTables(...)` 可以按表转发 comparer,而不改变既有运行时查找语义 +- `GeneratedConfigConsumerIntegrationTests` 去掉对 `GeneratedConfigCatalog.Tables.Count` 的全局数量锁定,改为只断言期望表项存在,避免随着测试项目新增 schema 而出现伪失败 +- `GFramework.SourceGenerators.Tests/Config` 新增聚合注册目录回归测试,并新增 `GeneratedConfigCatalog.g.txt` 快照 +- `GeneratedConfigConsumerIntegrationTests` 与 `ArchitectureConfigIntegrationTests` 改为优先走 `RegisterAllGeneratedConfigTables()`,验证聚合注册入口贯通真实消费者链路 +- `docs/zh-CN/game/config-system.md` 更新为优先推荐 `RegisterAllGeneratedConfigTables()`,并补充 `GeneratedConfigCatalog` 的启动/诊断用法与 comparer 覆盖方式 +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfig"` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~GeneratedConfigConsumerIntegrationTests|FullyQualifiedName~ArchitectureConfigIntegrationTests"` +- `GeneratedConfigRegistrationOptions` 新增 `IncludedConfigDomains`、`IncludedTableNames` 与 `TableFilter`,让 `RegisterAllGeneratedConfigTables()` 可以按域、按表名或按调用方谓词筛选要注册的 schema 子集 +- `GeneratedConfigRegistrationExtensions` 聚合注册流程改为先执行允许列表和谓词判断,再按表调用对应 `Register*Table(...)`,保持既有无筛选调用兼容 +- `GFramework.Game.Tests/schemas` 新增第二张 `item` schema,用真实消费者项目验证“项目中存在多张生成表,但当前启动仅注册一部分”的场景 +- `GeneratedConfigConsumerIntegrationTests` 新增按配置域、表名和谓词筛选聚合注册的回归测试,并补充多表项目的加载断言 + +### 2026-04-09(bootstrap 与 schema 子集) + +- `GFramework.Game/Config` 新增正式运行时入口 `GameConfigBootstrap` 与 `GameConfigBootstrapOptions`,把 `ConfigRegistry`、`YamlConfigLoader`、初次 `LoadAsync` 和热重载句柄收敛到单个生命周期对象 +- `GameConfigBootstrap` 提供 `InitializeAsync()`、`StartHotReload()`、`StopHotReload()` 与共享 `Registry` / `Loader` 访问,避免消费者继续复制文档模板实现 +- `GFramework.Game.Tests/Config` 新增 `GameConfigBootstrapTests`,覆盖共享注册表复用、初始化加载和显式热重载回写链路 +- `ArchitectureConfigIntegrationTests` 改为在 `Architecture.OnInitialize()` 中使用 `GameConfigBootstrap`,并通过 `IConfigRegistry` utility 验证官方启动入口可以直接嵌入架构生命周期 +- `docs/zh-CN/game/config-system.md` 更新为优先推荐 `GameConfigBootstrap` / `GameConfigBootstrapOptions`,并补充 `Architecture` 与热重载接入示例 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~GameConfigBootstrapTests|FullyQualifiedName~ArchitectureConfigIntegrationTests|FullyQualifiedName~GeneratedConfigConsumerIntegrationTests"` +- 已执行:`bash scripts/validate-csharp-naming.sh` +- Runtime / Generator / Tooling 共享新增 `pattern`、`minItems`、`maxItems`、`exclusiveMinimum`、`exclusiveMaximum` 子集支持:运行时与 VS Code 校验会拒绝模式、数组长度和开区间边界违规值,生成代码 XML 文档与表单 hint 同步暴露这些约束 +- `YamlConfigSchemaValidator` 新增数组元素数量约束模型与字符串正则/开区间数值边界校验,`YamlConfigLoaderTests` 补充三类回归测试 +- `SchemaConfigGenerator` 扩展 XML 文档约束输出,`MonsterConfig.g.txt` 快照更新为包含 `pattern`、`minItems/maxItems` 与 `exclusive*` 说明 +- `tools/gframework-config-tool` 扩展 schema 解析、本地化诊断与表单 hint;`configValidation.test.js` 新增对模式、数组元素数量和开区间数值边界的回归覆盖 +- 补齐上下界对称回归:`YamlConfigLoaderTests` 新增 `exclusiveMaximum`、`minItems` 与 regex backreference 用例,`configValidation.test.js` 额外覆盖 `exclusiveMaximum` / `maxItems` +- `GameConfigBootstrap` 生命周期改为单锁保护的原子状态提交:初始化和热重载启动只会在整步成功后才发布 `_loader` / `_hotReload`,并为并发进入与热重载启动失败补充回归测试 +- `YamlConfigSchemaValidator` 移除 `RegexOptions.ExplicitCapture`,让运行时 `pattern` 与 JS 工具保持一致的分组/反向引用语义 +- `tools/gframework-config-tool` 现在会在 schema 解析阶段显式拒绝非法 `pattern`,不再静默丢弃约束;`.github/workflows/ci.yml` 也新增该工具的 `bun run test` +- `docs/zh-CN/game/config-system.md` 同步更新共享 schema 子集与运行时校验行为说明 +- 修正文档示例命名冲突:目录模板和生命周期包装示例统一改为 `GameConfigHost.cs` / `GameConfigHost`,保留 `GameConfigRuntime.cs` 作为只读访问门面,并补充二者组合使用示例 +- Runtime / Generator / Tooling 共享新增 `multipleOf` 与 `uniqueItems` 子集支持:运行时会拒绝非整倍数数值与重复数组元素,VS Code 校验 / 表单 hint / 本地化诊断同步对齐,生成代码 XML 文档也会暴露这两类约束 +- `YamlConfigSchemaValidator` 为标量约束模型新增 `multipleOf`,为数组约束模型新增 `uniqueItems`,并按 schema 归一化结构比较对象数组重复项 +- `YamlConfigLoaderTests` 新增 `multipleOf` 与 `uniqueItems` 运行时回归测试 +- `SchemaConfigGeneratorSnapshotTests` 与 `MonsterConfig.g.txt` 快照更新为包含 `multipleOf` / `uniqueItems` 文档输出 +- `tools/gframework-config-tool/test/configValidation.test.js` 新增 `multipleOf` 与 `uniqueItems` 校验 / 元数据回归测试,`extension.js` 与 `localization.js` 同步补齐表单 hint 和诊断文案 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGeneratorSnapshotTests"` +- 已执行:`bun run test`(`tools/gframework-config-tool`) +- 已执行:`node --check tools/gframework-config-tool/src/extension.js` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGenerator"` +- 已执行:`node --test ./test/configValidation.test.js`(`tools/gframework-config-tool`) + +### 2026-04-08(索引能力) + +- `SchemaConfigGenerator` 新增 schema 元数据 `x-gframework-index`,允许对"顶层、必填、非主键、非引用标量字段"生成可选只读精确匹配索引 +- 生成的 `FindBy*` / `TryFindFirstBy*` 对显式声明索引的字段会改为惰性构建只读 bucket 字典;未声明字段继续保持 `All()` 线性扫描契约 +- `ConfigSchemaDiagnostics` 新增 `GF_ConfigSchema_008`,在 `x-gframework-index` 类型错误或落到不支持字段时提供稳定诊断 +- `GFramework.SourceGenerators.Tests/Config` 新增索引元数据诊断测试,并扩展查询辅助生成测试断言索引字段只为显式 opt-in 的属性生成 +- `GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterTable.g.txt` 更新为包含索引字段、惰性索引构建和索引化查询实现的最新快照 +- `GFramework.Game.Tests/schemas/monster.schema.json` 与 `GeneratedConfigConsumerIntegrationTests` 同步把 `name`、`faction` 标记为 `x-gframework-index: true`,验证真实消费者链路继续贯通 +- `docs/zh-CN/game/config-system.md` 补充 `x-gframework-index` 用法、约束范围和“索引字段自动走惰性只读索引、其他字段仍线性扫描”的语义说明 +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfig"` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~GeneratedConfigConsumerIntegrationTests"` +- 已执行:`bash scripts/validate-csharp-naming.sh` + +### 2026-04-09(目录辅助与模块化接入) + +- `GeneratedConfigCatalog` 继续补齐启动与诊断辅助,新增 `GetTablesInConfigDomain(...)`、`GetTablesForRegistration(...)` 与 `MatchesRegistrationOptions(...)` +- 聚合注册入口 `RegisterAllGeneratedConfigTables(...)` 改为复用 `GeneratedConfigCatalog.MatchesRegistrationOptions(...)`,让启动日志、诊断输出和真实注册路径共享同一套筛选逻辑 +- `GFramework.Game.Tests/Config/GeneratedConfigConsumerIntegrationTests.cs` 新增目录筛选 / 启动诊断视图回归,验证按配置域枚举、按注册选项筛选和单条元数据匹配判断 +- `GFramework.SourceGenerators.Tests/Config` 更新生成断言与 `GeneratedConfigCatalog.g.txt` 快照,覆盖新目录 API 和聚合注册实现细节 +- `docs/zh-CN/game/config-system.md` 补充 `GeneratedConfigCatalog.GetTablesInConfigDomain(...)` 与 `GetTablesForRegistration(...)` 的推荐用法 +- 根据 review 修正文档中的 `Architecture` 模板说明,明确 `.GetAwaiter().GetResult()` 只是同步桥接写法,并补充 `SynchronizationContext` 风险与适用前提 +- `GeneratedConfigConsumerIntegrationTests` 追加 `TableFilter` 分支断言,确保目录诊断视图与真实聚合注册在谓词筛选路径上继续保持一致 +- `GFramework.Game/Config` 新增 `GameConfigModule`,为 `Architecture` 宿主提供正式的配置模块入口 +- `GameConfigModule` 安装时会注册 `IConfigRegistry` utility,并在 `BeforeUtilityInit` 通过 lifecycle hook 完成首次加载 +- `GameConfigModule` 通过内部 context utility 跟随架构销毁自动释放 `GameConfigBootstrap` 与热重载句柄,同时保留 `StartHotReload(...)` / `StopHotReload()` 转发入口 +- `ArchitectureConfigIntegrationTests` 改为优先验证 `GameConfigModule` 链路,并新增“依赖 utility 在初始化阶段直接读取配置”和“同一模块实例不可复用安装”的回归测试 +- `docs/zh-CN/game/config-system.md` 更新为:`Architecture` 场景优先推荐 `GameConfigModule`,非 `Architecture` 场景继续直接使用 `GameConfigBootstrap` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGenerator"` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~GeneratedConfigConsumerIntegrationTests"` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~ArchitectureConfigIntegrationTests|FullyQualifiedName~GameConfigBootstrapTests"` + +### 2026-04-09(schema 校验对齐修正) + +- `tools/gframework-config-tool/src/configValidation.js` 放宽 `number` 标量兼容判定,补齐科学计数法,并同步让 YAML 回写路径沿用同一组数值 / 布尔标量判定 +- schema `pattern` 现在会在解析阶段以 Unicode `u` 模式编译并缓存,校验阶段直接复用已编译 `RegExp`,避免工具侧与运行时对 `\p{L}` 等 Unicode 模式语义漂移 +- `uniqueItems` 校验改为跳过本轮已产生子项诊断的数组元素,避免 shape/type 错误再被额外误报成重复项;同时移除“首个重复即中断”的行为,一次返回全部重复诊断 +- JS 与 C# 的 `uniqueItems` 比较键统一补齐长度前缀编码,避免对象值、数组元素或标量内容中包含分隔符时发生比较键碰撞 +- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 新增共享私有入口 `ValidateCore(...)`,让 `Validate(...)` 不再为无引用收集场景额外分配临时 `List` +- `GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs` 新增科学计数法数值接受用例和 `uniqueItems` 比较键碰撞回归;`tools/gframework-config-tool/test/configValidation.test.js` 新增科学计数法、Unicode pattern、`uniqueItems` 跳过无效项、全量重复诊断与碰撞回归 +- `docs/zh-CN/game/config-system.md` 收窄“批量编辑入口”能力描述,并补充 `pattern` 在 JS 工具侧按 Unicode `u` 模式解释的说明 +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests"` + +### 2026-04-10 + +- Runtime / Generator / Tooling 共享新增 `minProperties` 与 `maxProperties` 子集支持:运行时会拒绝对象属性数量越界,VS Code 校验与对象 section 表单 hint 同步对齐,生成代码 XML 文档也会暴露对象级约束 +- `YamlConfigSchemaValidator` 新增对象属性数量约束模型与运行时校验路径,`YamlConfigLoaderTests` 补充嵌套对象 `minProperties` / `maxProperties` 回归测试 +- `SchemaConfigGenerator` 为根配置类型与嵌套对象类型补充对象级约束文档输出,`MonsterConfig.g.txt` 快照更新为包含 `minProperties` / `maxProperties` 说明 +- `tools/gframework-config-tool/src/configValidation.js`、`extension.js` 与 `localization.js` 同步补齐对象属性数量诊断、本地化文案和表单 hint;`configValidation.test.js` 新增校验 / 元数据回归 +- `docs/zh-CN/game/config-system.md` 更新共享 schema 子集、运行时拒绝路径和表单元数据说明,记录对象级关键字的当前行为 +- 根据 review 修正工具链诊断路由:`expectedObject` 继续保留专用格式化入口,`minProperties` / `maxProperties` 重新补齐到 `localization.js` 并优先通过 `localizer.t(...)` 分发,避免 validation key 与本地化字典脱节 +- 根据 review 修正 `configValidation.js` 的对象属性数量统计,`minProperties` / `maxProperties` 现在按去重后的对象 key 计数,避免重复 YAML key 造成假阳性或假阴性 +- 根据 review 修正 `SchemaConfigGenerator` 的 `required` 大小写语义为 `StringComparer.Ordinal`,与运行时 validator 及 JSON Schema 的大小写敏感约定保持一致 +- `YamlConfigSchemaValidator` 针对对象级坏 schema 的诊断改为在根对象场景输出 `Root object ...`,避免出现 `Property ''` 之类的空路径文本;`YamlConfigLoaderTests` 额外补齐非法 `minProperties` / `maxProperties` 与倒置范围的坏 schema 回归 +- 根据 review 补齐 `tools/gframework-config-tool/test/configValidation.test.js` 的 `const` 成功路径、对象键归一化 / 数组顺序、整数与布尔固定值,以及 `createSampleConfigYaml` 优先使用 `const` 的回归覆盖 +- `AGENTS.md` 里的 WSL 宿主 Git 说明改为通用表述:当 Linux `git` 命中 worktree 路径翻译错误时,先让 shell 解析到宿主 Windows Git,再为当前会话显式绑定后续 Git 命令 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGeneratorSnapshotTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGenerator"` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --test tools/gframework-config-tool/test/localization.test.js` +- 已执行:`node --check tools/gframework-config-tool/src/extension.js` +- Runtime / Generator / Tooling 共享新增 `contains`、`minContains` 与 `maxContains` 子集支持:运行时会按同一套递归 schema 规则统计匹配 `contains` 子 schema 的数组元素数量,VS Code 校验 / 表单 hint / 本地化诊断同步对齐,生成代码 XML 文档会输出紧凑的 contains 摘要 +- `YamlConfigSchemaValidator` 新增数组 contains 约束模型与运行时校验路径,`YamlConfigLoaderTests` 补充默认 contains 最少 1 个匹配、显式 `minContains` / `maxContains`、缺失 `contains` 与倒置范围的回归测试 +- `SchemaConfigGenerator` 为数组字段 XML 文档新增 `contains = ...`、`minContains` 与 `maxContains` 说明,`MonsterConfig.g.txt` 快照更新为包含数组 contains 约束摘要 +- `tools/gframework-config-tool/src/configValidation.js`、`extension.js`、`localization.js` 与 `localizationKeys.js` 同步补齐数组 contains 匹配计数诊断、本地化文案和表单 hint;`configValidation.test.js` 与 `localization.test.js` 新增校验 / 元数据 / 本地化回归 +- `docs/zh-CN/game/config-system.md` 更新共享 schema 子集、运行时拒绝路径和表单元数据说明,记录 `contains` / `minContains` / `maxContains` 的当前行为 +- 已执行:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests"` +- 已执行:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGenerator"` +- 已执行:`node --test tools/gframework-config-tool/test/configValidation.test.js` +- 已执行:`node --test tools/gframework-config-tool/test/localization.test.js` +- 已执行:`node --check tools/gframework-config-tool/src/extension.js` +- 已执行:`bash scripts/validate-csharp-naming.sh` + +### 下次恢复建议 + +- 当前 C# 主线已推进到:单表注册辅助、强类型访问、结构化诊断、查询辅助、`Architecture` 推荐接入路径、项目级聚合注册目录、按域/按表筛选的聚合注册、目录级启动诊断辅助、官方 `GameConfigBootstrap` 生命周期帮助器,以及 `Architecture` 宿主专用的 `GameConfigModule` 均已落地 +- 本轮已补齐:`pattern`、`minItems`、`maxItems`、`exclusiveMinimum`、`exclusiveMaximum` +- 本轮额外补齐:可选只读索引 `x-gframework-index`、`GeneratedConfigCatalog` 的目录筛选 / 启动诊断辅助,以及 `GameConfigModule` 模块化接入入口 +- 最新补齐:数组级 `contains` / `minContains` / `maxContains` +- 最新补齐:稳定字符串 `format` 子集 `date` / `date-time` / `email` / `uri` / `uuid` +- 下次优先项:在当前稳定 `format` 子集落地后,继续评估是否补 `time` / `duration` 等剩余 format 家族成员,或转向下一批非 format 关键字;仍然优先 Runtime / Generator / VS Code 校验三端共同收益,而不是先扩工具 UI +- 恢复时优先检查: + - `GFramework.Game/Config/YamlConfigSchemaValidator.cs` + - `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` + - `tools/gframework-config-tool/src/configValidation.js` + - `GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs` + - `docs/zh-CN/game/config-system.md` \ No newline at end of file diff --git a/ai-plan/public/ai-first-config-system/archive/traces/ai-first-config-system-history-through-2026-04-17.md b/ai-plan/public/ai-first-config-system/archive/traces/ai-first-config-system-history-through-2026-04-17.md new file mode 100644 index 00000000..5169180d --- /dev/null +++ b/ai-plan/public/ai-first-config-system/archive/traces/ai-first-config-system-history-through-2026-04-17.md @@ -0,0 +1,93 @@ +# AI-First Config System 执行 Trace + +## 2026-04-19 + +### 阶段 + +- 主线:把当前 worktree 的旧本地恢复文档收口到 `ai-plan/public/ai-first-config-system/` +- 子任务:补齐公共索引映射,并把可提交文档中的本地环境痕迹清洗为治理允许的公共表述 +- 当前恢复点:`ai-plan-public-migration` + +### 关键决策 + +- 当前分支 `feat/ai-first-config` 的恢复文档应进入公共主题目录,而不是继续停留在 worktree 根下的旧本地恢复目录 +- `ai-plan/public/README.md` 只登记当前 worktree 实际需要恢复的功能主题,因此本 worktree 只映射到 `ai-first-config-system` +- 原本地恢复文档中的绝对路径与宿主环境细节不再直接迁入公共区,统一改写为仓库相对路径或抽象环境描述 + +### 实施记录 + +- 复制旧 tracking / next / trace 文档到 `ai-plan/public/ai-first-config-system/` 对应目录 +- 在公共 tracking 中补充迁移说明,并把旧目录路径引用改写为新的 `ai-plan/public/ai-first-config-system/` 路径 +- 清洗公共 tracking 中的宿主 Windows NuGet fallback 绝对路径和 `GIT_DIR` / `GIT_WORK_TREE` 形式的本地命令细节 +- 在公共索引中新增 `ai-first-config-system` 活跃主题,并将当前分支登记到对应 worktree 映射 + +### 下一步 + +- 后续继续在 `ai-plan/public/ai-first-config-system/` 下维护 C# 主线恢复点,不再回写到 worktree 根目录 +- 下一轮功能恢复时,优先评估 `if` / `then` / `else` 等仍不改变生成形状的关键字 + +## 2026-04-17 + +### 阶段 + +- 主线:`C# Runtime + Source Generator + Consumer DX` +- 子任务:补齐不改变生成类型形状的下一批 JSON Schema 共享关键字 +- 当前恢复点:`allOf-object-focused` + +### 关键决策 + +- 本轮选择 `allOf`,但收敛为 object-focused 版本:只接受 object 节点上的 object-typed inline schema 数组。 +- `allOf` 条目按 focused constraint block 语义叠加到当前对象,不做属性合并,不引入联合类型,也不改变生成代码形状。 +- Runtime、Generator、Tooling 三端都统一拒绝非 object 节点声明 `allOf`,避免三端接受范围漂移。 +- object-focused `allOf` 进一步约束为“只能引用父对象已声明字段”;由于当前不会做属性合并,因此把新字段只写进 `allOf` 会被视为不可满足 schema 并在解析阶段直接拒绝。 +- 深层 `allOf` 诊断路径统一采用运行时格式 `reward[allOf[0]]`,避免 Runtime / Generator / Tooling 排错信息割裂。 + +### 实施记录 + +- Runtime: + - `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 新增 `allOf` 解析、对象约束匹配与引用递归采集。 + - `allOf` 匹配路径复用现有试匹配逻辑,并沿用 allow-unknown focused block 语义。 + - review 修正后,`allOf` 条目的 `required` / `properties` 还会复用父对象字段白名单,提前拒绝不可满足 schema。 + - review 继续收紧后,`allOf.properties` 若存在则必须是对象映射,`allOf.required` 若存在则必须是数组;Generator 不再静默跳过坏形状,Runtime 也会在 schema 解析阶段直接失败。 + - 本轮继续把对象关键字解析/校验抽到 `GFramework.Game/Config/YamlConfigSchemaValidator.ObjectKeywords.cs`,并把 `allOf.required` 的非字符串 / 空白项从“跳过”改成 `SchemaUnsupported`。 +- Generator: + - `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 新增 `allOf` 递归元数据校验与 XML 文档摘要输出。 + - `GFramework.Game.SourceGenerators/Diagnostics/ConfigSchemaDiagnostics.cs` 新增 `GF_ConfigSchema_012`。 + - review 修正后,生成器会拒绝 `allOf` 引用父对象未声明字段,并统一深层 `allOf` 路径格式。 + - 本轮同步把 `allOf.required` 的非字符串 / 空白项改成 `GF_ConfigSchema_012`,避免生成器比 Runtime 更宽松。 +- Tooling: + - `tools/gframework-config-tool/src/configValidation.js` 新增 `allOf` 解析与校验。 + - `tools/gframework-config-tool/src/extension.js`、`localization.js`、`localizationKeys.js` 新增 `allOf` hint 和本地化诊断。 + - review 修正后,工具端也会拒绝 `allOf` 引入父对象未声明字段,并改为输出 `reward[allOf[0]]` 路径。 +- Tests: + - 新增 `GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs` + - 扩展 `GFramework.Game.Tests/Config/YamlConfigSchemaValidatorTests.cs` + - 扩展 `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` + - 扩展 `tools/gframework-config-tool/test/configValidation.test.js` + - 扩展 `tools/gframework-config-tool/test/localization.test.js` + - 本轮追加扩展 `GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs` 与 `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs`,锁住 `allOf.properties` / `allOf.required` 的坏形状、非字符串条目与空白字段名分支 + +### 验证 + +- `node --check tools/gframework-config-tool/src/configValidation.js` +- `node --check tools/gframework-config-tool/src/extension.js` +- `node --test tools/gframework-config-tool/test/configValidation.test.js` +- `node --test tools/gframework-config-tool/test/localization.test.js` +- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderAllOfTests|FullyQualifiedName~YamlConfigSchemaValidatorTests"` +- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_AllOf_Constraint_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_NonObject_Schema_Declares_AllOf|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Is_Not_An_Array|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Is_Not_Object_Typed"` +- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Is_Not_Object_Valued|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Targets_Undeclared_Parent_Property|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_With_Runtime_Aligned_Path_When_AllOf_Inner_Schema_Is_Invalid"` +- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~YamlConfigLoaderAllOfTests"` +- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_AllOf_Constraint_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_NonObject_Schema_Declares_AllOf|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Is_Not_An_Array|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Is_Not_Object_Valued|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Is_Not_Object_Typed|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Properties_Is_Not_Object_Valued|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Required_Is_Not_An_Array|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Targets_Undeclared_Parent_Property|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_With_Runtime_Aligned_Path_When_AllOf_Inner_Schema_Is_Invalid"` +- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release -p:RestoreFallbackFolders= --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Write_AllOf_Constraint_Into_Generated_Documentation|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_NonObject_Schema_Declares_AllOf|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Is_Not_An_Array|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Is_Not_Object_Valued|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Is_Not_Object_Typed|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Properties_Is_Not_Object_Valued|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Required_Is_Not_An_Array|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Required_Item_Is_Not_A_String|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Required_Item_Is_Blank|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_AllOf_Entry_Targets_Undeclared_Parent_Property|FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_With_Runtime_Aligned_Path_When_AllOf_Inner_Schema_Is_Invalid"` + +### 当前状态 + +- `allOf-object-focused` 已完成并通过定向回归。 +- 已知剩余噪音:`GFramework.Game.Tests` 仍存在既有 `GF_ContextRegistration_003` 告警,与本轮无关。 +- 本轮 review 修复已验证通过:`YamlConfigLoaderAllOfTests` 9/9 通过,`SchemaConfigGeneratorTests` 定向 `allOf` 回归 9/9 通过。 +- 本轮继续收紧与拆分后已验证通过:`YamlConfigLoaderAllOfTests` 11/11 通过,`SchemaConfigGeneratorTests` 定向 `allOf` 回归 11/11 通过。 + +### 下一步 + +- 继续评估下一批仍不改变生成类型形状的组合关键字,优先看 `if` / `then` / `else`。 +- 如果切回工具体验,应聚焦复杂对象数组表单与批量编辑,而不是回头扩大运行时语义。 diff --git a/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md b/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md new file mode 100644 index 00000000..8fb93b44 --- /dev/null +++ b/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md @@ -0,0 +1,112 @@ +# AI-First Config System C# 体验下一阶段清单 + +## 目标 + +继续把主线放在 `C# Runtime + Source Generator + Consumer DX`,以“新配置域接入成本是否足够低”为第一判断标准。 + +当前阶段不再把 VS Code 工具能力当作阻塞项;工具链只要不拖累 C# 首发可用版本即可。 + +## 当前状态 + +- [x] 单表注册辅助:`Register{Entity}Table()` +- [x] 强类型访问入口:`Get{Entity}Table()` / `TryGet{Entity}Table(...)` +- [x] 结构化加载诊断:`ConfigLoadException.Diagnostic` +- [x] 端到端消费者集成测试 +- [x] 顶层非主键标量字段查询辅助:`FindBy*` / `TryFindFirstBy*` +- [x] `Architecture` 推荐接入模板 +- [x] 项目级聚合注册入口:`RegisterAllGeneratedConfigTables()` +- [x] 项目级生成目录:`GeneratedConfigCatalog` +- [x] 项目级目录筛选 / 启动诊断辅助:`GetTablesInConfigDomain()` / `GetTablesForRegistration()` +- [x] 聚合注册 comparer 覆盖:`GeneratedConfigRegistrationOptions` +- [x] 官方 C# 启动帮助器:`GameConfigBootstrap` / `GameConfigBootstrapOptions` +- [x] 可选只读精确匹配索引:`x-gframework-index` + +## P0:下一轮优先做 + +- [x] 为聚合注册入口增加“按配置域过滤 / 分组注册”能力 + - 目标:大型项目不必在所有场景都一次性注册全部 schema + - 示例方向:按 `ConfigDomain`、表名集合或调用方谓词选择子集 + - 价值:这是聚合注册落地后的下一步,直接影响多模块项目的启动颗粒度 + +- [x] 提供官方 C# 启动帮助器 + - 目标:把 `ConfigRegistry + YamlConfigLoader + LoadAsync + 热重载句柄` 收敛成更稳定的框架入口 + - 示例方向:`GameConfigBootstrap` 的框架内版本,或轻量 runtime host / installer + - 价值:把当前“文档模板”升级为可复用实现,继续减少消费者样板 + +## P1:强烈建议尽快补齐 + +- [x] 继续扩展最有价值的 JSON Schema 子集 + - 原则:只做 Runtime / Generator / Tooling 三端都能稳定解释的关键字 + - 已补齐:`enum`(当前覆盖标量、对象、数组节点,以及标量数组元素)、`const`、`not`、`pattern`、`format`(当前稳定子集:`date`、`date-time`、`duration`、`email`、`time`、`uri`、`uuid`)、`minItems`、`maxItems`、`exclusiveMinimum`、`exclusiveMaximum`、`multipleOf`、`uniqueItems`、`minProperties`、`maxProperties`、`dependentRequired`、`dependentSchemas`、`allOf` + - 当前产出:运行时拒绝相关约束违规值,VS Code 校验与表单 hint 对齐,生成代码 XML 文档同步暴露新关键字;对象 / 数组 `enum` 当前主要参与校验与文档输出,不额外扩展复杂表单控件;`allOf` 当前收敛为 object-focused constraint block,不做属性合并 + +- [x] 评估可选只读索引能力 + - 目标:为高频查询字段提供比 `All()` 线性扫描更强的读取体验 + - 约束:不能破坏当前热重载与简单运行时契约,也不能强迫所有表都引入额外索引成本 + - 当前产出:通过 schema 元数据 `x-gframework-index: true` 为“顶层、必填、非主键、非引用标量字段”生成惰性只读精确匹配索引,未声明字段保持线性扫描 + +- [x] 用 `GeneratedConfigCatalog` 继续补齐启动与诊断辅助 + - 目标:让消费者可以稳定枚举已生成表、按表名反查元数据,并为后续分组注册做铺垫 + - 当前产出:补齐 `GetTablesInConfigDomain()`、`GetTablesForRegistration()` 与 `MatchesRegistrationOptions(...)`,让启动日志和真实聚合注册复用同一套筛选规则 + +## P2:可选增强 + +- [x] 补一条比 `Architecture.OnInitialize()` 更正式的模块化接入建议 + - 当前产出:`GFramework.Game.Config.GameConfigModule` + - 生命周期:模块安装时注册 `IConfigRegistry` utility,并在 `BeforeUtilityInit` 通过 lifecycle hook 完成首次加载 + - 清理策略:通过内部 context utility 跟随架构销毁自动释放 `GameConfigBootstrap` 和热重载句柄 + - 适用边界:`Architecture` 宿主优先使用模块;非 `Architecture` 场景继续直接使用 `GameConfigBootstrap` + +- [ ] 继续扩插件的复杂表单能力 + - 说明:这是可选项,不阻塞 C# 主线 + +## 暂缓 + +- [ ] 不追求完整 JSON Schema 全量支持 + - 原因:维护成本高,且容易造成 Runtime / Generator / Tooling 三端漂移 + +- [ ] 不优先做运行时可写配置 + - 原因:当前系统定位仍然是静态内容只读查询 + +- [ ] 不让 VS Code 扩展计划反过来主导 Runtime / Generator 设计 + - 原因:当前更大的收益点仍然在 C# 消费体验 + +## 建议执行顺序 + +1. 用 `GeneratedConfigCatalog` 继续补齐启动与诊断辅助 +2. 补一条比 `Architecture.OnInitialize()` 更正式的模块化接入建议 + 当前状态:第 1 项和第 2 项已完成,`allOf` 也已补齐;下一步转到仍不改变生成形状的组合关键字评估(优先看 `if` / `then` / `else`),或继续推进 VS Code 复杂编辑体验 + +## 完成标准 + +- 消费项目接入多个配置域时,启动代码仍然保持很薄 +- 消费者不需要手写重复的注册字符串、目录路径和 schema 路径 +- 配置系统能以“官方入口”而不是“文档拼装模板”接入真实项目 +- 新增 schema 后,回归测试能覆盖生成、加载、访问与聚合注册链路 + +## 下次恢复点 + +- 在当前稳定 `format` 子集(`date`、`date-time`、`duration`、`email`、`time`、`uri`、`uuid`)以及 object-focused `allOf` 之后,转到下一批仍不改变生成类型形状的关键字评估;仍然不要先回工具 UI +- 恢复时优先检查: + - `GFramework.Game/Config/YamlConfigSchemaValidator.cs` + - `GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs` + - `tools/gframework-config-tool/src/configValidation.js` + - `tools/gframework-config-tool/src/extension.js` + - `docs/zh-CN/game/config-system.md` + +### 恢复块 + +- 恢复点编号:`AI-FIRST-CONFIG-RP-002` +- 当前阶段:`C# Runtime + Source Generator + Consumer DX` +- 已知风险: + - 语义一致性风险:`if` / `then` / `else` 在 Runtime / Generator / Tooling 三端语义不一致的风险,需要先验证是否能在不引入生成类型形状漂移的前提下落地 + - 工具链非阻塞风险:将 VS Code 功能标为非阻塞后,可能导致 C# 主线补齐新关键字时缺少工具侧同步验证 + - 复杂关键字回退风险:`allOf` 已收敛为 object-focused constraint block,未来新增组合关键字时需明确是否同样限制范围 +- 最近验证: + - 时间:2026-04-17 + - 内容:截至该日期的历史跟踪与执行 trace 已归档到主题内归档目录 + - 结果:通过 +- 下一步: + 1. 检查 `YamlConfigSchemaValidator.cs`、`SchemaConfigGenerator.cs`、`configValidation.js` 中当前已支持的关键字列表 + 2. 评估 `if` / `then` / `else` 是否能在三端保持一致语义且不改变生成类型形状 + 3. 若结论否定,选择下一批共享解释关键字而不是先回工具 UI \ No newline at end of file diff --git a/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md b/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md new file mode 100644 index 00000000..cc319107 --- /dev/null +++ b/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md @@ -0,0 +1,61 @@ +# AI-First Config System 跟踪 + +## 目标 + +基于当前 `GFramework` 设计结论,继续推进 AI-First 游戏配置系统,并把主线保持在 +`C# Runtime + Source Generator + Consumer DX`。 + +## 当前恢复点 + +- 恢复点编号:`AI-FIRST-CONFIG-RP-002` +- 当前阶段:`C# Runtime + Source Generator + Consumer DX` +- 当前焦点: + - 在当前稳定 `format` 子集与 object-focused `allOf` 之后,继续评估仍不改变生成类型形状的下一批组合关键字 + - 优先考察 `if` / `then` / `else` 是否能在 Runtime / Generator / Tooling 三端保持一致语义 + - 继续把 VS Code 工具能力视为非阻塞项,不让复杂 UI 编辑器需求反过来拖慢 C# 主线 + +### 已知风险 + +- 语义一致性风险:`if` / `then` / `else` 在 Runtime / Generator / Tooling 三端语义不一致的风险 + - 缓解措施:先验证是否能在不引入生成类型形状漂移的前提下落地,若否则选择下一批共享解释关键字 +- 工具链验证风险:VS Code 与 CI / 发布管道验证覆盖不足 + - 缓解措施:继续为新增共享关键字补齐三端测试覆盖,优先保证 C# Runtime 与 Generator 回归通过 +- 非阻塞项回退风险:将 VS Code 功能标为非阻塞但导致主线回退的风险 + - 缓解措施:C# 主线补齐新关键字时仍需在 `configValidation.js` 与 `extension.js` 中同步落地,只是不让复杂表单控件阻塞发布 + +## 当前状态 + +- 已完成 Runtime、YAML Loader、Source Generator 与 VS Code Extension 的首轮可用版本 +- 已落地项目级聚合注册入口、`GeneratedConfigCatalog`、`GameConfigBootstrap`、`GameConfigModule` +- 已补齐一批共享 JSON Schema 子集,包括: + - `enum`、`const`、`not`、`pattern` + - `format` 稳定子集:`date`、`date-time`、`duration`、`email`、`time`、`uri`、`uuid` + - `minItems`、`maxItems`、`exclusiveMinimum`、`exclusiveMaximum`、`multipleOf`、`uniqueItems` + - `minProperties`、`maxProperties`、`dependentRequired`、`dependentSchemas`、`allOf` +- 当前最细粒度的下一阶段 backlog 保留在独立文件: + - `ai-plan/public/ai-first-config-system/todos/ai-first-config-system-csharp-experience-next.md` + +## 当前未完成项 + +- 继续扩展“不会改变生成类型形状”的共享关键字支持 +- 继续降低复杂 schema 与多配置域项目的接入成本 +- 让 VS Code 表单支持更深层对象数组嵌套,减少 raw YAML 回退 +- 为复杂结构提供比“顶层标量 / 标量数组”更强的批量编辑能力 +- 在真实 VS Code 宿主中完成对象数组编辑与复杂 schema 的交互式手工验证 + +## 活跃文档 + +- 当前 backlog:[ai-first-config-system-csharp-experience-next.md](./ai-first-config-system-csharp-experience-next.md) +- 历史跟踪归档:[ai-first-config-system-history-through-2026-04-17.md](../archive/todos/ai-first-config-system-history-through-2026-04-17.md) +- 历史 trace 归档:[ai-first-config-system-history-through-2026-04-17.md](../archive/traces/ai-first-config-system-history-through-2026-04-17.md) + +## 验证说明 + +- `2026-04-17` 之前的详细实现记录与定向验证命令已归档到历史 tracking / trace +- active 跟踪文件只保留当前恢复点、当前状态和下一步,不再重复堆积已完成阶段的完整历史 + +## 下一步 + +1. 先检查 `GFramework.Game/Config/YamlConfigSchemaValidator.cs`、`GFramework.SourceGenerators/Config/SchemaConfigGenerator.cs`、`tools/gframework-config-tool/src/configValidation.js` +2. 评估 `if` / `then` / `else` 是否能在不引入生成类型形状漂移的前提下落地 +3. 若结论是否定,再选择下一批仍能共享解释的关键字,而不是先回到工具 UI 深挖 \ No newline at end of file diff --git a/ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md b/ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md new file mode 100644 index 00000000..167219db --- /dev/null +++ b/ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md @@ -0,0 +1,35 @@ +# AI-First Config System 执行 Trace + +## 2026-04-19 + +### 阶段:active 入口归档收口(AI-FIRST-CONFIG-RP-002) + +- 已将截至 `2026-04-17` 的详细实现历史从默认 trace 入口移到主题内归档 +- active trace 现在只保留当前恢复点和下一步,避免 `boot` 每次恢复都重新读取已完成的长历史 +- 当前功能主线不变,仍是: + - `C# Runtime + Source Generator + Consumer DX` + - 下一批共享 JSON Schema 关键字评估 + - 优先看 `if` / `then` / `else` + +### Archive Context + +- 历史跟踪归档: + - `ai-plan/public/ai-first-config-system/archive/todos/ai-first-config-system-history-through-2026-04-17.md` +- 历史 trace 归档: + - `ai-plan/public/ai-first-config-system/archive/traces/ai-first-config-system-history-through-2026-04-17.md` + +### 验证 + +- 2026-04-19:入口归档收口验证 + - 执行命令:`wc -l ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md` + - 结果:通过 + - 备注:active 入口文件行数显著减少,已完成阶段详细历史已移至归档 +- 2026-04-17 之前:详细实现与定向验证命令 + - 参考:`ai-plan/public/ai-first-config-system/archive/todos/ai-first-config-system-history-through-2026-04-17.md` + - 备注:包含 Runtime / Generator / Tooling 三端同步落地的每日验证记录与具体测试命令 + +### 下一步 + +1. 从 `ai-first-config-system-csharp-experience-next.md` 读取当前 backlog,而不是继续翻已完成历史 +2. 先判断 `if` / `then` / `else` 是否满足“三端一致且不改变生成形状”的前提 +3. 若不满足,直接回退到下一批收益更明确的共享关键字评估 \ No newline at end of file diff --git a/ai-plan/public/ai-plan-governance/archive/todos/ai-plan-governance-history-rp002-rp004.md b/ai-plan/public/ai-plan-governance/archive/todos/ai-plan-governance-history-rp002-rp004.md new file mode 100644 index 00000000..a4ed8414 --- /dev/null +++ b/ai-plan/public/ai-plan-governance/archive/todos/ai-plan-governance-history-rp002-rp004.md @@ -0,0 +1,82 @@ +# AI-Plan 治理跟踪 + +## 目标 + +继续收口 `ai-plan/` 的目录语义、启动入口与归档策略,避免多 worktree 并行时的公共恢复文档持续膨胀并拖慢 +`boot` 的上下文定位。 + +- 为 `ai-plan/` 建立明确的目录分层 +- 区分“可提交共享状态”与“工作树私有状态” +- 明确禁止写入敏感数据、绝对路径和机器本地信息 +- 让 `AGENTS.md`、`ai-plan/README.md` 与 boot skill 使用同一套目录语义 +- 让 `boot` 能通过公共索引快速定位当前 worktree 的活跃主题 +- 为阶段完成和主题完成两类归档建立稳定规则 + +## 当前恢复点 + +- 恢复点编号:`AI-PLAN-GOV-RP-004` +- 当前阶段:`Phase 2` +- 当前焦点: + - 已将共享恢复文档按主题迁移到 `ai-plan/public//todos/` 与 `ai-plan/public//traces/` + - 已为 `boot` 新增 `ai-plan/public/README.md` 公共索引,并绑定当前 worktree 的活跃主题顺序 + - 已将完成度更高的 `cqrs-cache-docs-hardening` 移入 `ai-plan/public/archive/` + - 已同步更新 `.gitignore`、`AGENTS.md`、`ai-plan/README.md`、根 `README.md` 与 `gframework-boot` + - 已明确主题内归档与主题级归档的双层规则,避免活动区无限增长 + - 已将当前 `feat/ai-first-config` worktree 下遗留的 `local-plan/` 收口到新的公共主题目录,并补齐索引映射 + +## 已完成 + +- `.gitignore` 现允许 `ai-plan/public/**/*.md` 以主题目录与归档目录形式进入版本控制 +- `AGENTS.md` 已补充: + - `public/README.md`、活动主题目录、主题内归档与主题级归档的职责划分 + - `boot` 默认忽略 `ai-plan/public/archive/**` + - worktree 与活跃主题映射变化时,必须同步更新公共索引 +- `.codex/skills/gframework-boot/SKILL.md` 与其 `references/startup-artifacts.md` 已切换到: + - 优先读取 `ai-plan/public/README.md` + - 命中映射后优先读取对应主题目录 + - 未命中映射时再扫描活动主题目录,并排除公共归档区 +- `ai-plan/README.md` 已补充主题命名、归档触发条件与 `boot` 读取顺序 +- 根 `README.md` 已改为要求维护公共索引与对应主题目录 +- 现有共享文档已迁移为: + - `ai-plan/public/ai-plan-governance/**` + - `ai-plan/public/ai-first-config-system/**` + - `ai-plan/public/cqrs-rewrite/**` + - `ai-plan/public/archive/cqrs-cache-docs-hardening/**` +- 已根据 PR #253 的最新未解决 review thread 清理 `ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md` + 中重复的 `### 验证` / `### 下一步` 标题,并补充恢复点后缀以消除 MD024 锚点冲突 +- 已将当前 `feat/ai-first-config` worktree 的 `local-plan/` 文档迁移到 `ai-plan/public/ai-first-config-system/` +- 已在 `ai-plan/public/README.md` 为 `feat/ai-first-config` 登记 `ai-first-config-system` 活跃主题映射 +- 已清洗迁移文档中的 `local-plan` 旧路径、绝对文件系统路径与仅适用于本机的 Git worktree 命令细节 + +## 验证 + +- `find ai-plan/public -maxdepth 4 -type f | sort` + - 结果:通过 + - 备注:活动主题、公共索引与归档主题已按新目录语义落位 +- `rg -n "ai-plan/public/README.md|ai-plan/public/|ai-plan/public/archive|ai-plan/private/" AGENTS.md .codex/skills/gframework-boot/SKILL.md .codex/skills/gframework-boot/references/startup-artifacts.md ai-plan/README.md README.md .gitignore` + - 结果:通过 + - 备注:新目录语义、索引入口与归档规则已统一到仓库规则与 boot skill +- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release --no-restore` + - 结果:通过 + - 备注:本轮规则与文档调整未引入构建问题 +- `rg -n "^### (验证|下一步)" ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md` + - 结果:通过 + - 备注:同名标题已按恢复点后缀唯一化,不再产生重复锚点 +- `rg -n "local-plan|D:\\\\|/mnt/|GIT_DIR=" ai-plan/public/ai-first-config-system` + - 结果:通过 + - 备注:迁移后的公共主题文档未保留旧目录入口或机器本地路径 +- `find ai-plan/public/ai-first-config-system -maxdepth 3 -type f | sort` + - 结果:通过 + - 备注:当前 worktree 的 tracking / next / trace 已按主题目录落位 +- `test ! -e local-plan` + - 结果:通过 + - 备注:旧 worktree 根目录入口已删除,不再与新的公共主题目录并存 +- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` + - 结果:通过 + - 备注:默认 `--no-restore` 构建仍会命中旧的宿主 Windows NuGet fallback 配置,本轮通过显式清空 `RestoreFallbackFolders` 完成最小构建验证 + +## 下一步 + +1. 后续再发现 legacy `local-plan/` 或平铺恢复文档时,先迁入对应 `ai-plan/public//`,再补 `public/README.md` 映射 +2. 阶段完成后优先收入主题内 `archive/`;主题整体完成后,再整目录移入 `ai-plan/public/archive/` +3. 若未来再新增 skill 或仓库规则引用 `ai-plan/`,统一按“公共索引 + 活动主题 + 归档主题 + 私有目录”扩展,不再恢复平铺结构 diff --git a/ai-plan/public/ai-plan-governance/archive/traces/ai-plan-governance-history-rp002-rp004.md b/ai-plan/public/ai-plan-governance/archive/traces/ai-plan-governance-history-rp002-rp004.md new file mode 100644 index 00000000..37aabc19 --- /dev/null +++ b/ai-plan/public/ai-plan-governance/archive/traces/ai-plan-governance-history-rp002-rp004.md @@ -0,0 +1,84 @@ +# AI-Plan 治理追踪 + +## 2026-04-19 + +### 阶段:遗留 local-plan 收口(RP-004) + +- 建立 `AI-PLAN-GOV-RP-004` 恢复点 +- 发现当前 worktree `feat/ai-first-config` 仍保留旧 `local-plan/`,说明主题化 `ai-plan/public//` 落地后还有遗留入口未收口 +- 已据此完成本轮治理补齐: + - 新增 `ai-plan/public/ai-first-config-system/` 主题目录,并迁入 tracking / next / trace 文档 + - 在 `ai-plan/public/README.md` 为 `feat/ai-first-config` 绑定 `ai-first-config-system` + - 将迁移后的公共文档中的 `local-plan` 旧路径引用更新为新的 `ai-plan/public/...` 路径 + - 将绝对路径、宿主 NuGet fallback 目录和 `GIT_DIR` / `GIT_WORK_TREE` 命令细节改写为可提交的公共表述 + +### 验证(RP-004) + +- `find ai-plan/public/ai-first-config-system -maxdepth 3 -type f | sort` + - 结果:通过 +- `rg -n "local-plan|D:\\\\|/mnt/|GIT_DIR=" ai-plan/public/ai-first-config-system` + - 结果:通过 +- `test ! -e local-plan` + - 结果:通过 +- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` + - 结果:通过 + +### 下一步(RP-004) + +1. 后续若发现其他 worktree 仍有旧恢复文档,沿用“主题目录迁移 + 索引登记 + 公共内容清洗”同一流程处理 +2. 若某个主题后续再分阶段沉淀恢复文档,优先收入该主题自己的 `archive/`,避免活动入口再次膨胀 + +### 阶段:目录语义收口(RP-002) + +- 建立 `AI-PLAN-GOV-RP-002` 恢复点 +- 用户指出当前 `ai-plan/` 存在三个治理问题: + - 缺少更细的目录分层,容易随着 worktree 增长持续膨胀 + - 缺少“不得写入敏感数据、真实路径、机器信息”的明确约束 + - 目录语义没有区分共享恢复信息与 worktree 私有状态 +- 已据此完成以下收口: + - 将既有共享 tracking / trace 文件迁移到扁平的 `ai-plan/public/` 共享目录 + - 新增 `ai-plan/private/` 作为工作树私有恢复空间,并通过 `.gitignore` 保持未跟踪 + - 新增 `ai-plan/README.md` 作为目录语义与内容规范的单点说明 + - 在 `AGENTS.md` 中补齐 public/private 职责边界,以及敏感信息与绝对路径禁写规则 + - 在 `gframework-boot` 中同步新的读取顺序:优先 public,按需读取当前 worktree 私有目录 + +### 验证(RP-002) + +- `find ai-plan -maxdepth 3 -type f | sort` + - 结果:通过 +- `rg -n "ai-plan/public/|ai-plan/private/" AGENTS.md .codex/skills/gframework-boot/SKILL.md .codex/skills/gframework-boot/references/startup-artifacts.md ai-plan/README.md .gitignore` + - 结果:通过 +- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release` + - 结果:通过 + +### 下一步(RP-002) + +1. 后续若出现新的 worktree 私有恢复需求,直接在 `ai-plan/private//` 下创建,不再向共享目录追加本地临时状态 +2. 若将来需要进一步限制格式,可再为 `public/**` 与 `private/` 各自补一个模板文件,但本轮先把目录语义和安全边界固定下来 + +### 阶段:主题分组与启动索引(RP-003) + +- 建立 `AI-PLAN-GOV-RP-003` 恢复点 +- 用户进一步指出:即使 public/private 已分层,只要多 worktree 并行,扁平的活动主题集合仍会让 `boot` 随着 + `ai-plan/public` 增长而退化成大范围扫描 +- 已据此完成第二轮治理: + - 将活动共享文档迁移到 `ai-plan/public//todos/` 与 `ai-plan/public//traces/` + - 新增 `ai-plan/public/README.md` 作为公共启动索引,维护 worktree 到多个活跃主题的映射与优先顺序 + - 将已完成的 `cqrs-cache-docs-hardening` 整体移入 `ai-plan/public/archive/cqrs-cache-docs-hardening/` + - 在 `AGENTS.md`、`ai-plan/README.md`、根 `README.md` 与 `gframework-boot` 中统一“公共索引 + 活动主题 + + 主题内归档 + 主题级归档”的语义 + - 将 `.gitignore` 调整为允许 `ai-plan/public/**/*.md` 以新层级进入版本控制 + +### 验证(RP-003) + +- `find ai-plan/public -maxdepth 4 -type f | sort` + - 结果:通过 +- `rg -n "ai-plan/public/README.md|ai-plan/public/|ai-plan/public/archive|ai-plan/private/" AGENTS.md .codex/skills/gframework-boot/SKILL.md .codex/skills/gframework-boot/references/startup-artifacts.md ai-plan/README.md README.md .gitignore` + - 结果:通过 +- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release --no-restore` + - 结果:通过 + +### 下一步(RP-003) + +1. 未来每次新增或关闭主题,都同步更新 `ai-plan/public/README.md`,不要让 `boot` 回到全量扫描模式 +2. 若某个活跃主题内部继续积累阶段性完成物,优先收入该主题目录下的 `archive/` diff --git a/ai-plan/public/ai-plan-governance/todos/ai-plan-governance-tracking.md b/ai-plan/public/ai-plan-governance/todos/ai-plan-governance-tracking.md index 82d4949b..7e24954b 100644 --- a/ai-plan/public/ai-plan-governance/todos/ai-plan-governance-tracking.md +++ b/ai-plan/public/ai-plan-governance/todos/ai-plan-governance-tracking.md @@ -14,52 +14,56 @@ ## 当前恢复点 -- 恢复点编号:`AI-PLAN-GOV-RP-003` -- 当前阶段:`Phase 2` +- 恢复点编号:`AI-PLAN-GOV-RP-005` +- 当前阶段:`Phase 3` - 当前焦点: - - 已将共享恢复文档按主题迁移到 `ai-plan/public//todos/` 与 `ai-plan/public//traces/` - - 已为 `boot` 新增 `ai-plan/public/README.md` 公共索引,并绑定当前 worktree 的活跃主题顺序 - - 已将完成度更高的 `cqrs-cache-docs-hardening` 移入 `ai-plan/public/archive/` - - 已同步更新 `.gitignore`、`AGENTS.md`、`ai-plan/README.md`、根 `README.md` 与 `gframework-boot` - - 已明确主题内归档与主题级归档的双层规则,避免活动区无限增长 + - 将"主题内 `archive/` 已存在"升级为"active todo/trace 过长时必须归档已完成且已验证阶段"的显式规则 + - 让 active `todos/` / `traces/` 只保留当前恢复点、活跃事实、活跃风险、下一步与 archive 指针 + - 将 `ai-plan-governance`、`ai-first-config-system` 与 `cqrs-rewrite` 的历史阶段从默认启动入口移出 + +### 已知风险 + +- 归档遗漏:已完成且已验证的阶段未及时归档,导致 active 入口文件持续膨胀 + - 缓解措施:只要某个 active 主题积累了多个已完成且已验证阶段,就在同一变更里将其细节迁入该主题自己的 `archive/` +- 入口回膨胀:后续新任务直接追加到 active 入口,而不是先归档历史 + - 缓解措施:每次变更前先检查当前 active 入口行数,超过合理范围时优先归档已完成内容 +- 跨文档语义漂移:tracking / trace / README 三个入口对同一主题的状态描述不一致 + - 缓解措施:修改任一文档时同步检查其他入口,确保恢复点编号、阶段名称和下一步描述保持一致 ## 已完成 -- `.gitignore` 现允许 `ai-plan/public/**/*.md` 以主题目录与归档目录形式进入版本控制 -- `AGENTS.md` 已补充: - - `public/README.md`、活动主题目录、主题内归档与主题级归档的职责划分 - - `boot` 默认忽略 `ai-plan/public/archive/**` - - worktree 与活跃主题映射变化时,必须同步更新公共索引 -- `.codex/skills/gframework-boot/SKILL.md` 与其 `references/startup-artifacts.md` 已切换到: - - 优先读取 `ai-plan/public/README.md` - - 命中映射后优先读取对应主题目录 - - 未命中映射时再扫描活动主题目录,并排除公共归档区 -- `ai-plan/README.md` 已补充主题命名、归档触发条件与 `boot` 读取顺序 -- 根 `README.md` 已改为要求维护公共索引与对应主题目录 -- 现有共享文档已迁移为: - - `ai-plan/public/ai-plan-governance/**` - - `ai-plan/public/cqrs-rewrite/**` - - `ai-plan/public/archive/cqrs-cache-docs-hardening/**` -- 已根据 PR #253 的最新未解决 review thread 清理 `ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md` - 中重复的 `### 验证` / `### 下一步` 标题,并补充恢复点后缀以消除 MD024 锚点冲突 +- 已为活跃主题建立并使用主题内归档目录: + - `ai-plan/public/ai-plan-governance/archive/todos/` + - `ai-plan/public/ai-plan-governance/archive/traces/` + - `ai-plan/public/ai-first-config-system/archive/todos/` + - `ai-plan/public/ai-first-config-system/archive/traces/` + - `ai-plan/public/cqrs-rewrite/archive/todos/` + - `ai-plan/public/cqrs-rewrite/archive/traces/` +- 已将以下历史内容移出默认 boot 路径: + - `ai-plan-governance` 的 RP-002 至 RP-004 历史 + - `ai-first-config-system` 截至 `2026-04-17` 的详细跟踪与执行 trace + - `cqrs-rewrite` 截至 `RP-043` 的详细跟踪与执行 trace +- 已同步更新 `AGENTS.md`、`ai-plan/README.md` 与 `gframework-boot`,明确 active 文档不是追加式日志,已完成且已验证阶段必须归档 ## 验证 -- `find ai-plan/public -maxdepth 4 -type f | sort` +- `find ai-plan/public -maxdepth 5 -type f | sort` - 结果:通过 - - 备注:活动主题、公共索引与归档主题已按新目录语义落位 -- `rg -n "ai-plan/public/README.md|ai-plan/public/|ai-plan/public/archive|ai-plan/private/" AGENTS.md .codex/skills/gframework-boot/SKILL.md .codex/skills/gframework-boot/references/startup-artifacts.md ai-plan/README.md README.md .gitignore` + - 备注:活跃主题、主题内归档文件与主题级归档都已按新目录语义落位 +- `wc -l ai-plan/public/ai-plan-governance/todos/ai-plan-governance-tracking.md ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md` - 结果:通过 - - 备注:新目录语义、索引入口与归档规则已统一到仓库规则与 boot skill -- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release --no-restore` + - 备注:6 个 active 入口文件当前合计 `249` 行,已从治理前的 `3046` 行显著收短 +- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` - 结果:通过 - - 备注:本轮规则与文档调整未引入构建问题 -- `rg -n "^### (验证|下一步)" ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md` - - 结果:通过 - - 备注:同名标题已按恢复点后缀唯一化,不再产生重复锚点 + - 备注:`GFramework.Cqrs.Abstractions` 与 `GFramework.Core.Abstractions` 构建通过,`0 warning / 0 error` + +## Archive Index + +- 治理历史跟踪归档:[ai-plan-governance-history-rp002-rp004.md](../archive/todos/ai-plan-governance-history-rp002-rp004.md) +- 治理历史 trace 归档:[ai-plan-governance-history-rp002-rp004.md](../archive/traces/ai-plan-governance-history-rp002-rp004.md) ## 下一步 -1. 后续新增活动主题时,先在 `ai-plan/public/README.md` 登记 worktree 到主题映射,再创建对应主题目录 -2. 阶段完成后优先收入主题内 `archive/`;主题整体完成后,再整目录移入 `ai-plan/public/archive/` -3. 若未来再新增 skill 或仓库规则引用 `ai-plan/`,统一按“公共索引 + 活动主题 + 归档主题 + 私有目录”扩展,不再恢复平铺结构 +1. 后续只要某个 active 主题积累了多个已完成且已验证阶段,就在同一变更里将其细节迁入该主题自己的 `archive/` +2. 若某个主题整体完成,再将整个主题目录移入 `ai-plan/public/archive//` +3. 后续新增 topic 时,默认直接创建 `todos/`、`traces/` 与 `archive/`,不要再把历史阶段长期堆在 active 入口 \ No newline at end of file diff --git a/ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md b/ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md index b9f6ef08..e15f6019 100644 --- a/ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md +++ b/ai-plan/public/ai-plan-governance/traces/ai-plan-governance-trace.md @@ -2,57 +2,29 @@ ## 2026-04-19 -### 阶段:目录语义收口(RP-002) +### 阶段:active 入口归档收口(RP-005) -- 建立 `AI-PLAN-GOV-RP-002` 恢复点 -- 用户指出当前 `ai-plan/` 存在三个治理问题: - - 缺少更细的目录分层,容易随着 worktree 增长持续膨胀 - - 缺少“不得写入敏感数据、真实路径、机器信息”的明确约束 - - 目录语义没有区分共享恢复信息与 worktree 私有状态 -- 已据此完成以下收口: - - 将既有共享 tracking / trace 文件迁移到扁平的 `ai-plan/public/` 共享目录 - - 新增 `ai-plan/private/` 作为工作树私有恢复空间,并通过 `.gitignore` 保持未跟踪 - - 新增 `ai-plan/README.md` 作为目录语义与内容规范的单点说明 - - 在 `AGENTS.md` 中补齐 public/private 职责边界,以及敏感信息与绝对路径禁写规则 - - 在 `gframework-boot` 中同步新的读取顺序:优先 public,按需读取当前 worktree 私有目录 +- 建立 `AI-PLAN-GOV-RP-005` 恢复点 +- 复核现有活跃主题后确认:虽然治理规则已提到主题内 `archive/`,但 active `todos/` / `traces/` 仍在持续堆积已完成历史 +- 已据此完成本轮收口: + - 为三个活跃主题补齐并实际使用 `archive/todos/` 与 `archive/traces/` + - 将 `ai-first-config-system` 与 `cqrs-rewrite` 的已完成阶段详细历史迁入主题内归档 + - 将治理主题自身的 RP-002 至 RP-004 历史迁入归档,只保留当前治理入口 + - 同步更新 `AGENTS.md`、`ai-plan/README.md` 与 `gframework-boot`,明确 active 文档必须保持精简 -### 验证(RP-002) +### Archive Context -- `find ai-plan -maxdepth 3 -type f | sort` - - 结果:通过 -- `rg -n "ai-plan/public/|ai-plan/private/" AGENTS.md .codex/skills/gframework-boot/SKILL.md .codex/skills/gframework-boot/references/startup-artifacts.md ai-plan/README.md .gitignore` - - 结果:通过 -- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release` - - 结果:通过 +- 主题治理历史归档: + - `ai-plan/public/ai-plan-governance/archive/todos/ai-plan-governance-history-rp002-rp004.md` + - `ai-plan/public/ai-plan-governance/archive/traces/ai-plan-governance-history-rp002-rp004.md` +- AI-First Config 历史归档: + - `ai-plan/public/ai-first-config-system/archive/todos/ai-first-config-system-history-through-2026-04-17.md` + - `ai-plan/public/ai-first-config-system/archive/traces/ai-first-config-system-history-through-2026-04-17.md` +- CQRS 历史归档: + - `ai-plan/public/cqrs-rewrite/archive/todos/cqrs-rewrite-history-through-rp043.md` + - `ai-plan/public/cqrs-rewrite/archive/traces/cqrs-rewrite-history-through-rp043.md` -### 下一步(RP-002) +### 下一步 -1. 后续若出现新的 worktree 私有恢复需求,直接在 `ai-plan/private//` 下创建,不再向共享目录追加本地临时状态 -2. 若将来需要进一步限制格式,可再为 `public/**` 与 `private/` 各自补一个模板文件,但本轮先把目录语义和安全边界固定下来 - -### 阶段:主题分组与启动索引(RP-003) - -- 建立 `AI-PLAN-GOV-RP-003` 恢复点 -- 用户进一步指出:即使 public/private 已分层,只要多 worktree 并行,扁平的活动主题集合仍会让 `boot` 随着 - `ai-plan/public` 增长而退化成大范围扫描 -- 已据此完成第二轮治理: - - 将活动共享文档迁移到 `ai-plan/public//todos/` 与 `ai-plan/public//traces/` - - 新增 `ai-plan/public/README.md` 作为公共启动索引,维护 worktree 到多个活跃主题的映射与优先顺序 - - 将已完成的 `cqrs-cache-docs-hardening` 整体移入 `ai-plan/public/archive/cqrs-cache-docs-hardening/` - - 在 `AGENTS.md`、`ai-plan/README.md`、根 `README.md` 与 `gframework-boot` 中统一“公共索引 + 活动主题 + - 主题内归档 + 主题级归档”的语义 - - 将 `.gitignore` 调整为允许 `ai-plan/public/**/*.md` 以新层级进入版本控制 - -### 验证(RP-003) - -- `find ai-plan/public -maxdepth 4 -type f | sort` - - 结果:通过 -- `rg -n "ai-plan/public/README.md|ai-plan/public/|ai-plan/public/archive|ai-plan/private/" AGENTS.md .codex/skills/gframework-boot/SKILL.md .codex/skills/gframework-boot/references/startup-artifacts.md ai-plan/README.md README.md .gitignore` - - 结果:通过 -- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release --no-restore` - - 结果:通过 - -### 下一步(RP-003) - -1. 未来每次新增或关闭主题,都同步更新 `ai-plan/public/README.md`,不要让 `boot` 回到全量扫描模式 -2. 若某个活跃主题内部继续积累阶段性完成物,优先收入该主题目录下的 `archive/` +1. 未来若 active 入口再次因为已完成阶段累积而膨胀,直接按同一模式归档,不再保留为追加式历史日志 +2. 后续新增 topic 时,默认同步创建 `todos/`、`traces/` 与 `archive/` 目录 diff --git a/ai-plan/public/cqrs-rewrite/archive/todos/cqrs-rewrite-history-through-rp043.md b/ai-plan/public/cqrs-rewrite/archive/todos/cqrs-rewrite-history-through-rp043.md new file mode 100644 index 00000000..c9477930 --- /dev/null +++ b/ai-plan/public/cqrs-rewrite/archive/todos/cqrs-rewrite-history-through-rp043.md @@ -0,0 +1,738 @@ +# CQRS 重写迁移跟踪 + +## 目标 + +围绕 `GFramework` 当前的双轨 CQRS 现状,完成一轮以“去 Mediator 外部依赖”为目标的架构迁移: + +- 将 `Mediator` 从 GFramework 公共 API 和运行时主路径中移除 +- 基于 GFramework 自有抽象重建正式 CQRS runtime、行为管道和注册机制 +- 保留 `EventBus` 作为框架级事件系统,不与 CQRS notification 混同 +- 让 `CoreGrid-Migration` 直连本地 `GFramework`,作为真实迁移验证工程 +- 为复杂迁移建立明确恢复点与进度追踪,避免上下文过长或中断后失去状态 + +## 当前恢复点 + +- 恢复点编号:`CQRS-REWRITE-RP-042` +- 当前阶段:`Phase 8` +- 当前焦点: + - 已将迁移目标修正为 `Core = App Runtime`、`CQRS = 默认集成进去的可替换子系统`,停止继续追求 `Core` 对 `Cqrs` 的零依赖 + - 已完成 `Core` 侧 CQRS 实现细节泄漏盘点,并将容器内的 handler 注册去重/调度细节下沉到 `GFramework.Cqrs` + - 已将 generator 从“整程序集回退”推进到“可见 handlers 走 `typeof(...)` 直注册,不可直接引用但可定位的 handlers 走 generated registry 内部定向 `Assembly.GetType(...)` 注册” + - 已将 partial reflection fallback 从“命中 marker 后整程序集 `GetTypes()`”收敛到“优先消费显式 `Type` / type-name metadata;且对 generator 已能重新定位的隐藏 handlers 不再额外依赖 runtime fallback marker” + - 已为手写/第三方程序集补齐正式的精确 fallback 入口:`CqrsReflectionFallbackAttribute` 现在既支持 `string` type-name 清单,也支持直接传入 `Type` 集合 + - 已将“隐藏 implementation 但 handler interface 仍可直接引用”的 generator 输出进一步收敛为“仅对 implementation 执行一次 `Assembly.GetType(...)`,随后直接按 `typeof(handlerInterface)` 注册”,不再为这类场景额外生成 `GetInterfaces()` / `IsSupportedHandlerInterface(...)` 辅助逻辑 + - 已将“隐藏 implementation + 隐藏但可精确重建的 handler interface”进一步收敛为“生成期记录 open generic contract 与 type arguments,运行期只做 implementation/type-argument 定向 lookup + `MakeGenericType(...)`”,常见私有嵌套 request 场景已不再需要 `GetInterfaces()` 接口发现 + - 已将 `RuntimeTypeReference` 继续扩成递归结构,可表达数组与构造泛型;`List`、`HiddenEnvelope` 这类闭包类型现在也能按生成期已知结构直接重建,进一步贴近 `Mediator` 那种“生成期定结构,运行期只做常量时间绑定”的模式 + - 已在 `CqrsDispatcher` 热路径补齐 service-type 缓存,减少 `PublishAsync` / `SendAsync` / `CreateStream` 中重复的 `MakeGenericType` 开销 + - 已将 `CqrsDispatcher` 的 invoker method-definition 查找收敛为静态一次解析,并补齐 request/no-pipeline、request/with-pipeline、notification、stream 四条缓存路径的统一回归 + - 已将 `CqrsDispatcher` 的 request / pipeline invoker 从 `ValueTask` 缓存改为按 `TResponse` 分层的强类型委托缓存,减少 value-type 响应在热路径上的装箱与额外拆箱 + - 已将 `CqrsDispatcher` 的 notification/request/stream 热路径进一步收敛为聚合 dispatch binding cache,把服务类型与强类型 invoker 合并到单一缓存项; + request 路径继续按 `TResponse` 分层,并减少单次分发里的重复字典命中 + - 已将 `CqrsHandlerRegistrar` 的程序集级 attribute 读取、registry 激活分析与未命中 generated-registry 时的 `GetTypes()` 扫描收敛为进程级缓存; + 同一 `Assembly` 对象跨容器重复接入时不再重复做 registry metadata / fallback metadata / loadable-types 分析 + - 已完成一轮公开入口文档收口:`README.md`、`CLAUDE.md`、`docs/zh-CN/core/cqrs.md` 与 `docs/zh-CN/source-generators/index.md` + 现已统一到 `GFramework.Cqrs` 与分拆后的 `Core/Game/Godot/Cqrs SourceGenerators` 模块命名,不再把公开入口写成旧聚合 `GFramework.SourceGenerators` + - 已将 `CqrsHandlerRegistryGenerator` 从“同一 implementation 只走一种注册分支”的互斥输出,收敛为“按 handler interface 粒度组合 direct / reflected-implementation / precise-reflected 注册,并继续按接口名稳定排序”; + 同一 handler implementation 上的可静态绑定接口不再因为另一条接口需要更窄路径而被一起拖进更粗回退 + - 已将 `CqrsHandlerRegistryGenerator` 的“生成注册器是否能直接引用类型”判断改为基于 Roslyn `Compilation.IsSymbolAccessibleWithin(...)`,不再仅靠声明可见性粗判; + 对“当前继承/语义上下文可见、但生成注册器顶层上下文不可直接写”的外部 `protected internal` 嵌套类型,现会直接进入精确 type lookup,而不是误生成不可编译的 `typeof(...)` + - 已删除 generated registry 内部的 `RegisterRemainingReflectedHandlerInterfaces(...)` implementation 级 `GetInterfaces()` 尾分支; + 当前合法 C# closed handler contract 已统一收敛到 direct / reflected-implementation / precise-reflected 三类注册路径 + - 若未来再出现新的 Roslyn 类型形态暂时无法编码进 `RuntimeTypeReferenceSpec`,生成器现会退回到“保留已知静态注册 + 通过程序集级 `CqrsReflectionFallbackAttribute` 对具体 handler implementation 定向补反射”; + 若 runtime 合同不提供该 marker,则生成器保守地不输出 registry,避免静默漏注册 + - 已完成一轮 review follow-up 小修:补齐 dispatcher `string` pipeline cache 清理,给 generator 可访问性判断默认分支加上假设说明,并收敛多程序集 generator 测试基础设施的运行时引用缓存与编译错误诊断输出 + - 已完成三轮 review follow-up:补齐 `ICqrsRuntime` 异常/上下文约束文档,明确 `DefaultCqrsRegistrationService` 的线程安全假设,收窄 `MicrosoftDiContainer` 未冻结态的实例别名去重范围,提前固定/校验 CQRS 程序集枚举输入,并把一类隐藏 handler 从“implementation + interface 运行时发现”进一步压缩到“implementation-only lookup + direct interface registration” + - 已将 `GFramework.SourceGenerators` 按目标模块继续拆分为 `GFramework.Core.SourceGenerators` / `GFramework.Core.SourceGenerators.Abstractions` / `GFramework.Cqrs.SourceGenerators` / `GFramework.Game.SourceGenerators`,并同步修正 solution、测试项目与 `Game` schema targets 的引用链 + - 已删除公开 `Mediator` 兼容壳层:`RegisterMediatorBehavior()`、`ContextAwareMediator*Extensions` 与 `MediatorCoroutineExtensions`,不再继续维护旧命名兼容表面 + - 已将 generator 对“外部程序集不可直引 protected nested 类型”的 residual contract 从 `RegisterRemainingReflectedHandlerInterfaces(...)` 补洞推进到“按程序集名 + metadata name 定向 lookup”;`IRequestHandler` 这类场景现在走精确 type lookup,而不再退回 implementation 级 `GetInterfaces()` + - 当前已确认在 C# 可编译约束下,generator 主路径已不再需要 implementation 级 `GetInterfaces()` 回退;下一步可继续转向 dispatch/invoker 反射收敛,或补做 source-generator namespace/docs 收口 + - 已将本任务后续参考的 `Mediator` 源码收口到仓库内 `ai-libs/Mediator`;`ai-libs/**` 现作为第三方只读参考区,不纳入常规实现修改范围 + - 已开始把 `docs/zh-CN/**` 中残留的旧 `GFramework.SourceGenerators.Abstractions.*` 示例命名空间收口到当前 + `GFramework.Core.SourceGenerators.Abstractions.*`,避免文档继续指向不存在的旧公开命名空间 + - 已新增项目级 `$gframework-pr-review` skill,可在当前分支下直接抓取 GitHub PR 页面、提取 CodeRabbit 评论、 + `Failed checks` 与测试结果,不再依赖 `gh` CLI + - 已按 PR `#253` 当前公开 review 信号修正 `gframework-boot` 的 `resume/recovery` 语义、收窄 `AGENTS.md` + 中 `ai-libs/**` 观察写入 active plan/trace 的触发条件,并补齐模板/接口注释中的旧 `Rule` 命名空间残留 + +## 本轮计划 + +### Phase 0:工作流基础 + +- [x] 在 `ai-plan/public/cqrs-rewrite/todos/` 建立本任务跟踪文档 +- [x] 在 `ai-plan/public/cqrs-rewrite/traces/` 建立本任务追踪文档 +- [x] 将恢复点 / trace / subagent 协作规范写入 `AGENTS.md` + +### Phase 1:本地验证链路 + +- [x] 确认 `CoreGrid-Migration` 当前引用形态 +- [x] 将 `CoreGrid-Migration` 从 NuGet 包切到本地 `GFramework` 工程引用 +- [x] 让 `CoreGrid-Migration` 使用本地 Source Generator 而不是外部已发布版本 +- [x] 验证本地引用链路至少能完成 restore / build + +### Phase 2:CQRS 基础重建 + +- [x] 在 `GFramework.Core.Abstractions` 定义自有 CQRS 契约 +- [x] 在 `GFramework.Core` 落地 dispatcher / handler registry / behavior pipeline +- [x] 清理 `IArchitectureContext` 中对 `Mediator.*` 的公共签名依赖 +- [x] 设计 CQRS 模块启用方式,替代 `Configurator => AddMediator(...)` + +### Phase 3:接入迁移 + +- [x] 迁移 `GFramework.Core.Cqrs.*` 基类到新契约 +- [x] 迁移 `ContextAwareMediator*Extensions` 与协程扩展 +- [x] 迁移 `CoreGrid-Migration/scripts/cqrs/**` 到新契约 +- [x] 删除 `GameArchitecture.Configurator` 中的 `AddMediator(...)` + +### Phase 4:收尾 + +- [x] 移除 `Mediator` 包依赖与相关测试/文档残留 +- [x] 运行目标构建与测试 +- [x] 记录剩余风险与下一恢复点 +- [x] 根据 review 收紧 `AbstractStreamCommandHandler` 的生命周期 XML 文档 +- [x] 将 `CqrsHandlerRegistrar` 容错回归改挂到 `RegisterHandlers` 真实入口 +- [x] 提升 `CqrsTestRuntime` 反射绑定的签名鲁棒性并补齐 XML 文档 +- [x] 将剩余 `Mediator` 兼容入口推进到正式弃用周期(隐藏 IntelliSense + 明确 future major 移除) +- [x] 落地 CQRS handler source-generator MVP,并保留运行时反射回退 +- [x] 落地非默认程序集的显式 CQRS handler 注册入口,并避免重复程序集接入导致的重复 handler 映射 + +### Phase 5:模块边界再评估 + +- [x] 评估是否将 CQRS 拆分为独立的 abstractions 模块与 runtime 实现模块,并梳理 `GFramework.Core` 对其依赖倒置方案 +- [x] 若拆分收益成立,输出包边界与迁移草案: + - `GFramework.Cqrs.Abstractions`:消息契约、handler 契约、pipeline 契约、registry 契约 + - `GFramework.Cqrs`:dispatcher、runtime 注册、behavior 执行、source-generator 接入 + - `GFramework.Core`:改为依赖 CQRS 抽象或可选 runtime 接入,而不是继续承载全部 CQRS 实现 +- [x] 评估拆分后的兼容影响: + - `IArchitectureContext` / `ArchitectureBootstrapper` / `MicrosoftDiContainer` 的程序集依赖变化 + - 现有 `CoreGrid-Migration` 与其他消费端项目的引用路径变化 + - source-generator、AOT、冷启动、包发布与文档迁移成本 + +### Phase 6:拆分前置 seams + +- [x] 在新 CQRS 抽象边界中定义 `ICqrsRuntime` / `ICqrsHandlerRegistrar` 等 runtime seam +- [x] 将 `ArchitectureContext` 从直接 `new CqrsDispatcher(...)` 改为依赖容器解析的 runtime 抽象 +- [x] 将 `MicrosoftDiContainer.RegisterCqrsHandlersFromAssemblies(...)` 从直接调用 `CqrsHandlerRegistrar` 改为依赖 runtime 注册抽象 +- [x] 保持默认架构程序集 + `GFramework.Core` 程序集 handler 自动接入行为不变 +- [x] 跑通至少覆盖上述 seam 改造的定向测试 + +### Phase 7:项目骨架与类型迁移 + +- [x] 创建 `GFramework.Cqrs.Abstractions` 与 `GFramework.Cqrs` 项目骨架,并接入 solution / package graph +- [x] 将纯 `GFramework.Core.Abstractions/Cqrs/*` 契约迁移到新 abstractions 项目,同时保持现有命名空间不变 +- [x] 将 CQRS 聚焦测试拆分到 `GFramework.Cqrs.Tests`,并在 `CI` 中纳入独立测试项目 +- [x] 将 `ICqrsRuntime` / `ICqrsHandlerRegistry` / `CqrsHandlerRegistryAttribute` 的最终归属与循环依赖处理方案单独收敛 +- [x] 收敛 `CqrsCoroutineExtensions` 的最终归属: + - 结论改为“保留在 `GFramework.Core` 作为 App Runtime 对 CQRS 的协程桥接”,不再作为必须迁往 `GFramework.Cqrs` 的剩余项 +- [x] 处理 `GFramework.SourceGenerators` 对 CQRS 抽象程序集元数据名的引用迁移 +- [x] 跑通迁移后最小构建与回归 + +### Phase 8:方向修正后的收敛 + +- [x] 将 `ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md` 的目标边界正式改写为: + - `GFramework.Core.Abstractions -> GFramework.Cqrs.Abstractions` + - `GFramework.Core -> GFramework.Cqrs` + - `Core` 默认集成 CQRS,但不依赖其细节结构 +- [x] 将后续实现约束明确写入计划: + - CQRS runtime / generator / 低反射增强阶段必须优先参考 `ai-libs/Mediator` 中已经成熟可用的实现,而不是完全从零重做 +- [x] 盘点 `Core` 中仍可能泄漏 CQRS 实现细节的位置: + - 直接实例化具体 runtime 类型 + - 直接依赖 generator 生成类型 + - 硬编码 handler/internal registry 结构 +- [x] 将 partial reflection fallback 从“整程序集 `GetTypes()` 扫描”推进到“generator 输出精确 handler type-name 清单后,runtime 定向 `Assembly.GetType(...)` 补扫” +- [x] 为手写/第三方程序集补齐精确 fallback metadata 入口,避免这类场景只能依赖旧版空 marker 或脆弱的字符串约定 +- [x] 为 `CqrsDispatcher` 补齐 notification/request/stream service-type 缓存,减少热路径 `MakeGenericType` 重复开销 +- [x] 为 `CqrsDispatcher` 补齐 invoker method-definition 静态缓存,并把 request/no-pipeline、request/with-pipeline、notification、stream 四条缓存路径纳入统一回归 +- [x] 将 `CqrsDispatcher` 的分散 service-type / invoker cache 收敛为按消息类别聚合的 dispatch binding cache,进一步减少热路径重复缓存查询 +- [x] 将 `CqrsHandlerRegistrar` 的程序集级 metadata 分析与 loadable-types 扫描收敛为进程级缓存,减少跨容器重复初始化时的冷路径反射 +- [x] 收口公开入口文档中的旧 SourceGenerators 聚合模块表述与旧 CQRS 示例命名空间 +- [x] 将私有/不可直接引用 handler 从“generator 输出 fallback marker + runtime 补扫”推进到“generated registry 内部定向反射注册”,进一步压缩 runtime fallback 面积 +- [ ] 将后续主线转向 CQRS 子系统增强: + - 参考 `ai-libs/Mediator` 现有成熟实现 + - generator 覆盖面继续扩大 + - 减少 dispatch/invoker 路径的反射占比 + - package/facade/兼容层收敛 + +## 当前完成结果 + +- `GFramework.Cqrs.Abstractions` 与 `GFramework.Cqrs` 项目骨架已创建,并已加入 `GFramework.sln`。 +- `GFramework.Core.Abstractions` 已新增到 `GFramework.Cqrs.Abstractions` 的项目引用。 +- `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 已扩展为可承载精确 fallback handler 类型名清单;当清单为空时,仍保持旧版“整程序集补扫”的兼容语义。 +- `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 现同时支持 `params string[]` 与 `params Type[]` 两种精确 fallback 声明方式,使手写/第三方程序集可以直接提供 handler 类型而不必再走字符串名称回查。 +- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 现会在检测到不可直接引用、但仍可按元数据名从当前程序集重新定位的 concrete handler 时,直接在生成注册器内部输出定向 `Assembly.GetType(...)` 注册逻辑,不再为这些场景额外生成 fallback marker。 +- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已进一步将“隐藏 implementation,但 handler interface 仍可合法 `typeof(...)` 引用”的场景收敛为: + - generated registry 只对 implementation 做一次 `Assembly.GetType(...)` + - service type 继续使用 `typeof(IRequestHandler<...>)` / `typeof(INotificationHandler<...>)` / `typeof(IStreamRequestHandler<...>)` + - 不再为该类场景生成 `GetInterfaces()` / `IsSupportedHandlerInterface(...)` / `GetRuntimeTypeDisplayName(...)` 等全反射辅助代码 +- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已继续把“handler interface 本身不可直接引用,但其 open generic contract 与 type arguments 仍可精确表达”的场景收敛为: + - 生成期记录 `IRequestHandler<,>` / `INotificationHandler<>` / `IStreamRequestHandler<,>` 的 open generic contract + - 对不可直引、但属于当前编译程序集的 type argument 输出定向 `Assembly.GetType(...)` + - 运行期通过 `MakeGenericType(...)` 重建精确 service type 并直接注册到隐藏 implementation + - 当前合法 closed contract 已不再需要 generated registry 内部的 `GetInterfaces()` 本地回退辅助逻辑; + 若未来出现新的未覆盖类型形态,则改由程序集级 targeted fallback 合同补位 +- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 现已支持递归重建以下 `RuntimeTypeReference` 形态: + - 直接 `typeof(...)` 可引用类型 + - 当前编译程序集内的隐藏类型定向 `Assembly.GetType(...)` + - 数组类型 `T[]` / 多维数组的 `MakeArrayType(...)` + - 构造泛型类型 `Generic` 的递归 `MakeGenericType(...)` +- 因此常见“隐藏消息类型嵌套在数组或泛型响应里”的 handler interface,已不再需要退回 `GetInterfaces()` 接口发现。 +- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已进一步修正同一 implementation 的注册组合策略: + - direct registration、reflected-implementation registration 与 precise-reflected registration 不再互斥 + - 当一个 implementation 同时命中多种注册路径时,生成器会合并这些注册步骤,并继续按 `HandlerInterfaceLogName` 稳定排序输出 + - 这样可静态绑定的接口不会再因为同实现上的另一条接口需要更窄反射路径而被一起降级 +- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 现已把“生成注册器顶层上下文是否能合法书写某个类型”改为通过 `Compilation.IsSymbolAccessibleWithin(symbol, compilation.Assembly, null)` 判定: + - 该调整修正了此前把 `protected internal` 等声明可见类型误当作“可直接 `typeof(...)`”的粗判 + - 对当前程序集私有类型仍继续走 metadata-name 定向 lookup + - 对外部程序集里只能通过继承语义可见、但生成注册器顶层上下文不可直接写的类型,现会正确进入精确的程序集定向 lookup 路径 +- `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已删除 generated registry 内部的 implementation 级 `GetInterfaces()` 补洞辅助逻辑: + - 不再生成 `RegisterRemainingReflectedHandlerInterfaces(...)`、`knownServiceTypes`、`GetRuntimeTypeDisplayName(...)` 等尾分支辅助代码 + - 对当前合法 closed handler contract,统一走 direct / reflected-implementation / precise-reflected 三类静态化路径 + - 若未来出现暂未被 `RuntimeTypeReferenceSpec` 表达的新类型形态,则仅通过程序集级 `CqrsReflectionFallbackAttribute` 对具体 handler implementation 定向补反射 +- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已新增两条混合场景快照回归: + - 同一可见 implementation 同时包含 direct + precise registration + - 同一隐藏 implementation 同时包含 reflected-implementation + precise registration +- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已新增多程序集回归: + - 通过额外元数据引用构造“外部基类携带 `protected internal` 嵌套 request/response 类型”的真实边界 + - 锁定生成器会保留已知直注册、输出 `ResolveReferencedAssemblyType(...)` 精确 lookup,且不会重新带回 `RegisterRemainingReflectedHandlerInterfaces(...)` 尾分支或对不可访问 protected-nested 类型生成 `typeof(...)` +- `GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs` 与 `MetadataReferenceTestBuilder.cs` 已补齐多程序集测试基础设施,允许 CQRS generator 回归显式追加内存元数据引用。 +- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已改为优先消费 fallback metadata 中的显式 `Type` 集合,其次才按 type-name 清单定向 `Assembly.GetType(...)` 补扫;只有缺少精确元数据时才继续走整程序集 `GetTypes()` 扫描。 +- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已新增回归,分别锁定“string type-name fallback 不触发 `GetTypes()` 全量扫描”与“direct `Type` fallback 同时不触发 `GetType()` / `GetTypes()`”行为;`GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已同步覆盖“隐藏 handler 由 generated registry 自行定向注册”与“无论 runtime 是否暴露 legacy fallback marker,均不再为该场景输出 marker”。 +- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已新增 notification/request/stream service-type 缓存,避免重复为同一消息类型组合执行 `MakeGenericType`。 +- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已将 invoker 对应的泛型方法定义查找收敛为静态缓存,避免每种新消息类型首次命中 invoker cache 时再次执行 `GetMethod(...)` 查找。 +- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已将 request/no-pipeline 与 request/with-pipeline 两条 invoker 路径收敛为按 `TResponse` 分层的强类型缓存: + - `SendAsync(...)` 现在直接复用 `ValueTask` 委托 + - 不再通过 `object` 桥接承接 request 结果,减少 value-type 响应装箱 +- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已继续把 notification/request/stream 的分散 service-type / invoker 缓存收敛为聚合 dispatch binding cache: + - `NotificationDispatchBindings` / `RequestDispatchBindingCache` / `StreamDispatchBindings` 会把服务类型与强类型调用委托绑定到单一缓存项 + - request 路径在保持按 `TResponse` 分层的同时,进一步减少一次分发中的重复字典查询 +- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 已补齐回归,额外锁定 request invoker 会按 `int` / `string` 等不同响应类型分层建缓存,并在 `SetUp` 中显式清空 dispatcher 静态缓存,避免跨测试污染导致断言漂移。 +- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 已新增/更新回归,统一锁定 dispatcher 对 request/no-pipeline、request/with-pipeline、notification、stream 的 service-type / invoker cache 都满足“一次建缓存,多次复用”。 +- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已新增三类进程级缓存: + - 程序集级 `AssemblyMetadataCache`,复用 generated registry attribute 与 reflection fallback metadata 的分析结果 + - registry 类型级 `RegistryActivationMetadataCache`,复用接口/抽象态/无参构造激活分析 + - `LoadableTypesCache`,为未命中 generated registry 的程序集复用首次 `GetTypes()` / `ReflectionTypeLoadException` 恢复结果 +- 上述缓存对 `Assembly` 统一使用引用相等键,避免 mock/proxy 或自定义 `Assembly.Equals(...)` 语义干扰缓存命中。 +- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已新增回归: + - 锁定同一程序集对象跨两个容器重复接入时,`CqrsHandlerRegistryAttribute` / `CqrsReflectionFallbackAttribute` / `Assembly.GetType(...)` 只会解析一次 + - 锁定未命中 generated registry 时,同一程序集对象跨两个容器重复接入只会执行一次 `GetTypes()`,且两边都能复用首次恢复出的可加载 handler 集合 +- `README.md`、`CLAUDE.md`、`docs/zh-CN/core/cqrs.md` 与 `docs/zh-CN/source-generators/index.md` 已完成最小同步: + - 根 README 的模块表、仓库结构与安装指引已补入 `GFramework.Cqrs` 以及拆分后的 SourceGenerators 家族 + - `CLAUDE.md` 的依赖图与模块结构说明已从旧 `GFramework.SourceGenerators` 聚合表述改为 `Core/Game/Godot/Cqrs SourceGenerators` + - `docs/zh-CN/core/cqrs.md` 的示例命名空间已切到当前 `GFramework.Cqrs*` 公开路径,并把迁移说明改成“直接改用 `RegisterCqrsPipelineBehavior()`” + - `docs/zh-CN/source-generators/index.md` 已从“单一 `GFramework.SourceGenerators` 包”口径改为“按模块拆分的 Source Generators 家族”口径 +- 以下纯 CQRS 契约已从 `GFramework.Core.Abstractions/Cqrs/*` 迁移到 `GFramework.Cqrs.Abstractions/Cqrs/*`,并保持原命名空间不变: + - `IRequest*` / `IStreamRequest*` / `INotification*` + - `IPipelineBehavior` + - `MessageHandlerDelegate` + - `Unit` + - `Command/Query/Request/Notification` 输入与标记契约 + - `ICqrsHandlerRegistrar` +- `GFramework.Cqrs.Abstractions/GlobalUsings.cs` 已补齐基础 system 命名空间,避免新项目在关闭 `ImplicitUsings` 约束下丢失 `ValueTask` / `CancellationToken` / `IAsyncEnumerable`。 +- `GFramework.Cqrs.Tests` 已创建并加入 `GFramework.sln`,当前已承接以下 CQRS 聚焦测试: + - `CqrsHandlerRegistrarTests` + - `CqrsCoroutineExtensionsTests` + - `MediatorAdvancedFeaturesTests` + - `MediatorArchitectureIntegrationTests` + - `MediatorComprehensiveTests` +- `GFramework.Cqrs.Tests/GlobalUsings.cs` 与 `Logging/TestLogger.cs` 已补齐,确保新测试项目不再隐式依赖 `GFramework.Core.Tests` 的编译上下文。 +- `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 已移除对已迁移测试类型的编译期耦合,改为复用本地最小 CQRS fixture 验证容器层程序集去重与重新注册行为。 +- `GFramework.Cqrs/ICqrsRegistrationService.cs` 与 `Internal/DefaultCqrsRegistrationService.cs` 已新增: + - 将 CQRS handler 程序集接入的稳定键去重与 registrar 调度从 `MicrosoftDiContainer` 下沉到 CQRS runtime 内部 + - `MicrosoftDiContainer.RegisterCqrsHandlersFromAssemblies(...)` 现只保留公开委托入口,不再直接维护 handler 注册细节状态 +- `GFramework.Core/Services/Modules/CqrsRuntimeModule.cs` 与 `GFramework.Tests.Common/CqrsTestRuntime.cs` 已同步注册新的 `ICqrsRegistrationService`,确保生产路径与裸测试容器路径都通过同一协调服务接入 handler 程序集。 +- `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 已新增并保留,作为“手写/第三方程序集或 generator 仍未直接覆盖的场景需要 runtime 补反射”的程序集级兼容入口。 +- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已从“发现任一不可见 handler 就整程序集不生成”收敛为: + - 仍为可由生成代码合法引用的 handlers 生成 registry + - 对私有/不可直接引用但可按元数据名重新定位的 handlers,在 generated registry 内部输出定向反射注册 + - runtime fallback marker 仅保留给手写 metadata 或未来仍无法由生成器自处理的真正残余回退场景; + 这些场景也不再通过 generated registry 内部 `GetInterfaces()` 尾分支处理 +- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已支持“generated registry + reflection fallback”组合路径: + - 当程序集带有 `CqrsReflectionFallbackAttribute` 时,运行时会在执行 generated registry 后继续补一次 reflection 扫描 + - 反射补扫前会按 `ServiceType + ImplementationType` 去重,避免已由 generated registry 注册的映射重复写入 +- 覆盖该行为的新增/更新测试已落地: + - `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` + - `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` +- 本轮盘点结论已明确: + - `ArchitectureContext -> ICqrsRuntime` 属于合理 seam,不视为实现细节泄漏 + - `Core` 当前未直接依赖任何 generator 生成类型名 + - 仍值得继续收敛的点主要是 `ArchitectureBootstrapper` 默认 handler 程序集硬编码,以及更长线的 `IIocContainer` / `IArchitecture` CQRS 装配 API 暴露面 +- `GFramework/.github/workflows/ci.yml` 已新增 `dotnet test GFramework.Cqrs.Tests ...`,使 CQRS 模块拆分后的测试在 PR CI 中继续被覆盖。 +- `GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs`、`CqrsHandlerRegistrar.cs` 与 `DefaultCqrsHandlerRegistrar.cs` 已物理迁移到 `GFramework.Cqrs` 项目,同时保持现有 `GFramework.Core.Cqrs.Internal` 命名空间不变,避免消费端源码感知程序集拆分。 +- `GFramework.Core/Cqrs/Command/CommandBase.cs`、`Query/QueryBase.cs`、`Request/RequestBase.cs` 与 `Notification/NotificationBase.cs` 已迁移到 `GFramework.Cqrs`,继续保留原公开命名空间。 +- `GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs` 与 `PerformanceBehavior.cs` 已物理迁移到 `GFramework.Cqrs/Cqrs/Behaviors/*`,继续保留 `GFramework.Core.Cqrs.Behaviors` 公开命名空间,避免消费端源码感知程序集调整。 +- `GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs`、`ContextAwareCqrsCommandExtensions.cs` 与 `ContextAwareCqrsQueryExtensions.cs` 已迁移到 `GFramework.Cqrs`,`GFramework.Godot` 与兼容层调用点无需修改源码即可继续解析这些扩展。 +- `GFramework.Core/Logging/LoggerFactoryResolver.cs` 已下沉到 `GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs`,并保持 `GFramework.Core.Logging` 命名空间不变: + - 默认 provider 会优先通过反射解析 `GFramework.Core` 中的 `ConsoleLoggerFactoryProvider` + - 若宿主未加载默认日志实现,则退回静默 provider,避免 `GFramework.Cqrs -> GFramework.Core` 形成反向依赖 + - `GFramework.Core` 已通过 type forward 继续暴露该公开类型,降低已编译消费端的运行时兼容风险 +- `ICqrsHandlerRegistry` 与 `CqrsHandlerRegistryAttribute` 已从 `GFramework.Core.Abstractions` 收敛到 `GFramework.Cqrs` 运行时根命名空间: + - 这两个类型依赖 `ILogger` / `IServiceCollection`,不再适合继续放在“纯消息契约”抽象层 + - 该调整避免了 `GFramework.Core.Abstractions <-> GFramework.Cqrs.Abstractions` 循环引用 +- `ICqrsRuntime` 已进一步收敛到 `GFramework.Cqrs.Abstractions`: + - 新增轻量 marker seam `ICqrsContext`,让 runtime 契约不再直接依赖 `IArchitectureContext` + - `IArchitectureContext` 现在实现 `ICqrsContext`,保留当前架构上下文作为默认 CQRS 分发上下文 + - 旧 `GFramework.Core.Abstractions.Cqrs.ICqrsRuntime` 保留为兼容别名,并由默认 runtime 模块同时注册新旧接口,避免立即打断历史公开路径 +- `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,而不再跨程序集直接 `new` 内部实现。 +- 已确认 `GFramework.Core/Coroutine/Extensions/CqrsCoroutineExtensions.cs` 暂不适合迁移到 `GFramework.Cqrs`: + - 它直接依赖 `TaskCoroutineExtensions.AsCoroutineInstruction()` 等 `Core` 协程工具链 + - 若一并迁出会把非 CQRS 的协程基础设施反向拉进 runtime 项目 + - 在方向修正后,该类型不再作为“必须迁走”的剩余项,而是正式视为 `Core` 对 CQRS 的桥接层 +- 当前边界结论已修正为: + - `GFramework.Core.Abstractions -> GFramework.Cqrs.Abstractions` + - `GFramework.Core -> GFramework.Cqrs` + - `Core` 默认集成 CQRS,但不应继续依赖其 dispatcher / generator / handler registry 细节结构 +- 当前主阻塞不再是“如何让 `Core` 摆脱 `Cqrs`”,而是: + - 如何继续降低 runtime 对反射的依赖 + - 如何让 generator 从“注册器 MVP”继续走向更完整的低反射支持 + - 如何收敛 package/facade/兼容层,而不破坏 `CoreGrid-Migration` 等真实消费端 +- `GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs` 与 `ICqrsHandlerRegistrar.cs` 已新增,形成 `ArchitectureContext` 与容器注册路径共享的 runtime seam。 +- `GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs` 已新增,复用现有 `CqrsHandlerRegistrar` 静态流水线承接 `ICqrsHandlerRegistrar` 默认实现。 +- `GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs` 已改为实现 `ICqrsRuntime`,并将当前 `IArchitectureContext` 作为调用参数传入,而不再由 `ArchitectureContext` 直接持有具体实现依赖。 +- `GFramework.Core/Architectures/ArchitectureContext.cs` 已从直接 `new CqrsDispatcher(...)` 改为解析 `ICqrsRuntime` seam。 +- `GFramework.Core/Ioc/MicrosoftDiContainer.cs` 已从直接调用 `CqrsHandlerRegistrar` 改为解析已注册的 `ICqrsHandlerRegistrar` seam。 +- `GFramework.Core/Services/Modules/CqrsRuntimeModule.cs` 已新增,并由 `ServiceModuleManager` 纳入 built-in modules,确保默认架构启动路径继续自动具备 CQRS runtime 与 handler 注册能力。 +- `GFramework.Core.Tests/CqrsTestRuntime.cs` 已补充裸测试容器的 CQRS seam 注册辅助,以便不经过 `ServiceModuleManager` 的单元测试继续观察正式 runtime 行为。 +- `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 中“Clear 后重新接入 handler”回归已适配 seam 方案:在裸容器 `Clear()` 后显式补回测试基础设施,再验证程序集去重状态重置。 +- `ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md` 已新增,完成 Phase 5 的模块边界评估、依赖倒置方案与包拆分草案输出。 +- Phase 5 结论已收敛为“两阶段拆分”: + - 先做 `Core -> runtime abstraction` 的 seam 改造 + - 再拆 `GFramework.Cqrs.Abstractions` / `GFramework.Cqrs` 项目与 public runtime 类型归属 +- 已确认当前真正阻塞 CQRS 拆分的硬耦合点主要是: + - `ArchitectureContext` 直接实例化 `CqrsDispatcher` + - `MicrosoftDiContainer` 直接调用 `CqrsHandlerRegistrar` + - `ArchitectureBootstrapper` 在默认启动路径内建 CQRS handler 注册假设 +- 已确认当前消费端兼容面的主要压力来自: + - `CoreGrid-Migration/scripts/cqrs/**` 对 `CommandBase`、`Abstract*Handler`、`GFramework.Core.Cqrs.Extensions` 的直接依赖 + - `GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs` 对 `GFramework.Core.Cqrs.Extensions` 的直接依赖 +- `CoreGrid-Migration` 已直连本地 `GFramework` 源码与本地 source generators。 +- `GameArchitecture` 已不再依赖 `collection.AddMediator(...)` 即可使用 CQRS。 +- `GFramework.Core.Abstractions` 与 `GFramework.Core.Tests` 已移除 `Mediator.Abstractions` / + `Mediator.SourceGenerator` 包引用;`IServiceCollection` / `IServiceScope` 所需依赖改为显式引用 + `Microsoft.Extensions.DependencyInjection.Abstractions`。 +- `GFramework.Core.Abstractions` 新增自有 CQRS 契约: + - `IRequest` / `INotification` / `IStreamRequest` + - `IRequestHandler<,>` / `INotificationHandler<>` / `IStreamRequestHandler<,>` + - `Unit` + - `IPipelineBehavior<,>` / `MessageHandlerDelegate<,>` +- `ArchitectureBootstrapper` 会在初始化阶段自动扫描并注册当前架构程序集与 `GFramework.Core` 程序集中的 CQRS handlers。 +- `IArchitecture`、`IIocContainer`、`Architecture`、`ArchitectureModules` 与 `MicrosoftDiContainer` + 已新增 `RegisterCqrsHandlersFromAssembly(...)` / `RegisterCqrsHandlersFromAssemblies(...)`, + 允许模块程序集或扩展包程序集在初始化阶段显式接入与默认路径相同的“生成注册器优先 + 反射回退”注册链路。 +- `MicrosoftDiContainer` 已把默认启动路径与显式扩展路径统一到同一个 CQRS handler 注册入口, + 并按稳定程序集键去重,避免默认扫描与后续模块接入重复写入同一程序集的 handler 映射。 +- `IArchitectureContext`、`QueryBase`、`Abstract*Handler` 以及 `MessageHandlerDelegate` 的 XML 文档已补齐迁移后的契约边界,明确旧 + Command/Query 总线与新 CQRS runtime 的推荐入口。 +- `CqrsDispatcher` 已支持: + - request dispatch + - notification publish + - stream dispatch + - context-aware handler 注入 + - request pipeline behavior 链式执行 +- `CqrsHandlerRegistrar` 已补齐三项运行时硬化: + - 按程序集名、处理器类型名与处理器接口名稳定排序,避免通知处理顺序随反射枚举漂移 + - 在 `ReflectionTypeLoadException` 场景下保留可加载处理器并记录告警 + - 自动扫描到的 request / notification / stream handler 统一改为 transient,避免上下文感知处理器在并发分发时共享可变状态 +- `GFramework.Core.Tests` 中原依赖 `Mediator` 注册路径的测试已切换到框架内建 CQRS 注册路径;`CqrsTestRuntime` 现仅保留 + handler 注册职责,行为测试改为走 `ArchitectureContext.SendRequestAsync(...)` 正式入口。 +- `AbstractStreamCommandHandler` 已把上下文注入窗口、瞬态实例复用约束和流创建/枚举取消边界写入显式 + ``,避免公共流式命令处理器基类的生命周期约束只停留在摘要里。 +- `CqrsHandlerRegistrarTests` 已改为通过 `CqrsTestRuntime.RegisterHandlers(...)` 真实入口验证部分加载失败恢复路径,并同时断言 + “剩余 handler 仍被注册 + warning 已记录”。 +- `CqrsTestRuntime` 已补齐 XML 文档,并改为按 `IIocContainer + IEnumerable + ILogger` 精确绑定 + `RegisterHandlers(...)`,避免未来新增同名重载后出现运行时歧义。 +- `MediatorCoroutineExtensions` 已改为直接走 `IArchitectureContext.SendAsync(...)` 内建 CQRS 入口,不再从容器解析 + `IMediator`;该类型名仅作为历史兼容命名保留。 +- `RegisterCqrsPipelineBehavior()` 已作为新的公开推荐入口加入 `IArchitecture`、`IIocContainer`、 + `Architecture` 与 `MicrosoftDiContainer`;旧的 `RegisterMediatorBehavior()` 改为显式兼容转发。 +- `ContextAwareCqrsExtensions`、`ContextAwareCqrsCommandExtensions`、`ContextAwareCqrsQueryExtensions` 与 + `CqrsCoroutineExtensions` 已作为新的中性命名扩展入口加入;旧的 `ContextAwareMediator*Extensions` 与 + `MediatorCoroutineExtensions` 保留为 `[Obsolete]` 兼容包装层。 +- `RegisterMediatorBehavior()`、`ContextAwareMediator*Extensions` 与 `MediatorCoroutineExtensions` + 已进一步收紧为“正式弃用”状态: + - `Obsolete` 提示统一明确迁移目标与 “future major version” 移除预期 + - `EditorBrowsable(EditorBrowsableState.Never)` 已将这些历史别名从 IntelliSense 主路径隐藏 + - `GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs` 已锁定上述弃用元数据,防止兼容层退回“仅名义弃用” +- `GFramework.Cqrs` 已新增 `ICqrsHandlerRegistry` 与 `CqrsHandlerRegistryAttribute`, + 作为消费端程序集暴露源码生成注册器的正式 runtime 契约。 +- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已落地 CQRS handler source-generator MVP: + - 对当前消费端程序集中的 concrete request / notification / stream handlers 生成单一程序集级注册器 + - 生成注册顺序与 runtime 反射排序口径保持一致,按实现类型名与处理器接口名稳定排序 + - 对生成代码无法直接 `typeof(...)` 引用、但仍可按元数据名从当前程序集重新定位的处理器(例如私有嵌套 handler),生成注册器会改走定向反射注册,而不是退回整程序集补扫 +- `CqrsHandlerRegistrar` 已改为优先查找 `CqrsHandlerRegistryAttribute` 指向的生成注册器; + 当注册器缺失、元数据损坏或实例化失败时,会记录 warning 并自动回退到原有反射扫描路径。 +- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 与 + `GFramework.Core.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已补齐: + - 生成器输出快照测试 + - 私有嵌套 handler 走 generated registry 内部定向反射注册的测试 + - runtime 优先使用生成注册器的测试 + - 生成注册器无效时自动回退反射的测试 +- 新的 `ContextAwareCqrs*` / `CqrsCoroutineExtensions` 位于独立命名空间,避免与旧 `Mediator` 扩展在同一 + `using` 范围内触发扩展方法解析歧义。 +- `CoreGrid-Migration` 中命中的旧扩展调用点已切到新的 `ContextAwareCqrs*` 入口,避免新旧扩展同时可见时的重载歧义。 +- `docs/zh-CN/core/cqrs.md`、`docs/zh-CN/core/index.md` 与 `CLAUDE.md` 已从“依赖外部 Mediator”改写为 + “内建 CQRS runtime + 历史命名兼容”表述。 +- `docs/zh-CN/core/cqrs.md` 与 `CLAUDE.md` 已补充“如何显式接入非默认程序集的 CQRS handlers”说明,避免文档仍停留在“需要额外接入” + 但未给出正式入口的状态。 +- 在验证阶段发现 `CqrsHandlerRegistrar.cs` 缺少 `Microsoft.Extensions.DependencyInjection` 的 `using`, + 已补齐以恢复 `IServiceCollection` 编译通过。 +- 当前验证状态: + - `dotnet build GFramework/GFramework.sln` 通过 + - `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj --no-build` 通过,`1621` 个测试全部通过 + - `dotnet build CoreGrid-Migration/CoreGrid.sln` 通过 + - `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` 个测试全部通过 + - `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` 个测试全部通过 + - `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 与 Godot generator warning + - `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` 在迁移 `ContextAwareCqrs*` 调用点后再次通过,仅存在既有 analyzer warnings + - `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` 个测试全部通过 + - `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release` 在 CQRS generator MVP 后通过 + - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` 在 CQRS generator MVP 后通过 + - `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` 个测试全部通过 +- `dotnet build GFramework/GFramework.sln -c Release` + 在当前 WSL 环境下命中既有 `GFramework.csproj` NuGet fallback package folder 配置问题 + (机器本地路径已省略), + 与本轮 CQRS 改动无关;`GFramework.Core.Tests` 相关项目构建与回归已通过 + - `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` + 在显式额外程序集 CQRS 注册入口落地后通过,仅存在既有 `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"` + 在显式额外程序集 CQRS 注册入口落地后通过,`13` 个测试全部通过 + - `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` + 在 runtime seam 落地后通过,`0` warnings / `0` errors + - `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"` + 在 runtime seam 落地后通过,`97` 个测试全部通过 + - `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` + 在纯 CQRS 契约外提到 `GFramework.Cqrs.Abstractions` 后通过,仅存在既有 `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"` + 在纯 CQRS 契约外提后再次通过,`97` 个测试全部通过 + - `dotnet build GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` + 在 CQRS 聚焦测试拆分后通过,`0` warnings / `0` errors + - `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build` + 在 CQRS 聚焦测试拆分后通过,`54` 个测试全部通过 + - `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` + 在迁出 CQRS 聚焦测试并消除跨项目 fixture 耦合后再次通过,`0` warnings / `0` errors + - `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~MicrosoftDiContainerTests|FullyQualifiedName~ArchitectureAdditionalCqrsHandlersTests|FullyQualifiedName~ArchitectureModulesBehaviorTests|FullyQualifiedName~MediatorCompatibilityDeprecationTests"` + 在测试拆分后通过,`44` 个测试全部通过 + +## 当前已知事实 + +- `GFramework` 当前仍同时维护: + - 基于 `CommandExecutor` / `QueryExecutor` / `EventBus` 的轻量旧 CQRS + - 基于 GFramework 自有抽象的新 CQRS runtime +- CQRS runtime 主实现已迁到 `GFramework.Cqrs`,`GFramework.Core` 当前主要保留架构接线、默认 behaviors 与协程扩展;`Abstract*Handler` 基类已物理迁到 `GFramework.Cqrs` 并改为依赖轻量 `CqrsContextAwareHandlerBase`,不再直接继承 `GFramework.Core.Rule.ContextAwareBase`。 +- Phase 5 评估结论是“拆分收益成立,但必须先做依赖倒置 seam”,该 seam 已在当前恢复点落地完毕,下一步可以正式进入项目骨架拆分。 +- Phase 7 已验证可以先把“纯 CQRS 契约”独立成 `GFramework.Cqrs.Abstractions`,并把 runtime registry 契约收敛到 `GFramework.Cqrs`;当前真正残留的边界阻塞点主要是 `ICqrsRuntime -> IArchitectureContext`、`CqrsCoroutineExtensions -> TaskCoroutineExtensions` 的耦合,以及是否继续承诺旧 `GFramework.Core.Abstractions.Cqrs*` namespace 兼容。 +- Phase 7 已额外验证:CQRS 聚焦测试可以先拆到 `GFramework.Cqrs.Tests`,而架构壳层、容器 seam 与兼容层测试继续留在 `GFramework.Core.Tests`,不会破坏当前回归覆盖。 +- 仍存在 `Mediator` 残留的区域主要集中在: + - 兼容 API 与测试目录中的历史命名 + - 少量本地计划/历史记录文档中的迁移过程描述 +- `CoreGrid-Migration` 已切到本地源码引用,并在当前恢复点完成构建验证 +- 本轮已接受的 review / subagent 结论: + - `CqrsTestRuntime.ExecutePipelineAsync(...)` 会掩盖正式 dispatcher 行为,已移除 + - handler 自动注册必须保持稳定顺序、容忍部分类型加载失败,并避免单例上下文污染 + - `AbstractStreamCommandHandler` 的上下文可用窗口、实例复用约束与取消边界需要放入显式 + ``,避免公共基类被误用 + - `CqrsHandlerRegistrar` 的部分加载失败回归必须从 `RegisterHandlers` 公开测试入口观察,而不是反射私有 + `RecoverLoadableTypes(...)` + - `CqrsTestRuntime` 反射绑定不能只按名称解析 `RegisterHandlers`,否则新增重载后会出现不确定行为 + - 生产代码与主文档中保留的 `Mediator` 标识目前只剩历史兼容命名,不再代表外部包依赖,可作为下一阶段 + API 命名统一任务单独处理 + +## 当前风险 + +- `GFramework` 仓库存在与本任务无关的既有改动,提交时必须避免覆盖 +- `CoreGrid-Migration` 是 worktree,WSL 下原生 `git` 解析该 worktree 路径有兼容问题 +- 当前 `RegisterMediatorBehavior`、`MediatorCoroutineExtensions` 等旧名已降级为兼容包装层;若后续要彻底移除历史命名,需要单独规划弃用周期 +- 当前历史 `Mediator` 兼容入口虽已隐藏 IntelliSense 并明确 future-major 移除,但仍保留公开签名以避免立即破坏旧调用方 +- 当前 handler 自动注册已具备“默认程序集自动接入 + 额外程序集显式接入”的统一入口,并支持“消费端程序集生成注册器 + 其余程序集反射回退”的双路径;若后续追求更强 AOT/冷启动收益,还需继续减少仍依赖反射回退的程序集范围 +- 若在依赖倒置 seam 落地前就直接拆项目,`GFramework.Core` 与新 `GFramework.Cqrs` 之间很容易形成“项目名分开、实现仍互相硬引用”的伪边界 +- 当前 `CoreGrid-Migration` 与 `GFramework.Godot` 均直接依赖 `GFramework.Core.Cqrs.*` public types;若后续拆分时同时改 namespace 或取消 transitive 依赖,会放大消费端迁移成本 +- 当前 `ICqrsRuntime -> IArchitectureContext` 仍会阻止完整搬空 `GFramework.Core.Abstractions/Cqrs`;若不先收敛这条依赖,后续很容易重新引入项目循环引用 +- 当前 `Abstract*Handler` 已摆脱 `GFramework.Core.Rule.ContextAwareBase`,但 `CqrsCoroutineExtensions` 仍依赖 `TaskCoroutineExtensions`;若还希望继续压缩 `GFramework.Core` 对 CQRS 的承载范围,下一步仍需处理协程工具链边界 +- `CoreGrid-Migration` 本轮通过 consumer 侧 namespace/extension 对齐恢复构建;若产品层仍希望承诺旧 `GFramework.Core.Abstractions.Cqrs*` 公共路径,需要单独设计兼容层或迁移策略,否则会在后续真实消费端继续重复暴露同类断点 +- 当前 `GFramework.Cqrs.Tests` 仍直接引用 `GFramework.Core`,说明测试已经按模块意图拆分,但 runtime 物理迁移尚未完成;若后续直接切断该依赖,测试会失去承载实现 +- `dotnet build GFramework.sln -c Release` 在当前 WSL 环境仍会受顶层 `GFramework.csproj` 的 Windows NuGet fallback package + folder 配置影响,若需要恢复 solution 级全量验证,需先处理该环境问题 + +## 下次恢复建议 + +若本轮中断,优先从以下顺序恢复: + +1. 查看 `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md` +2. 确认当前恢复点 `CQRS-REWRITE-RP-042` 已对应到最新提交 +3. 优先继续执行 `ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md` 中的 Phase 8(当前主线): + - 先决定是否正式支持旧 `GFramework.Core.Abstractions.Cqrs*` / `GFramework.Core.Cqrs.Extensions` public namespace 兼容,还是明确要求消费端迁到当前 `GFramework.Cqrs*` 路径 + - 再评估 `CqrsCoroutineExtensions` 是否保留在 `GFramework.Core`,或连同所需协程辅助一起形成更小的可迁移边界 +4. 在 runtime 项目真正承接实现后,再处理 source-generator、meta package 与消费端 transitive 依赖的迁移 +5. 在规划 future major 版本时,再决定何时真正移除 `RegisterMediatorBehavior` / `MediatorCoroutineExtensions` / `ContextAwareMediator*Extensions` + +## 2026-04-15 补充记录(RP-015) + +### 阶段:轻量 handler 上下文基类与消费端兼容性收敛 + +- 建立 `CQRS-REWRITE-RP-015` 恢复点 +- `GFramework.Cqrs/Cqrs/CqrsContextAwareHandlerBase.cs` 已新增,作为仅依赖 `IContextAware` / `IArchitectureContext` 的轻量 CQRS handler 上下文基类: + - 保留 `OnContextReady()` 生命周期扩展点 + - 去掉对 `GameContext` 的兜底依赖 + - 在运行时注入前访问 `Context` / `GetContext()` 时显式抛出异常,避免静默落回全局上下文 +- `AbstractCommandHandler` / `AbstractQueryHandler` / `AbstractRequestHandler` / `AbstractNotificationHandler` 及其 stream 变体已物理迁到 `GFramework.Cqrs`,并改为继承该轻量基类 +- `GFramework.Cqrs.Tests/Cqrs/AbstractCqrsHandlerContextTests.cs` 已新增,回归覆盖: + - 未注入上下文时 fail-fast + - 注入后 `Context` 可用 + - `OnContextReady()` 生命周期钩子仍然生效 +- 在真实迁移验证阶段,`CoreGrid-Migration` 暴露出旧 `GFramework.Core.Abstractions.Cqrs*` 与旧 `GFramework.Core.Cqrs.Extensions` 命名空间不再自动可用的问题 +- 本轮先采用最小 consumer 修复路径恢复验证链路: + - `scripts/GlobalUsings.cs` 已补齐新的 `GFramework.Cqrs.Abstractions.Cqrs*`、`GFramework.Cqrs.*` 与 `GFramework.Core.Cqrs.*` handler namespace 导入 + - `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 编译错误 + +## 备注 + +- 本文档是历史归档快照,用于回溯 `CQRS-REWRITE-RP-043` 及之前阶段 +- 后续请在 active `todos/` 与 `traces/` 维护当前恢复点,并将完成阶段继续归档 + +## 2026-04-15 补充记录 + +### 阶段:review 收尾修正(并发 lazy 初始化与共享测试运行时) + +- `GFramework.Core/Architectures/ArchitectureContext.cs` 已将 `ICqrsRuntime` 的延迟解析从 `??=` + 改为 `Lazy`,并显式使用 `LazyThreadSafetyMode.ExecutionAndPublication` + 保证并发首次访问时只执行一次容器解析 +- `ArchitectureContext` 已补齐公开构造函数 XML 文档,以及 `CqrsRuntime` 惰性初始化的并发语义说明 +- `GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs` 已新增并发回归测试, + 锁定“多线程首次访问 `SendRequestAsync(...)` 时只解析一次 `ICqrsRuntime`”的行为 +- 原先位于 `GFramework.Core.Tests/CqrsTestRuntime.cs` 与 `GFramework.Cqrs.Tests/CqrsTestRuntime.cs` + 的重复实现已提取到共享源码文件 `tests/Shared/CqrsTestRuntime.cs` +- `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 已通过链接编译同一份共享源码并补齐 `global using`, + 避免两个测试项目继续维护分叉的反射绑定逻辑 +- 共享版 `CqrsTestRuntime` 已顺手清理 `GetType(..., throwOnError: true)! ?? throw ...` + 里的不可达 `?? 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` 命令时,`NuGet` restore 在共享 `obj/project.assets.json` 上发生文件竞争; + 顺序重跑 `GFramework.Core.Tests --no-restore` 后验证通过,属于本地并行 restore 的环境噪音,不是代码回归 + +### 阶段:测试公共基础设施模块化 + +- 已新增 `GFramework.Tests.Common` 项目,并加入 `GFramework.sln` +- 原先临时放在仓库根目录 `tests/Shared/CqrsTestRuntime.cs` 的共享源码已迁入 + `GFramework.Tests.Common/CqrsTestRuntime.cs` +- `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 已从源码链接切换为显式 `ProjectReference` +- 两个测试项目的 `global using` 已从 `GFramework.Tests.Shared` 迁到 `GFramework.Tests.Common` +- 该调整保留了原有测试调用方式,但把公共测试基础设施收敛到单独模块,避免继续以目录约定模拟模块边界 + +### 阶段:测试公共基础设施模块化验证 + +- `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` 个测试全部通过 + +## 2026-04-15 补充记录(RP-016) + +### 阶段:日志入口下沉与剩余 runtime behaviors 迁移 + +- 建立 `CQRS-REWRITE-RP-016` 恢复点 +- `GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs` 已新增,继续使用 `GFramework.Core.Logging` 命名空间: + - 该类型已从 `GFramework.Core` 实现层下沉到抽象层,允许 `GFramework.Cqrs` 在不反向引用 `GFramework.Core` 的前提下获取日志器 + - 默认 provider 会优先反射创建 `GFramework.Core.Logging.ConsoleLoggerFactoryProvider` + - 当宿主仅加载抽象层时,会退回到静默 provider,避免抽象层默认日志入口因缺少实现程序集而崩溃 +- `GFramework.Core/Properties/TypeForwarders.cs` 已新增,对 `LoggerFactoryResolver` 做 type forwarding,避免把公开类型迁出 `GFramework.Core` 后留下运行时兼容断点 +- `GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs` 与 `PerformanceBehavior.cs` 已物理迁移到 `GFramework.Cqrs/Cqrs/Behaviors/*` +- 上述两个 behavior 继续保留 `GFramework.Core.Cqrs.Behaviors` 命名空间,消费端源码无需改 using 即可继续解析 +- 通过这一步,`GFramework.Core/Cqrs/*` 已完全搬空;Phase 7 的 runtime 物理迁移残项现只剩 `CqrsCoroutineExtensions` +- 并行 explorer 结论已收敛: + - `ICqrsRuntime` 当前不能迁到 `GFramework.Cqrs.Abstractions`,根因不是单个 using,而是整条 `ICqrsRuntime -> IArchitectureContext -> IContextAware / handler Context` 的上下文模型仍绑定 `GFramework.Core.Abstractions` + - `CqrsCoroutineExtensions` 本质上是 `GFramework.Core` 协程桥接层,不是纯 CQRS runtime 代码;若原样迁到 `GFramework.Cqrs`,会重新形成 `GFramework.Cqrs -> GFramework.Core` 反向依赖 + - 下一阶段的最小可行方案应优先考虑“新增 CQRS 专用上下文 seam + 继续让协程扩展留在 `GFramework.Core`”,而不是先新增组合项目 + +### 阶段:RP-016 验证 + +- `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` 个测试全部通过 + +## 2026-04-16 补充记录(RP-020) + +### 阶段:review 收尾修正(线程安全文档、未冻结态去重与 runtime 契约说明) + +- 建立 `CQRS-REWRITE-RP-020` 恢复点 +- `GFramework.Cqrs/Internal/DefaultCqrsRegistrationService.cs` 已在类级 `` 中明确“该类型不是线程安全的,必须由外部同步边界串行访问”的设计约束 +- `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()` / `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 补充记录(RP-033) + +### 阶段:review 小修(缓存清理、诊断信息与测试辅助性能) + +- 建立 `CQRS-REWRITE-RP-033` 恢复点 +- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 的 `ClearDispatcherCaches()` 已补齐 + `RequestPipelineInvokerCache.Invokers` 清理,避免未来新增 `string` pipeline 路径测试时残留静态状态 +- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已在 + `CanReferenceFromGeneratedRegistry(...)` 的默认分支补充注释,明确当前把 `dynamic`、error type 等其他 Roslyn 类型种类视为暂时可引用的实现假设 +- `GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs` 已将运行时可信平台程序集引用收敛为惰性静态缓存,避免多程序集生成器测试反复解析 + `TRUSTED_PLATFORM_ASSEMBLIES` +- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已增强 `RunGenerator(...)` + 中的编译错误断言消息,失败时会输出完整 diagnostics 文本,便于直接定位生成代码问题 + +### 阶段:RP-033 验证 + +- `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` 个测试全部通过 + +## 2026-04-18 补充记录(RP-040) + +### 阶段:第三方参考源码治理 + +- 建立 `CQRS-REWRITE-RP-040` 恢复点 +- `AGENTS.md` 已新增仓库级约束: + - `ai-libs/` 用于存放第三方项目源码副本,仅供对照、追踪与设计参考 + - `ai-libs/**` 默认为只读区域,除非用户明确要求同步或更新第三方快照,否则不允许修改 + - 后续计划、trace、评审与设计说明引用第三方实现时,优先写明仓库内参考路径,而不是使用模糊的外部项目名 +- CQRS 迁移主线已将 `Mediator` 的本地参考源正式收口到 `ai-libs/Mediator` +- 本跟踪文档中“后续继续参考 Mediator 成熟实现”的执行语义已同步更新为“优先参考 `ai-libs/Mediator`” + +### 阶段:RP-040 验证 + +- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release` + - 结果:通过 + - 备注:存在既有 `MA0051` 与 `MA0158` analyzer warnings,无新增构建错误 +- `rg -n "ai-libs/Mediator|只读|第三方项目源码副本" AGENTS.md ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md` + - 结果:通过 + - 备注:`AGENTS.md`、tracking 与 trace 均已命中新规则和本地参考路径说明 + +### 下一步 + +1. 后续若继续推进 CQRS runtime、generator 或低反射优化,统一以 `ai-libs/Mediator` 作为本地参考源 +2. 若未来需要升级 `Mediator` 参考副本,单独作为“同步第三方快照”任务处理,不与框架实现改动混在同一变更里 + +## 2026-04-18 补充记录(RP-041) + +### 阶段:source-generator 文档命名空间收口 + +- 建立 `CQRS-REWRITE-RP-041` 恢复点 +- 已将 `docs/zh-CN/**` 中残留的旧示例 `using GFramework.SourceGenerators.Abstractions.*;` 批量改为当前公开命名空间: + - `GFramework.Core.SourceGenerators.Abstractions.Rule` + - `GFramework.Core.SourceGenerators.Abstractions.Bases` + - `GFramework.Core.SourceGenerators.Abstractions.Logging` + - `GFramework.Core.SourceGenerators.Abstractions.Enums` + - `GFramework.Core.SourceGenerators.Abstractions.Architectures` +- 已同步修正文档中的叙述性旧口径: + - `docs/zh-CN/source-generators/logging-generator.md` 不再把日志生成器描述成旧聚合 `GFramework.SourceGenerators` + - `docs/zh-CN/source-generators/context-get-generator.md` 改为明确由 `GFramework.Core.SourceGenerators` 执行注册可见性分析 + - `docs/zh-CN/api-reference/index.md` 将“`GFramework.SourceGenerators` 单一模块”改写为当前拆分后的 Source Generators 家族说明 + +### 阶段:RP-041 验证 + +- `rg -n "using GFramework\\.SourceGenerators\\.Abstractions\\.|### GFramework\\.SourceGenerators|GFramework\\.SourceGenerators 自动生成|GFramework\\.SourceGenerators 现在还会分析" docs/zh-CN` + - 结果:通过 + - 备注:`docs/zh-CN/**` 中上述旧公开命名空间与旧聚合表述已清理完毕 + +### 下一步 + +1. 若继续文档主线,扩大清理范围到更多说明页中的旧 API 参考与历史命名残留,优先处理 adoption 入口最靠前的页面 +2. 若切回实现主线,则重新盘点 `GFramework.Cqrs` 中仍值得继续压缩的冷路径/热路径反射点,优先选择能带来明确收益的低复杂度改动 + +## 2026-04-19 补充记录(RP-042) + +### 阶段:PR review 技能接入与 PR-253 follow-up + +- 已新增项目级 `$gframework-pr-review` skill: + - 目录:`.codex/skills/gframework-pr-review/` + - 作用:定位当前分支对应的 GitHub PR,并优先通过 GitHub PR / issue comments / review comments API 提取 + CodeRabbit 汇总、最新 head commit review threads、`Failed checks` 与 CTRF 测试结果 + - 约束:不依赖 `gh` CLI;不再把重型 PR HTML 页面当作主数据源 +- 已根据 PR `#253` 的公开 review 内容完成本地修正: + - `.codex/skills/gframework-boot/SKILL.md` 的恢复 heuristics 不再把 `next step/continue/继续` + 直接映射为 `recovery` + - `AGENTS.md` 中 `ai-libs/**` 观察写入 active plan/trace 的规则已收窄到“多步/复杂任务或已有 active + tracking document” + - `Godot/script_templates/Node/*.cs` 与 `GFramework.Core.Abstractions/Controller/IController.cs` + 中旧 `Rule` 命名空间残留已同步修正 + - `fetch_current_pr_review.py` 已改为: + - Git 路径支持环境变量覆盖并回退到 `git.exe` / `git` + - `--pr` 模式不再强制读取当前分支 + - `--branch` 到 PR 编号的解析改为走 GitHub PR API + - CodeRabbit summary / CTRF 测试报告改为走 issue comments API + - 最新 review 依据改为 latest head commit review threads,而不是只看汇总块 + - `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` 已移除公开文档中的机器本地绝对路径,并统一 + 下次恢复建议里的恢复点编号 + +### 阶段:RP-042 验证 + +- `python3 - <<'PY' ... ast.parse(...) ... PY` + - 结果:通过 + - 备注:`fetch_current_pr_review.py` 语法正确 +- `python3 .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --pr 253` + - 结果:通过 + - 备注:已通过 API-first 路径解析 PR 元数据、latest head commit review threads、CodeRabbit summary + 与 CTRF 测试结果,不再依赖 PR HTML +- `python3 .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --branch feat/cqrs-optimization` + - 结果:通过 + - 备注:已验证 branch -> PR 解析同样通过 GitHub API 工作 +- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release` + - 结果:通过 + - 备注:相关 C# 改动已完成构建验证 + +### 下一步 + +1. 若要让 PR `#253` 上的 latest head review threads 反映本轮本地修正,需要先提交并推送当前分支,再重新执行 `$gframework-pr-review` +2. PR 当前公开 warning 仍包含 `Docstring Coverage`,若后续要继续消除此项,需要单独规划并提交文档注释覆盖率改进 \ No newline at end of file diff --git a/ai-plan/public/cqrs-rewrite/archive/traces/cqrs-rewrite-history-through-rp043.md b/ai-plan/public/cqrs-rewrite/archive/traces/cqrs-rewrite-history-through-rp043.md new file mode 100644 index 00000000..bde7925b --- /dev/null +++ b/ai-plan/public/cqrs-rewrite/archive/traces/cqrs-rewrite-history-through-rp043.md @@ -0,0 +1,1444 @@ +# 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` 已把上下文注入窗口、瞬态实例约束与流创建/枚举取消边界移入显式 + `` +- `CqrsHandlerRegistrarTests` 已改为从 `CqrsTestRuntime.RegisterHandlers(...)` 真实入口覆盖部分类型加载失败场景,不再反射 + `RecoverLoadableTypes(...)` +- `CqrsTestRuntime` 已补齐 XML 文档,并改为按 `IIocContainer + IEnumerable + 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()` 推荐入口 +- 旧的 `RegisterMediatorBehavior()` 已降级为 `[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()` 已统一补充 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`, + 并显式指定 `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` 已在类级 `` 中明确“该类型不是线程安全的,必须由外部同步边界串行访问”的设计约束 +- `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()` / `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` 与 `PerformanceBehavior` 已迁移到 `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 Runtime,CQRS = 集成子系统” + +- 在重新评估 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 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` 修正为合法的 `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` 这类“可引用泛型定义 + 隐藏实参” + - `HiddenEnvelope` 这类“隐藏泛型定义 + 可重建实参” +- 实现过程中额外收敛了两处细节: + - 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` 负责无 pipeline 的 request handler 调用缓存 + - `RequestPipelineInvokerCache` 负责带 pipeline 的 request 调用缓存 + - 缓存键从 `(RequestType, ResponseType)` 收敛为当前 `TResponse` 层内的 `RequestType` + - `InvokeRequestHandlerAsync(...)` 与 `InvokeRequestPipelineAsync(...)` 现直接返回 `ValueTask`,不再通过 `ValueTask` 桥接 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` + - generator 可访问性判断默认分支缺少显式假设说明 + - `MetadataReferenceTestBuilder` 每次都会重建运行时元数据引用集合 + - `RunGenerator(...)` 的编译错误断言消息上下文偏少 +- 已完成对应实现: + - 在 `ClearDispatcherCaches()` 中补齐 `string` pipeline invoker cache 清理 + - 为 `CanReferenceFromGeneratedRegistry(...)` 默认分支补上“暂按可引用处理,后续可收紧”的注释 + - 用 `Lazy>` 缓存 `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` + 会出现在生成注释中,避免后续又退回“只知道走了 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` + 这类案例会生成 `ResolveReferencedAssemblyType(...)` 精确查找,而不是保留 residual 注释 + `GetInterfaces()` 补洞 +- 已删除不再保留的公开 `Mediator` 兼容层: + - `RegisterMediatorBehavior()` + - `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 + RequestPipelineInvokerCache` 收敛为 `RequestDispatchBindingCache` +- 新的 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()`”,不再把已删除的 `RegisterMediatorBehavior()` 写成仍可替换的并存入口 + - `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/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/cqrs-rewrite/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-up(RP-042) + +- 建立 `CQRS-REWRITE-RP-042` 恢复点 +- 新增项目级 skill `.codex/skills/gframework-pr-review/`: + - 暗号为 `$gframework-pr-review` + - 使用 Windows Git 解析当前分支,并通过 GitHub PR API 定位当前分支对应的 PR + - 通过 GitHub issue comments / reviews / review comments API 提取 `Summary by CodeRabbit`、最新 head + commit review threads、`Failed checks` 与 CTRF 测试结果 + - 不再把重型 PR HTML 页面作为主数据源,只在调试或兼容场景下保留为兜底思路 + - 不依赖 `gh` CLI,也不要求登录态;脚本会显式绕过当前 shell 中失效的代理变量 +- 用新脚本验证了 PR `#253` 的当前状态: + - latest head commit review threads 已可直接从 API 提取;在远端最新提交未更新前,当前仍显示 4 条 open + threads,其中 2 条落在 `fetch_current_pr_review.py`、2 条落在 `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` + - PR 页面当前无 `Failed Tests`,CTRF 测试报告显示 `2103 passed / 0 failed` + - `Failed checks` 当前可稳定提取到 `Docstring Coverage` warning;该项属于 PR 级文档注释覆盖率问题,不是 FPR + skill 解析链路故障 +- 已按 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` + - 结果:通过 + - 备注:成功通过 API-first 路径解析当前 PR 元数据、latest head commit review threads、`Docstring Coverage` + warning 和 CTRF 测试报告 +- `python3 .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --branch feat/cqrs-optimization` + - 结果:通过 + - 备注:验证 branch -> PR 解析也已摆脱 HTML 搜索 +- `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. 若要验证本轮本地修正已经消除远端 latest head review threads,需要在提交并推送当前分支后重新执行 `$gframework-pr-review` + +## 2026-04-19 + +### 阶段:PR review 复核与 FPR 输出澄清(RP-043) + +- 建立 `CQRS-REWRITE-RP-043` 恢复点 +- 复核 PR `#253` 时确认: + - `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` 的“下次恢复建议”仍把主线写成 `Phase 7` + - 这与 tracking 顶部当前阶段 `Phase 8` 不一致,恢复时会造成执行顺序歧义 +- 进一步检查 `$gframework-pr-review` 脚本后确认: + - 该评论并非“未覆盖”,而是已出现在 latest head commit 的 open review threads 中 + - 我先前误把评论正文里可见的 `Addressed in commit ...` 文案当成了“已经本地验证修复” + - 实际脚本只会在 CodeRabbit thread 带有隐藏 marker `` 时才将状态归类为 `addressed` +- 已据此完成两项修正: + - 将 tracking 中的恢复主线更新为 `Phase 8(当前主线)` + - 调整 `fetch_current_pr_review.py` 输出:当 open thread 里出现 `Addressed in commit ...` 文案但线程并未真正 closed/addressed 时,显式提示“仍需本地验证,不要视为已解决” + +### 验证 + +- `sed -n '469,475p' ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` + - 结果:通过 + - 备注:恢复建议已切到 `Phase 8(当前主线)` +- `python3 -m py_compile .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py` + - 结果:通过 + - 备注:脚本语法正确 + +### 下一步 + +1. 推送当前分支后重新执行 `$gframework-pr-review`,确认 PR `#253` 的 latest head review threads 是否已收敛 +2. 若后续仍保留 CodeRabbit 的 `Addressed in commit ...` 文案但 thread 继续 open,优先以 latest thread 状态和本地文件事实为准,不再按文案字面判定 diff --git a/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md b/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md index db67b893..4dda7fbc 100644 --- a/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md +++ b/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md @@ -2,741 +2,57 @@ ## 目标 -围绕 `GFramework` 当前的双轨 CQRS 现状,完成一轮以“去 Mediator 外部依赖”为目标的架构迁移: - -- 将 `Mediator` 从 GFramework 公共 API 和运行时主路径中移除 -- 基于 GFramework 自有抽象重建正式 CQRS runtime、行为管道和注册机制 -- 保留 `EventBus` 作为框架级事件系统,不与 CQRS notification 混同 -- 让 `CoreGrid-Migration` 直连本地 `GFramework`,作为真实迁移验证工程 -- 为复杂迁移建立明确恢复点与进度追踪,避免上下文过长或中断后失去状态 +围绕 `GFramework` 当前的双轨 CQRS 现状,继续完成以“去外部依赖、降低反射、收口公开入口”为目标的 +CQRS 迁移与收敛。 ## 当前恢复点 -- 恢复点编号:`CQRS-REWRITE-RP-042` +- 恢复点编号:`CQRS-REWRITE-RP-044` - 当前阶段:`Phase 8` - 当前焦点: - - 已将迁移目标修正为 `Core = App Runtime`、`CQRS = 默认集成进去的可替换子系统`,停止继续追求 `Core` 对 `Cqrs` 的零依赖 - - 已完成 `Core` 侧 CQRS 实现细节泄漏盘点,并将容器内的 handler 注册去重/调度细节下沉到 `GFramework.Cqrs` - - 已将 generator 从“整程序集回退”推进到“可见 handlers 走 `typeof(...)` 直注册,不可直接引用但可定位的 handlers 走 generated registry 内部定向 `Assembly.GetType(...)` 注册” - - 已将 partial reflection fallback 从“命中 marker 后整程序集 `GetTypes()`”收敛到“优先消费显式 `Type` / type-name metadata;且对 generator 已能重新定位的隐藏 handlers 不再额外依赖 runtime fallback marker” - - 已为手写/第三方程序集补齐正式的精确 fallback 入口:`CqrsReflectionFallbackAttribute` 现在既支持 `string` type-name 清单,也支持直接传入 `Type` 集合 - - 已将“隐藏 implementation 但 handler interface 仍可直接引用”的 generator 输出进一步收敛为“仅对 implementation 执行一次 `Assembly.GetType(...)`,随后直接按 `typeof(handlerInterface)` 注册”,不再为这类场景额外生成 `GetInterfaces()` / `IsSupportedHandlerInterface(...)` 辅助逻辑 - - 已将“隐藏 implementation + 隐藏但可精确重建的 handler interface”进一步收敛为“生成期记录 open generic contract 与 type arguments,运行期只做 implementation/type-argument 定向 lookup + `MakeGenericType(...)`”,常见私有嵌套 request 场景已不再需要 `GetInterfaces()` 接口发现 - - 已将 `RuntimeTypeReference` 继续扩成递归结构,可表达数组与构造泛型;`List`、`HiddenEnvelope` 这类闭包类型现在也能按生成期已知结构直接重建,进一步贴近 `Mediator` 那种“生成期定结构,运行期只做常量时间绑定”的模式 - - 已在 `CqrsDispatcher` 热路径补齐 service-type 缓存,减少 `PublishAsync` / `SendAsync` / `CreateStream` 中重复的 `MakeGenericType` 开销 - - 已将 `CqrsDispatcher` 的 invoker method-definition 查找收敛为静态一次解析,并补齐 request/no-pipeline、request/with-pipeline、notification、stream 四条缓存路径的统一回归 - - 已将 `CqrsDispatcher` 的 request / pipeline invoker 从 `ValueTask` 缓存改为按 `TResponse` 分层的强类型委托缓存,减少 value-type 响应在热路径上的装箱与额外拆箱 - - 已将 `CqrsDispatcher` 的 notification/request/stream 热路径进一步收敛为聚合 dispatch binding cache,把服务类型与强类型 invoker 合并到单一缓存项; - request 路径继续按 `TResponse` 分层,并减少单次分发里的重复字典命中 - - 已将 `CqrsHandlerRegistrar` 的程序集级 attribute 读取、registry 激活分析与未命中 generated-registry 时的 `GetTypes()` 扫描收敛为进程级缓存; - 同一 `Assembly` 对象跨容器重复接入时不再重复做 registry metadata / fallback metadata / loadable-types 分析 - - 已完成一轮公开入口文档收口:`README.md`、`CLAUDE.md`、`docs/zh-CN/core/cqrs.md` 与 `docs/zh-CN/source-generators/index.md` - 现已统一到 `GFramework.Cqrs` 与分拆后的 `Core/Game/Godot/Cqrs SourceGenerators` 模块命名,不再把公开入口写成旧聚合 `GFramework.SourceGenerators` - - 已将 `CqrsHandlerRegistryGenerator` 从“同一 implementation 只走一种注册分支”的互斥输出,收敛为“按 handler interface 粒度组合 direct / reflected-implementation / precise-reflected 注册,并继续按接口名稳定排序”; - 同一 handler implementation 上的可静态绑定接口不再因为另一条接口需要更窄路径而被一起拖进更粗回退 - - 已将 `CqrsHandlerRegistryGenerator` 的“生成注册器是否能直接引用类型”判断改为基于 Roslyn `Compilation.IsSymbolAccessibleWithin(...)`,不再仅靠声明可见性粗判; - 对“当前继承/语义上下文可见、但生成注册器顶层上下文不可直接写”的外部 `protected internal` 嵌套类型,现会直接进入精确 type lookup,而不是误生成不可编译的 `typeof(...)` - - 已删除 generated registry 内部的 `RegisterRemainingReflectedHandlerInterfaces(...)` implementation 级 `GetInterfaces()` 尾分支; - 当前合法 C# closed handler contract 已统一收敛到 direct / reflected-implementation / precise-reflected 三类注册路径 - - 若未来再出现新的 Roslyn 类型形态暂时无法编码进 `RuntimeTypeReferenceSpec`,生成器现会退回到“保留已知静态注册 + 通过程序集级 `CqrsReflectionFallbackAttribute` 对具体 handler implementation 定向补反射”; - 若 runtime 合同不提供该 marker,则生成器保守地不输出 registry,避免静默漏注册 - - 已完成一轮 review follow-up 小修:补齐 dispatcher `string` pipeline cache 清理,给 generator 可访问性判断默认分支加上假设说明,并收敛多程序集 generator 测试基础设施的运行时引用缓存与编译错误诊断输出 - - 已完成三轮 review follow-up:补齐 `ICqrsRuntime` 异常/上下文约束文档,明确 `DefaultCqrsRegistrationService` 的线程安全假设,收窄 `MicrosoftDiContainer` 未冻结态的实例别名去重范围,提前固定/校验 CQRS 程序集枚举输入,并把一类隐藏 handler 从“implementation + interface 运行时发现”进一步压缩到“implementation-only lookup + direct interface registration” - - 已将 `GFramework.SourceGenerators` 按目标模块继续拆分为 `GFramework.Core.SourceGenerators` / `GFramework.Core.SourceGenerators.Abstractions` / `GFramework.Cqrs.SourceGenerators` / `GFramework.Game.SourceGenerators`,并同步修正 solution、测试项目与 `Game` schema targets 的引用链 - - 已删除公开 `Mediator` 兼容壳层:`RegisterMediatorBehavior()`、`ContextAwareMediator*Extensions` 与 `MediatorCoroutineExtensions`,不再继续维护旧命名兼容表面 - - 已将 generator 对“外部程序集不可直引 protected nested 类型”的 residual contract 从 `RegisterRemainingReflectedHandlerInterfaces(...)` 补洞推进到“按程序集名 + metadata name 定向 lookup”;`IRequestHandler` 这类场景现在走精确 type lookup,而不再退回 implementation 级 `GetInterfaces()` - - 当前已确认在 C# 可编译约束下,generator 主路径已不再需要 implementation 级 `GetInterfaces()` 回退;下一步可继续转向 dispatch/invoker 反射收敛,或补做 source-generator namespace/docs 收口 - - 已将本任务后续参考的 `Mediator` 源码收口到仓库内 `ai-libs/Mediator`;`ai-libs/**` 现作为第三方只读参考区,不纳入常规实现修改范围 - - 已开始把 `docs/zh-CN/**` 中残留的旧 `GFramework.SourceGenerators.Abstractions.*` 示例命名空间收口到当前 - `GFramework.Core.SourceGenerators.Abstractions.*`,避免文档继续指向不存在的旧公开命名空间 - - 已新增项目级 `$gframework-pr-review` skill,可在当前分支下直接抓取 GitHub PR 页面、提取 CodeRabbit 评论、 - `Failed checks` 与测试结果,不再依赖 `gh` CLI - - 已按 PR `#253` 当前公开 review 信号修正 `gframework-boot` 的 `resume/recovery` 语义、收窄 `AGENTS.md` - 中 `ai-libs/**` 观察写入 active plan/trace 的触发条件,并补齐模板/接口注释中的旧 `Rule` 命名空间残留 + - 当前功能历史已归档,active 跟踪仅保留 `Phase 8` 主线的恢复入口 + - 短期上先处理 `PR #253` 的 latest head review thread 复核,确认当前本地修正是否已在远端收敛 + - 中期上继续 `Phase 8` 主线:参考 `ai-libs/Mediator`,扩大 generator 覆盖、减少 dispatch/invoker 热路径反射,并继续收口 package / facade / 兼容层 -## 本轮计划 +## 当前状态摘要 -### Phase 0:工作流基础 +- 已完成 `Mediator` 外部依赖移除、CQRS runtime 重建、默认架构接线和显式程序集 handler 注册入口 +- 已完成 `GFramework.Cqrs.Abstractions` / `GFramework.Cqrs` 项目骨架与 runtime seam 收敛 +- 已完成 handler registry generator 的多轮收敛,当前合法 closed handler contract 已统一收敛到更窄的注册路径 +- 已完成一轮公开入口文档与 source-generator 命名空间收口 +- 已接入 `$gframework-pr-review`,可直接抓取当前分支对应 PR 的 CodeRabbit 评论、checks 和测试结果 -- [x] 在 `ai-plan/public/cqrs-rewrite/todos/` 建立本任务跟踪文档 -- [x] 在 `ai-plan/public/cqrs-rewrite/traces/` 建立本任务追踪文档 -- [x] 将恢复点 / trace / subagent 协作规范写入 `AGENTS.md` +## 当前活跃事实 -### Phase 1:本地验证链路 - -- [x] 确认 `CoreGrid-Migration` 当前引用形态 -- [x] 将 `CoreGrid-Migration` 从 NuGet 包切到本地 `GFramework` 工程引用 -- [x] 让 `CoreGrid-Migration` 使用本地 Source Generator 而不是外部已发布版本 -- [x] 验证本地引用链路至少能完成 restore / build - -### Phase 2:CQRS 基础重建 - -- [x] 在 `GFramework.Core.Abstractions` 定义自有 CQRS 契约 -- [x] 在 `GFramework.Core` 落地 dispatcher / handler registry / behavior pipeline -- [x] 清理 `IArchitectureContext` 中对 `Mediator.*` 的公共签名依赖 -- [x] 设计 CQRS 模块启用方式,替代 `Configurator => AddMediator(...)` - -### Phase 3:接入迁移 - -- [x] 迁移 `GFramework.Core.Cqrs.*` 基类到新契约 -- [x] 迁移 `ContextAwareMediator*Extensions` 与协程扩展 -- [x] 迁移 `CoreGrid-Migration/scripts/cqrs/**` 到新契约 -- [x] 删除 `GameArchitecture.Configurator` 中的 `AddMediator(...)` - -### Phase 4:收尾 - -- [x] 移除 `Mediator` 包依赖与相关测试/文档残留 -- [x] 运行目标构建与测试 -- [x] 记录剩余风险与下一恢复点 -- [x] 根据 review 收紧 `AbstractStreamCommandHandler` 的生命周期 XML 文档 -- [x] 将 `CqrsHandlerRegistrar` 容错回归改挂到 `RegisterHandlers` 真实入口 -- [x] 提升 `CqrsTestRuntime` 反射绑定的签名鲁棒性并补齐 XML 文档 -- [x] 将剩余 `Mediator` 兼容入口推进到正式弃用周期(隐藏 IntelliSense + 明确 future major 移除) -- [x] 落地 CQRS handler source-generator MVP,并保留运行时反射回退 -- [x] 落地非默认程序集的显式 CQRS handler 注册入口,并避免重复程序集接入导致的重复 handler 映射 - -### Phase 5:模块边界再评估 - -- [x] 评估是否将 CQRS 拆分为独立的 abstractions 模块与 runtime 实现模块,并梳理 `GFramework.Core` 对其依赖倒置方案 -- [x] 若拆分收益成立,输出包边界与迁移草案: - - `GFramework.Cqrs.Abstractions`:消息契约、handler 契约、pipeline 契约、registry 契约 - - `GFramework.Cqrs`:dispatcher、runtime 注册、behavior 执行、source-generator 接入 - - `GFramework.Core`:改为依赖 CQRS 抽象或可选 runtime 接入,而不是继续承载全部 CQRS 实现 -- [x] 评估拆分后的兼容影响: - - `IArchitectureContext` / `ArchitectureBootstrapper` / `MicrosoftDiContainer` 的程序集依赖变化 - - 现有 `CoreGrid-Migration` 与其他消费端项目的引用路径变化 - - source-generator、AOT、冷启动、包发布与文档迁移成本 - -### Phase 6:拆分前置 seams - -- [x] 在新 CQRS 抽象边界中定义 `ICqrsRuntime` / `ICqrsHandlerRegistrar` 等 runtime seam -- [x] 将 `ArchitectureContext` 从直接 `new CqrsDispatcher(...)` 改为依赖容器解析的 runtime 抽象 -- [x] 将 `MicrosoftDiContainer.RegisterCqrsHandlersFromAssemblies(...)` 从直接调用 `CqrsHandlerRegistrar` 改为依赖 runtime 注册抽象 -- [x] 保持默认架构程序集 + `GFramework.Core` 程序集 handler 自动接入行为不变 -- [x] 跑通至少覆盖上述 seam 改造的定向测试 - -### Phase 7:项目骨架与类型迁移 - -- [x] 创建 `GFramework.Cqrs.Abstractions` 与 `GFramework.Cqrs` 项目骨架,并接入 solution / package graph -- [x] 将纯 `GFramework.Core.Abstractions/Cqrs/*` 契约迁移到新 abstractions 项目,同时保持现有命名空间不变 -- [x] 将 CQRS 聚焦测试拆分到 `GFramework.Cqrs.Tests`,并在 `CI` 中纳入独立测试项目 -- [x] 将 `ICqrsRuntime` / `ICqrsHandlerRegistry` / `CqrsHandlerRegistryAttribute` 的最终归属与循环依赖处理方案单独收敛 -- [x] 收敛 `CqrsCoroutineExtensions` 的最终归属: - - 结论改为“保留在 `GFramework.Core` 作为 App Runtime 对 CQRS 的协程桥接”,不再作为必须迁往 `GFramework.Cqrs` 的剩余项 -- [x] 处理 `GFramework.SourceGenerators` 对 CQRS 抽象程序集元数据名的引用迁移 -- [x] 跑通迁移后最小构建与回归 - -### Phase 8:方向修正后的收敛 - -- [x] 将 `ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md` 的目标边界正式改写为: - - `GFramework.Core.Abstractions -> GFramework.Cqrs.Abstractions` - - `GFramework.Core -> GFramework.Cqrs` - - `Core` 默认集成 CQRS,但不依赖其细节结构 -- [x] 将后续实现约束明确写入计划: - - CQRS runtime / generator / 低反射增强阶段必须优先参考 `ai-libs/Mediator` 中已经成熟可用的实现,而不是完全从零重做 -- [x] 盘点 `Core` 中仍可能泄漏 CQRS 实现细节的位置: - - 直接实例化具体 runtime 类型 - - 直接依赖 generator 生成类型 - - 硬编码 handler/internal registry 结构 -- [x] 将 partial reflection fallback 从“整程序集 `GetTypes()` 扫描”推进到“generator 输出精确 handler type-name 清单后,runtime 定向 `Assembly.GetType(...)` 补扫” -- [x] 为手写/第三方程序集补齐精确 fallback metadata 入口,避免这类场景只能依赖旧版空 marker 或脆弱的字符串约定 -- [x] 为 `CqrsDispatcher` 补齐 notification/request/stream service-type 缓存,减少热路径 `MakeGenericType` 重复开销 -- [x] 为 `CqrsDispatcher` 补齐 invoker method-definition 静态缓存,并把 request/no-pipeline、request/with-pipeline、notification、stream 四条缓存路径纳入统一回归 -- [x] 将 `CqrsDispatcher` 的分散 service-type / invoker cache 收敛为按消息类别聚合的 dispatch binding cache,进一步减少热路径重复缓存查询 -- [x] 将 `CqrsHandlerRegistrar` 的程序集级 metadata 分析与 loadable-types 扫描收敛为进程级缓存,减少跨容器重复初始化时的冷路径反射 -- [x] 收口公开入口文档中的旧 SourceGenerators 聚合模块表述与旧 CQRS 示例命名空间 -- [x] 将私有/不可直接引用 handler 从“generator 输出 fallback marker + runtime 补扫”推进到“generated registry 内部定向反射注册”,进一步压缩 runtime fallback 面积 -- [ ] 将后续主线转向 CQRS 子系统增强: - - 参考 `ai-libs/Mediator` 现有成熟实现 +- `Phase 8` 仍是当前主线,不再回退到 `Phase 7` +- 最近一轮功能恢复点是 `RP-043`: + - tracking 顶部阶段与恢复建议已对齐到 `Phase 8` + - `$gframework-pr-review` 会在 open thread 中显式提醒“`Addressed in commit ...` 文案不等于线程已关闭” +- 若当前分支已推送,应优先重新执行 `$gframework-pr-review`,确认 PR `#253` 的 latest head review threads 是否已收敛 +- 若 PR review 噪音已收敛,再回到以下主线优先级: - generator 覆盖面继续扩大 - - 减少 dispatch/invoker 路径的反射占比 - - package/facade/兼容层收敛 - -## 当前完成结果 - -- `GFramework.Cqrs.Abstractions` 与 `GFramework.Cqrs` 项目骨架已创建,并已加入 `GFramework.sln`。 -- `GFramework.Core.Abstractions` 已新增到 `GFramework.Cqrs.Abstractions` 的项目引用。 -- `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 已扩展为可承载精确 fallback handler 类型名清单;当清单为空时,仍保持旧版“整程序集补扫”的兼容语义。 -- `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 现同时支持 `params string[]` 与 `params Type[]` 两种精确 fallback 声明方式,使手写/第三方程序集可以直接提供 handler 类型而不必再走字符串名称回查。 -- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 现会在检测到不可直接引用、但仍可按元数据名从当前程序集重新定位的 concrete handler 时,直接在生成注册器内部输出定向 `Assembly.GetType(...)` 注册逻辑,不再为这些场景额外生成 fallback marker。 -- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已进一步将“隐藏 implementation,但 handler interface 仍可合法 `typeof(...)` 引用”的场景收敛为: - - generated registry 只对 implementation 做一次 `Assembly.GetType(...)` - - service type 继续使用 `typeof(IRequestHandler<...>)` / `typeof(INotificationHandler<...>)` / `typeof(IStreamRequestHandler<...>)` - - 不再为该类场景生成 `GetInterfaces()` / `IsSupportedHandlerInterface(...)` / `GetRuntimeTypeDisplayName(...)` 等全反射辅助代码 -- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已继续把“handler interface 本身不可直接引用,但其 open generic contract 与 type arguments 仍可精确表达”的场景收敛为: - - 生成期记录 `IRequestHandler<,>` / `INotificationHandler<>` / `IStreamRequestHandler<,>` 的 open generic contract - - 对不可直引、但属于当前编译程序集的 type argument 输出定向 `Assembly.GetType(...)` - - 运行期通过 `MakeGenericType(...)` 重建精确 service type 并直接注册到隐藏 implementation - - 当前合法 closed contract 已不再需要 generated registry 内部的 `GetInterfaces()` 本地回退辅助逻辑; - 若未来出现新的未覆盖类型形态,则改由程序集级 targeted fallback 合同补位 -- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 现已支持递归重建以下 `RuntimeTypeReference` 形态: - - 直接 `typeof(...)` 可引用类型 - - 当前编译程序集内的隐藏类型定向 `Assembly.GetType(...)` - - 数组类型 `T[]` / 多维数组的 `MakeArrayType(...)` - - 构造泛型类型 `Generic` 的递归 `MakeGenericType(...)` -- 因此常见“隐藏消息类型嵌套在数组或泛型响应里”的 handler interface,已不再需要退回 `GetInterfaces()` 接口发现。 -- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已进一步修正同一 implementation 的注册组合策略: - - direct registration、reflected-implementation registration 与 precise-reflected registration 不再互斥 - - 当一个 implementation 同时命中多种注册路径时,生成器会合并这些注册步骤,并继续按 `HandlerInterfaceLogName` 稳定排序输出 - - 这样可静态绑定的接口不会再因为同实现上的另一条接口需要更窄反射路径而被一起降级 -- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 现已把“生成注册器顶层上下文是否能合法书写某个类型”改为通过 `Compilation.IsSymbolAccessibleWithin(symbol, compilation.Assembly, null)` 判定: - - 该调整修正了此前把 `protected internal` 等声明可见类型误当作“可直接 `typeof(...)`”的粗判 - - 对当前程序集私有类型仍继续走 metadata-name 定向 lookup - - 对外部程序集里只能通过继承语义可见、但生成注册器顶层上下文不可直接写的类型,现会正确进入精确的程序集定向 lookup 路径 -- `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已删除 generated registry 内部的 implementation 级 `GetInterfaces()` 补洞辅助逻辑: - - 不再生成 `RegisterRemainingReflectedHandlerInterfaces(...)`、`knownServiceTypes`、`GetRuntimeTypeDisplayName(...)` 等尾分支辅助代码 - - 对当前合法 closed handler contract,统一走 direct / reflected-implementation / precise-reflected 三类静态化路径 - - 若未来出现暂未被 `RuntimeTypeReferenceSpec` 表达的新类型形态,则仅通过程序集级 `CqrsReflectionFallbackAttribute` 对具体 handler implementation 定向补反射 -- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已新增两条混合场景快照回归: - - 同一可见 implementation 同时包含 direct + precise registration - - 同一隐藏 implementation 同时包含 reflected-implementation + precise registration -- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已新增多程序集回归: - - 通过额外元数据引用构造“外部基类携带 `protected internal` 嵌套 request/response 类型”的真实边界 - - 锁定生成器会保留已知直注册、输出 `ResolveReferencedAssemblyType(...)` 精确 lookup,且不会重新带回 `RegisterRemainingReflectedHandlerInterfaces(...)` 尾分支或对不可访问 protected-nested 类型生成 `typeof(...)` -- `GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs` 与 `MetadataReferenceTestBuilder.cs` 已补齐多程序集测试基础设施,允许 CQRS generator 回归显式追加内存元数据引用。 -- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已改为优先消费 fallback metadata 中的显式 `Type` 集合,其次才按 type-name 清单定向 `Assembly.GetType(...)` 补扫;只有缺少精确元数据时才继续走整程序集 `GetTypes()` 扫描。 -- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已新增回归,分别锁定“string type-name fallback 不触发 `GetTypes()` 全量扫描”与“direct `Type` fallback 同时不触发 `GetType()` / `GetTypes()`”行为;`GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已同步覆盖“隐藏 handler 由 generated registry 自行定向注册”与“无论 runtime 是否暴露 legacy fallback marker,均不再为该场景输出 marker”。 -- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已新增 notification/request/stream service-type 缓存,避免重复为同一消息类型组合执行 `MakeGenericType`。 -- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已将 invoker 对应的泛型方法定义查找收敛为静态缓存,避免每种新消息类型首次命中 invoker cache 时再次执行 `GetMethod(...)` 查找。 -- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已将 request/no-pipeline 与 request/with-pipeline 两条 invoker 路径收敛为按 `TResponse` 分层的强类型缓存: - - `SendAsync(...)` 现在直接复用 `ValueTask` 委托 - - 不再通过 `object` 桥接承接 request 结果,减少 value-type 响应装箱 -- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已继续把 notification/request/stream 的分散 service-type / invoker 缓存收敛为聚合 dispatch binding cache: - - `NotificationDispatchBindings` / `RequestDispatchBindingCache` / `StreamDispatchBindings` 会把服务类型与强类型调用委托绑定到单一缓存项 - - request 路径在保持按 `TResponse` 分层的同时,进一步减少一次分发中的重复字典查询 -- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 已补齐回归,额外锁定 request invoker 会按 `int` / `string` 等不同响应类型分层建缓存,并在 `SetUp` 中显式清空 dispatcher 静态缓存,避免跨测试污染导致断言漂移。 -- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 已新增/更新回归,统一锁定 dispatcher 对 request/no-pipeline、request/with-pipeline、notification、stream 的 service-type / invoker cache 都满足“一次建缓存,多次复用”。 -- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已新增三类进程级缓存: - - 程序集级 `AssemblyMetadataCache`,复用 generated registry attribute 与 reflection fallback metadata 的分析结果 - - registry 类型级 `RegistryActivationMetadataCache`,复用接口/抽象态/无参构造激活分析 - - `LoadableTypesCache`,为未命中 generated registry 的程序集复用首次 `GetTypes()` / `ReflectionTypeLoadException` 恢复结果 -- 上述缓存对 `Assembly` 统一使用引用相等键,避免 mock/proxy 或自定义 `Assembly.Equals(...)` 语义干扰缓存命中。 -- `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已新增回归: - - 锁定同一程序集对象跨两个容器重复接入时,`CqrsHandlerRegistryAttribute` / `CqrsReflectionFallbackAttribute` / `Assembly.GetType(...)` 只会解析一次 - - 锁定未命中 generated registry 时,同一程序集对象跨两个容器重复接入只会执行一次 `GetTypes()`,且两边都能复用首次恢复出的可加载 handler 集合 -- `README.md`、`CLAUDE.md`、`docs/zh-CN/core/cqrs.md` 与 `docs/zh-CN/source-generators/index.md` 已完成最小同步: - - 根 README 的模块表、仓库结构与安装指引已补入 `GFramework.Cqrs` 以及拆分后的 SourceGenerators 家族 - - `CLAUDE.md` 的依赖图与模块结构说明已从旧 `GFramework.SourceGenerators` 聚合表述改为 `Core/Game/Godot/Cqrs SourceGenerators` - - `docs/zh-CN/core/cqrs.md` 的示例命名空间已切到当前 `GFramework.Cqrs*` 公开路径,并把迁移说明改成“直接改用 `RegisterCqrsPipelineBehavior()`” - - `docs/zh-CN/source-generators/index.md` 已从“单一 `GFramework.SourceGenerators` 包”口径改为“按模块拆分的 Source Generators 家族”口径 -- 以下纯 CQRS 契约已从 `GFramework.Core.Abstractions/Cqrs/*` 迁移到 `GFramework.Cqrs.Abstractions/Cqrs/*`,并保持原命名空间不变: - - `IRequest*` / `IStreamRequest*` / `INotification*` - - `IPipelineBehavior` - - `MessageHandlerDelegate` - - `Unit` - - `Command/Query/Request/Notification` 输入与标记契约 - - `ICqrsHandlerRegistrar` -- `GFramework.Cqrs.Abstractions/GlobalUsings.cs` 已补齐基础 system 命名空间,避免新项目在关闭 `ImplicitUsings` 约束下丢失 `ValueTask` / `CancellationToken` / `IAsyncEnumerable`。 -- `GFramework.Cqrs.Tests` 已创建并加入 `GFramework.sln`,当前已承接以下 CQRS 聚焦测试: - - `CqrsHandlerRegistrarTests` - - `CqrsCoroutineExtensionsTests` - - `MediatorAdvancedFeaturesTests` - - `MediatorArchitectureIntegrationTests` - - `MediatorComprehensiveTests` -- `GFramework.Cqrs.Tests/GlobalUsings.cs` 与 `Logging/TestLogger.cs` 已补齐,确保新测试项目不再隐式依赖 `GFramework.Core.Tests` 的编译上下文。 -- `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 已移除对已迁移测试类型的编译期耦合,改为复用本地最小 CQRS fixture 验证容器层程序集去重与重新注册行为。 -- `GFramework.Cqrs/ICqrsRegistrationService.cs` 与 `Internal/DefaultCqrsRegistrationService.cs` 已新增: - - 将 CQRS handler 程序集接入的稳定键去重与 registrar 调度从 `MicrosoftDiContainer` 下沉到 CQRS runtime 内部 - - `MicrosoftDiContainer.RegisterCqrsHandlersFromAssemblies(...)` 现只保留公开委托入口,不再直接维护 handler 注册细节状态 -- `GFramework.Core/Services/Modules/CqrsRuntimeModule.cs` 与 `GFramework.Tests.Common/CqrsTestRuntime.cs` 已同步注册新的 `ICqrsRegistrationService`,确保生产路径与裸测试容器路径都通过同一协调服务接入 handler 程序集。 -- `GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs` 已新增并保留,作为“手写/第三方程序集或 generator 仍未直接覆盖的场景需要 runtime 补反射”的程序集级兼容入口。 -- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已从“发现任一不可见 handler 就整程序集不生成”收敛为: - - 仍为可由生成代码合法引用的 handlers 生成 registry - - 对私有/不可直接引用但可按元数据名重新定位的 handlers,在 generated registry 内部输出定向反射注册 - - runtime fallback marker 仅保留给手写 metadata 或未来仍无法由生成器自处理的真正残余回退场景; - 这些场景也不再通过 generated registry 内部 `GetInterfaces()` 尾分支处理 -- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已支持“generated registry + reflection fallback”组合路径: - - 当程序集带有 `CqrsReflectionFallbackAttribute` 时,运行时会在执行 generated registry 后继续补一次 reflection 扫描 - - 反射补扫前会按 `ServiceType + ImplementationType` 去重,避免已由 generated registry 注册的映射重复写入 -- 覆盖该行为的新增/更新测试已落地: - - `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` - - `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` -- 本轮盘点结论已明确: - - `ArchitectureContext -> ICqrsRuntime` 属于合理 seam,不视为实现细节泄漏 - - `Core` 当前未直接依赖任何 generator 生成类型名 - - 仍值得继续收敛的点主要是 `ArchitectureBootstrapper` 默认 handler 程序集硬编码,以及更长线的 `IIocContainer` / `IArchitecture` CQRS 装配 API 暴露面 -- `GFramework/.github/workflows/ci.yml` 已新增 `dotnet test GFramework.Cqrs.Tests ...`,使 CQRS 模块拆分后的测试在 PR CI 中继续被覆盖。 -- `GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs`、`CqrsHandlerRegistrar.cs` 与 `DefaultCqrsHandlerRegistrar.cs` 已物理迁移到 `GFramework.Cqrs` 项目,同时保持现有 `GFramework.Core.Cqrs.Internal` 命名空间不变,避免消费端源码感知程序集拆分。 -- `GFramework.Core/Cqrs/Command/CommandBase.cs`、`Query/QueryBase.cs`、`Request/RequestBase.cs` 与 `Notification/NotificationBase.cs` 已迁移到 `GFramework.Cqrs`,继续保留原公开命名空间。 -- `GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs` 与 `PerformanceBehavior.cs` 已物理迁移到 `GFramework.Cqrs/Cqrs/Behaviors/*`,继续保留 `GFramework.Core.Cqrs.Behaviors` 公开命名空间,避免消费端源码感知程序集调整。 -- `GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs`、`ContextAwareCqrsCommandExtensions.cs` 与 `ContextAwareCqrsQueryExtensions.cs` 已迁移到 `GFramework.Cqrs`,`GFramework.Godot` 与兼容层调用点无需修改源码即可继续解析这些扩展。 -- `GFramework.Core/Logging/LoggerFactoryResolver.cs` 已下沉到 `GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs`,并保持 `GFramework.Core.Logging` 命名空间不变: - - 默认 provider 会优先通过反射解析 `GFramework.Core` 中的 `ConsoleLoggerFactoryProvider` - - 若宿主未加载默认日志实现,则退回静默 provider,避免 `GFramework.Cqrs -> GFramework.Core` 形成反向依赖 - - `GFramework.Core` 已通过 type forward 继续暴露该公开类型,降低已编译消费端的运行时兼容风险 -- `ICqrsHandlerRegistry` 与 `CqrsHandlerRegistryAttribute` 已从 `GFramework.Core.Abstractions` 收敛到 `GFramework.Cqrs` 运行时根命名空间: - - 这两个类型依赖 `ILogger` / `IServiceCollection`,不再适合继续放在“纯消息契约”抽象层 - - 该调整避免了 `GFramework.Core.Abstractions <-> GFramework.Cqrs.Abstractions` 循环引用 -- `ICqrsRuntime` 已进一步收敛到 `GFramework.Cqrs.Abstractions`: - - 新增轻量 marker seam `ICqrsContext`,让 runtime 契约不再直接依赖 `IArchitectureContext` - - `IArchitectureContext` 现在实现 `ICqrsContext`,保留当前架构上下文作为默认 CQRS 分发上下文 - - 旧 `GFramework.Core.Abstractions.Cqrs.ICqrsRuntime` 保留为兼容别名,并由默认 runtime 模块同时注册新旧接口,避免立即打断历史公开路径 -- `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,而不再跨程序集直接 `new` 内部实现。 -- 已确认 `GFramework.Core/Coroutine/Extensions/CqrsCoroutineExtensions.cs` 暂不适合迁移到 `GFramework.Cqrs`: - - 它直接依赖 `TaskCoroutineExtensions.AsCoroutineInstruction()` 等 `Core` 协程工具链 - - 若一并迁出会把非 CQRS 的协程基础设施反向拉进 runtime 项目 - - 在方向修正后,该类型不再作为“必须迁走”的剩余项,而是正式视为 `Core` 对 CQRS 的桥接层 -- 当前边界结论已修正为: - - `GFramework.Core.Abstractions -> GFramework.Cqrs.Abstractions` - - `GFramework.Core -> GFramework.Cqrs` - - `Core` 默认集成 CQRS,但不应继续依赖其 dispatcher / generator / handler registry 细节结构 -- 当前主阻塞不再是“如何让 `Core` 摆脱 `Cqrs`”,而是: - - 如何继续降低 runtime 对反射的依赖 - - 如何让 generator 从“注册器 MVP”继续走向更完整的低反射支持 - - 如何收敛 package/facade/兼容层,而不破坏 `CoreGrid-Migration` 等真实消费端 -- `GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs` 与 `ICqrsHandlerRegistrar.cs` 已新增,形成 `ArchitectureContext` 与容器注册路径共享的 runtime seam。 -- `GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs` 已新增,复用现有 `CqrsHandlerRegistrar` 静态流水线承接 `ICqrsHandlerRegistrar` 默认实现。 -- `GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs` 已改为实现 `ICqrsRuntime`,并将当前 `IArchitectureContext` 作为调用参数传入,而不再由 `ArchitectureContext` 直接持有具体实现依赖。 -- `GFramework.Core/Architectures/ArchitectureContext.cs` 已从直接 `new CqrsDispatcher(...)` 改为解析 `ICqrsRuntime` seam。 -- `GFramework.Core/Ioc/MicrosoftDiContainer.cs` 已从直接调用 `CqrsHandlerRegistrar` 改为解析已注册的 `ICqrsHandlerRegistrar` seam。 -- `GFramework.Core/Services/Modules/CqrsRuntimeModule.cs` 已新增,并由 `ServiceModuleManager` 纳入 built-in modules,确保默认架构启动路径继续自动具备 CQRS runtime 与 handler 注册能力。 -- `GFramework.Core.Tests/CqrsTestRuntime.cs` 已补充裸测试容器的 CQRS seam 注册辅助,以便不经过 `ServiceModuleManager` 的单元测试继续观察正式 runtime 行为。 -- `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 中“Clear 后重新接入 handler”回归已适配 seam 方案:在裸容器 `Clear()` 后显式补回测试基础设施,再验证程序集去重状态重置。 -- `ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md` 已新增,完成 Phase 5 的模块边界评估、依赖倒置方案与包拆分草案输出。 -- Phase 5 结论已收敛为“两阶段拆分”: - - 先做 `Core -> runtime abstraction` 的 seam 改造 - - 再拆 `GFramework.Cqrs.Abstractions` / `GFramework.Cqrs` 项目与 public runtime 类型归属 -- 已确认当前真正阻塞 CQRS 拆分的硬耦合点主要是: - - `ArchitectureContext` 直接实例化 `CqrsDispatcher` - - `MicrosoftDiContainer` 直接调用 `CqrsHandlerRegistrar` - - `ArchitectureBootstrapper` 在默认启动路径内建 CQRS handler 注册假设 -- 已确认当前消费端兼容面的主要压力来自: - - `CoreGrid-Migration/scripts/cqrs/**` 对 `CommandBase`、`Abstract*Handler`、`GFramework.Core.Cqrs.Extensions` 的直接依赖 - - `GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs` 对 `GFramework.Core.Cqrs.Extensions` 的直接依赖 -- `CoreGrid-Migration` 已直连本地 `GFramework` 源码与本地 source generators。 -- `GameArchitecture` 已不再依赖 `collection.AddMediator(...)` 即可使用 CQRS。 -- `GFramework.Core.Abstractions` 与 `GFramework.Core.Tests` 已移除 `Mediator.Abstractions` / - `Mediator.SourceGenerator` 包引用;`IServiceCollection` / `IServiceScope` 所需依赖改为显式引用 - `Microsoft.Extensions.DependencyInjection.Abstractions`。 -- `GFramework.Core.Abstractions` 新增自有 CQRS 契约: - - `IRequest` / `INotification` / `IStreamRequest` - - `IRequestHandler<,>` / `INotificationHandler<>` / `IStreamRequestHandler<,>` - - `Unit` - - `IPipelineBehavior<,>` / `MessageHandlerDelegate<,>` -- `ArchitectureBootstrapper` 会在初始化阶段自动扫描并注册当前架构程序集与 `GFramework.Core` 程序集中的 CQRS handlers。 -- `IArchitecture`、`IIocContainer`、`Architecture`、`ArchitectureModules` 与 `MicrosoftDiContainer` - 已新增 `RegisterCqrsHandlersFromAssembly(...)` / `RegisterCqrsHandlersFromAssemblies(...)`, - 允许模块程序集或扩展包程序集在初始化阶段显式接入与默认路径相同的“生成注册器优先 + 反射回退”注册链路。 -- `MicrosoftDiContainer` 已把默认启动路径与显式扩展路径统一到同一个 CQRS handler 注册入口, - 并按稳定程序集键去重,避免默认扫描与后续模块接入重复写入同一程序集的 handler 映射。 -- `IArchitectureContext`、`QueryBase`、`Abstract*Handler` 以及 `MessageHandlerDelegate` 的 XML 文档已补齐迁移后的契约边界,明确旧 - Command/Query 总线与新 CQRS runtime 的推荐入口。 -- `CqrsDispatcher` 已支持: - - request dispatch - - notification publish - - stream dispatch - - context-aware handler 注入 - - request pipeline behavior 链式执行 -- `CqrsHandlerRegistrar` 已补齐三项运行时硬化: - - 按程序集名、处理器类型名与处理器接口名稳定排序,避免通知处理顺序随反射枚举漂移 - - 在 `ReflectionTypeLoadException` 场景下保留可加载处理器并记录告警 - - 自动扫描到的 request / notification / stream handler 统一改为 transient,避免上下文感知处理器在并发分发时共享可变状态 -- `GFramework.Core.Tests` 中原依赖 `Mediator` 注册路径的测试已切换到框架内建 CQRS 注册路径;`CqrsTestRuntime` 现仅保留 - handler 注册职责,行为测试改为走 `ArchitectureContext.SendRequestAsync(...)` 正式入口。 -- `AbstractStreamCommandHandler` 已把上下文注入窗口、瞬态实例复用约束和流创建/枚举取消边界写入显式 - ``,避免公共流式命令处理器基类的生命周期约束只停留在摘要里。 -- `CqrsHandlerRegistrarTests` 已改为通过 `CqrsTestRuntime.RegisterHandlers(...)` 真实入口验证部分加载失败恢复路径,并同时断言 - “剩余 handler 仍被注册 + warning 已记录”。 -- `CqrsTestRuntime` 已补齐 XML 文档,并改为按 `IIocContainer + IEnumerable + ILogger` 精确绑定 - `RegisterHandlers(...)`,避免未来新增同名重载后出现运行时歧义。 -- `MediatorCoroutineExtensions` 已改为直接走 `IArchitectureContext.SendAsync(...)` 内建 CQRS 入口,不再从容器解析 - `IMediator`;该类型名仅作为历史兼容命名保留。 -- `RegisterCqrsPipelineBehavior()` 已作为新的公开推荐入口加入 `IArchitecture`、`IIocContainer`、 - `Architecture` 与 `MicrosoftDiContainer`;旧的 `RegisterMediatorBehavior()` 改为显式兼容转发。 -- `ContextAwareCqrsExtensions`、`ContextAwareCqrsCommandExtensions`、`ContextAwareCqrsQueryExtensions` 与 - `CqrsCoroutineExtensions` 已作为新的中性命名扩展入口加入;旧的 `ContextAwareMediator*Extensions` 与 - `MediatorCoroutineExtensions` 保留为 `[Obsolete]` 兼容包装层。 -- `RegisterMediatorBehavior()`、`ContextAwareMediator*Extensions` 与 `MediatorCoroutineExtensions` - 已进一步收紧为“正式弃用”状态: - - `Obsolete` 提示统一明确迁移目标与 “future major version” 移除预期 - - `EditorBrowsable(EditorBrowsableState.Never)` 已将这些历史别名从 IntelliSense 主路径隐藏 - - `GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs` 已锁定上述弃用元数据,防止兼容层退回“仅名义弃用” -- `GFramework.Cqrs` 已新增 `ICqrsHandlerRegistry` 与 `CqrsHandlerRegistryAttribute`, - 作为消费端程序集暴露源码生成注册器的正式 runtime 契约。 -- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已落地 CQRS handler source-generator MVP: - - 对当前消费端程序集中的 concrete request / notification / stream handlers 生成单一程序集级注册器 - - 生成注册顺序与 runtime 反射排序口径保持一致,按实现类型名与处理器接口名稳定排序 - - 对生成代码无法直接 `typeof(...)` 引用、但仍可按元数据名从当前程序集重新定位的处理器(例如私有嵌套 handler),生成注册器会改走定向反射注册,而不是退回整程序集补扫 -- `CqrsHandlerRegistrar` 已改为优先查找 `CqrsHandlerRegistryAttribute` 指向的生成注册器; - 当注册器缺失、元数据损坏或实例化失败时,会记录 warning 并自动回退到原有反射扫描路径。 -- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 与 - `GFramework.Core.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 已补齐: - - 生成器输出快照测试 - - 私有嵌套 handler 走 generated registry 内部定向反射注册的测试 - - runtime 优先使用生成注册器的测试 - - 生成注册器无效时自动回退反射的测试 -- 新的 `ContextAwareCqrs*` / `CqrsCoroutineExtensions` 位于独立命名空间,避免与旧 `Mediator` 扩展在同一 - `using` 范围内触发扩展方法解析歧义。 -- `CoreGrid-Migration` 中命中的旧扩展调用点已切到新的 `ContextAwareCqrs*` 入口,避免新旧扩展同时可见时的重载歧义。 -- `docs/zh-CN/core/cqrs.md`、`docs/zh-CN/core/index.md` 与 `CLAUDE.md` 已从“依赖外部 Mediator”改写为 - “内建 CQRS runtime + 历史命名兼容”表述。 -- `docs/zh-CN/core/cqrs.md` 与 `CLAUDE.md` 已补充“如何显式接入非默认程序集的 CQRS handlers”说明,避免文档仍停留在“需要额外接入” - 但未给出正式入口的状态。 -- 在验证阶段发现 `CqrsHandlerRegistrar.cs` 缺少 `Microsoft.Extensions.DependencyInjection` 的 `using`, - 已补齐以恢复 `IServiceCollection` 编译通过。 -- 当前验证状态: - - `dotnet build GFramework/GFramework.sln` 通过 - - `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj --no-build` 通过,`1621` 个测试全部通过 - - `dotnet build CoreGrid-Migration/CoreGrid.sln` 通过 - - - `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` 个测试全部通过 - - - `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` 个测试全部通过 - - `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 与 Godot generator warning - - `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` 在迁移 `ContextAwareCqrs*` 调用点后再次通过,仅存在既有 analyzer warnings - - `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` 个测试全部通过 - - `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release` 在 CQRS generator MVP 后通过 - - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` 在 CQRS generator MVP 后通过 - - `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` 个测试全部通过 -- `dotnet build GFramework/GFramework.sln -c Release` - 在当前 WSL 环境下命中既有 `GFramework.csproj` NuGet fallback package folder 配置问题 - (机器本地路径已省略), - 与本轮 CQRS 改动无关;`GFramework.Core.Tests` 相关项目构建与回归已通过 - - `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 在显式额外程序集 CQRS 注册入口落地后通过,仅存在既有 `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"` - 在显式额外程序集 CQRS 注册入口落地后通过,`13` 个测试全部通过 - - `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 在 runtime seam 落地后通过,`0` warnings / `0` errors - - `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"` - 在 runtime seam 落地后通过,`97` 个测试全部通过 - - `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 在纯 CQRS 契约外提到 `GFramework.Cqrs.Abstractions` 后通过,仅存在既有 `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"` - 在纯 CQRS 契约外提后再次通过,`97` 个测试全部通过 - - `dotnet build GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - 在 CQRS 聚焦测试拆分后通过,`0` warnings / `0` errors - - `dotnet test GFramework/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build` - 在 CQRS 聚焦测试拆分后通过,`54` 个测试全部通过 - - `dotnet build GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 在迁出 CQRS 聚焦测试并消除跨项目 fixture 耦合后再次通过,`0` warnings / `0` errors - - `dotnet test GFramework/GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~MicrosoftDiContainerTests|FullyQualifiedName~ArchitectureAdditionalCqrsHandlersTests|FullyQualifiedName~ArchitectureModulesBehaviorTests|FullyQualifiedName~MediatorCompatibilityDeprecationTests"` - 在测试拆分后通过,`44` 个测试全部通过 - -## 当前已知事实 - -- `GFramework` 当前仍同时维护: - - 基于 `CommandExecutor` / `QueryExecutor` / `EventBus` 的轻量旧 CQRS - - 基于 GFramework 自有抽象的新 CQRS runtime -- CQRS runtime 主实现已迁到 `GFramework.Cqrs`,`GFramework.Core` 当前主要保留架构接线、默认 behaviors 与协程扩展;`Abstract*Handler` 基类已物理迁到 `GFramework.Cqrs` 并改为依赖轻量 `CqrsContextAwareHandlerBase`,不再直接继承 `GFramework.Core.Rule.ContextAwareBase`。 -- Phase 5 评估结论是“拆分收益成立,但必须先做依赖倒置 seam”,该 seam 已在当前恢复点落地完毕,下一步可以正式进入项目骨架拆分。 -- Phase 7 已验证可以先把“纯 CQRS 契约”独立成 `GFramework.Cqrs.Abstractions`,并把 runtime registry 契约收敛到 `GFramework.Cqrs`;当前真正残留的边界阻塞点主要是 `ICqrsRuntime -> IArchitectureContext`、`CqrsCoroutineExtensions -> TaskCoroutineExtensions` 的耦合,以及是否继续承诺旧 `GFramework.Core.Abstractions.Cqrs*` namespace 兼容。 -- Phase 7 已额外验证:CQRS 聚焦测试可以先拆到 `GFramework.Cqrs.Tests`,而架构壳层、容器 seam 与兼容层测试继续留在 `GFramework.Core.Tests`,不会破坏当前回归覆盖。 -- 仍存在 `Mediator` 残留的区域主要集中在: - - 兼容 API 与测试目录中的历史命名 - - 少量本地计划/历史记录文档中的迁移过程描述 -- `CoreGrid-Migration` 已切到本地源码引用,并在当前恢复点完成构建验证 -- 本轮已接受的 review / subagent 结论: - - `CqrsTestRuntime.ExecutePipelineAsync(...)` 会掩盖正式 dispatcher 行为,已移除 - - handler 自动注册必须保持稳定顺序、容忍部分类型加载失败,并避免单例上下文污染 - - `AbstractStreamCommandHandler` 的上下文可用窗口、实例复用约束与取消边界需要放入显式 - ``,避免公共基类被误用 - - `CqrsHandlerRegistrar` 的部分加载失败回归必须从 `RegisterHandlers` 公开测试入口观察,而不是反射私有 - `RecoverLoadableTypes(...)` - - `CqrsTestRuntime` 反射绑定不能只按名称解析 `RegisterHandlers`,否则新增重载后会出现不确定行为 - - 生产代码与主文档中保留的 `Mediator` 标识目前只剩历史兼容命名,不再代表外部包依赖,可作为下一阶段 - API 命名统一任务单独处理 + - dispatch/invoker 反射占比继续下降 + - package / facade / 兼容层继续收口 ## 当前风险 -- `GFramework` 仓库存在与本任务无关的既有改动,提交时必须避免覆盖 -- `CoreGrid-Migration` 是 worktree,WSL 下原生 `git` 解析该 worktree 路径有兼容问题 -- 当前 `RegisterMediatorBehavior`、`MediatorCoroutineExtensions` 等旧名已降级为兼容包装层;若后续要彻底移除历史命名,需要单独规划弃用周期 -- 当前历史 `Mediator` 兼容入口虽已隐藏 IntelliSense 并明确 future-major 移除,但仍保留公开签名以避免立即破坏旧调用方 -- 当前 handler 自动注册已具备“默认程序集自动接入 + 额外程序集显式接入”的统一入口,并支持“消费端程序集生成注册器 + 其余程序集反射回退”的双路径;若后续追求更强 AOT/冷启动收益,还需继续减少仍依赖反射回退的程序集范围 -- 若在依赖倒置 seam 落地前就直接拆项目,`GFramework.Core` 与新 `GFramework.Cqrs` 之间很容易形成“项目名分开、实现仍互相硬引用”的伪边界 -- 当前 `CoreGrid-Migration` 与 `GFramework.Godot` 均直接依赖 `GFramework.Core.Cqrs.*` public types;若后续拆分时同时改 namespace 或取消 transitive 依赖,会放大消费端迁移成本 -- 当前 `ICqrsRuntime -> IArchitectureContext` 仍会阻止完整搬空 `GFramework.Core.Abstractions/Cqrs`;若不先收敛这条依赖,后续很容易重新引入项目循环引用 -- 当前 `Abstract*Handler` 已摆脱 `GFramework.Core.Rule.ContextAwareBase`,但 `CqrsCoroutineExtensions` 仍依赖 `TaskCoroutineExtensions`;若还希望继续压缩 `GFramework.Core` 对 CQRS 的承载范围,下一步仍需处理协程工具链边界 -- `CoreGrid-Migration` 本轮通过 consumer 侧 namespace/extension 对齐恢复构建;若产品层仍希望承诺旧 `GFramework.Core.Abstractions.Cqrs*` 公共路径,需要单独设计兼容层或迁移策略,否则会在后续真实消费端继续重复暴露同类断点 -- 当前 `GFramework.Cqrs.Tests` 仍直接引用 `GFramework.Core`,说明测试已经按模块意图拆分,但 runtime 物理迁移尚未完成;若后续直接切断该依赖,测试会失去承载实现 -- `dotnet build GFramework.sln -c Release` 在当前 WSL 环境仍会受顶层 `GFramework.csproj` 的 Windows NuGet fallback package - folder 配置影响,若需要恢复 solution 级全量验证,需先处理该环境问题 +- 当前 `dotnet build GFramework.sln -c Release` 在 WSL 环境仍会受顶层 `GFramework.csproj` 的 Windows NuGet fallback 配置影响 +- 当前 `GFramework.Cqrs.Tests` 仍直接引用 `GFramework.Core`,说明测试已按模块意图拆分,但 runtime 物理迁移尚未完全切断依赖 +- `RegisterMediatorBehavior`、`MediatorCoroutineExtensions` 与 `ContextAwareMediator*Extensions` 仍作为兼容层存在,未来真正移除时仍需单独规划弃用窗口 -## 下次恢复建议 +## 活跃文档 -若本轮中断,优先从以下顺序恢复: +- 模块拆分计划:`ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md` +- 历史跟踪归档:[cqrs-rewrite-history-through-rp043.md](../archive/todos/cqrs-rewrite-history-through-rp043.md) +- 历史 trace 归档:[cqrs-rewrite-history-through-rp043.md](../archive/traces/cqrs-rewrite-history-through-rp043.md) -1. 查看 `ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md` -2. 确认当前恢复点 `CQRS-REWRITE-RP-042` 已对应到最新提交 -3. 优先继续执行 `ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md` 中的 Phase 8(当前主线): - - 先决定是否正式支持旧 `GFramework.Core.Abstractions.Cqrs*` / `GFramework.Core.Cqrs.Extensions` public namespace 兼容,还是明确要求消费端迁到当前 `GFramework.Cqrs*` 路径 - - 再评估 `CqrsCoroutineExtensions` 是否保留在 `GFramework.Core`,或连同所需协程辅助一起形成更小的可迁移边界 -4. 在 runtime 项目真正承接实现后,再处理 source-generator、meta package 与消费端 transitive 依赖的迁移 -5. 在规划 future major 版本时,再决定何时真正移除 `RegisterMediatorBehavior` / `MediatorCoroutineExtensions` / `ContextAwareMediator*Extensions` +## 验证说明 -## 2026-04-15 补充记录(RP-015) +- `RP-043` 之前的详细阶段记录、定向验证命令和阶段性决策均已移入主题内归档 +- active 跟踪文件只保留当前恢复点、当前活跃事实、风险和下一步,避免 `boot` 在默认入口中重复扫描 1000+ 行历史 trace -### 阶段:轻量 handler 上下文基类与消费端兼容性收敛 +## 下一步 -- 建立 `CQRS-REWRITE-RP-015` 恢复点 -- `GFramework.Cqrs/Cqrs/CqrsContextAwareHandlerBase.cs` 已新增,作为仅依赖 `IContextAware` / `IArchitectureContext` 的轻量 CQRS handler 上下文基类: - - 保留 `OnContextReady()` 生命周期扩展点 - - 去掉对 `GameContext` 的兜底依赖 - - 在运行时注入前访问 `Context` / `GetContext()` 时显式抛出异常,避免静默落回全局上下文 -- `AbstractCommandHandler` / `AbstractQueryHandler` / `AbstractRequestHandler` / `AbstractNotificationHandler` 及其 stream 变体已物理迁到 `GFramework.Cqrs`,并改为继承该轻量基类 -- `GFramework.Cqrs.Tests/Cqrs/AbstractCqrsHandlerContextTests.cs` 已新增,回归覆盖: - - 未注入上下文时 fail-fast - - 注入后 `Context` 可用 - - `OnContextReady()` 生命周期钩子仍然生效 -- 在真实迁移验证阶段,`CoreGrid-Migration` 暴露出旧 `GFramework.Core.Abstractions.Cqrs*` 与旧 `GFramework.Core.Cqrs.Extensions` 命名空间不再自动可用的问题 -- 本轮先采用最小 consumer 修复路径恢复验证链路: - - `scripts/GlobalUsings.cs` 已补齐新的 `GFramework.Cqrs.Abstractions.Cqrs*`、`GFramework.Cqrs.*` 与 `GFramework.Core.Cqrs.*` handler namespace 导入 - - `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 编译错误 - -## 备注 - -- 本文档是当前任务的主恢复点,后续每个关键阶段完成后都要更新 -- 发生方向调整时,不覆盖旧结论,直接追加阶段记录与新的恢复点编号 - -## 2026-04-15 补充记录 - -### 阶段:review 收尾修正(并发 lazy 初始化与共享测试运行时) - -- `GFramework.Core/Architectures/ArchitectureContext.cs` 已将 `ICqrsRuntime` 的延迟解析从 `??=` - 改为 `Lazy`,并显式使用 `LazyThreadSafetyMode.ExecutionAndPublication` - 保证并发首次访问时只执行一次容器解析 -- `ArchitectureContext` 已补齐公开构造函数 XML 文档,以及 `CqrsRuntime` 惰性初始化的并发语义说明 -- `GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs` 已新增并发回归测试, - 锁定“多线程首次访问 `SendRequestAsync(...)` 时只解析一次 `ICqrsRuntime`”的行为 -- 原先位于 `GFramework.Core.Tests/CqrsTestRuntime.cs` 与 `GFramework.Cqrs.Tests/CqrsTestRuntime.cs` - 的重复实现已提取到共享源码文件 `tests/Shared/CqrsTestRuntime.cs` -- `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 已通过链接编译同一份共享源码并补齐 `global using`, - 避免两个测试项目继续维护分叉的反射绑定逻辑 -- 共享版 `CqrsTestRuntime` 已顺手清理 `GetType(..., throwOnError: true)! ?? throw ...` - 里的不可达 `?? 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` 命令时,`NuGet` restore 在共享 `obj/project.assets.json` 上发生文件竞争; - 顺序重跑 `GFramework.Core.Tests --no-restore` 后验证通过,属于本地并行 restore 的环境噪音,不是代码回归 - -### 阶段:测试公共基础设施模块化 - -- 已新增 `GFramework.Tests.Common` 项目,并加入 `GFramework.sln` -- 原先临时放在仓库根目录 `tests/Shared/CqrsTestRuntime.cs` 的共享源码已迁入 - `GFramework.Tests.Common/CqrsTestRuntime.cs` -- `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 已从源码链接切换为显式 `ProjectReference` -- 两个测试项目的 `global using` 已从 `GFramework.Tests.Shared` 迁到 `GFramework.Tests.Common` -- 该调整保留了原有测试调用方式,但把公共测试基础设施收敛到单独模块,避免继续以目录约定模拟模块边界 - -### 阶段:测试公共基础设施模块化验证 - -- `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` 个测试全部通过 - -## 2026-04-15 补充记录(RP-016) - -### 阶段:日志入口下沉与剩余 runtime behaviors 迁移 - -- 建立 `CQRS-REWRITE-RP-016` 恢复点 -- `GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs` 已新增,继续使用 `GFramework.Core.Logging` 命名空间: - - 该类型已从 `GFramework.Core` 实现层下沉到抽象层,允许 `GFramework.Cqrs` 在不反向引用 `GFramework.Core` 的前提下获取日志器 - - 默认 provider 会优先反射创建 `GFramework.Core.Logging.ConsoleLoggerFactoryProvider` - - 当宿主仅加载抽象层时,会退回到静默 provider,避免抽象层默认日志入口因缺少实现程序集而崩溃 -- `GFramework.Core/Properties/TypeForwarders.cs` 已新增,对 `LoggerFactoryResolver` 做 type forwarding,避免把公开类型迁出 `GFramework.Core` 后留下运行时兼容断点 -- `GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs` 与 `PerformanceBehavior.cs` 已物理迁移到 `GFramework.Cqrs/Cqrs/Behaviors/*` -- 上述两个 behavior 继续保留 `GFramework.Core.Cqrs.Behaviors` 命名空间,消费端源码无需改 using 即可继续解析 -- 通过这一步,`GFramework.Core/Cqrs/*` 已完全搬空;Phase 7 的 runtime 物理迁移残项现只剩 `CqrsCoroutineExtensions` -- 并行 explorer 结论已收敛: - - `ICqrsRuntime` 当前不能迁到 `GFramework.Cqrs.Abstractions`,根因不是单个 using,而是整条 `ICqrsRuntime -> IArchitectureContext -> IContextAware / handler Context` 的上下文模型仍绑定 `GFramework.Core.Abstractions` - - `CqrsCoroutineExtensions` 本质上是 `GFramework.Core` 协程桥接层,不是纯 CQRS runtime 代码;若原样迁到 `GFramework.Cqrs`,会重新形成 `GFramework.Cqrs -> GFramework.Core` 反向依赖 - - 下一阶段的最小可行方案应优先考虑“新增 CQRS 专用上下文 seam + 继续让协程扩展留在 `GFramework.Core`”,而不是先新增组合项目 - -### 阶段:RP-016 验证 - -- `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` 个测试全部通过 - -## 2026-04-16 补充记录(RP-020) - -### 阶段:review 收尾修正(线程安全文档、未冻结态去重与 runtime 契约说明) - -- 建立 `CQRS-REWRITE-RP-020` 恢复点 -- `GFramework.Cqrs/Internal/DefaultCqrsRegistrationService.cs` 已在类级 `` 中明确“该类型不是线程安全的,必须由外部同步边界串行访问”的设计约束 -- `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()` / `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 补充记录(RP-033) - -### 阶段:review 小修(缓存清理、诊断信息与测试辅助性能) - -- 建立 `CQRS-REWRITE-RP-033` 恢复点 -- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` 的 `ClearDispatcherCaches()` 已补齐 - `RequestPipelineInvokerCache.Invokers` 清理,避免未来新增 `string` pipeline 路径测试时残留静态状态 -- `GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 已在 - `CanReferenceFromGeneratedRegistry(...)` 的默认分支补充注释,明确当前把 `dynamic`、error type 等其他 Roslyn 类型种类视为暂时可引用的实现假设 -- `GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs` 已将运行时可信平台程序集引用收敛为惰性静态缓存,避免多程序集生成器测试反复解析 - `TRUSTED_PLATFORM_ASSEMBLIES` -- `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 已增强 `RunGenerator(...)` - 中的编译错误断言消息,失败时会输出完整 diagnostics 文本,便于直接定位生成代码问题 - -### 阶段:RP-033 验证 - -- `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` 个测试全部通过 - -## 2026-04-18 补充记录(RP-040) - -### 阶段:第三方参考源码治理 - -- 建立 `CQRS-REWRITE-RP-040` 恢复点 -- `AGENTS.md` 已新增仓库级约束: - - `ai-libs/` 用于存放第三方项目源码副本,仅供对照、追踪与设计参考 - - `ai-libs/**` 默认为只读区域,除非用户明确要求同步或更新第三方快照,否则不允许修改 - - 后续计划、trace、评审与设计说明引用第三方实现时,优先写明仓库内参考路径,而不是使用模糊的外部项目名 -- CQRS 迁移主线已将 `Mediator` 的本地参考源正式收口到 `ai-libs/Mediator` -- 本跟踪文档中“后续继续参考 Mediator 成熟实现”的执行语义已同步更新为“优先参考 `ai-libs/Mediator`” - -### 阶段:RP-040 验证 - -- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release` - - 结果:通过 - - 备注:存在既有 `MA0051` 与 `MA0158` analyzer warnings,无新增构建错误 -- `rg -n "ai-libs/Mediator|只读|第三方项目源码副本" AGENTS.md ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md` - - 结果:通过 - - 备注:`AGENTS.md`、tracking 与 trace 均已命中新规则和本地参考路径说明 - -### 下一步 - -1. 后续若继续推进 CQRS runtime、generator 或低反射优化,统一以 `ai-libs/Mediator` 作为本地参考源 -2. 若未来需要升级 `Mediator` 参考副本,单独作为“同步第三方快照”任务处理,不与框架实现改动混在同一变更里 - -## 2026-04-18 补充记录(RP-041) - -### 阶段:source-generator 文档命名空间收口 - -- 建立 `CQRS-REWRITE-RP-041` 恢复点 -- 已将 `docs/zh-CN/**` 中残留的旧示例 `using GFramework.SourceGenerators.Abstractions.*;` 批量改为当前公开命名空间: - - `GFramework.Core.SourceGenerators.Abstractions.Rule` - - `GFramework.Core.SourceGenerators.Abstractions.Bases` - - `GFramework.Core.SourceGenerators.Abstractions.Logging` - - `GFramework.Core.SourceGenerators.Abstractions.Enums` - - `GFramework.Core.SourceGenerators.Abstractions.Architectures` -- 已同步修正文档中的叙述性旧口径: - - `docs/zh-CN/source-generators/logging-generator.md` 不再把日志生成器描述成旧聚合 `GFramework.SourceGenerators` - - `docs/zh-CN/source-generators/context-get-generator.md` 改为明确由 `GFramework.Core.SourceGenerators` 执行注册可见性分析 - - `docs/zh-CN/api-reference/index.md` 将“`GFramework.SourceGenerators` 单一模块”改写为当前拆分后的 Source Generators 家族说明 - -### 阶段:RP-041 验证 - -- `rg -n "using GFramework\\.SourceGenerators\\.Abstractions\\.|### GFramework\\.SourceGenerators|GFramework\\.SourceGenerators 自动生成|GFramework\\.SourceGenerators 现在还会分析" docs/zh-CN` - - 结果:通过 - - 备注:`docs/zh-CN/**` 中上述旧公开命名空间与旧聚合表述已清理完毕 - -### 下一步 - -1. 若继续文档主线,扩大清理范围到更多说明页中的旧 API 参考与历史命名残留,优先处理 adoption 入口最靠前的页面 -2. 若切回实现主线,则重新盘点 `GFramework.Cqrs` 中仍值得继续压缩的冷路径/热路径反射点,优先选择能带来明确收益的低复杂度改动 - -## 2026-04-19 补充记录(RP-042) - -### 阶段:PR review 技能接入与 PR-253 follow-up - -- 已新增项目级 `$gframework-pr-review` skill: - - 目录:`.codex/skills/gframework-pr-review/` - - 作用:定位当前分支对应的 GitHub PR,并优先通过 GitHub PR / issue comments / review comments API 提取 - CodeRabbit 汇总、最新 head commit review threads、`Failed checks` 与 CTRF 测试结果 - - 约束:不依赖 `gh` CLI;不再把重型 PR HTML 页面当作主数据源 -- 已根据 PR `#253` 的公开 review 内容完成本地修正: - - `.codex/skills/gframework-boot/SKILL.md` 的恢复 heuristics 不再把 `next step/continue/继续` - 直接映射为 `recovery` - - `AGENTS.md` 中 `ai-libs/**` 观察写入 active plan/trace 的规则已收窄到“多步/复杂任务或已有 active - tracking document” - - `Godot/script_templates/Node/*.cs` 与 `GFramework.Core.Abstractions/Controller/IController.cs` - 中旧 `Rule` 命名空间残留已同步修正 - - `fetch_current_pr_review.py` 已改为: - - Git 路径支持环境变量覆盖并回退到 `git.exe` / `git` - - `--pr` 模式不再强制读取当前分支 - - `--branch` 到 PR 编号的解析改为走 GitHub PR API - - CodeRabbit summary / CTRF 测试报告改为走 issue comments API - - 最新 review 依据改为 latest head commit review threads,而不是只看汇总块 - - `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` 已移除公开文档中的机器本地绝对路径,并统一 - 下次恢复建议里的恢复点编号 - -### 阶段:RP-042 验证 - -- `python3 - <<'PY' ... ast.parse(...) ... PY` - - 结果:通过 - - 备注:`fetch_current_pr_review.py` 语法正确 -- `python3 .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --pr 253` - - 结果:通过 - - 备注:已通过 API-first 路径解析 PR 元数据、latest head commit review threads、CodeRabbit summary - 与 CTRF 测试结果,不再依赖 PR HTML -- `python3 .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --branch feat/cqrs-optimization` - - 结果:通过 - - 备注:已验证 branch -> PR 解析同样通过 GitHub API 工作 -- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release` - - 结果:通过 - - 备注:相关 C# 改动已完成构建验证 - -### 下一步 - -1. 若要让 PR `#253` 上的 latest head review threads 反映本轮本地修正,需要先提交并推送当前分支,再重新执行 `$gframework-pr-review` -2. PR 当前公开 warning 仍包含 `Docstring Coverage`,若后续要继续消除此项,需要单独规划并提交文档注释覆盖率改进 +1. 推送当前分支后重新执行 `$gframework-pr-review`,确认 `PR #253` 的 latest head review threads 是否已收敛 +2. 若 PR review 已收敛,回到 `Phase 8` 主线,优先选择一个收益明确的反射收敛点继续推进 +3. 若继续文档主线,优先再扫 `docs/zh-CN/api-reference` 与教程入口页,补齐仍过时的 CQRS API / 命名空间表述 diff --git a/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md b/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md index bde7925b..dc02bae2 100644 --- a/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md +++ b/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md @@ -1,1444 +1,24 @@ # 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` 已把上下文注入窗口、瞬态实例约束与流创建/枚举取消边界移入显式 - `` -- `CqrsHandlerRegistrarTests` 已改为从 `CqrsTestRuntime.RegisterHandlers(...)` 真实入口覆盖部分类型加载失败场景,不再反射 - `RecoverLoadableTypes(...)` -- `CqrsTestRuntime` 已补齐 XML 文档,并改为按 `IIocContainer + IEnumerable + 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()` 推荐入口 -- 旧的 `RegisterMediatorBehavior()` 已降级为 `[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()` 已统一补充 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`, - 并显式指定 `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` 已在类级 `` 中明确“该类型不是线程安全的,必须由外部同步边界串行访问”的设计约束 -- `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()` / `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` 与 `PerformanceBehavior` 已迁移到 `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 Runtime,CQRS = 集成子系统” - -- 在重新评估 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 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` 修正为合法的 `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` 这类“可引用泛型定义 + 隐藏实参” - - `HiddenEnvelope` 这类“隐藏泛型定义 + 可重建实参” -- 实现过程中额外收敛了两处细节: - - 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` 负责无 pipeline 的 request handler 调用缓存 - - `RequestPipelineInvokerCache` 负责带 pipeline 的 request 调用缓存 - - 缓存键从 `(RequestType, ResponseType)` 收敛为当前 `TResponse` 层内的 `RequestType` - - `InvokeRequestHandlerAsync(...)` 与 `InvokeRequestPipelineAsync(...)` 现直接返回 `ValueTask`,不再通过 `ValueTask` 桥接 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` - - generator 可访问性判断默认分支缺少显式假设说明 - - `MetadataReferenceTestBuilder` 每次都会重建运行时元数据引用集合 - - `RunGenerator(...)` 的编译错误断言消息上下文偏少 -- 已完成对应实现: - - 在 `ClearDispatcherCaches()` 中补齐 `string` pipeline invoker cache 清理 - - 为 `CanReferenceFromGeneratedRegistry(...)` 默认分支补上“暂按可引用处理,后续可收紧”的注释 - - 用 `Lazy>` 缓存 `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` - 会出现在生成注释中,避免后续又退回“只知道走了 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` - 这类案例会生成 `ResolveReferencedAssemblyType(...)` 精确查找,而不是保留 residual 注释 + `GetInterfaces()` 补洞 -- 已删除不再保留的公开 `Mediator` 兼容层: - - `RegisterMediatorBehavior()` - - `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 + RequestPipelineInvokerCache` 收敛为 `RequestDispatchBindingCache` -- 新的 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()`”,不再把已删除的 `RegisterMediatorBehavior()` 写成仍可替换的并存入口 - - `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/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md ai-plan/public/cqrs-rewrite/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-up(RP-042) +### 阶段:active 入口归档收口(CQRS-REWRITE-RP-044) -- 建立 `CQRS-REWRITE-RP-042` 恢复点 -- 新增项目级 skill `.codex/skills/gframework-pr-review/`: - - 暗号为 `$gframework-pr-review` - - 使用 Windows Git 解析当前分支,并通过 GitHub PR API 定位当前分支对应的 PR - - 通过 GitHub issue comments / reviews / review comments API 提取 `Summary by CodeRabbit`、最新 head - commit review threads、`Failed checks` 与 CTRF 测试结果 - - 不再把重型 PR HTML 页面作为主数据源,只在调试或兼容场景下保留为兜底思路 - - 不依赖 `gh` CLI,也不要求登录态;脚本会显式绕过当前 shell 中失效的代理变量 -- 用新脚本验证了 PR `#253` 的当前状态: - - latest head commit review threads 已可直接从 API 提取;在远端最新提交未更新前,当前仍显示 4 条 open - threads,其中 2 条落在 `fetch_current_pr_review.py`、2 条落在 `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` - - PR 页面当前无 `Failed Tests`,CTRF 测试报告显示 `2103 passed / 0 failed` - - `Failed checks` 当前可稳定提取到 `Docstring Coverage` warning;该项属于 PR 级文档注释覆盖率问题,不是 FPR - skill 解析链路故障 -- 已按 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` +- 已将截至 `RP-043` 的详细实现历史、验证记录与阶段性 trace 迁入主题内归档 +- active trace 现在只保留当前恢复点与下一步,避免默认 boot 入口继续读取 1400+ 行已完成历史 +- 当前功能主线保持不变: + - 先复核 `PR #253` 的 latest head review threads 是否已收敛 + - 再继续 `Phase 8` 的 generator / dispatch / package 收口工作 -### 验证 +### Archive Context -- `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` - - 结果:通过 - - 备注:成功通过 API-first 路径解析当前 PR 元数据、latest head commit review threads、`Docstring Coverage` - warning 和 CTRF 测试报告 -- `python3 .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --branch feat/cqrs-optimization` - - 结果:通过 - - 备注:验证 branch -> PR 解析也已摆脱 HTML 搜索 -- `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` 命名空间残留 +- 历史跟踪归档: + - `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. 若继续沿用当前 PR 驱动修复流程,可直接用 `$gframework-pr-review` 复查后续 PR 的 CodeRabbit 评论与测试状态 -2. 若要验证本轮本地修正已经消除远端 latest head review threads,需要在提交并推送当前分支后重新执行 `$gframework-pr-review` - -## 2026-04-19 - -### 阶段:PR review 复核与 FPR 输出澄清(RP-043) - -- 建立 `CQRS-REWRITE-RP-043` 恢复点 -- 复核 PR `#253` 时确认: - - `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` 的“下次恢复建议”仍把主线写成 `Phase 7` - - 这与 tracking 顶部当前阶段 `Phase 8` 不一致,恢复时会造成执行顺序歧义 -- 进一步检查 `$gframework-pr-review` 脚本后确认: - - 该评论并非“未覆盖”,而是已出现在 latest head commit 的 open review threads 中 - - 我先前误把评论正文里可见的 `Addressed in commit ...` 文案当成了“已经本地验证修复” - - 实际脚本只会在 CodeRabbit thread 带有隐藏 marker `` 时才将状态归类为 `addressed` -- 已据此完成两项修正: - - 将 tracking 中的恢复主线更新为 `Phase 8(当前主线)` - - 调整 `fetch_current_pr_review.py` 输出:当 open thread 里出现 `Addressed in commit ...` 文案但线程并未真正 closed/addressed 时,显式提示“仍需本地验证,不要视为已解决” - -### 验证 - -- `sed -n '469,475p' ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` - - 结果:通过 - - 备注:恢复建议已切到 `Phase 8(当前主线)` -- `python3 -m py_compile .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py` - - 结果:通过 - - 备注:脚本语法正确 - -### 下一步 - -1. 推送当前分支后重新执行 `$gframework-pr-review`,确认 PR `#253` 的 latest head review threads 是否已收敛 -2. 若后续仍保留 CodeRabbit 的 `Addressed in commit ...` 文案但 thread 继续 open,优先以 latest thread 状态和本地文件事实为准,不再按文案字面判定 +1. 推送当前分支后重新执行 `$gframework-pr-review` +2. 以 latest head review thread 状态和本地文件事实为准,确认 `RP-042` / `RP-043` 修正是否真正收敛 +3. 若收敛完成,回到 `Phase 8` 主线,优先选一个明确的反射缩减点继续推进