From a9904a35be8c802ace21dcb575da6daf3294ad96 Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Mon, 27 Apr 2026 11:57:49 +0800
Subject: [PATCH] =?UTF-8?q?fix(warning-reduction):=20=E6=B8=85=E7=90=86?=
=?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=B8=8E=E6=B5=8B=E8=AF=95=E5=88=87=E7=89=87?=
=?UTF-8?q?=E5=91=8A=E8=AD=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 修复 YamlConfigLoader 的超长方法、依赖比较与热重载同步原语告警
- 拆分 MicrosoftDiContainerTests 与 AbstractAsyncQueryTests 的辅助类型文件以消除 MA0048
- 更新 analyzer warning reduction 跟踪文档并记录 non-incremental 构建基线变化
---
.../Ioc/AliasAwareService.cs | 8 +
GFramework.Core.Tests/Ioc/IMixedService.cs | 9 +
.../Ioc/IPrimaryAliasService.cs | 6 +
.../Ioc/IPrioritizedService.cs | 11 +
.../Ioc/ISecondaryAliasService.cs | 6 +
GFramework.Core.Tests/Ioc/IService.cs | 6 +
.../Ioc/ISharedAliasService.cs | 6 +
.../Ioc/MicrosoftDiContainerTests.cs | 72 ----
.../Ioc/NonPrioritizedService.cs | 12 +
.../Ioc/PrioritizedService.cs | 17 +
GFramework.Core.Tests/Ioc/TestService.cs | 12 +
.../Query/AbstractAsyncQueryTests.cs | 177 ----------
.../Query/TestAsyncComplexQueryV4.cs | 38 +++
.../Query/TestAsyncQueryChildV4.cs | 33 ++
.../Query/TestAsyncQueryInputV2.cs | 14 +
.../Query/TestAsyncQueryResultV2.cs | 17 +
.../Query/TestAsyncQueryV4.cs | 33 ++
.../Query/TestAsyncQueryWithExceptionV4.cs | 27 ++
.../Query/TestAsyncStringQueryV4.cs | 33 ++
GFramework.Game/Config/YamlConfigLoader.cs | 311 +++++++++++-------
.../analyzer-warning-reduction-tracking.md | 78 ++---
.../analyzer-warning-reduction-trace.md | 50 +--
22 files changed, 539 insertions(+), 437 deletions(-)
create mode 100644 GFramework.Core.Tests/Ioc/AliasAwareService.cs
create mode 100644 GFramework.Core.Tests/Ioc/IMixedService.cs
create mode 100644 GFramework.Core.Tests/Ioc/IPrimaryAliasService.cs
create mode 100644 GFramework.Core.Tests/Ioc/IPrioritizedService.cs
create mode 100644 GFramework.Core.Tests/Ioc/ISecondaryAliasService.cs
create mode 100644 GFramework.Core.Tests/Ioc/IService.cs
create mode 100644 GFramework.Core.Tests/Ioc/ISharedAliasService.cs
create mode 100644 GFramework.Core.Tests/Ioc/NonPrioritizedService.cs
create mode 100644 GFramework.Core.Tests/Ioc/PrioritizedService.cs
create mode 100644 GFramework.Core.Tests/Ioc/TestService.cs
create mode 100644 GFramework.Core.Tests/Query/TestAsyncComplexQueryV4.cs
create mode 100644 GFramework.Core.Tests/Query/TestAsyncQueryChildV4.cs
create mode 100644 GFramework.Core.Tests/Query/TestAsyncQueryInputV2.cs
create mode 100644 GFramework.Core.Tests/Query/TestAsyncQueryResultV2.cs
create mode 100644 GFramework.Core.Tests/Query/TestAsyncQueryV4.cs
create mode 100644 GFramework.Core.Tests/Query/TestAsyncQueryWithExceptionV4.cs
create mode 100644 GFramework.Core.Tests/Query/TestAsyncStringQueryV4.cs
diff --git a/GFramework.Core.Tests/Ioc/AliasAwareService.cs b/GFramework.Core.Tests/Ioc/AliasAwareService.cs
new file mode 100644
index 00000000..65de6dd1
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/AliasAwareService.cs
@@ -0,0 +1,8 @@
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 同时实现多个别名接口的测试服务。
+///
+public sealed class AliasAwareService : IPrimaryAliasService, ISecondaryAliasService
+{
+}
diff --git a/GFramework.Core.Tests/Ioc/IMixedService.cs b/GFramework.Core.Tests/Ioc/IMixedService.cs
new file mode 100644
index 00000000..0a788f9b
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/IMixedService.cs
@@ -0,0 +1,9 @@
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 混合服务接口(用于测试优先级和非优先级混合)
+///
+public interface IMixedService
+{
+ string? Name { get; set; }
+}
diff --git a/GFramework.Core.Tests/Ioc/IPrimaryAliasService.cs b/GFramework.Core.Tests/Ioc/IPrimaryAliasService.cs
new file mode 100644
index 00000000..c8937b90
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/IPrimaryAliasService.cs
@@ -0,0 +1,6 @@
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 主服务别名接口。
+///
+public interface IPrimaryAliasService : ISharedAliasService;
diff --git a/GFramework.Core.Tests/Ioc/IPrioritizedService.cs b/GFramework.Core.Tests/Ioc/IPrioritizedService.cs
new file mode 100644
index 00000000..b0998c0d
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/IPrioritizedService.cs
@@ -0,0 +1,11 @@
+using GFramework.Core.Abstractions.Bases;
+
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 优先级服务接口
+///
+public interface IPrioritizedService : IPrioritized
+{
+ string? Name { get; set; }
+}
diff --git a/GFramework.Core.Tests/Ioc/ISecondaryAliasService.cs b/GFramework.Core.Tests/Ioc/ISecondaryAliasService.cs
new file mode 100644
index 00000000..215cbeef
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/ISecondaryAliasService.cs
@@ -0,0 +1,6 @@
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 次级兼容别名接口。
+///
+public interface ISecondaryAliasService : ISharedAliasService;
diff --git a/GFramework.Core.Tests/Ioc/IService.cs b/GFramework.Core.Tests/Ioc/IService.cs
new file mode 100644
index 00000000..b9559fef
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/IService.cs
@@ -0,0 +1,6 @@
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 服务接口定义
+///
+public interface IService;
diff --git a/GFramework.Core.Tests/Ioc/ISharedAliasService.cs b/GFramework.Core.Tests/Ioc/ISharedAliasService.cs
new file mode 100644
index 00000000..e12425bc
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/ISharedAliasService.cs
@@ -0,0 +1,6 @@
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 用于验证未冻结查询路径中的服务别名去重行为。
+///
+public interface ISharedAliasService;
diff --git a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
index 19c59dcb..13fb782e 100644
--- a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
+++ b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
@@ -1,5 +1,4 @@
using System.Reflection;
-using GFramework.Core.Abstractions.Bases;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
@@ -734,74 +733,3 @@ public class MicrosoftDiContainerTests
Assert.That(((IPrioritizedService)services[1]).Priority, Is.EqualTo(30));
}
}
-
-///
-/// 服务接口定义
-///
-public interface IService;
-
-///
-/// 测试服务类,实现 IService 接口
-///
-public sealed class TestService : IService
-{
- ///
- /// 获取或设置优先级
- ///
- public int Priority { get; set; }
-}
-
-///
-/// 优先级服务接口
-///
-public interface IPrioritizedService : IPrioritized
-{
- string? Name { get; set; }
-}
-
-///
-/// 混合服务接口(用于测试优先级和非优先级混合)
-///
-public interface IMixedService
-{
- string? Name { get; set; }
-}
-
-///
-/// 用于验证未冻结查询路径中的服务别名去重行为。
-///
-public interface ISharedAliasService;
-
-///
-/// 主服务别名接口。
-///
-public interface IPrimaryAliasService : ISharedAliasService;
-
-///
-/// 次级兼容别名接口。
-///
-public interface ISecondaryAliasService : ISharedAliasService;
-
-///
-/// 同时实现多个别名接口的测试服务。
-///
-public sealed class AliasAwareService : IPrimaryAliasService, ISecondaryAliasService
-{
-}
-
-///
-/// 实现优先级的服务
-///
-public sealed class PrioritizedService : IPrioritizedService, IMixedService
-{
- public int Priority { get; set; }
- public string? Name { get; set; }
-}
-
-///
-/// 不实现优先级的服务
-///
-public sealed class NonPrioritizedService : IMixedService
-{
- public string? Name { get; set; }
-}
diff --git a/GFramework.Core.Tests/Ioc/NonPrioritizedService.cs b/GFramework.Core.Tests/Ioc/NonPrioritizedService.cs
new file mode 100644
index 00000000..7f4f87d9
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/NonPrioritizedService.cs
@@ -0,0 +1,12 @@
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 不实现优先级的服务
+///
+public sealed class NonPrioritizedService : IMixedService
+{
+ ///
+ /// 获取或设置服务名称
+ ///
+ public string? Name { get; set; }
+}
diff --git a/GFramework.Core.Tests/Ioc/PrioritizedService.cs b/GFramework.Core.Tests/Ioc/PrioritizedService.cs
new file mode 100644
index 00000000..da06740b
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/PrioritizedService.cs
@@ -0,0 +1,17 @@
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 实现优先级的服务
+///
+public sealed class PrioritizedService : IPrioritizedService, IMixedService
+{
+ ///
+ /// 获取或设置优先级
+ ///
+ public int Priority { get; set; }
+
+ ///
+ /// 获取或设置服务名称
+ ///
+ public string? Name { get; set; }
+}
diff --git a/GFramework.Core.Tests/Ioc/TestService.cs b/GFramework.Core.Tests/Ioc/TestService.cs
new file mode 100644
index 00000000..21dc1faa
--- /dev/null
+++ b/GFramework.Core.Tests/Ioc/TestService.cs
@@ -0,0 +1,12 @@
+namespace GFramework.Core.Tests.Ioc;
+
+///
+/// 测试服务类,实现 IService 接口
+///
+public sealed class TestService : IService
+{
+ ///
+ /// 获取或设置优先级
+ ///
+ public int Priority { get; set; }
+}
diff --git a/GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs b/GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs
index a7312baa..305c5f0d 100644
--- a/GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs
+++ b/GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs
@@ -6,7 +6,6 @@ using GFramework.Core.Environment;
using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Query;
-using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Tests.Query;
@@ -236,179 +235,3 @@ public class AbstractAsyncQueryTests
Assert.That(result2, Is.EqualTo(40));
}
}
-
-///
-/// 测试用异步查询输入类V2
-///
-public sealed class TestAsyncQueryInputV2 : IQueryInput
-{
- ///
- /// 获取或设置值
- ///
- public int Value { get; init; }
-}
-
-///
-/// 整数类型测试异步查询类V4,继承AbstractAsyncQuery
-///
-public sealed class TestAsyncQueryV4 : AbstractAsyncQuery
-{
- ///
- /// 初始化TestAsyncQueryV4的新实例
- ///
- /// 查询输入参数
- public TestAsyncQueryV4(TestAsyncQueryInputV2 input) : base(input)
- {
- }
-
- ///
- /// 获取查询是否已执行
- ///
- public bool Executed { get; private set; }
-
- ///
- /// 执行异步查询操作的具体实现
- ///
- /// 查询输入参数
- /// 查询结果,将输入值乘以2
- protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
- {
- Executed = true;
- return Task.FromResult(input.Value * 2);
- }
-}
-
-///
-/// 字符串类型测试异步查询类V4,继承AbstractAsyncQuery
-///
-public sealed class TestAsyncStringQueryV4 : AbstractAsyncQuery
-{
- ///
- /// 初始化TestAsyncStringQueryV4的新实例
- ///
- /// 查询输入参数
- public TestAsyncStringQueryV4(TestAsyncQueryInputV2 input) : base(input)
- {
- }
-
- ///
- /// 获取查询是否已执行
- ///
- public bool Executed { get; private set; }
-
- ///
- /// 执行异步查询操作的具体实现
- ///
- /// 查询输入参数
- /// 格式化的字符串结果
- protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
- {
- Executed = true;
- return Task.FromResult($"Value: {input.Value * 2}");
- }
-}
-
-///
-/// 复杂对象类型测试异步查询类V4,继承AbstractAsyncQuery
-///
-public sealed class TestAsyncComplexQueryV4 : AbstractAsyncQuery
-{
- ///
- /// 初始化TestAsyncComplexQueryV4的新实例
- ///
- /// 查询输入参数
- public TestAsyncComplexQueryV4(TestAsyncQueryInputV2 input) : base(input)
- {
- }
-
- ///
- /// 获取查询是否已执行
- ///
- public bool Executed { get; private set; }
-
- ///
- /// 执行异步查询操作的具体实现
- ///
- /// 查询输入参数
- /// 复杂对象查询结果
- protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
- {
- Executed = true;
- var result = new TestAsyncQueryResultV2
- {
- Value = input.Value * 2,
- DoubleValue = input.Value * 3
- };
- return Task.FromResult(result);
- }
-}
-
-///
-/// 测试用异步查询类(抛出异常)
-///
-public sealed class TestAsyncQueryWithExceptionV4 : AbstractAsyncQuery
-{
- ///
- /// 初始化TestAsyncQueryWithExceptionV4的新实例
- ///
- /// 查询输入参数
- public TestAsyncQueryWithExceptionV4(TestAsyncQueryInputV2 input) : base(input)
- {
- }
-
- ///
- /// 执行异步查询操作并抛出异常
- ///
- /// 查询输入参数
- /// 总是抛出异常
- protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
- {
- throw new InvalidOperationException("Test exception");
- }
-}
-
-///
-/// 测试用异步查询子类V4,继承AbstractAsyncQuery
-///
-public sealed class TestAsyncQueryChildV4 : AbstractAsyncQuery
-{
- ///
- /// 初始化TestAsyncQueryChildV4的新实例
- ///
- /// 查询输入参数
- public TestAsyncQueryChildV4(TestAsyncQueryInputV2 input) : base(input)
- {
- }
-
- ///
- /// 获取查询是否已执行
- ///
- public bool Executed { get; private set; }
-
- ///
- /// 执行异步查询操作的具体实现(子类实现,乘以3)
- ///
- /// 查询输入参数
- /// 查询结果,将输入值乘以3
- protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
- {
- Executed = true;
- return Task.FromResult(input.Value * 3);
- }
-}
-
-///
-/// 测试用复杂查询结果类V2
-///
-public sealed class TestAsyncQueryResultV2
-{
- ///
- /// 获取或设置值
- ///
- public int Value { get; init; }
-
- ///
- /// 获取或设置双倍值
- ///
- public int DoubleValue { get; init; }
-}
diff --git a/GFramework.Core.Tests/Query/TestAsyncComplexQueryV4.cs b/GFramework.Core.Tests/Query/TestAsyncComplexQueryV4.cs
new file mode 100644
index 00000000..a5661efb
--- /dev/null
+++ b/GFramework.Core.Tests/Query/TestAsyncComplexQueryV4.cs
@@ -0,0 +1,38 @@
+using GFramework.Core.Query;
+
+namespace GFramework.Core.Tests.Query;
+
+///
+/// 复杂对象类型测试异步查询类V4,继承AbstractAsyncQuery
+///
+public sealed class TestAsyncComplexQueryV4 : AbstractAsyncQuery
+{
+ ///
+ /// 初始化TestAsyncComplexQueryV4的新实例
+ ///
+ /// 查询输入参数
+ public TestAsyncComplexQueryV4(TestAsyncQueryInputV2 input) : base(input)
+ {
+ }
+
+ ///
+ /// 获取查询是否已执行
+ ///
+ public bool Executed { get; private set; }
+
+ ///
+ /// 执行异步查询操作的具体实现
+ ///
+ /// 查询输入参数
+ /// 复杂对象查询结果
+ protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
+ {
+ Executed = true;
+ var result = new TestAsyncQueryResultV2
+ {
+ Value = input.Value * 2,
+ DoubleValue = input.Value * 3
+ };
+ return Task.FromResult(result);
+ }
+}
diff --git a/GFramework.Core.Tests/Query/TestAsyncQueryChildV4.cs b/GFramework.Core.Tests/Query/TestAsyncQueryChildV4.cs
new file mode 100644
index 00000000..f931a52f
--- /dev/null
+++ b/GFramework.Core.Tests/Query/TestAsyncQueryChildV4.cs
@@ -0,0 +1,33 @@
+using GFramework.Core.Query;
+
+namespace GFramework.Core.Tests.Query;
+
+///
+/// 测试用异步查询子类V4,继承AbstractAsyncQuery
+///
+public sealed class TestAsyncQueryChildV4 : AbstractAsyncQuery
+{
+ ///
+ /// 初始化TestAsyncQueryChildV4的新实例
+ ///
+ /// 查询输入参数
+ public TestAsyncQueryChildV4(TestAsyncQueryInputV2 input) : base(input)
+ {
+ }
+
+ ///
+ /// 获取查询是否已执行
+ ///
+ public bool Executed { get; private set; }
+
+ ///
+ /// 执行异步查询操作的具体实现(子类实现,乘以3)
+ ///
+ /// 查询输入参数
+ /// 查询结果,将输入值乘以3
+ protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
+ {
+ Executed = true;
+ return Task.FromResult(input.Value * 3);
+ }
+}
diff --git a/GFramework.Core.Tests/Query/TestAsyncQueryInputV2.cs b/GFramework.Core.Tests/Query/TestAsyncQueryInputV2.cs
new file mode 100644
index 00000000..cba1d6d1
--- /dev/null
+++ b/GFramework.Core.Tests/Query/TestAsyncQueryInputV2.cs
@@ -0,0 +1,14 @@
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
+
+namespace GFramework.Core.Tests.Query;
+
+///
+/// 测试用异步查询输入类V2
+///
+public sealed class TestAsyncQueryInputV2 : IQueryInput
+{
+ ///
+ /// 获取或设置值
+ ///
+ public int Value { get; init; }
+}
diff --git a/GFramework.Core.Tests/Query/TestAsyncQueryResultV2.cs b/GFramework.Core.Tests/Query/TestAsyncQueryResultV2.cs
new file mode 100644
index 00000000..068b2a66
--- /dev/null
+++ b/GFramework.Core.Tests/Query/TestAsyncQueryResultV2.cs
@@ -0,0 +1,17 @@
+namespace GFramework.Core.Tests.Query;
+
+///
+/// 测试用复杂查询结果类V2
+///
+public sealed class TestAsyncQueryResultV2
+{
+ ///
+ /// 获取或设置值
+ ///
+ public int Value { get; init; }
+
+ ///
+ /// 获取或设置双倍值
+ ///
+ public int DoubleValue { get; init; }
+}
diff --git a/GFramework.Core.Tests/Query/TestAsyncQueryV4.cs b/GFramework.Core.Tests/Query/TestAsyncQueryV4.cs
new file mode 100644
index 00000000..a01e42c1
--- /dev/null
+++ b/GFramework.Core.Tests/Query/TestAsyncQueryV4.cs
@@ -0,0 +1,33 @@
+using GFramework.Core.Query;
+
+namespace GFramework.Core.Tests.Query;
+
+///
+/// 整数类型测试异步查询类V4,继承AbstractAsyncQuery
+///
+public sealed class TestAsyncQueryV4 : AbstractAsyncQuery
+{
+ ///
+ /// 初始化TestAsyncQueryV4的新实例
+ ///
+ /// 查询输入参数
+ public TestAsyncQueryV4(TestAsyncQueryInputV2 input) : base(input)
+ {
+ }
+
+ ///
+ /// 获取查询是否已执行
+ ///
+ public bool Executed { get; private set; }
+
+ ///
+ /// 执行异步查询操作的具体实现
+ ///
+ /// 查询输入参数
+ /// 查询结果,将输入值乘以2
+ protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
+ {
+ Executed = true;
+ return Task.FromResult(input.Value * 2);
+ }
+}
diff --git a/GFramework.Core.Tests/Query/TestAsyncQueryWithExceptionV4.cs b/GFramework.Core.Tests/Query/TestAsyncQueryWithExceptionV4.cs
new file mode 100644
index 00000000..f321d756
--- /dev/null
+++ b/GFramework.Core.Tests/Query/TestAsyncQueryWithExceptionV4.cs
@@ -0,0 +1,27 @@
+using GFramework.Core.Query;
+
+namespace GFramework.Core.Tests.Query;
+
+///
+/// 测试用异步查询类(抛出异常)
+///
+public sealed class TestAsyncQueryWithExceptionV4 : AbstractAsyncQuery
+{
+ ///
+ /// 初始化TestAsyncQueryWithExceptionV4的新实例
+ ///
+ /// 查询输入参数
+ public TestAsyncQueryWithExceptionV4(TestAsyncQueryInputV2 input) : base(input)
+ {
+ }
+
+ ///
+ /// 执行异步查询操作并抛出异常
+ ///
+ /// 查询输入参数
+ /// 总是抛出异常
+ protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
+ {
+ throw new InvalidOperationException("Test exception");
+ }
+}
diff --git a/GFramework.Core.Tests/Query/TestAsyncStringQueryV4.cs b/GFramework.Core.Tests/Query/TestAsyncStringQueryV4.cs
new file mode 100644
index 00000000..59d313d5
--- /dev/null
+++ b/GFramework.Core.Tests/Query/TestAsyncStringQueryV4.cs
@@ -0,0 +1,33 @@
+using GFramework.Core.Query;
+
+namespace GFramework.Core.Tests.Query;
+
+///
+/// 字符串类型测试异步查询类V4,继承AbstractAsyncQuery
+///
+public sealed class TestAsyncStringQueryV4 : AbstractAsyncQuery
+{
+ ///
+ /// 初始化TestAsyncStringQueryV4的新实例
+ ///
+ /// 查询输入参数
+ public TestAsyncStringQueryV4(TestAsyncQueryInputV2 input) : base(input)
+ {
+ }
+
+ ///
+ /// 获取查询是否已执行
+ ///
+ public bool Executed { get; private set; }
+
+ ///
+ /// 执行异步查询操作的具体实现
+ ///
+ /// 查询输入参数
+ /// 格式化的字符串结果
+ protected override Task OnDoAsync(TestAsyncQueryInputV2 input)
+ {
+ Executed = true;
+ return Task.FromResult($"Value: {input.Value * 2}");
+ }
+}
diff --git a/GFramework.Game/Config/YamlConfigLoader.cs b/GFramework.Game/Config/YamlConfigLoader.cs
index ce6d9ae3..975befe1 100644
--- a/GFramework.Game/Config/YamlConfigLoader.cs
+++ b/GFramework.Game/Config/YamlConfigLoader.cs
@@ -1,4 +1,6 @@
using System.Diagnostics;
+using System.Globalization;
+using System.Threading;
using GFramework.Core.Abstractions.Events;
using GFramework.Game.Abstractions.Config;
using YamlDotNet.Serialization;
@@ -472,93 +474,159 @@ public sealed class YamlConfigLoader : IConfigLoader
IDeserializer deserializer,
CancellationToken cancellationToken)
{
- var directoryPath = Path.Combine(rootPath, RelativePath);
- if (!Directory.Exists(directoryPath))
- {
- throw ConfigLoadExceptionFactory.Create(
- ConfigLoadFailureKind.ConfigDirectoryNotFound,
- Name,
- $"Config directory '{directoryPath}' was not found for table '{Name}'.",
- configDirectoryPath: directoryPath);
- }
-
- YamlConfigSchema? schema = null;
- IReadOnlyCollection referencedTableNames = Array.Empty();
- if (!string.IsNullOrEmpty(SchemaRelativePath))
- {
- var schemaPath = Path.Combine(rootPath, SchemaRelativePath);
- schema = await YamlConfigSchemaValidator.LoadAsync(Name, schemaPath, cancellationToken)
- .ConfigureAwait(false);
- referencedTableNames = schema.ReferencedTableNames;
- }
-
+ var directoryPath = GetValidatedDirectoryPath(rootPath);
+ var schema = await LoadSchemaAsync(rootPath, cancellationToken).ConfigureAwait(false);
var referenceUsages = new List();
+ var values = await LoadValuesAsync(
+ directoryPath,
+ deserializer,
+ schema,
+ referenceUsages,
+ cancellationToken)
+ .ConfigureAwait(false);
+ return BuildLoadResult(directoryPath, schema, values, referenceUsages);
+ }
+
+ private string GetValidatedDirectoryPath(string rootPath)
+ {
+ var directoryPath = Path.Combine(rootPath, RelativePath);
+ if (Directory.Exists(directoryPath))
+ {
+ return directoryPath;
+ }
+
+ throw ConfigLoadExceptionFactory.Create(
+ ConfigLoadFailureKind.ConfigDirectoryNotFound,
+ Name,
+ $"Config directory '{directoryPath}' was not found for table '{Name}'.",
+ configDirectoryPath: directoryPath);
+ }
+
+ private async Task LoadSchemaAsync(string rootPath, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(SchemaRelativePath))
+ {
+ return null;
+ }
+
+ var schemaPath = Path.Combine(rootPath, SchemaRelativePath);
+ return await YamlConfigSchemaValidator.LoadAsync(Name, schemaPath, cancellationToken).ConfigureAwait(false);
+ }
+
+ private async Task> LoadValuesAsync(
+ string directoryPath,
+ IDeserializer deserializer,
+ YamlConfigSchema? schema,
+ List referenceUsages,
+ CancellationToken cancellationToken)
+ {
var values = new List();
- var files = Directory
+ foreach (var file in GetYamlFiles(directoryPath))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var yaml = await ReadYamlAsync(directoryPath, file, schema, cancellationToken).ConfigureAwait(false);
+ CollectReferenceUsages(referenceUsages, schema, file, yaml);
+ values.Add(DeserializeValue(deserializer, directoryPath, file, schema, yaml));
+ }
+
+ return values;
+ }
+
+ private static string[] GetYamlFiles(string directoryPath)
+ {
+ return Directory
.EnumerateFiles(directoryPath, "*.*", SearchOption.TopDirectoryOnly)
.Where(static path =>
path.EndsWith(".yaml", StringComparison.OrdinalIgnoreCase) ||
path.EndsWith(".yml", StringComparison.OrdinalIgnoreCase))
.OrderBy(static path => path, StringComparer.Ordinal)
.ToArray();
+ }
- foreach (var file in files)
+ private async Task ReadYamlAsync(
+ string directoryPath,
+ string file,
+ YamlConfigSchema? schema,
+ CancellationToken cancellationToken)
+ {
+ try
{
- cancellationToken.ThrowIfCancellationRequested();
+ return await File.ReadAllTextAsync(file, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception exception)
+ {
+ throw ConfigLoadExceptionFactory.Create(
+ ConfigLoadFailureKind.ConfigFileReadFailed,
+ Name,
+ $"Failed to read config file '{file}' for table '{Name}'.",
+ configDirectoryPath: directoryPath,
+ yamlPath: file,
+ schemaPath: schema?.SchemaPath,
+ innerException: exception);
+ }
+ }
- string yaml;
- try
- {
- yaml = await File.ReadAllTextAsync(file, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception exception)
- {
- throw ConfigLoadExceptionFactory.Create(
- ConfigLoadFailureKind.ConfigFileReadFailed,
- Name,
- $"Failed to read config file '{file}' for table '{Name}'.",
- configDirectoryPath: directoryPath,
- yamlPath: file,
- schemaPath: schema?.SchemaPath,
- innerException: exception);
- }
-
- if (schema != null)
- {
- // 先按 schema 拒绝结构问题并提取跨表引用,避免被 IgnoreUnmatchedProperties 或默认值掩盖配置错误。
- referenceUsages.AddRange(
- YamlConfigSchemaValidator.ValidateAndCollectReferences(Name, schema, file, yaml));
- }
-
- try
- {
- var value = deserializer.Deserialize(yaml);
-
- if (value == null)
- {
- throw new InvalidOperationException("YAML content was deserialized to null.");
- }
-
- values.Add(value);
- }
- catch (Exception exception)
- {
- throw ConfigLoadExceptionFactory.Create(
- ConfigLoadFailureKind.DeserializationFailed,
- Name,
- $"Failed to deserialize config file '{file}' for table '{Name}' as '{typeof(TValue).Name}'.",
- configDirectoryPath: directoryPath,
- yamlPath: file,
- schemaPath: schema?.SchemaPath,
- detail: $"Target CLR type: {typeof(TValue).FullName}.",
- innerException: exception);
- }
+ private void CollectReferenceUsages(
+ List referenceUsages,
+ YamlConfigSchema? schema,
+ string file,
+ string yaml)
+ {
+ if (schema == null)
+ {
+ return;
}
+ // 先按 schema 拒绝结构问题并提取跨表引用,避免被 IgnoreUnmatchedProperties 或默认值掩盖配置错误。
+ referenceUsages.AddRange(
+ YamlConfigSchemaValidator.ValidateAndCollectReferences(Name, schema, file, yaml));
+ }
+
+ private TValue DeserializeValue(
+ IDeserializer deserializer,
+ string directoryPath,
+ string file,
+ YamlConfigSchema? schema,
+ string yaml)
+ {
+ try
+ {
+ var value = deserializer.Deserialize(yaml);
+ if (value != null)
+ {
+ return value;
+ }
+
+ throw new InvalidOperationException("YAML content was deserialized to null.");
+ }
+ catch (Exception exception)
+ {
+ throw ConfigLoadExceptionFactory.Create(
+ ConfigLoadFailureKind.DeserializationFailed,
+ Name,
+ $"Failed to deserialize config file '{file}' for table '{Name}' as '{typeof(TValue).Name}'.",
+ configDirectoryPath: directoryPath,
+ yamlPath: file,
+ schemaPath: schema?.SchemaPath,
+ detail: $"Target CLR type: {typeof(TValue).FullName}.",
+ innerException: exception);
+ }
+ }
+
+ private YamlTableLoadResult BuildLoadResult(
+ string directoryPath,
+ YamlConfigSchema? schema,
+ List values,
+ List referenceUsages)
+ {
try
{
var table = new InMemoryConfigTable(values, _keySelector, _comparer);
- return new YamlTableLoadResult(Name, table, referencedTableNames, referenceUsages);
+ return new YamlTableLoadResult(
+ Name,
+ table,
+ schema?.ReferencedTableNames ?? Array.Empty(),
+ referenceUsages);
}
catch (Exception exception)
{
@@ -630,6 +698,12 @@ public sealed class YamlConfigLoader : IConfigLoader
///
private static class CrossTableReferenceValidator
{
+ private delegate bool IntegerTryParseDelegate(
+ string value,
+ NumberStyles style,
+ IFormatProvider? provider,
+ out T result);
+
///
/// 使用本轮新加载结果与注册表中保留的旧表,一起验证跨表引用是否全部有效。
///
@@ -754,59 +828,15 @@ public sealed class YamlConfigLoader : IConfigLoader
convertedKey = null;
errorMessage = string.Empty;
- if (targetKeyType == typeof(int) &&
- int.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
+ if (TryConvertIntegerKey(rawValue, targetKeyType, typeof(int), int.TryParse, out convertedKey) ||
+ TryConvertIntegerKey(rawValue, targetKeyType, typeof(long), long.TryParse, out convertedKey) ||
+ TryConvertIntegerKey(rawValue, targetKeyType, typeof(short), short.TryParse, out convertedKey) ||
+ TryConvertIntegerKey(rawValue, targetKeyType, typeof(byte), byte.TryParse, out convertedKey) ||
+ TryConvertIntegerKey(rawValue, targetKeyType, typeof(uint), uint.TryParse, out convertedKey) ||
+ TryConvertIntegerKey(rawValue, targetKeyType, typeof(ulong), ulong.TryParse, out convertedKey) ||
+ TryConvertIntegerKey(rawValue, targetKeyType, typeof(ushort), ushort.TryParse, out convertedKey) ||
+ TryConvertIntegerKey(rawValue, targetKeyType, typeof(sbyte), sbyte.TryParse, out convertedKey))
{
- convertedKey = intValue;
- return true;
- }
-
- if (targetKeyType == typeof(long) &&
- long.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue))
- {
- convertedKey = longValue;
- return true;
- }
-
- if (targetKeyType == typeof(short) &&
- short.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var shortValue))
- {
- convertedKey = shortValue;
- return true;
- }
-
- if (targetKeyType == typeof(byte) &&
- byte.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var byteValue))
- {
- convertedKey = byteValue;
- return true;
- }
-
- if (targetKeyType == typeof(uint) &&
- uint.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var uintValue))
- {
- convertedKey = uintValue;
- return true;
- }
-
- if (targetKeyType == typeof(ulong) &&
- ulong.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var ulongValue))
- {
- convertedKey = ulongValue;
- return true;
- }
-
- if (targetKeyType == typeof(ushort) &&
- ushort.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var ushortValue))
- {
- convertedKey = ushortValue;
- return true;
- }
-
- if (targetKeyType == typeof(sbyte) &&
- sbyte.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var sbyteValue))
- {
- convertedKey = sbyteValue;
return true;
}
@@ -815,6 +845,25 @@ public sealed class YamlConfigLoader : IConfigLoader
return false;
}
+ private static bool TryConvertIntegerKey(
+ string rawValue,
+ Type targetKeyType,
+ Type supportedType,
+ IntegerTryParseDelegate tryParse,
+ out object? convertedKey)
+ where T : struct
+ {
+ convertedKey = null;
+ if (targetKeyType != supportedType ||
+ !tryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedValue))
+ {
+ return false;
+ }
+
+ convertedKey = parsedValue;
+ return true;
+ }
+
private static bool ContainsKey(IConfigTable table, object key)
{
var tableInterface = table.GetType()
@@ -838,7 +887,13 @@ public sealed class YamlConfigLoader : IConfigLoader
new(StringComparer.Ordinal);
private readonly IDeserializer _deserializer;
+#if NET9_0_OR_GREATER
+ // net9.0 及以上目标使用专用 Lock,以满足分析器对专用同步原语的建议。
+ private readonly Lock _gate = new();
+#else
+ // net8.0 目标仍回退到 object 锁,以保持多目标编译兼容性。
private readonly object _gate = new();
+#endif
private readonly Action? _onTableReloaded;
private readonly Action? _onTableReloadFailed;
private readonly Dictionary _registrations = new(StringComparer.Ordinal);
@@ -1121,7 +1176,7 @@ public sealed class YamlConfigLoader : IConfigLoader
foreach (var dependency in _dependenciesByTable)
{
- if (!dependency.Value.Contains(currentTableName))
+ if (!ContainsDependency(dependency.Value, currentTableName))
{
continue;
}
@@ -1138,6 +1193,14 @@ public sealed class YamlConfigLoader : IConfigLoader
.ToArray();
}
+ private static bool ContainsDependency(
+ IReadOnlyCollection dependencies,
+ string tableName)
+ {
+ return dependencies.Any(
+ dependency => string.Equals(dependency, tableName, StringComparison.Ordinal));
+ }
+
private void InvokeReloaded(string tableName)
{
if (_onTableReloaded == null)
diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md
index 67fa6d19..2227a3d0 100644
--- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md
+++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md
@@ -6,54 +6,48 @@
## 当前恢复点
-- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-081`
-- 当前阶段:`Phase 81`
+- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-083`
+- 当前阶段:`Phase 83`
- 当前焦点:
- - `2026-04-27` 已复核 PR `#295` 的 latest-head review,确认 `ThrowShouldNotRetry` 的 `ParamName` open thread 属于 stale finding,本地代码已经使用传入值而非 `nameof(parameterName)`
- - 已清理 `AsyncExtensionsTests.WithRetry_Should_Respect_ShouldRetry_Predicate` 中的冗余 `Task.Delay(50)`,保留 `ParamName == nameof(taskFactory)` 断言锁定契约
- - 已增强 `.agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py` 的 failed-test 表格解析,允许 `Name` / `Failure Message` 后出现尾随额外列
- - 已新增 Python `unittest` 回归用例覆盖“尾随额外列不影响前两列提取”的场景
- - 当前剩余 warning 热点仍集中在 `YamlConfigSchemaValidator*`、`YamlConfigLoader.cs` 与大批量 `MA0048` 文件名拆分;这些 slice 仍高于本轮 PR review follow-up 的低风险边界
+ - `2026-04-27` 主线程已修复 `GFramework.Game/Config/YamlConfigLoader.cs` 的 `MA0051`、`MA0002` 与 `MA0158`,当前非增量仓库根构建已不再报告该文件 warning
+ - 并行 worker 已将 `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 末尾的 `10` 个测试辅助接口/类拆分到 `Ioc/` 同目录独立文件
+ - 已接受第二波 worker 的已落地结果:`GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs` 末尾辅助类型已拆分到 `Query/` 同目录独立文件
+ - 最新 non-incremental 仓库根基线已从 `397` 条 warning / `316` 个唯一位点降到 `353` 条 warning / `279` 个唯一位点
+ - 当前剩余 warning 热点仍集中在 `GFramework.Cqrs.Tests/Mediator/*` 的大体量 `MA0048`、以及 `YamlConfigSchemaValidator*` 等高耦合 slice
## 当前活跃事实
-- 当前 `origin/main` 基线提交为 `617e0bf`(`2026-04-26T12:17:15+08:00`)。
-- 当前 PR review 真值:
- - `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output `
- - 最新结果:成功;当前分支对应 PR 为 `#295`
- - 当前测试报告输出已能显示 `Summary` 统计、失败测试名称,以及 `Name / Failure Message` 表格中的关键信息
- - 当前 GitHub latest-head review 仍显示 `1` 条 open thread,但该线程指向的 `nameof(parameterName)` 问题已不在本地代码中成立,属于 stale finding
- - 当前 latest review 中仍有 `2` 条与本地工作树一致的 nitpick:`AsyncExtensionsTests` 冗余等待,以及 failed-test 表格解析对尾随列不鲁棒
+- 当前 `origin/main` 基线提交为 `b6a9fef`(`2026-04-27T10:53:34+08:00`)。
- 当前直接验证结果:
- - `python3 .agents/skills/gframework-pr-review/scripts/test_fetch_current_pr_review.py`
- - 最新结果:成功;`Ran 1 test in 0.000s`, `OK`
- - `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --section tests --json-output /tmp/current-pr-review-postfix.json`
- - 最新结果:成功;真实 PR 评论抓取仍能输出 `2` 份测试报告,失败用例详情保持可见
- - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~WithRetry_Should_Respect_ShouldRetry_Predicate"`
- - 最新结果:成功;`Failed: 0, Passed: 1, Skipped: 0, Total: 1`
- - `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~RegisterMigration_During_Cache_Rebuild_Should_Not_Leave_Stale_Type_Cache"`
- - 最新结果:成功;`Failed: 0, Passed: 1, Skipped: 0, Total: 1`
+ - `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
+ - 最新结果:成功;`111 Warning(s)`、`0 Error(s)`,其中不再包含 `GFramework.Game/Config/YamlConfigLoader.cs` 的 warning
+ - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
+ - 最新结果:成功;`0 Warning(s)`、`0 Error(s)`
+ - `dotnet clean`
+ - 最新结果:成功;为本轮最终 warning 基线刷新提供非增量起点
+ - `dotnet build`
+ - 最新结果:成功;`353 Warning(s)`、`0 Error(s)`,唯一 warning 位点 `279`
+ - 当前构建输出已不再包含 `GFramework.Game/Config/YamlConfigLoader.cs`、`GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 与 `GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs`
- 当前分支 stop-condition 指标:
- - `git diff --name-only refs/remotes/origin/main...HEAD | wc -l`
- - 最新结果:`35`
- - `git diff --numstat refs/remotes/origin/main...HEAD`
- - 最新结果:`642` changed lines
+ - 当前待提交工作树 footprint:
+ - 最新结果:`22` changed files,距离 `$gframework-batch-boot 50` 的停止线仍有余量
- 当前批次摘要:
- - 三轮低风险 warning 清理已在此前验证中将仓库根 warning 从 `639` 降到 `397`
- - 当前批次的已完成 slice 明细已迁移到归档,active todo 仅保留恢复真值
- - 本轮新增内容为 PR review nitpick 收口与脚本回归测试补齐,不扩展 warning reduction 的热点清理边界
+ - 本轮完成 `YamlConfigLoader.cs` 的单文件 warning 清理,并通过受影响模块 Release 构建验证
+ - 本轮完成 `MicrosoftDiContainerTests.cs` 的 ownership-bounded `MA0048` 拆分 slice,新增 `10` 个同目录辅助类型文件并保持测试语义不变
+ - 本轮还完成 `AbstractAsyncQueryTests.cs` 的 `MA0048` 拆分 slice,新增 `7` 个同目录辅助类型文件并保持测试语义不变
+ - 本轮 non-incremental 仓库根 warning 真值从 `397` 降到 `353`,减少 `44` 条;唯一位点从 `316` 降到 `279`,减少 `37` 个
+ - 已尝试为 `ArchitectureContextTests.cs` 启动下一波 subagent,但在共享工作树落地前已停止,不计入本轮已完成事实
- 当前建议保留到下一波次的候选:
- - `GFramework.Game/Config/YamlConfigLoader.cs` 的 `MA0158`(单点可修,但文件本身同时承载其他高耦合 warning)
- - 测试项目中的 `MA0048` 文件名拆分波次(会显著增加 changed-file 数)
+ - `GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs` 的 `7` 个 `MA0048`
+ - `GFramework.Core.Tests/Query/AsyncQueryExecutorTests.cs` 的 `7` 个 `MA0048`
+ - `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 与 `YamlConfigSchemaValidator.ObjectKeywords.cs` 的高耦合 warning 热点
## 当前风险
-- `GFramework.Game/Config/YamlConfigSchemaValidator*.cs` 仍然聚集多类高耦合 warning。
- - 缓解措施:本轮先避开该热点,只清理低风险且 ownership 清晰的文件集合。
-- `MA0158` 迁移涉及 `net8.0` / `net9.0` / `net10.0` 多目标兼容。
- - 缓解措施:复用 `StoreSelection.cs` 已存在的 `#if NET9_0_OR_GREATER` 专用锁模式,不在 `net8.0` 引入不兼容 API。
-- 当前 PR open thread 与 CI 失败信号仍依赖新提交进入远端 PR head 才能复核。
- - 缓解措施:本轮提交并推送后重新执行 `$gframework-pr-review`,确认 stale open thread 是否被 GitHub 收口,以及两条 nitpick 是否从 latest review 中消失。
+- `GFramework.Cqrs.Tests/Mediator/*` 仍有 `47` / `44` / `34` 个唯一 warning 位点,属于高 changed-file 风险的 `MA0048` 大波次。
+ - 缓解措施:优先继续处理 `6-7` 个 warning 的小文件切片,避免一次性推高文件数。
+- `YamlConfigSchemaValidator*` 仍然聚集多类高耦合 warning。
+ - 缓解措施:继续把它们留在独立波次,不与测试项目的低风险拆分混提。
## 活跃文档
@@ -73,11 +67,11 @@
## 验证说明
- 权威验证结果统一维护在“当前活跃事实”。
-- `GFramework.Core.Tests` 当前仍有既有 analyzer / nullable warning 基线,因此本轮验证只证明 PR review 修复未引入构建错误,未将该项目 warning 清零。
-- 后续若刷新构建或 PR review 真值,只更新上述权威区块,不在本节重复抄录。
+- `GFramework.Core.Tests` 项目级 Release 构建已在本轮清零,但仓库根 non-incremental 构建仍保留大量既有 warning。
+- warning reduction 的仓库级真值只以同轮 `dotnet clean` 后的 `dotnet build` 为准。
## 下一步建议
-1. 提交本轮 `AsyncExtensionsTests` / `$gframework-pr-review` nitpick 修复、Python 回归测试与 `ai-plan` 同步。
-2. 推送后重新执行 `$gframework-pr-review`,确认 PR `#295` 的 stale open thread、nitpick 与测试报告是否已刷新为新 head 真值。
-3. 若后续继续推进 warning reduction,建议另开下一波次处理 `YamlConfigLoader.cs` 热点或测试项目 `MA0048` 拆分波次。
+1. 提交本轮 `YamlConfigLoader.cs`、`MicrosoftDiContainerTests.cs`、`AbstractAsyncQueryTests.cs` 的 warning reduction 结果及 `ai-plan` 同步。
+2. 下一波优先挑选 `ArchitectureContextTests.cs` 或 `AsyncQueryExecutorTests.cs` 这类 `7`-warning 的纯 `MA0048` 单文件切片。
+3. 继续将 `YamlConfigSchemaValidator*` 与 `GFramework.Cqrs.Tests/Mediator/*` 作为独立高风险波次处理。
diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md
index 9c8bc443..efb1d296 100644
--- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md
+++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md
@@ -1,39 +1,45 @@
# Analyzer Warning Reduction 追踪
-## 2026-04-27 — RP-081
+## 2026-04-27 — RP-083
-### 阶段:核实 PR `#295` 的剩余 nitpick,并补齐脚本解析回归测试
+### 阶段:修复 `YamlConfigLoader` 单文件 warning,并拆分 `MicrosoftDiContainerTests` 的辅助类型
- 触发背景:
- - 用户再次执行 `$gframework-pr-review`,需要根据当前 PR `#295` 的 latest-head review 继续核实哪些反馈仍需在本地处理
- - 远端 review 显示 `1` 条 open thread 与 `2` 条 nitpick,需要区分 stale finding 与仍然成立的本地问题
+ - 用户执行 `$gframework-batch-boot 50`,要求先拿仓库根构建 warning,再按 bounded slice 分派给不同 subagent 并持续推进
+ - 当前分支在本轮开始时与 `origin/main@b6a9fef` 零提交差异,适合从低风险 warning slice 起步
- 主线程实施:
- - 复核 `/tmp/current-pr-review.json` 与本地 `AsyncExtensionsTests.cs`,确认 open thread 指向的 `nameof(parameterName)` 问题已在现有代码中修复,属于 stale finding
- - 删除 `GFramework.Core.Tests/Extensions/AsyncExtensionsTests.cs` 中 `WithRetry_Should_Respect_ShouldRetry_Predicate` 的冗余 `Task.Delay(50)`,将测试改回同步断言路径
- - 调整 `.agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py` 的 `parse_failed_test_details`,允许 failed-test HTML 表格在 `Name` / `Failure Message` 后追加额外列
- - 新增 `.agents/skills/gframework-pr-review/scripts/test_fetch_current_pr_review.py`,以 `unittest` 覆盖“尾随额外列不影响前两列提取”的回归场景
+ - 先执行 non-incremental 仓库根基线:`dotnet clean` + `dotnet build`,得到 `397 Warning(s)` / `316` 个唯一位点
+ - 主线程修复 `GFramework.Game/Config/YamlConfigLoader.cs` 的 `MA0051`、`MA0002` 与 `MA0158`
+ - 接受一个 worker batch:将 `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 末尾的 `10` 个测试辅助接口/类拆分到 `Ioc/` 同目录独立文件
+ - 接受第二波 worker 的已落地结果:将 `GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs` 末尾的 `7` 个测试辅助类型拆分到 `Query/` 同目录独立文件
+ - 启动 `ArchitectureContextTests.cs` 候选 worker,但在共享工作树落地前主动停止,以避免本轮上下文与 review 面积继续膨胀
- 验证里程碑:
- - `python3 .agents/skills/gframework-pr-review/scripts/test_fetch_current_pr_review.py`
- - 结果:成功;`Ran 1 test in 0.000s`, `OK`
- - `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --section tests --json-output /tmp/current-pr-review-postfix.json`
- - 结果:成功;真实 PR 评论抓取仍显示 `2` 份测试报告,失败测试名与 failure message 摘要保持可见
- - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~WithRetry_Should_Respect_ShouldRetry_Predicate"`
- - 结果:成功;`Failed: 0, Passed: 1, Skipped: 0, Total: 1`
+ - `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
+ - 结果:成功;`111 Warning(s)`、`0 Error(s)`
+ - 观察:构建输出未再报告 `GFramework.Game/Config/YamlConfigLoader.cs`
+ - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
+ - 结果:成功;`0 Warning(s)`、`0 Error(s)`
+ - `dotnet clean`
+ - 结果:成功;刷新最终 non-incremental 仓库根 warning 基线
+ - `dotnet build`
+ - 结果:成功;`353 Warning(s)`、`0 Error(s)`,唯一位点 `279`
+ - 观察:构建输出未再报告 `GFramework.Game/Config/YamlConfigLoader.cs`、`GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 与 `GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs`
- 当前结论:
- - 本轮 latest-head review 中只有 `AsyncExtensionsTests` 冗余等待与 failed-test 表格尾随列容错性两个 nitpick 仍与本地代码一致,现已修复
- - `ThrowShouldNotRetry` 的 `ParamName` open thread 属于 stale finding,本地代码已经符合预期,只需等待新提交进入远端后复核 thread 状态
+ - 本轮已完成一个主线程单文件 slice 和两个 worker 拆分 slice;仓库根 non-incremental warning 从 `397` 降到 `353`
+ - 当前共享工作树 footprint 为 `22` 个 changed files,仍低于 `$gframework-batch-boot 50` 的停止线
+ - 下一波更适合继续处理 `7` 个 `MA0048` 的小文件,而不是立即进入 `Mediator*` 或 `YamlConfigSchemaValidator*` 的高耦合热点
## 活跃风险
-- PR 上的 latest-head review thread 与测试报告仍需要等新提交进入远端后再复核。
- - 缓解措施:提交并推送后重新执行 `$gframework-pr-review`,只以新的 latest-head 和 test report 为准。
-- `YamlConfigSchemaValidator*`、`YamlConfigLoader.cs` 与 `MA0048` 拆分仍是下一波次的高耦合候选。
- - 缓解措施:保持本轮边界只处理 PR review nitpick follow-up,不顺手扩展 warning reduction 范围。
+- `GFramework.Cqrs.Tests/Mediator/*` 的 `MA0048` 位点密度很高,一次性拆分会迅速推高 changed-file 数。
+ - 缓解措施:下一波优先继续拿 `7` warning 级别的小切片。
+- `YamlConfigSchemaValidator*` 仍然聚集多类高耦合 warning。
+ - 缓解措施:继续维持为独立波次,不与测试项目拆分混提。
## 下一步
-1. 完成本轮提交。
-2. 推送后重新执行 `$gframework-pr-review`,确认 PR `#295` 的 stale open thread 与 nitpick 是否已刷新。
+1. 完成本轮 `YamlConfigLoader.cs`、`MicrosoftDiContainerTests.cs` 与 `ai-plan` 的提交。
+2. 下一波优先从 `ArchitectureContextTests.cs` 或 `AsyncQueryExecutorTests.cs` 继续拆分纯 `MA0048`。
## 历史归档指针