fix(pr-review): 修复当前评审中仍然成立的问题

- 修复 Mediator 集成测试中的阻塞等待、缓存竞态与共享状态原子性问题

- 补充 YamlConfig 运行时模型的构造期约束与 exception XML 文档

- 新增 模型契约回归测试并更新 analyzer warning reduction 恢复文档
This commit is contained in:
gewuyou 2026-04-29 09:19:24 +08:00
parent 7da985947c
commit f5f2c251e5
15 changed files with 278 additions and 60 deletions

View File

@ -236,6 +236,7 @@ public class MediatorAdvancedFeaturesTests
}
}
// 这些高级特性测试需要把一组仅供当前文件使用的辅助类型共置,避免拆成多个噪声文件。
#pragma warning disable MA0048
#region Advanced Test Classes

View File

@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
@ -331,13 +332,7 @@ public class MediatorArchitectureIntegrationTests
public ValueTask<string> Handle(TestNestedRequest request, CancellationToken cancellationToken)
{
TestNestedRequestHandler2.ExecutionCount++;
if (request.Depth >= 1) // 简化条件
{
// 模拟嵌套调用
return new ValueTask<string>($"Nested execution completed at depth {request.Depth}");
}
// 模拟嵌套调用
return new ValueTask<string>($"Nested execution completed at depth {request.Depth}");
}
}
@ -391,30 +386,28 @@ public class MediatorArchitectureIntegrationTests
public sealed class TestUncachedRequestHandler : IRequestHandler<TestUncachedRequest, int>
{
public ValueTask<int> Handle(TestUncachedRequest request, CancellationToken cancellationToken)
public async ValueTask<int> Handle(TestUncachedRequest request, CancellationToken cancellationToken)
{
// 模拟一些处理时间
Task.Delay(5, cancellationToken).Wait(cancellationToken);
return new ValueTask<int>(request.Id);
await Task.Delay(5, cancellationToken).ConfigureAwait(false);
return request.Id;
}
}
public sealed class TestCachedRequestHandler : IRequestHandler<TestCachedRequest, int>
{
private static readonly Dictionary<int, int> _cache = new();
private static readonly ConcurrentDictionary<int, int> _cache = new();
public ValueTask<int> Handle(TestCachedRequest request, CancellationToken cancellationToken)
public async ValueTask<int> Handle(TestCachedRequest request, CancellationToken cancellationToken)
{
if (_cache.TryGetValue(request.Id, out var cachedValue))
{
return new ValueTask<int>(cachedValue);
return cachedValue;
}
// 模拟处理时间
Task.Delay(10, cancellationToken).Wait(cancellationToken);
var newValue = request.Id;
_cache[request.Id] = newValue;
return new ValueTask<int>(newValue);
await Task.Delay(10, cancellationToken).ConfigureAwait(false);
return _cache.GetOrAdd(request.Id, static id => id);
}
}
@ -435,7 +428,7 @@ public class MediatorArchitectureIntegrationTests
{
public ValueTask<string> Handle(TestStateModificationRequest request, CancellationToken cancellationToken)
{
request.SharedState.Counter += request.Increment;
request.SharedState.IncrementBy(request.Increment);
return new ValueTask<string>("State modified");
}
}
@ -567,7 +560,14 @@ public class MediatorArchitectureIntegrationTests
// 并发测试相关类
public class SharedState
{
public int Counter { get; set; }
private int _counter;
public int Counter => _counter;
public void IncrementBy(int increment)
{
Interlocked.Add(ref _counter, increment);
}
}
public sealed record TestConcurrentRequest : IRequest<int>

View File

@ -0,0 +1,125 @@
using System.Text.RegularExpressions;
using GFramework.Game.Config;
namespace GFramework.Game.Tests.Config;
/// <summary>
/// 验证内部 schema 运行时模型会在构造阶段拒绝无效状态,
/// 避免调用方把不一致的约束对象继续传入加载器和校验器。
/// </summary>
[TestFixture]
public sealed class YamlConfigModelContractTests
{
/// <summary>
/// 验证枚举允许值模型会拒绝空白比较键。
/// </summary>
[Test]
public void AllowedValue_Should_Reject_Whitespace_Comparable_Value()
{
Assert.Throws<ArgumentException>(() => new YamlConfigAllowedValue(" ", "visible"));
}
/// <summary>
/// 验证常量约束模型会拒绝空白比较键。
/// </summary>
[Test]
public void ConstantValue_Should_Reject_Whitespace_Comparable_Value()
{
Assert.Throws<ArgumentException>(() => new YamlConfigConstantValue(" ", "\"visible\""));
}
/// <summary>
/// 验证 contains 约束模型会在构造阶段拦截负值和反向区间。
/// </summary>
[Test]
public void ArrayContainsConstraints_Should_Reject_Invalid_Bounds()
{
var itemNode = CreateStringNode();
Assert.Multiple(() =>
{
Assert.Throws<ArgumentOutOfRangeException>(() => new YamlConfigArrayContainsConstraints(itemNode, -1, null));
Assert.Throws<ArgumentOutOfRangeException>(() => new YamlConfigArrayContainsConstraints(itemNode, null, -1));
Assert.Throws<ArgumentException>(() => new YamlConfigArrayContainsConstraints(itemNode, 3, 2));
});
}
/// <summary>
/// 验证数组约束模型会在构造阶段拦截负值和反向区间。
/// </summary>
[Test]
public void ArrayConstraints_Should_Reject_Invalid_Bounds()
{
Assert.Multiple(() =>
{
Assert.Throws<ArgumentOutOfRangeException>(() => new YamlConfigArrayConstraints(-1, null, false, null));
Assert.Throws<ArgumentOutOfRangeException>(() => new YamlConfigArrayConstraints(null, -1, false, null));
Assert.Throws<ArgumentException>(() => new YamlConfigArrayConstraints(4, 3, false, null));
});
}
/// <summary>
/// 验证对象约束模型会在构造阶段拦截负值和反向区间。
/// </summary>
[Test]
public void ObjectConstraints_Should_Reject_Invalid_Bounds()
{
Assert.Multiple(() =>
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
new YamlConfigObjectConstraints(-1, null, null, null, null, null));
Assert.Throws<ArgumentOutOfRangeException>(() =>
new YamlConfigObjectConstraints(null, -1, null, null, null, null));
Assert.Throws<ArgumentException>(() =>
new YamlConfigObjectConstraints(5, 4, null, null, null, null));
});
}
/// <summary>
/// 验证字符串约束模型要求正则原文与预编译正则成对出现。
/// </summary>
[Test]
public void StringConstraints_Should_Require_Pattern_And_Regex_To_Be_Paired()
{
Assert.Multiple(() =>
{
Assert.Throws<ArgumentException>(() =>
new YamlConfigStringConstraints(null, null, "value", null, null));
Assert.Throws<ArgumentException>(() =>
new YamlConfigStringConstraints(
null,
null,
null,
new Regex("value", RegexOptions.None, TimeSpan.FromSeconds(1)),
null));
});
}
/// <summary>
/// 验证 schema 模型会复制引用表集合,避免外部可变集合继续污染内部状态。
/// </summary>
[Test]
public void Schema_Should_Copy_Referenced_Table_Names()
{
var referencedTableNames = new List<string> { "item" };
var schema = new YamlConfigSchema("monster.schema.json", CreateStringNode(), referencedTableNames);
referencedTableNames.Add("weapon");
Assert.Multiple(() =>
{
Assert.That(schema.ReferencedTableNames, Is.EqualTo(new[] { "item" }));
Assert.That(schema.ReferencedTableNames, Is.Not.SameAs(referencedTableNames));
});
}
private static YamlConfigSchemaNode CreateStringNode()
{
return YamlConfigSchemaNode.CreateScalar(
YamlConfigSchemaPropertyType.String,
referenceTableName: null,
allowedValues: null,
constraints: null,
schemaPathHint: "tests.schema.json");
}
}

View File

@ -11,9 +11,11 @@ internal sealed class YamlConfigAllowedValue
/// </summary>
/// <param name="comparableValue">用于与 YAML 节点比较的稳定键。</param>
/// <param name="displayValue">用于诊断输出的原始 JSON 文本。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="comparableValue"/> 或 <paramref name="displayValue"/> 为 <see langword="null" /> 时抛出。</exception>
/// <exception cref="ArgumentException">当 <paramref name="comparableValue"/> 或 <paramref name="displayValue"/> 为空或仅包含空白字符时抛出。</exception>
public YamlConfigAllowedValue(string comparableValue, string displayValue)
{
ArgumentNullException.ThrowIfNull(comparableValue);
ArgumentException.ThrowIfNullOrWhiteSpace(comparableValue);
ArgumentException.ThrowIfNullOrWhiteSpace(displayValue);
ComparableValue = comparableValue;

View File

@ -13,12 +13,31 @@ internal sealed class YamlConfigArrayConstraints
/// <param name="maxItems">最大元素数量约束。</param>
/// <param name="uniqueItems">是否要求数组元素唯一。</param>
/// <param name="containsConstraints">数组 contains 约束;未声明时为空。</param>
/// <exception cref="ArgumentOutOfRangeException">当 <paramref name="minItems"/> 或 <paramref name="maxItems"/> 为负数时抛出。</exception>
/// <exception cref="ArgumentException">当 <paramref name="minItems"/> 大于 <paramref name="maxItems"/> 时抛出。</exception>
public YamlConfigArrayConstraints(
int? minItems,
int? maxItems,
bool uniqueItems,
YamlConfigArrayContainsConstraints? containsConstraints)
{
if (minItems is < 0)
{
throw new ArgumentOutOfRangeException(nameof(minItems), minItems, "minItems 不能为负数。");
}
if (maxItems is < 0)
{
throw new ArgumentOutOfRangeException(nameof(maxItems), maxItems, "maxItems 不能为负数。");
}
if (minItems.HasValue &&
maxItems.HasValue &&
minItems.Value > maxItems.Value)
{
throw new ArgumentException("minItems 不能大于 maxItems。", nameof(minItems));
}
MinItems = minItems;
MaxItems = maxItems;
UniqueItems = uniqueItems;

View File

@ -12,12 +12,31 @@ internal sealed class YamlConfigArrayContainsConstraints
/// <param name="containsNode">contains 子 schema。</param>
/// <param name="minContains">最小匹配数量;为 <see langword="null" /> 时按 JSON Schema 语义默认 1。</param>
/// <param name="maxContains">最大匹配数量。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="containsNode"/> 为 <see langword="null" /> 时抛出。</exception>
/// <exception cref="ArgumentOutOfRangeException">当 <paramref name="minContains"/> 或 <paramref name="maxContains"/> 为负数时抛出。</exception>
/// <exception cref="ArgumentException">当 <paramref name="minContains"/> 大于 <paramref name="maxContains"/> 时抛出。</exception>
public YamlConfigArrayContainsConstraints(
YamlConfigSchemaNode containsNode,
int? minContains,
int? maxContains)
{
ArgumentNullException.ThrowIfNull(containsNode);
if (minContains is < 0)
{
throw new ArgumentOutOfRangeException(nameof(minContains), minContains, "minContains 不能为负数。");
}
if (maxContains is < 0)
{
throw new ArgumentOutOfRangeException(nameof(maxContains), maxContains, "maxContains 不能为负数。");
}
if (minContains.HasValue &&
maxContains.HasValue &&
minContains.Value > maxContains.Value)
{
throw new ArgumentException("minContains 不能大于 maxContains。", nameof(minContains));
}
ContainsNode = containsNode;
MinContains = minContains;

View File

@ -12,6 +12,7 @@ internal sealed class YamlConfigConditionalSchemas
/// <param name="ifSchema">条件判断 schema。</param>
/// <param name="thenSchema">条件命中时需要满足的 schema。</param>
/// <param name="elseSchema">条件未命中时需要满足的 schema。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="ifSchema"/> 为 <see langword="null" /> 时抛出。</exception>
public YamlConfigConditionalSchemas(
YamlConfigSchemaNode ifSchema,
YamlConfigSchemaNode? thenSchema,

View File

@ -11,9 +11,11 @@ internal sealed class YamlConfigConstantValue
/// </summary>
/// <param name="comparableValue">用于与 YAML 节点比较的稳定键。</param>
/// <param name="displayValue">用于诊断输出的原始常量文本。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="comparableValue"/> 或 <paramref name="displayValue"/> 为 <see langword="null" /> 时抛出。</exception>
/// <exception cref="ArgumentException">当 <paramref name="comparableValue"/> 或 <paramref name="displayValue"/> 为空或仅包含空白字符时抛出。</exception>
public YamlConfigConstantValue(string comparableValue, string displayValue)
{
ArgumentNullException.ThrowIfNull(comparableValue);
ArgumentException.ThrowIfNullOrWhiteSpace(comparableValue);
ArgumentException.ThrowIfNullOrWhiteSpace(displayValue);
ComparableValue = comparableValue;

View File

@ -15,6 +15,8 @@ internal sealed class YamlConfigObjectConstraints
/// <param name="dependentSchemas">对象内条件 schema 约束。</param>
/// <param name="allOfSchemas">对象内组合 schema 约束。</param>
/// <param name="conditionalSchemas">对象内条件分支约束。</param>
/// <exception cref="ArgumentOutOfRangeException">当 <paramref name="minProperties"/> 或 <paramref name="maxProperties"/> 为负数时抛出。</exception>
/// <exception cref="ArgumentException">当 <paramref name="minProperties"/> 大于 <paramref name="maxProperties"/> 时抛出。</exception>
public YamlConfigObjectConstraints(
int? minProperties,
int? maxProperties,
@ -23,6 +25,23 @@ internal sealed class YamlConfigObjectConstraints
IReadOnlyList<YamlConfigSchemaNode>? allOfSchemas,
YamlConfigConditionalSchemas? conditionalSchemas)
{
if (minProperties is < 0)
{
throw new ArgumentOutOfRangeException(nameof(minProperties), minProperties, "minProperties 不能为负数。");
}
if (maxProperties is < 0)
{
throw new ArgumentOutOfRangeException(nameof(maxProperties), maxProperties, "maxProperties 不能为负数。");
}
if (minProperties.HasValue &&
maxProperties.HasValue &&
minProperties.Value > maxProperties.Value)
{
throw new ArgumentException("minProperties 不能大于 maxProperties。", nameof(minProperties));
}
MinProperties = minProperties;
MaxProperties = maxProperties;
DependentRequired = dependentRequired;

View File

@ -12,6 +12,7 @@ internal sealed class YamlConfigSchema
/// <param name="schemaPath">Schema 文件路径。</param>
/// <param name="rootNode">根节点模型。</param>
/// <param name="referencedTableNames">Schema 声明的目标引用表名称集合。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="schemaPath"/>、<paramref name="rootNode"/> 或 <paramref name="referencedTableNames"/> 为 <see langword="null" /> 时抛出。</exception>
public YamlConfigSchema(
string schemaPath,
YamlConfigSchemaNode rootNode,
@ -23,7 +24,7 @@ internal sealed class YamlConfigSchema
SchemaPath = schemaPath;
RootNode = rootNode;
ReferencedTableNames = referencedTableNames;
ReferencedTableNames = [.. referencedTableNames];
}
/// <summary>

View File

@ -280,15 +280,6 @@ internal sealed class YamlConfigSchemaNode
NegatedSchemaNode = negatedSchemaNode;
}
public static NodeValidation None { get; } = new(
referenceTableName: null,
allowedValues: null,
constraints: null,
arrayConstraints: null,
objectConstraints: null,
constantValue: null,
negatedSchemaNode: null);
public string? ReferenceTableName { get; }
public IReadOnlyCollection<YamlConfigAllowedValue>? AllowedValues { get; }

View File

@ -17,6 +17,7 @@ internal sealed class YamlConfigStringConstraints
/// <param name="pattern">正则模式约束原文。</param>
/// <param name="patternRegex">已编译的正则表达式。</param>
/// <param name="formatConstraint">字符串 format 约束。</param>
/// <exception cref="ArgumentException">当 <paramref name="pattern"/> 与 <paramref name="patternRegex"/> 未成对出现时抛出。</exception>
public YamlConfigStringConstraints(
int? minLength,
int? maxLength,
@ -24,6 +25,11 @@ internal sealed class YamlConfigStringConstraints
Regex? patternRegex,
YamlConfigStringFormatConstraint? formatConstraint)
{
if ((pattern is null) != (patternRegex is null))
{
throw new ArgumentException("pattern 与 patternRegex 必须同时为空或同时提供。", nameof(pattern));
}
MinLength = minLength;
MaxLength = maxLength;
Pattern = pattern;

View File

@ -11,6 +11,8 @@ internal sealed class YamlConfigStringFormatConstraint
/// </summary>
/// <param name="schemaName">schema 中声明的 format 名称。</param>
/// <param name="kind">归一化后的共享 format 枚举。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="schemaName"/> 为 <see langword="null" /> 时抛出。</exception>
/// <exception cref="ArgumentException">当 <paramref name="schemaName"/> 为空或仅包含空白字符时抛出。</exception>
public YamlConfigStringFormatConstraint(
string schemaName,
YamlConfigStringFormatKind kind)

View File

@ -6,43 +6,40 @@
## 当前恢复点
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-094`
- 当前阶段:`Phase 94`
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-095`
- 当前阶段:`Phase 95`
- 当前焦点:
- `2026-04-29` 继续`$gframework-batch-boot 50` 从仓库根 `dotnet clean` + `dotnet build` 的权威 warning 基线收尾 `YamlConfigSchemaValidator`
- 本轮 clean build 只剩 `15` 条 warning但实际只对应 `YamlConfigSchemaValidator.cs` 同一文件中的 `5` 个独立 `MA0051` 热点,因此不再并发派发 worker避免同文件冲突
- 已`ParseNode``ValidateObjectNode``ValidateObjectConstraints``ValidateScalarNode``ValidateNumericScalarConstraints` 按语义拆成 helper并补齐对象条件分支 helper
- 当前仓库根 clean build 已收敛到 `0` warnings、`0` errors本轮停止原因从“接近文件阈值”切换为“当前 warning hotspot 已耗尽”
- `2026-04-29` 继续处理 `PR #301` 的 latest-head review threads只修复当前工作树上仍然成立的问题
- 已修复 `MediatorArchitectureIntegrationTests` 中仍然成立的并发与阻塞问题:移除冗余分支、把 `Task.Delay().Wait()` 改为 `await`、把静态缓存换成 `ConcurrentDictionary`、并把共享计数更新改成原子操作
- 已`GFramework.Game/Config` 运行时 schema 模型的构造期契约校验与 `<exception>` XML 文档,并新增 `YamlConfigModelContractTests` 锁定这些无效状态保护
- 本轮明确暂不接受两个误报方向:`YamlConfigReferenceUsage.DisplayPath` 别名删除建议,以及两个本地枚举补 `[GenerateEnumExtensions]` 的泛化建议
## 当前活跃事实
- 当前 `origin/main` 基线提交为 `0e32dab``2026-04-28T17:15:47+08:00`)。
- 当前直接验证结果:
- `dotnet clean`
- 最新结果:成功;标准仓库根 clean 本轮可直接运行,未再命中需要额外绕开的环境噪音
- `dotnet build`
- 最新结果:成功;`0 Warning(s)``0 Error(s)`;本轮开始时同一口径 clean build 的 `15` 条 warning 已全部清零
- `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~YamlConfigLoaderTests|FullyQualifiedName~YamlConfigSchemaValidatorTests"`
- 最新结果:成功;`80` 通过、`0` 失败
- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigSchemaValidatorTests|FullyQualifiedName~YamlConfigModelContractTests"`
- 最新结果:成功;`10` 通过、`0` 失败
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~MediatorArchitectureIntegrationTests|FullyQualifiedName~MediatorAdvancedFeaturesTests"`
- 最新结果:成功;`25` 通过、`0` 失败
- `git diff --check`
- 最新结果:成功;无新增 whitespace / conflict-marker 问题
- 当前批次摘要:
- 当前分支提交后预计相对 `origin/main...HEAD` 包含 `22` 个变更文件,低于 `50` 个文件阈值
- 已完成 worker 切片:
- `ed269d4``MediatorArchitectureIntegrationTests.cs`,清理 `MA0048` / `MA0004` / `MA0016`
- `121df44``MediatorAdvancedFeaturesTests.cs`,清理 `MA0048` / `MA0004` / `MA0015`
- `9109eec``MediatorComprehensiveTests.cs`,清理 `MA0048` / `MA0004` / `MA0016` / `MA0002` / `MA0015`
- 主线程切片:`YamlConfigSchemaValidator.cs` 方法拆分,清理剩余 `MA0051`,并修正新增 helper 里的 `MA0006`
- Game 追加切片:
- `1395b84``YamlConfigSchemaValidator.ObjectKeywords.cs`,清理该文件 `MA0051`
- 已完成:将 `YamlConfigSchemaValidator.cs` 末尾 schema model 类型拆到独立同名文件,清理 `MA0048`
- 当前切片直接修改 `12` 个已有文件,并新增 `YamlConfigModelContractTests.cs` 作为模型契约回归覆盖
- 本轮修复集中在 `GFramework.Cqrs.Tests``GFramework.Game` 两个最新 review thread 热区,没有再扩写回 warning-batch 的多文件并发清理范围
- PR review triage 结论:
- 接受:并发共享状态、阻塞等待、无效约束状态、缺失 `<exception>` 文档
- 延后:`DisplayPath` 诊断别名删除建议
- 驳回:两个枚举补 `[GenerateEnumExtensions]` 的泛化建议
## 当前风险
- 当前仓库根 clean build warning 已清零,本主题暂时没有剩余源码 warning 风险。
- 缓解措施:若后续继续 batch warning 清理,先重新执行同轮 `dotnet clean` + `dotnet build` 采样,再决定是否需要分派 subagent。
- 当前 GitHub PR 仍会保留尚未推送折叠的 open threads以及被明确延后 / 驳回的机器人建议。
- 缓解措施:提交并推送后重新执行 `$gframework-pr-review`,只保留仍有真实依据的剩余线程。
- 本轮未重跑仓库根 `dotnet clean` + `dotnet build`,因此 RP-094 的仓库级 warning 真值不能直接外推到这次 PR-review follow-up 之后。
- 缓解措施:若下一轮重新回到 analyzer warning reduction 主线,先按仓库规则重新采样仓库根 clean build。
## 活跃文档
@ -63,13 +60,13 @@
## 验证说明
- 权威验证结果统一维护在“当前活跃事实”。
- `GFramework.Game` 当前 Release 构建已清零,并通过 config 定向测试;本轮标准仓库根 Debug clean build 也已清零
- 本轮标准仓库根 `dotnet clean` + `dotnet build` 已直接回到 `0 Warning(s)``0 Error(s)`,因此 warning reduction 真值已从模块级验证收口到仓库级 clean build
- `GFramework.Game` 当前 Release 构建已清零,并通过 config 定向测试。
- `GFramework.Cqrs.Tests` 当前 PR-review follow-up 定向测试通过,说明并发/缓存测试辅助实现的行为修正没有破坏现有集成断言
- `git diff --check` 结果为空,说明本轮新增改动没有引入新的尾随空格或冲突标记。
- warning reduction 的仓库级真值以同轮 `dotnet build`、定向 `dotnet test``git diff --check` 为准,并与 trace 中的验证里程碑保持一致
- 本轮以受影响项目的 Release build / tests 为完成条件;若下轮恢复 warning reduction 仓库级真值,需要重新执行仓库根 `dotnet clean` + `dotnet build`
## 下一步建议
1. 提交 `YamlConfigSchemaValidator` 收尾重构与本轮 `ai-plan` 同步。
2. 如需继续 warning reduction先从新的仓库根 clean build 重新采样是否还有新增 warning hotspot
3. 若未来 warning 再次分散到多个文件,再按 `$gframework-batch-boot 50` 规则切换回多 worker 并行模式
1. 提交当前 PR-review follow-up 与本轮 `ai-plan` 同步。
2. 推送分支后重新执行 `$gframework-pr-review`,确认剩余 open threads 是否只剩延后 / 误报项
3. 若下一轮恢复 warning reduction 主线,先重新执行仓库根 `dotnet clean` + `dotnet build` 建立新的权威基线

View File

@ -1,5 +1,38 @@
# Analyzer Warning Reduction 追踪
## 2026-04-29 — RP-095
### 阶段:复核 `PR #301` latest-head review threads并只修复当前工作树上仍然成立的问题
- 触发背景:
- 用户显式要求执行 `$gframework-pr-review`,需要把 GitHub PR review 信号与本地代码现状重新核对,而不是沿用旧的 warning-batch 假设
- 本轮 triage 结论:
- 接受并修复:
- `MediatorArchitectureIntegrationTests``Task.Delay().Wait()` 阻塞、静态 `Dictionary` 竞态、`SharedState.Counter +=` 非原子更新、以及 `TestNestedRequestHandler` 冗余分支
- `GFramework.Game/Config` 中仍然成立的模型契约缺口:空白比较键、数组 / 对象边界非法状态、`Pattern` / `PatternRegex` 不一致、`ReferencedTableNames` 未做 defensive copy、以及缺失的 `<exception>` XML 文档
- `MediatorAdvancedFeaturesTests``MA0048` 抑制缺少原因注释
- `YamlConfigSchemaNode.NodeValidation.None` 未被引用,按 review 建议删除死代码
- 明确不接受或延后:
- `YamlConfigReferenceUsage.DisplayPath`:当前在 loader 诊断与测试断言中承担独立语义标签,不作为“纯冗余 alias”删除
- `YamlConfigSchemaPropertyType` / `YamlConfigStringFormatKind``[GenerateEnumExtensions]`:仓库产品代码没有现成约定或使用面,判断为泛化误报
- 主线程实施:
- 将 CQRS 集成测试辅助处理器改为真正异步,并用 `ConcurrentDictionary` / `Interlocked` 收口并发共享状态
- 为 `YamlConfigAllowedValue``YamlConfigConstantValue``YamlConfigArrayContainsConstraints``YamlConfigArrayConstraints``YamlConfigObjectConstraints``YamlConfigStringConstraints``YamlConfigSchema``YamlConfigConditionalSchemas``YamlConfigStringFormatConstraint` 补运行时契约或 `<exception>` 注释
- 新增 `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~YamlConfigSchemaValidatorTests|FullyQualifiedName~YamlConfigModelContractTests"`
- 第一次结果:成功;`10` 通过、`0` 失败,但新增测试触发 `MA0009`
- 第二次结果:成功;`10` 通过、`0` 失败;为测试中的 `Regex` 补 timeout 后 warning 清零
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~MediatorArchitectureIntegrationTests|FullyQualifiedName~MediatorAdvancedFeaturesTests"`
- 结果:成功;`25` 通过、`0` 失败
- `git diff --check`
- 结果:成功;无新增 whitespace / conflict-marker 问题
- 下一步:
- 提交当前 PR-review follow-up 与 `ai-plan` 同步
- 推送后重新执行 `$gframework-pr-review`,确认 remaining open threads 是否已缩减到延后 / 误报项
## 2026-04-29 — RP-094
### 阶段:收尾 `YamlConfigSchemaValidator` 剩余 `MA0051` 并将仓库根 clean build 归零