From 0ad2ed17617b9ee923859db6c7300ae71b3bfbe0 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Wed, 29 Apr 2026 10:27:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(game):=20=E4=BF=AE=E5=A4=8D=E7=A9=BA?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E9=85=8D=E7=BD=AE=E6=AF=94=E8=BE=83=E9=94=AE?= =?UTF-8?q?=E5=B9=B6=E5=BD=92=E6=A1=A3=20warning=20reduction=20=E4=B8=BB?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 YamlConfigAllowedValue 与 YamlConfigConstantValue 对空对象 const 或 enum 比较键的误判,同时继续拒绝非空纯空白输入 - 补充 YamlConfigModelContractTests 对空比较键与纯空白比较键的回归覆盖,并验证空对象 const 场景 - 更新 ai-plan 公共索引并归档 analyzer-warning-reduction 主题,保留最终 PR review 结论与验证记录 --- .../Config/YamlConfigModelContractTests.cs | 22 ++++++++++ .../Config/YamlConfigAllowedValue.cs | 9 +++- .../Config/YamlConfigConstantValue.cs | 9 +++- ai-plan/public/README.md | 11 ++--- ...nalyzer-warning-reduction-history-rp001.md | 0 ...r-warning-reduction-history-rp002-rp041.md | 0 ...r-warning-reduction-history-rp042-rp048.md | 0 ...r-warning-reduction-history-rp074-rp078.md | 0 ...nalyzer-warning-reduction-history-rp001.md | 0 ...r-warning-reduction-history-rp002-rp041.md | 0 ...r-warning-reduction-history-rp042-rp048.md | 0 ...r-warning-reduction-history-rp062-rp071.md | 0 ...r-warning-reduction-history-rp073-rp078.md | 0 ...r-warning-reduction-history-rp083-rp088.md | 0 .../analyzer-warning-reduction-tracking.md | 42 +++++++++---------- .../analyzer-warning-reduction-trace.md | 29 +++++++++++++ 16 files changed, 89 insertions(+), 33 deletions(-) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp001.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp002-rp041.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp042-rp048.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp074-rp078.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp001.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp002-rp041.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp042-rp048.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp062-rp071.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp073-rp078.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp083-rp088.md (100%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md (50%) rename ai-plan/public/{ => archive}/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md (85%) diff --git a/GFramework.Game.Tests/Config/YamlConfigModelContractTests.cs b/GFramework.Game.Tests/Config/YamlConfigModelContractTests.cs index 9839b853..8abea9d6 100644 --- a/GFramework.Game.Tests/Config/YamlConfigModelContractTests.cs +++ b/GFramework.Game.Tests/Config/YamlConfigModelContractTests.cs @@ -19,6 +19,17 @@ public sealed class YamlConfigModelContractTests Assert.Throws(() => new YamlConfigAllowedValue(" ", "visible")); } + /// + /// 验证枚举允许值模型会保留空对象等合法结构产生的空比较键。 + /// + [Test] + public void AllowedValue_Should_Accept_Empty_Comparable_Value() + { + var allowedValue = new YamlConfigAllowedValue(string.Empty, "{}"); + + Assert.That(allowedValue.ComparableValue, Is.Empty); + } + /// /// 验证常量约束模型会拒绝空白比较键。 /// @@ -28,6 +39,17 @@ public sealed class YamlConfigModelContractTests Assert.Throws(() => new YamlConfigConstantValue(" ", "\"visible\"")); } + /// + /// 验证常量约束模型会保留空对象等合法结构产生的空比较键。 + /// + [Test] + public void ConstantValue_Should_Accept_Empty_Comparable_Value() + { + var constantValue = new YamlConfigConstantValue(string.Empty, "{}"); + + Assert.That(constantValue.ComparableValue, Is.Empty); + } + /// /// 验证 contains 约束模型会在构造阶段拦截负值和反向区间。 /// diff --git a/GFramework.Game/Config/YamlConfigAllowedValue.cs b/GFramework.Game/Config/YamlConfigAllowedValue.cs index bbbfd99b..504d285b 100644 --- a/GFramework.Game/Config/YamlConfigAllowedValue.cs +++ b/GFramework.Game/Config/YamlConfigAllowedValue.cs @@ -12,11 +12,16 @@ internal sealed class YamlConfigAllowedValue /// 用于与 YAML 节点比较的稳定键。 /// 用于诊断输出的原始 JSON 文本。 /// 时抛出。 - /// 为空或仅包含空白字符时抛出。 + /// 虽然非空但仅包含空白字符,或 为空或仅包含空白字符时抛出。 public YamlConfigAllowedValue(string comparableValue, string displayValue) { - ArgumentException.ThrowIfNullOrWhiteSpace(comparableValue); + ArgumentNullException.ThrowIfNull(comparableValue); ArgumentException.ThrowIfNullOrWhiteSpace(displayValue); + if (comparableValue.Length > 0 && + string.IsNullOrWhiteSpace(comparableValue)) + { + throw new ArgumentException("The value cannot be composed entirely of whitespace.", nameof(comparableValue)); + } ComparableValue = comparableValue; DisplayValue = displayValue; diff --git a/GFramework.Game/Config/YamlConfigConstantValue.cs b/GFramework.Game/Config/YamlConfigConstantValue.cs index b40a1e7a..0156a710 100644 --- a/GFramework.Game/Config/YamlConfigConstantValue.cs +++ b/GFramework.Game/Config/YamlConfigConstantValue.cs @@ -12,11 +12,16 @@ internal sealed class YamlConfigConstantValue /// 用于与 YAML 节点比较的稳定键。 /// 用于诊断输出的原始常量文本。 /// 时抛出。 - /// 为空或仅包含空白字符时抛出。 + /// 虽然非空但仅包含空白字符,或 为空或仅包含空白字符时抛出。 public YamlConfigConstantValue(string comparableValue, string displayValue) { - ArgumentException.ThrowIfNullOrWhiteSpace(comparableValue); + ArgumentNullException.ThrowIfNull(comparableValue); ArgumentException.ThrowIfNullOrWhiteSpace(displayValue); + if (comparableValue.Length > 0 && + string.IsNullOrWhiteSpace(comparableValue)) + { + throw new ArgumentException("The value cannot be composed entirely of whitespace.", nameof(comparableValue)); + } ComparableValue = comparableValue; DisplayValue = displayValue; diff --git a/ai-plan/public/README.md b/ai-plan/public/README.md index 9ead4d21..c77c8a14 100644 --- a/ai-plan/public/README.md +++ b/ai-plan/public/README.md @@ -12,11 +12,6 @@ help the current worktree land on the right recovery documents without scanning ## Active Topics -- `analyzer-warning-reduction` - - Purpose: track the analyzer warning reduction branch, including the current recovery point, remaining warning - hotspots, and the next safe warning-reduction slice. - - Tracking: `ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md` - - Trace: `ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md` - `ai-plan-governance` - 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` @@ -50,9 +45,6 @@ help the current worktree land on the right recovery documents without scanning ## Worktree To Active Topic Map -- Branch: `fix/analyzer-warning-reduction-batch` - - Worktree hint: `GFramework-analyzer` - - Priority 1: `analyzer-warning-reduction` - Branch: `feat/ai-first-config` - Worktree hint: `GFramework-Ai-First-Config` - Priority 1: `ai-first-config-system` @@ -75,6 +67,9 @@ help the current worktree land on the right recovery documents without scanning - Priority 1: `documentation-full-coverage-governance` ## Archived Topics +- `analyzer-warning-reduction` + - Archive root: `ai-plan/public/archive/analyzer-warning-reduction/` + - Note: 长期 warning-reduction 分支已收尾;PR #301 的最终 review follow-up 已本地闭环,后续仅作为历史恢复材料保留。 - `cqrs-cache-docs-hardening` - Archive root: `ai-plan/public/archive/cqrs-cache-docs-hardening/` - Note: archived topics stay outside the default `boot` context until a user explicitly requests historical review. diff --git a/ai-plan/public/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp001.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp001.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp001.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp001.md diff --git a/ai-plan/public/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp002-rp041.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp002-rp041.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp002-rp041.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp002-rp041.md diff --git a/ai-plan/public/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp042-rp048.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp042-rp048.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp042-rp048.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp042-rp048.md diff --git a/ai-plan/public/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp074-rp078.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp074-rp078.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp074-rp078.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/todos/analyzer-warning-reduction-history-rp074-rp078.md diff --git a/ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp001.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp001.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp001.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp001.md diff --git a/ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp002-rp041.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp002-rp041.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp002-rp041.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp002-rp041.md diff --git a/ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp042-rp048.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp042-rp048.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp042-rp048.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp042-rp048.md diff --git a/ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp062-rp071.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp062-rp071.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp062-rp071.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp062-rp071.md diff --git a/ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp073-rp078.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp073-rp078.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp073-rp078.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp073-rp078.md diff --git a/ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp083-rp088.md b/ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp083-rp088.md similarity index 100% rename from ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp083-rp088.md rename to ai-plan/public/archive/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp083-rp088.md diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/archive/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md similarity index 50% rename from ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md rename to ai-plan/public/archive/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index 66d65d6e..e073676d 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/archive/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -6,13 +6,12 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-095` -- 当前阶段:`Phase 95` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-096` +- 当前阶段:`Completed` - 当前焦点: - - `2026-04-29` 继续处理 `PR #301` 的 latest-head review threads,只修复当前工作树上仍然成立的问题 - - 已修复 `MediatorArchitectureIntegrationTests` 中仍然成立的并发与阻塞问题:移除冗余分支、把 `Task.Delay().Wait()` 改为 `await`、把静态缓存换成 `ConcurrentDictionary`、并把共享计数更新改成原子操作 - - 已补 `GFramework.Game/Config` 运行时 schema 模型的构造期契约校验与 `` XML 文档,并新增 `YamlConfigModelContractTests` 锁定这些无效状态保护 - - 本轮明确暂不接受两个误报方向:`YamlConfigReferenceUsage.DisplayPath` 别名删除建议,以及两个本地枚举补 `[GenerateEnumExtensions]` 的泛化建议 + - `2026-04-29` 已完成 `PR #301` latest-head review threads 的最终本地复核,并修复仍然成立的空对象 `const` 比较键回归 + - 当前 topic 已达到归档条件:长期 warning-reduction 分支的实现、PR review follow-up 与最小验证均已完成 + - 当前目录已迁入 `ai-plan/public/archive/analyzer-warning-reduction/`,后续仅保留历史恢复价值 ## 当前活跃事实 @@ -20,26 +19,27 @@ - 当前直接验证结果: - `dotnet build GFramework.Game/GFramework.Game.csproj -c Release -clp:Summary` - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigSchemaValidatorTests|FullyQualifiedName~YamlConfigModelContractTests"` + - `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~LoadAsync_Should_Accept_Empty_Object_Schema_Const|FullyQualifiedName~YamlConfigModelContractTests"` - 最新结果:成功;`10` 通过、`0` 失败 - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~MediatorArchitectureIntegrationTests|FullyQualifiedName~MediatorAdvancedFeaturesTests"` - 最新结果:成功;`25` 通过、`0` 失败 + - `dotnet format GFramework.sln --verify-no-changes --include GFramework.Game/Config/YamlConfigAllowedValue.cs GFramework.Game/Config/YamlConfigConstantValue.cs GFramework.Game.Tests/Config/YamlConfigModelContractTests.cs` + - 最新结果:成功;当前修复范围内无格式漂移 - `git diff --check` - 最新结果:成功;无新增 whitespace / conflict-marker 问题 - 当前批次摘要: - - 当前切片直接修改 `12` 个已有文件,并新增 `YamlConfigModelContractTests.cs` 作为模型契约回归覆盖 - - 本轮修复集中在 `GFramework.Cqrs.Tests` 与 `GFramework.Game` 两个最新 review thread 热区,没有再扩写回 warning-batch 的多文件并发清理范围 + - 当前最终收尾切片直接修改 `3` 个已有文件,不再扩写 warning-batch 的多文件清理范围 + - 这次收尾把 `YamlConfigAllowedValue` / `YamlConfigConstantValue` 的 `comparableValue` 契约收窄为“允许空字符串,但拒绝非空纯空白”,恢复空对象 `const` / `enum` 的合法比较键语义 - PR review triage 结论: - - 接受:并发共享状态、阻塞等待、无效约束状态、缺失 `` 文档 - - 延后:`DisplayPath` 诊断别名删除建议 - - 驳回:两个枚举补 `[GenerateEnumExtensions]` 的泛化建议 + - 接受并完成:并发共享状态、阻塞等待、无效约束状态、缺失 `` 文档、空对象比较键回归 + - 归档前剩余 open threads 只包含两类:尚未推送折叠的 stale 线程,以及已明确延后 / 驳回的建议(`DisplayPath` 与枚举特性泛化) ## 当前风险 -- 当前 GitHub PR 仍会保留尚未推送折叠的 open threads,以及被明确延后 / 驳回的机器人建议。 - - 缓解措施:提交并推送后重新执行 `$gframework-pr-review`,只保留仍有真实依据的剩余线程。 -- 本轮未重跑仓库根 `dotnet clean` + `dotnet build`,因此 RP-094 的仓库级 warning 真值不能直接外推到这次 PR-review follow-up 之后。 - - 缓解措施:若下一轮重新回到 analyzer warning reduction 主线,先按仓库规则重新采样仓库根 clean build。 +- 当前 GitHub PR 在本地提交并推送前仍可能显示旧的 open threads。 + - 缓解措施:以本文件中的本地验证结果为 archive 真值;若未来需要复查 PR 页面,应从 archive 恢复而不是重新激活 topic。 +- 本轮仅对 `GFramework.Game` 收尾回归做了受影响模块验证,没有重新建立新的仓库根 clean build 基线。 + - 缓解措施:后续若有新的 warning-reduction 任务,应创建新 topic,并重新执行仓库根 `dotnet clean` + `dotnet build` 采样。 ## 活跃文档 @@ -60,13 +60,13 @@ ## 验证说明 - 权威验证结果统一维护在“当前活跃事实”。 -- `GFramework.Game` 当前 Release 构建已清零,并通过 config 定向测试。 +- `GFramework.Game` 当前 Release 构建已清零,并通过空对象 `const` 回归与模型契约定向测试。 - `GFramework.Cqrs.Tests` 当前 PR-review follow-up 定向测试通过,说明并发/缓存测试辅助实现的行为修正没有破坏现有集成断言。 +- `dotnet format --verify-no-changes` 已确认当前收尾改动未引入新的格式化偏差。 - `git diff --check` 结果为空,说明本轮新增改动没有引入新的尾随空格或冲突标记。 -- 本轮以受影响项目的 Release build / tests 为完成条件;若下轮恢复 warning reduction 仓库级真值,需要重新执行仓库根 `dotnet clean` + `dotnet build`。 +- 本 topic 已进入 archive;若未来重启 warning reduction,应以新 topic 和新的仓库级 clean build 基线继续。 ## 下一步建议 -1. 提交当前 PR-review follow-up 与本轮 `ai-plan` 同步。 -2. 推送分支后重新执行 `$gframework-pr-review`,确认剩余 open threads 是否只剩延后 / 误报项。 -3. 若下一轮恢复 warning reduction 主线,先重新执行仓库根 `dotnet clean` + `dotnet build` 建立新的权威基线。 +1. 保持当前 archive 状态,不要再把该 topic 作为默认 boot 入口。 +2. 若未来需要继续 warning reduction,创建新的 active topic,并重新建立仓库根 clean build 真值。 diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/archive/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md similarity index 85% rename from ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md rename to ai-plan/public/archive/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index 26dab494..233ab639 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/archive/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -1,5 +1,34 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-29 — RP-096 + +### 阶段:完成 `PR #301` 最终收尾并归档长期 warning-reduction 主题 + +- 触发背景: + - 用户要求先用 `$gframework-pr-review` 解决当前 PR review 的剩余问题,然后把整个长期分支主题归档 +- 本轮 triage 结论: + - `MediatorArchitectureIntegrationTests` 并发更新、`YamlConfigConditionalSchemas` / `YamlConfigStringFormatConstraint` 的 `` 文档,以及两个枚举的 `[GenerateEnumExtensions]` 在当前工作树上均已存在,对应 open threads 判定为 stale + - `YamlConfigReferenceUsage.DisplayPath` 删除建议继续判定为不成立,因为 loader 诊断、引用索引和测试断言仍把它作为稳定语义标签使用 + - `LoadAsync_Should_Accept_Empty_Object_Schema_Const` 失败仍然成立:上轮把 `YamlConfigAllowedValue` / `YamlConfigConstantValue` 的 `comparableValue` 收紧成 `ThrowIfNullOrWhiteSpace(...)` 后,误伤了空对象常量的合法空比较键 +- 主线程实施: + - 将 `YamlConfigAllowedValue` 与 `YamlConfigConstantValue` 的比较键契约调整为: + - 允许 `string.Empty` + - 继续拒绝非空纯空白字符串 + - 保留 `displayValue` 的非空白要求 + - 扩充 `YamlConfigModelContractTests`,新增空比较键的正向覆盖,同时保留纯空白比较键的回归保护 +- 验证里程碑: + - `dotnet build GFramework.Game/GFramework.Game.csproj -c Release` + - 结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~LoadAsync_Should_Accept_Empty_Object_Schema_Const|FullyQualifiedName~YamlConfigModelContractTests"` + - 结果:成功;`10` 通过、`0` 失败 + - `dotnet format GFramework.sln --verify-no-changes --include GFramework.Game/Config/YamlConfigAllowedValue.cs GFramework.Game/Config/YamlConfigConstantValue.cs GFramework.Game.Tests/Config/YamlConfigModelContractTests.cs` + - 结果:成功 + - `git diff --check` + - 结果:成功;无新增 whitespace / conflict-marker 问题 +- 归档结论: + - `analyzer-warning-reduction` 当前 topic 已满足归档条件:长期 warning-reduction 主线已收尾,PR #301 的本地 follow-up 闭环完成 + - 整个 topic 目录已迁入 `ai-plan/public/archive/analyzer-warning-reduction/`,不再作为 active 默认入口 + ## 2026-04-29 — RP-095 ### 阶段:复核 `PR #301` latest-head review threads,并只修复当前工作树上仍然成立的问题