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 边界