From 6ff07ad3d91a54bc07c95f30f485cb02160157b1 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Fri, 24 Apr 2026 17:04:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(godot):=20=E6=B8=85=E7=90=86=20Godot=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=B8=8E=E6=B5=8B=E8=AF=95=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化 GodotYamlConfigEnvironment 目录枚举逻辑,拆分 helper 以消除 MA0051 - 修复 Godot 生命周期 await 的上下文声明,显式保留主线程同步上下文 - 更新 Godot.Tests 异步断言与字符串 comparer,用例项目构建收敛到 0 warning(s) - 补充 analyzer-warning-reduction 跟踪与 trace,记录 RP-053 的批次结果与验证 --- ...ractArchitectureModuleInstallationTests.cs | 2 +- .../Config/GodotYamlConfigLoaderTests.cs | 6 +- .../Text/RichTextMarkupTests.cs | 5 +- .../Architectures/AbstractArchitecture.cs | 4 +- .../Config/GodotYamlConfigEnvironment.cs | 61 +++++++++--------- GFramework.Godot/Scene/SceneBehaviorBase.cs | 31 +++++----- .../analyzer-warning-reduction-tracking.md | 62 ++++++++----------- .../analyzer-warning-reduction-trace.md | 31 ++++++++++ 8 files changed, 115 insertions(+), 87 deletions(-) diff --git a/GFramework.Godot.Tests/Architectures/AbstractArchitectureModuleInstallationTests.cs b/GFramework.Godot.Tests/Architectures/AbstractArchitectureModuleInstallationTests.cs index 8952a12c..83dc2458 100644 --- a/GFramework.Godot.Tests/Architectures/AbstractArchitectureModuleInstallationTests.cs +++ b/GFramework.Godot.Tests/Architectures/AbstractArchitectureModuleInstallationTests.cs @@ -20,7 +20,7 @@ public sealed class AbstractArchitectureModuleInstallationTests var module = new RecordingGodotModule(); var exception = Assert.ThrowsAsync(async () => - await architecture.InstallGodotModuleForTestAsync(module)); + await architecture.InstallGodotModuleForTestAsync(module).ConfigureAwait(false)); Assert.Multiple(() => { diff --git a/GFramework.Godot.Tests/Config/GodotYamlConfigLoaderTests.cs b/GFramework.Godot.Tests/Config/GodotYamlConfigLoaderTests.cs index 33a0feb6..fbf5a71f 100644 --- a/GFramework.Godot.Tests/Config/GodotYamlConfigLoaderTests.cs +++ b/GFramework.Godot.Tests/Config/GodotYamlConfigLoaderTests.cs @@ -197,7 +197,7 @@ public sealed class GodotYamlConfigLoaderTests var loader = CreateLoader(isEditor: false); var exception = Assert.ThrowsAsync(async () => - await loader.LoadAsync(new ConfigRegistry())); + await loader.LoadAsync(new ConfigRegistry()).ConfigureAwait(false)); Assert.Multiple(() => { @@ -225,7 +225,7 @@ public sealed class GodotYamlConfigLoaderTests configureLoader: static _ => { }); var exception = Assert.ThrowsAsync(async () => - await loader.LoadAsync(new ConfigRegistry())); + await loader.LoadAsync(new ConfigRegistry()).ConfigureAwait(false)); Assert.That(exception!.ParamName, Is.EqualTo("relativePath")); } @@ -254,7 +254,7 @@ public sealed class GodotYamlConfigLoaderTests configureLoader: static _ => { }); var exception = Assert.ThrowsAsync(async () => - await loader.LoadAsync(new ConfigRegistry())); + await loader.LoadAsync(new ConfigRegistry()).ConfigureAwait(false)); Assert.That(exception!.ParamName, Is.EqualTo("relativePath")); } diff --git a/GFramework.Godot.Tests/Text/RichTextMarkupTests.cs b/GFramework.Godot.Tests/Text/RichTextMarkupTests.cs index 127d783c..c3062f28 100644 --- a/GFramework.Godot.Tests/Text/RichTextMarkupTests.cs +++ b/GFramework.Godot.Tests/Text/RichTextMarkupTests.cs @@ -1,3 +1,4 @@ +using System; using GFramework.Godot.Text; namespace GFramework.Godot.Tests.Text; @@ -25,7 +26,7 @@ public sealed class RichTextMarkupTests [Test] public void Effect_Should_Sort_Environment_Parameters_By_Key() { - var env = new Dictionary + var env = new Dictionary(StringComparer.Ordinal) { ["tick"] = 0.1f, ["speed"] = 4 @@ -53,7 +54,7 @@ public sealed class RichTextMarkupTests [Test] public void Effect_Should_Reject_Invalid_Environment_Key_Tokens() { - var env = new Dictionary + var env = new Dictionary(StringComparer.Ordinal) { ["bad key"] = 1 }; diff --git a/GFramework.Godot/Architectures/AbstractArchitecture.cs b/GFramework.Godot/Architectures/AbstractArchitecture.cs index 1beb8609..f18483a1 100644 --- a/GFramework.Godot/Architectures/AbstractArchitecture.cs +++ b/GFramework.Godot/Architectures/AbstractArchitecture.cs @@ -112,8 +112,8 @@ public abstract class AbstractArchitecture( // 在附加流程完成前先登记模块,保证后续任一步失败时仍能参与架构销毁阶段的清理。 _extensions.Add(module); - // 等待锚点准备就绪,并保持 Godot 同步上下文,以便后续附加逻辑安全访问节点 API。 - await anchor.WaitUntilReadyAsync(); + // 显式保留 Godot 同步上下文,确保后续 AddChild 和 OnAttach 仍在节点可访问的主线程执行。 + await anchor.WaitUntilReadyAsync().ConfigureAwait(true); // 延迟调用将扩展节点添加为锚点的子节点 anchor.CallDeferred(Node.MethodName.AddChild, module.Node); diff --git a/GFramework.Godot/Config/GodotYamlConfigEnvironment.cs b/GFramework.Godot/Config/GodotYamlConfigEnvironment.cs index e77d325e..828f32c5 100644 --- a/GFramework.Godot/Config/GodotYamlConfigEnvironment.cs +++ b/GFramework.Godot/Config/GodotYamlConfigEnvironment.cs @@ -104,41 +104,36 @@ internal sealed class GodotYamlConfigEnvironment private static IReadOnlyList? EnumerateDirectoryCore(string path) { - if (!path.IsGodotPath()) + return path.IsGodotPath() + ? EnumerateGodotDirectory(path) + : EnumerateFileSystemDirectory(path); + } + + private static IReadOnlyList? EnumerateFileSystemDirectory(string path) + { + try { - try + if (!Directory.Exists(path)) { - if (!Directory.Exists(path)) - { - return null; - } + return null; + } - return Directory - .EnumerateFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly) - .Select(static entryPath => new GodotYamlConfigDirectoryEntry( - Path.GetFileName(entryPath), - Directory.Exists(entryPath))) - .ToArray(); - } - catch (IOException) - { - // 非 Godot 路径分支与公开契约保持一致:宿主无法访问目录时返回 null,而不是泄漏底层异常。 - return null; - } - catch (UnauthorizedAccessException) - { - return null; - } - catch (ArgumentException) - { - return null; - } - catch (NotSupportedException) - { - return null; - } + return Directory + .EnumerateFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly) + .Select(static entryPath => new GodotYamlConfigDirectoryEntry( + Path.GetFileName(entryPath), + Directory.Exists(entryPath))) + .ToArray(); } + catch (Exception ex) when (IsExpectedDirectoryEnumerationException(ex)) + { + // 非 Godot 路径分支与公开契约保持一致:宿主无法访问目录时返回 null,而不是泄漏底层异常。 + return null; + } + } + private static IReadOnlyList? EnumerateGodotDirectory(string path) + { using var directory = DirAccess.Open(path); if (directory == null) { @@ -170,9 +165,15 @@ internal sealed class GodotYamlConfigEnvironment // 目录枚举句柄必须成对结束,避免未来循环体扩展后在异常路径上遗留引擎状态。 directory.ListDirEnd(); } + return entries; } + private static bool IsExpectedDirectoryEnumerationException(Exception exception) + { + return exception is IOException or UnauthorizedAccessException or ArgumentException or NotSupportedException; + } + private static bool FileExistsCore(string path) { return path.IsGodotPath() diff --git a/GFramework.Godot/Scene/SceneBehaviorBase.cs b/GFramework.Godot/Scene/SceneBehaviorBase.cs index bfee418f..fee000f5 100644 --- a/GFramework.Godot/Scene/SceneBehaviorBase.cs +++ b/GFramework.Godot/Scene/SceneBehaviorBase.cs @@ -141,10 +141,11 @@ public abstract class SceneBehaviorBase : ISceneBehavior /// 当场景被其他场景覆盖或失去焦点时调用。 /// /// 表示暂停操作完成的ValueTask。 - public virtual async ValueTask OnPauseAsync() - { - if (_scene != null) - await _scene.OnPauseAsync(); + public virtual async ValueTask OnPauseAsync() + { + if (_scene != null) + // 暂停后紧接着会修改 Owner 的处理开关,必须回到 Godot 主线程继续执行。 + await _scene.OnPauseAsync().ConfigureAwait(true); // 暂停处理 Owner.SetProcess(false); @@ -159,13 +160,14 @@ public abstract class SceneBehaviorBase : ISceneBehavior /// 当场景重新获得焦点或从暂停状态恢复时调用。 /// /// 表示恢复操作完成的ValueTask。 - public virtual async ValueTask OnResumeAsync() - { - if (Owner.IsInvalidNode()) - return; + public virtual async ValueTask OnResumeAsync() + { + if (Owner.IsInvalidNode()) + return; - if (_scene != null) - await _scene.OnResumeAsync(); + if (_scene != null) + // 恢复完成后要立刻重新启用节点处理流程,因此显式保留当前同步上下文。 + await _scene.OnResumeAsync().ConfigureAwait(true); // 恢复处理 Owner.SetProcess(true); @@ -195,10 +197,11 @@ public abstract class SceneBehaviorBase : ISceneBehavior /// 在场景完全退出后调用,释放占用的内存和资源。 /// /// 表示卸载操作完成的ValueTask。 - public virtual async ValueTask OnUnloadAsync() - { - if (_scene != null) - await _scene.OnUnloadAsync(); + public virtual async ValueTask OnUnloadAsync() + { + if (_scene != null) + // 卸载后的 QueueFreeX 必须在 Godot 节点线程上调用,不能切走同步上下文。 + await _scene.OnUnloadAsync().ConfigureAwait(true); // 释放节点 Owner.QueueFreeX(); 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 3fa5eb3a..424cc2b6 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,36 +6,34 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-052` -- 当前阶段:`Phase 52` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-053` +- 当前阶段:`Phase 53` - 当前焦点: - - `2026-04-24` 本轮从当前 PR review 的未解决线程回切到 `GFramework.Game` / `GFramework.Godot.SourceGenerators.Tests` - - `UnifiedSettingsFile.Sections` 与 `CloneFile` fallback 已对齐为“可保留原 comparer 时保留,否则显式回退到 `StringComparer.Ordinal`”的文档与实现契约 - - `AutoRegisterExportedCollectionsGeneratorTests` 中剩余的 `await test.RunAsync();` 已统一补齐 `.ConfigureAwait(false)`,并同步让 `VerifyDiagnosticsAsync` 内部消费异步等待 - - 当前批次仍需避免混入与 analyzer-warning-reduction 无关的既有工作树改动 + - `2026-04-24` 本轮按 `$gframework-batch-boot 75` 重新建立当前分支相对 `origin/main` 的 batch 基线,并从整仓 `Release` build 里挑选低风险热点 + - `GFramework.Godot` 已完成一轮独立 warning 清理:`GodotYamlConfigEnvironment` 的目录枚举逻辑已拆分 helper,`AbstractArchitecture` / `SceneBehaviorBase` 中需要保留 Godot 同步上下文的 await 已显式改为 `.ConfigureAwait(true)` + - `GFramework.Godot.Tests` 已同步清理对应测试 warning:异步断言显式使用 `.ConfigureAwait(false)`,`RichTextMarkupTests` 中测试字典显式指定 `StringComparer.Ordinal` + - 当前代码批次相对 `origin/main` 的待提交 diff 为 `6` 个文件、`107` 行变更,远低于 `$gframework-batch-boot 75` 的主停止阈值;本轮在这里收口,是因为下一批候选将进入 `GFramework.Game` 等高基线模块,而不再是同等级低风险切片 ## 当前活跃事实 - 之前记录的 plain `dotnet build` `0 Warning(s)` 属于增量构建假阴性,不能再作为 warning 检查真值 -- 本轮已完成 `GFramework.Godot.SourceGenerators` warning 清理:clean `Release` build 从 9 个 warning 降至 0 个 warning -- 当前已确认解决的文件包括 `BindNodeSignalGenerator.cs`、`GetNodeGenerator.cs`、`GodotProjectMetadataGenerator.cs`、`Registration/AutoRegisterExportedCollectionsGenerator.cs` - 本轮直接执行仓库根目录 `dotnet clean` 仍在 `ValidateSolutionConfiguration` 阶段失败,输出未提供具体 error 文本 -- 本轮直接执行仓库根目录 `dotnet build` 成功,并给出 `1184 warning(s)` 的真实输出 -- `GFramework.Godot.SourceGenerators.Tests` 已通过测试辅助模板抽取与 `ConfigureAwait(false)` 修正,当前 `Debug` / `Release` 构建均为 `0 Warning(s)` -- 本轮已验证 `dotnet test GFramework.Godot.SourceGenerators.Tests/GFramework.Godot.SourceGenerators.Tests.csproj -c Release --no-build`,结果为 `Passed: 48` -- 本轮已把 PR #283 中仍打开的 `UnifiedSettingsDataRepository.cs` comparer 契约线程落到代码与 XML 注释,避免 fallback 语义继续依赖隐式默认 comparer -- 本轮已确认 `AutoRegisterExportedCollectionsGeneratorTests` 的 5 处裸 `await test.RunAsync();` 不是当前 Release build 告警来源,但仍作为 PR review 一致性项一并修正 +- 本轮直接执行仓库根目录 `dotnet build GFramework.sln -c Release` 成功,并给出 `1122 warning(s)` 的当前整仓观测值 +- `GFramework.Godot/GFramework.Godot.csproj -c Release` 在本轮收尾验证中已达到 `0 Warning(s)`、`0 Error(s)` +- `GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release` 在串行复验中已达到 `0 Warning(s)`、`0 Error(s)` +- 本轮已验证 `dotnet test GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --filter "FullyQualifiedName~AbstractArchitectureModuleInstallationTests|FullyQualifiedName~GodotYamlConfigLoaderTests|FullyQualifiedName~RichTextMarkupTests"`,结果为 `Passed: 15` +- `GFramework.Godot` 原先暴露的 `MA0051` 与 `MA0004` 热点都已清理完成;当前同域低风险切片基本耗尽 ## 当前风险 - 如果后续继续依赖增量 `dotnet build`,容易再次把 warning 数量误判为 0 - 缓解措施:每轮 warning 检查前先执行 `dotnet clean`,再执行目标 `dotnet build` - 仓库根目录 `dotnet clean` 目前仍然无法给出新的 clean 基线 - - 缓解措施:若下一轮继续做整仓 warning reduction,先定位 `dotnet clean` 的 solution-level 失败原因,或明确继续沿用用户确认的 `1193 warning(s)` clean 基线与本轮 `1184 warning(s)` direct build 观测值 -- 当前 worktree 已存在与本批次无关的未提交改动 - - 缓解措施:提交当前批次时只暂存 `GFramework.Godot.SourceGenerators.Tests` 与对应 `ai-plan` 文件,避免混入其他 topic 变更 -- `GFramework.Game` 当前 `Release` build 仍带有既有 analyzer warning 基线 - - 缓解措施:本轮仅验证改动未新增 `UnifiedSettingsDataRepository` / `UnifiedSettingsFile` 相关 warning;若继续在该模块做 warning reduction,需要另开切片处理现存基线 + - 缓解措施:若下一轮继续做整仓 warning reduction,先定位 `dotnet clean` 的 solution-level 失败原因,或明确继续沿用用户确认的 `1193 warning(s)` clean 基线与本轮 `1122 warning(s)` direct build 观测值 +- 当前 worktree 仍存在未跟踪的 `.codex` 目录 + - 缓解措施:提交当前批次时只暂存 analyzer-warning-reduction 相关源码与 `ai-plan` 文件,避免把工作目录辅助文件混入提交 +- 下一轮最明显的剩余热点将转入 `GFramework.Game` 等高 warning 基线模块 + - 缓解措施:恢复时先重新跑整仓 build 热点筛选,再决定是否接受更高上下文成本的 `GFramework.Game` 切片,或先排查 solution-level clean 失败原因 ## 活跃文档 @@ -51,25 +49,19 @@ ## 验证说明 -- `dotnet clean` +- `dotnet clean GFramework.sln -c Release` - 结果:失败;停在 solution `ValidateSolutionConfiguration`,`0 Warning(s)`、`0 Error(s)`,未输出更具体的 error 文本 -- `dotnet build` - - 结果:成功;`1184 Warning(s)`、`0 Error(s)` -- `dotnet build GFramework.Godot.SourceGenerators.Tests/GFramework.Godot.SourceGenerators.Tests.csproj` - - 初始结果:成功;`24 Warning(s)`、`0 Error(s)` - - 本轮收尾结果:成功;`0 Warning(s)`、`0 Error(s)` -- `dotnet build GFramework.Godot.SourceGenerators.Tests/GFramework.Godot.SourceGenerators.Tests.csproj -c Release` +- `dotnet build GFramework.sln -c Release` + - 结果:成功;`1122 Warning(s)`、`0 Error(s)` +- `dotnet build GFramework.Godot/GFramework.Godot.csproj -c Release` - 结果:成功;`0 Warning(s)`、`0 Error(s)` -- `dotnet test GFramework.Godot.SourceGenerators.Tests/GFramework.Godot.SourceGenerators.Tests.csproj -c Release --no-build` - - 结果:成功;`Passed: 48`、`Failed: 0` -- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release` - - 结果:成功;`533 Warning(s)`、`0 Error(s)`;模块仍存在既有 warning 基线,本轮 follow-up 仅处理 PR review 指向的 comparer 契约与测试异步等待一致性 -- `dotnet build GFramework.Godot.SourceGenerators.Tests/GFramework.Godot.SourceGenerators.Tests.csproj -c Release` - - 结果:成功;`0 Warning(s)`、`0 Error(s)` -- `dotnet test GFramework.Godot.SourceGenerators.Tests/GFramework.Godot.SourceGenerators.Tests.csproj -c Release --no-build` - - 结果:成功;`Passed: 48`、`Failed: 0` +- `dotnet test GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --filter "FullyQualifiedName~AbstractArchitectureModuleInstallationTests|FullyQualifiedName~GodotYamlConfigLoaderTests|FullyQualifiedName~RichTextMarkupTests"` + - 结果:成功;`Passed: 15`、`Failed: 0` +- `dotnet build GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release` + - 首次并行验证:成功;`1 Warning(s)`、`0 Error(s)`;`MSB3026` 来自与并行 `dotnet test` 竞争同一输出 DLL + - 串行复验:成功;`0 Warning(s)`、`0 Error(s)` ## 下一步建议 -1. 提交当前 comparer 契约与 `ConfigureAwait(false)` PR follow-up,并确认只纳入本 topic 相关文件 -2. 视 PR review 反馈决定是否继续收敛 `GFramework.Game` 现有 warning 基线,或返回下一轮整仓 warning 热点筛选 +1. 提交当前 `GFramework.Godot` / `GFramework.Godot.Tests` warning 清理批次,并继续保持只纳入本 topic 相关文件 +2. 下一轮若继续使用 `$gframework-batch-boot 75`,先决定是优先排查 solution-level `dotnet clean` 失败,还是接受更高上下文成本进入 `GFramework.Game` warning 热点 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 95ac513a..e05a9c54 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 @@ -2,6 +2,37 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-24 — RP-053 + +### 阶段:`GFramework.Godot` / `GFramework.Godot.Tests` 小批次 warning 清理 + +- 触发背景: + - 用户以 `$gframework-batch-boot 75` 要求继续按批次推进 analyzer warning reduction,并以 `origin/main` 作为累计分支 diff 基线 + - 当前 worktree `fix/analyzer-warning-reduction-batch` 相对 `origin/main` 的已提交分支 diff 为 `0` 个文件,具备继续落一个低风险 warning batch 的空间 + - solution-level `dotnet clean GFramework.sln -c Release` 仍在 `ValidateSolutionConfiguration` 阶段失败,因此本轮继续用直接 `dotnet build GFramework.sln -c Release` 建立热点观察值 +- 主线程实施: + - 运行 `dotnet build GFramework.sln -c Release`,确认当前整仓观测值为 `1122 warning(s)`,并从输出中挑选 `GFramework.Godot` 的小范围热点作为本轮批次 + - 在 `GodotYamlConfigEnvironment.cs` 中按“普通文件系统 / Godot 路径”拆分目录枚举 helper,消除 `MA0051` + - 在 `AbstractArchitecture.cs` 与 `SceneBehaviorBase.cs` 中将必须保留 Godot 主线程上下文的 await 显式改为 `.ConfigureAwait(true)`,清理 `MA0004` 并把线程意图写入注释 + - 在 `GFramework.Godot.Tests` 中补齐异步断言的 `.ConfigureAwait(false)`,并让 `RichTextMarkupTests` 的测试字典显式指定 `StringComparer.Ordinal` +- 验证里程碑: + - `dotnet clean GFramework.sln -c Release` + - 结果:失败;停在 `ValidateSolutionConfiguration`,`0 Warning(s)`、`0 Error(s)` + - `dotnet build GFramework.sln -c Release` + - 结果:成功;`1122 Warning(s)`、`0 Error(s)` + - `dotnet build GFramework.Godot/GFramework.Godot.csproj -c Release` + - 第一轮修复后:成功;`12 Warning(s)`、`0 Error(s)`,仅剩 `MA0004` + - 第二轮修复后:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet test GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --filter "FullyQualifiedName~AbstractArchitectureModuleInstallationTests|FullyQualifiedName~GodotYamlConfigLoaderTests|FullyQualifiedName~RichTextMarkupTests"` + - 结果:成功;`Passed: 15`、`Failed: 0` + - `dotnet build GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release` + - 并行验证时:成功;`1 Warning(s)`、`0 Error(s)`;`MSB3026` 为与并行 `dotnet test` 竞争输出 DLL 的文件占用 + - 串行复验:成功;`0 Warning(s)`、`0 Error(s)` +- 当前结论: + - `GFramework.Godot` 与 `GFramework.Godot.Tests` 本轮直接涉及的 warning 已全部清零 + - 当前待提交代码批次相对 `origin/main` 的源码 diff 为 `6` 个文件、`107` 行,距离 `$gframework-batch-boot 75` 主停止阈值仍有充足余量 + - 继续推进的下一批候选将主要落在 `GFramework.Game` 等高 warning 基线模块,已不再属于当前同等级低风险切片,因此本轮在这里收口并进入提交 + ## 2026-04-24 — RP-052 ### 阶段:PR review follow-up(comparer 契约 + `ConfigureAwait(false)` 收尾)