Merge pull request #254 from GeWuYou/feat/ai-first-config

Feat/ai first config
This commit is contained in:
gewuyou 2026-04-19 15:57:15 +08:00 committed by GitHub
commit 011cba3ce3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 3393 additions and 2247 deletions

View File

@ -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/<topic>/todos/` and `ai-plan/public/<topic>/traces/` across active
topics, and ignore `ai-plan/public/archive/` unless the user explicitly asks for historical context.
6. If `ai-plan/private/<branch-or-worktree>/` exists and is relevant, treat it as private recovery context for the
6. Treat `ai-plan/public/<topic>/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/<branch-or-worktree>/` 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/<topic>/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/<topic>/archive/` and leave archive
pointers in the active files.
- Move stage-complete artifacts into `ai-plan/public/<topic>/archive/`, and move completed topics into
`ai-plan/public/archive/<topic>/` 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.

View File

@ -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/<branch-or-worktree>/` directory exists, use it only as private context for the current worktree.
## Complexity Defaults

View File

@ -334,7 +334,8 @@ bash scripts/validate-csharp-naming.sh
entry points
- `ai-plan/public/<topic>/todos/`: repository-safe recovery documents for an active topic
- `ai-plan/public/<topic>/traces/`: repository-safe execution traces for an active topic
- `ai-plan/public/<topic>/archive/`: archived stage-level artifacts that still belong to an active topic
- `ai-plan/public/<topic>/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/<topic>/`: 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/<topic>/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/<topic>/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/<topic>/` 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

View File

@ -18,6 +18,7 @@ bootstrapping purpose.
- `public/<topic>/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/<topic>/`
- 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/<topic>/archive/`.
- Keep the active `todos/` and `traces/` entry files concise. Once a stage is complete and validated, move its detailed
history into `public/<topic>/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/<topic>/` and remove it from the
shared startup index.

View File

@ -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`

View File

@ -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<TKey, TValue>``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<TProperty>` 现在会跳过运行时空 key并在生成代码 XML 注释中说明这是为了避免 `Lazy<T>` 因格式错误配置而永久缓存异常
- 收紧生成器内部的索引查询空值守卫分类逻辑:不再依赖“只有 `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-09bootstrap 与 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-09schema 校验对齐修正)
- `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<YamlConfigReferenceUsage>`
- `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`

View File

@ -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`
- 如果切回工具体验,应聚焦复杂对象数组表单与批量编辑,而不是回头扩大运行时语义。

View File

@ -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

View File

@ -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 深挖

View File

@ -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. 若不满足,直接回退到下一批收益更明确的共享关键字评估

View File

@ -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/<topic>/todos/``ai-plan/public/<topic>/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/<topic>|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/<topic>/`,再补 `public/README.md` 映射
2. 阶段完成后优先收入主题内 `archive/`;主题整体完成后,再整目录移入 `ai-plan/public/archive/`
3. 若未来再新增 skill 或仓库规则引用 `ai-plan/`,统一按“公共索引 + 活动主题 + 归档主题 + 私有目录”扩展,不再恢复平铺结构

View File

@ -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/<topic>/` 落地后还有遗留入口未收口
- 已据此完成本轮治理补齐:
- 新增 `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/<branch-or-worktree>/` 下创建,不再向共享目录追加本地临时状态
2. 若将来需要进一步限制格式,可再为 `public/**``private/` 各自补一个模板文件,但本轮先把目录语义和安全边界固定下来
### 阶段主题分组与启动索引RP-003
- 建立 `AI-PLAN-GOV-RP-003` 恢复点
- 用户进一步指出:即使 public/private 已分层,只要多 worktree 并行,扁平的活动主题集合仍会让 `boot` 随着
`ai-plan/public` 增长而退化成大范围扫描
- 已据此完成第二轮治理:
- 将活动共享文档迁移到 `ai-plan/public/<topic>/todos/``ai-plan/public/<topic>/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/<topic>|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/`

View File

@ -14,52 +14,56 @@
## 当前恢复点
- 恢复点编号:`AI-PLAN-GOV-RP-003`
- 当前阶段:`Phase 2`
- 恢复点编号:`AI-PLAN-GOV-RP-005`
- 当前阶段:`Phase 3`
- 当前焦点:
- 已将共享恢复文档按主题迁移到 `ai-plan/public/<topic>/todos/``ai-plan/public/<topic>/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/<topic>|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/<topic>/`
3. 后续新增 topic 时,默认直接创建 `todos/``traces/``archive/`,不要再把历史阶段长期堆在 active 入口

View File

@ -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/<branch-or-worktree>/` 下创建,不再向共享目录追加本地临时状态
2. 若将来需要进一步限制格式,可再为 `public/**``private/` 各自补一个模板文件,但本轮先把目录语义和安全边界固定下来
### 阶段主题分组与启动索引RP-003
- 建立 `AI-PLAN-GOV-RP-003` 恢复点
- 用户进一步指出:即使 public/private 已分层,只要多 worktree 并行,扁平的活动主题集合仍会让 `boot` 随着
`ai-plan/public` 增长而退化成大范围扫描
- 已据此完成第二轮治理:
- 将活动共享文档迁移到 `ai-plan/public/<topic>/todos/``ai-plan/public/<topic>/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/<topic>|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/` 目录

View File

@ -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<HiddenType>``HiddenEnvelope<string>` 这类闭包类型现在也能按生成期已知结构直接重建,进一步贴近 `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<object?>` 缓存改为按 `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<TBehavior>()``ContextAwareMediator*Extensions``MediatorCoroutineExtensions`,不再继续维护旧命名兼容表面
- 已将 generator 对“外部程序集不可直引 protected nested 类型”的 residual contract 从 `RegisterRemainingReflectedHandlerInterfaces(...)` 补洞推进到“按程序集名 + metadata name 定向 lookup”`IRequestHandler<Dep.VisibilityScope.ProtectedRequest, Dep.VisibilityScope.ProtectedResponse[]>` 这类场景现在走精确 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 2CQRS 基础重建
- [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<T1, T2, ...>` 的递归 `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<TResponse>(...)` 现在直接复用 `ValueTask<TResponse>` 委托
- 不再通过 `object` 桥接承接 request 结果,减少 value-type 响应装箱
- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已继续把 notification/request/stream 的分散 service-type / invoker 缓存收敛为聚合 dispatch binding cache
- `NotificationDispatchBindings` / `RequestDispatchBindingCache<TResponse>` / `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<TBehavior>()`
- `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<TResponse>` / `INotification` / `IStreamRequest<TResponse>`
- `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<TCommand, TResponse>` 已把上下文注入窗口、瞬态实例复用约束和流创建/枚举取消边界写入显式
`<remarks>`,避免公共流式命令处理器基类的生命周期约束只停留在摘要里。
- `CqrsHandlerRegistrarTests` 已改为通过 `CqrsTestRuntime.RegisterHandlers(...)` 真实入口验证部分加载失败恢复路径,并同时断言
“剩余 handler 仍被注册 + warning 已记录”。
- `CqrsTestRuntime` 已补齐 XML 文档,并改为按 `IIocContainer + IEnumerable<Assembly> + ILogger` 精确绑定
`RegisterHandlers(...)`,避免未来新增同名重载后出现运行时歧义。
- `MediatorCoroutineExtensions` 已改为直接走 `IArchitectureContext.SendAsync(...)` 内建 CQRS 入口,不再从容器解析
`IMediator`;该类型名仅作为历史兼容命名保留。
- `RegisterCqrsPipelineBehavior<TBehavior>()` 已作为新的公开推荐入口加入 `IArchitecture``IIocContainer`
`Architecture``MicrosoftDiContainer`;旧的 `RegisterMediatorBehavior<TBehavior>()` 改为显式兼容转发。
- `ContextAwareCqrsExtensions``ContextAwareCqrsCommandExtensions``ContextAwareCqrsQueryExtensions`
`CqrsCoroutineExtensions` 已作为新的中性命名扩展入口加入;旧的 `ContextAwareMediator*Extensions`
`MediatorCoroutineExtensions` 保留为 `[Obsolete]` 兼容包装层。
- `RegisterMediatorBehavior<TBehavior>()``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<TCommand, TResponse>` 的上下文可用窗口、实例复用约束与取消边界需要放入显式
`<remarks>`,避免公共基类被误用
- `CqrsHandlerRegistrar` 的部分加载失败回归必须从 `RegisterHandlers` 公开测试入口观察,而不是反射私有
`RecoverLoadableTypes(...)`
- `CqrsTestRuntime` 反射绑定不能只按名称解析 `RegisterHandlers`,否则新增重载后会出现不确定行为
- 生产代码与主文档中保留的 `Mediator` 标识目前只剩历史兼容命名,不再代表外部包依赖,可作为下一阶段
API 命名统一任务单独处理
## 当前风险
- `GFramework` 仓库存在与本任务无关的既有改动,提交时必须避免覆盖
- `CoreGrid-Migration` 是 worktreeWSL 下原生 `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<ICqrsRuntime>`,并显式使用 `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` 已在类级 `<remarks>` 中明确“该类型不是线程安全的,必须由外部同步边界串行访问”的设计约束
- `GFramework.Cqrs.Abstractions/Cqrs/ICqrsRuntime.cs` 已补齐三个公开方法的 XML 契约:
- `null` 参数对应 `ArgumentNullException`
- handler 缺失或上下文不满足 `IArchitectureContext` 注入前提时对应 `InvalidOperationException`
- `CreateStream(...)` 额外说明了上下文需覆盖整个异步枚举生命周期
- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已补充线性去重扫描的性能特征注释,并记录未来若出现大服务集合热点,可在更高层批处理中引入 `HashSet<(Type, Type)>`
- `GFramework.Core/Ioc/MicrosoftDiContainer.cs` 已将未冻结态 `GetAll<T>()` / `GetAll(Type)` 的引用去重逻辑收窄为:
- 仍会折叠“不同 `ServiceType` 指向同一实例”的兼容别名重复
- 不再吞掉“同一 `ServiceType` 对同一实例的重复显式注册”
- 当同一实例同时暴露多个服务类型时,优先保留请求类型对应分组,否则保留注册次数最多且首次出现最早的分组,以维持确定性
- `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 已新增两个回归测试,分别锁定泛型与非泛型 `GetAll` 在未冻结态下的上述语义
### 阶段RP-020 验证
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests"`
- 结果:通过
- 明细:`40` 个测试全部通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests"`
- 结果:通过
- 明细:`40` 个测试全部通过
### 下一步
1. 继续评估 `CqrsHandlerRegistrar` / generated registry 的命中率提升空间,优先减少需要 `GetTypes()` 全量扫描的回退场景
2. 若后续 `MicrosoftDiContainer` 继续保留“未冻结时支持按可赋值类型观察实例”的宽语义,可考虑为 mixed alias + duplicate registration 组合场景再补一条更细的回归测试
## 2026-04-16 补充记录RP-033
### 阶段review 小修(缓存清理、诊断信息与测试辅助性能)
- 建立 `CQRS-REWRITE-RP-033` 恢复点
- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs``ClearDispatcherCaches()` 已补齐
`RequestPipelineInvokerCache<string>.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`,若后续要继续消除此项,需要单独规划并提交文档注释覆盖率改进

File diff suppressed because it is too large Load Diff

View File

@ -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<HiddenType>``HiddenEnvelope<string>` 这类闭包类型现在也能按生成期已知结构直接重建,进一步贴近 `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<object?>` 缓存改为按 `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<TBehavior>()``ContextAwareMediator*Extensions``MediatorCoroutineExtensions`,不再继续维护旧命名兼容表面
- 已将 generator 对“外部程序集不可直引 protected nested 类型”的 residual contract 从 `RegisterRemainingReflectedHandlerInterfaces(...)` 补洞推进到“按程序集名 + metadata name 定向 lookup”`IRequestHandler<Dep.VisibilityScope.ProtectedRequest, Dep.VisibilityScope.ProtectedResponse[]>` 这类场景现在走精确 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 2CQRS 基础重建
- [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<T1, T2, ...>` 的递归 `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<TResponse>(...)` 现在直接复用 `ValueTask<TResponse>` 委托
- 不再通过 `object` 桥接承接 request 结果,减少 value-type 响应装箱
- `GFramework.Cqrs/Internal/CqrsDispatcher.cs` 已继续把 notification/request/stream 的分散 service-type / invoker 缓存收敛为聚合 dispatch binding cache
- `NotificationDispatchBindings` / `RequestDispatchBindingCache<TResponse>` / `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<TBehavior>()`
- `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<TResponse>` / `INotification` / `IStreamRequest<TResponse>`
- `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<TCommand, TResponse>` 已把上下文注入窗口、瞬态实例复用约束和流创建/枚举取消边界写入显式
`<remarks>`,避免公共流式命令处理器基类的生命周期约束只停留在摘要里。
- `CqrsHandlerRegistrarTests` 已改为通过 `CqrsTestRuntime.RegisterHandlers(...)` 真实入口验证部分加载失败恢复路径,并同时断言
“剩余 handler 仍被注册 + warning 已记录”。
- `CqrsTestRuntime` 已补齐 XML 文档,并改为按 `IIocContainer + IEnumerable<Assembly> + ILogger` 精确绑定
`RegisterHandlers(...)`,避免未来新增同名重载后出现运行时歧义。
- `MediatorCoroutineExtensions` 已改为直接走 `IArchitectureContext.SendAsync(...)` 内建 CQRS 入口,不再从容器解析
`IMediator`;该类型名仅作为历史兼容命名保留。
- `RegisterCqrsPipelineBehavior<TBehavior>()` 已作为新的公开推荐入口加入 `IArchitecture``IIocContainer`
`Architecture``MicrosoftDiContainer`;旧的 `RegisterMediatorBehavior<TBehavior>()` 改为显式兼容转发。
- `ContextAwareCqrsExtensions``ContextAwareCqrsCommandExtensions``ContextAwareCqrsQueryExtensions`
`CqrsCoroutineExtensions` 已作为新的中性命名扩展入口加入;旧的 `ContextAwareMediator*Extensions`
`MediatorCoroutineExtensions` 保留为 `[Obsolete]` 兼容包装层。
- `RegisterMediatorBehavior<TBehavior>()``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<TCommand, TResponse>` 的上下文可用窗口、实例复用约束与取消边界需要放入显式
`<remarks>`,避免公共基类被误用
- `CqrsHandlerRegistrar` 的部分加载失败回归必须从 `RegisterHandlers` 公开测试入口观察,而不是反射私有
`RecoverLoadableTypes(...)`
- `CqrsTestRuntime` 反射绑定不能只按名称解析 `RegisterHandlers`,否则新增重载后会出现不确定行为
- 生产代码与主文档中保留的 `Mediator` 标识目前只剩历史兼容命名,不再代表外部包依赖,可作为下一阶段
API 命名统一任务单独处理
- dispatch/invoker 反射占比继续下降
- package / facade / 兼容层继续收口
## 当前风险
- `GFramework` 仓库存在与本任务无关的既有改动,提交时必须避免覆盖
- `CoreGrid-Migration` 是 worktreeWSL 下原生 `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<ICqrsRuntime>`,并显式使用 `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` 已在类级 `<remarks>` 中明确“该类型不是线程安全的,必须由外部同步边界串行访问”的设计约束
- `GFramework.Cqrs.Abstractions/Cqrs/ICqrsRuntime.cs` 已补齐三个公开方法的 XML 契约:
- `null` 参数对应 `ArgumentNullException`
- handler 缺失或上下文不满足 `IArchitectureContext` 注入前提时对应 `InvalidOperationException`
- `CreateStream(...)` 额外说明了上下文需覆盖整个异步枚举生命周期
- `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 已补充线性去重扫描的性能特征注释,并记录未来若出现大服务集合热点,可在更高层批处理中引入 `HashSet<(Type, Type)>`
- `GFramework.Core/Ioc/MicrosoftDiContainer.cs` 已将未冻结态 `GetAll<T>()` / `GetAll(Type)` 的引用去重逻辑收窄为:
- 仍会折叠“不同 `ServiceType` 指向同一实例”的兼容别名重复
- 不再吞掉“同一 `ServiceType` 对同一实例的重复显式注册”
- 当同一实例同时暴露多个服务类型时,优先保留请求类型对应分组,否则保留注册次数最多且首次出现最早的分组,以维持确定性
- `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 已新增两个回归测试,分别锁定泛型与非泛型 `GetAll` 在未冻结态下的上述语义
### 阶段RP-020 验证
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests"`
- 结果:通过
- 明细:`40` 个测试全部通过
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~GFramework.Core.Tests.Ioc.MicrosoftDiContainerTests"`
- 结果:通过
- 明细:`40` 个测试全部通过
### 下一步
1. 继续评估 `CqrsHandlerRegistrar` / generated registry 的命中率提升空间,优先减少需要 `GetTypes()` 全量扫描的回退场景
2. 若后续 `MicrosoftDiContainer` 继续保留“未冻结时支持按可赋值类型观察实例”的宽语义,可考虑为 mixed alias + duplicate registration 组合场景再补一条更细的回归测试
## 2026-04-16 补充记录RP-033
### 阶段review 小修(缓存清理、诊断信息与测试辅助性能)
- 建立 `CQRS-REWRITE-RP-033` 恢复点
- `GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs``ClearDispatcherCaches()` 已补齐
`RequestPipelineInvokerCache<string>.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 / 命名空间表述