diff --git a/GFramework.Game.Abstractions/README.md b/GFramework.Game.Abstractions/README.md index 40693bc5..dbdd00c6 100644 --- a/GFramework.Game.Abstractions/README.md +++ b/GFramework.Game.Abstractions/README.md @@ -278,4 +278,4 @@ public sealed class ContinueGameCommandHandler - 最常见。公共层依赖 abstractions,应用层或引擎层依赖 runtime For configuration-specific adoption decisions, treat `GFramework.Game` and -[`docs/zh-CN/game/config-system.md`](../docs/zh-CN/game/config-system.md) as the authoritative next step. +[配置系统](../docs/zh-CN/game/config-system.md) as the authoritative next step. diff --git a/GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs b/GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs index af840c96..017c6dff 100644 --- a/GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs @@ -324,6 +324,60 @@ public sealed class YamlConfigLoaderAllOfTests }); } + /// + /// 验证运行时会显式拒绝当前共享子集尚未支持的 anyOf。 + /// + [Test] + public void LoadAsync_Should_Throw_When_Object_Schema_Declares_Unsupported_AnyOf() + { + CreateConfigFile( + "monster/slime.yaml", + BuildMonsterConfigYaml( + """ + itemCount: 3 + """)); + CreateSchemaFile( + "schemas/monster.schema.json", + BuildMonsterSchema( + DefaultRewardPropertiesJson, + """ + [ + { + "type": "object", + "required": ["itemCount"], + "properties": { + "itemCount": { "type": "integer" } + } + } + ] + """, + """ + "anyOf": [ + { + "type": "object", + "required": ["bonus"], + "properties": { + "bonus": { "type": "integer" } + } + } + ] + """)); + + var loader = CreateMonsterRewardLoader(); + var registry = CreateRegistry(); + + var exception = Assert.ThrowsAsync(() => loader.LoadAsync(registry)); + + Assert.Multiple(() => + { + Assert.That(exception, Is.Not.Null); + Assert.That(exception!.Diagnostic.FailureKind, Is.EqualTo(ConfigLoadFailureKind.SchemaUnsupported)); + Assert.That(exception.Diagnostic.DisplayPath, Is.EqualTo("reward")); + Assert.That(exception.Message, Does.Contain("unsupported combinator keyword 'anyOf'")); + Assert.That(registry.Count, Is.EqualTo(0)); + }); + } + /// /// 验证运行时接受显式声明的 additionalProperties: false, /// 因为这与当前闭合对象字段集语义保持一致。 diff --git a/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md b/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md index cbfa6585..f2bcd033 100644 --- a/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md +++ b/ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md @@ -85,21 +85,8 @@ - `2026-04-17` 之前的详细实现记录与定向验证命令已归档到历史 tracking / trace - active 跟踪文件只保留当前恢复点、当前状态和下一步,不再重复堆积已完成阶段的完整历史 -- `2026-04-20` 当前恢复点验证: - - `python3 .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --pr 262 --format json`:通过(`CodeRabbit outside-diff comments: 1 declared, 1 parsed`,`CodeRabbit nitpick comments: 2 declared, 2 parsed`) - - `bun run test`(`tools/gframework-config-tool`):通过(122 tests;包含条件分支坏形状回归) - - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGeneratorTests"`:通过 - - `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderIfThenElseTests"`:通过(8 tests;新增 `else without if` 运行时回归) - - `dotnet build GFramework.sln -c Release`:通过(存在仓库既有 analyzer warning,无新增错误) -- `2026-04-30` Tooling lane 收口验证: - - `dotnet build GFramework.sln -c Release`:通过(0 Warnings, 0 Errors;本轮仅改 ai-plan 文件,确认没有伴随未验证的代码漂移) -- `2026-04-30` Tooling / Docs reader-facing 收口: - - `git diff --check -- docs/zh-CN/game/config-tool.md docs/zh-CN/game/config-system.md tools/gframework-config-tool/README.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`:通过 - - 已补齐工具能力边界、`additionalProperties: false` / `oneOf` / `anyOf` 说明、工具与 Runtime 契约关系,以及复杂 shape 的 raw YAML 回退路径 -- `2026-04-30` Tooling parser 边界收紧: - - `bun run test`(`tools/gframework-config-tool`):通过(4 test files) - - 已让工具侧对 `additionalProperties` 的共享边界与 Runtime / Generator 对齐,只接受 `additionalProperties: false` - - 已让数组 `items` / `contains` 子 schema 必须显式声明 object-shaped 且带 `type`,避免 tuple-array 或缺失类型的坏形状被工具侧宽松吞掉 +- 最近验证摘要:`2026-04-30` 已完成 Tooling / Docs reader-facing 收口与工具 parser 边界收紧,详细命令、批次背景与验证结果保留在 trace 的 `2026-04-30` 分阶段记录中 +- PR review 跟进指针:当前分支的 latest review follow-up 与后续本地核验结论以 `ai-first-config-system-trace.md` 为准,active tracking 不再重复展开逐条命令历史 ## 下一步 diff --git a/ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md b/ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md index 7c7a1cb4..1b6139f3 100644 --- a/ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md +++ b/ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md @@ -136,8 +136,6 @@ 2. 不再重复评估 `oneOf` / `anyOf` 的 object-focused 子集,除非未来主线明确接受联合形状生成 3. 若后续关键字需要新诊断编号或文档边界说明,继续保持 Runtime / Generator / Tooling 同步收口 -## 2026-04-30 - ### 阶段:Tooling lane 收口整理(AI-FIRST-CONFIG-RP-003) - 已把 Tooling / Docs 后续动作从 active 入口的主线叙述中剥离,改成 backlog 文件里的非阻塞并行 lane @@ -165,8 +163,6 @@ 2. 若另开 Tooling / Docs batch,先读取 `ai-first-config-system-csharp-experience-next.md` 的并行 lane,再把结果摘要写回 active tracking / trace 3. 继续保持 active 入口精简,不在默认恢复文件中追加 UI 细节、治理台账或面向读者的文档草稿 -## 2026-04-30 - ### 阶段:Tooling / Docs reader-facing 边界补齐(AI-FIRST-CONFIG-RP-003) - 已在 `config-tool.md`、`config-system.md` 和 `tools/gframework-config-tool/README.md` 明确 reader-facing 能力边界 @@ -190,8 +186,6 @@ 1. Tooling / Docs 后续若继续推进,优先补真实采用示例,而不是重复扩写边界清单 2. 主线代码批次继续以 Runtime / Generator / Tooling 三端共享关键字收口为中心 -## 2026-04-30 - ### 阶段:Tooling parser 坏形状拒绝收紧(AI-FIRST-CONFIG-RP-003) - 已在 `tools/gframework-config-tool/src/configValidation.js` 收紧工具侧 schema parser 边界