From 685897f2de66fa05e3b9ce11cc45571bd8e6a487 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:50:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(core):=20=E6=94=B6=E5=8F=A3=20PR267=20?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E5=A5=91=E7=BA=A6=E9=81=97=E7=95=99=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 AsyncLogAppender 接口刷新路径重复触发完成事件,并补充单次通知回归测试 - 补充 Architecture、CoroutineExceptionEventArgs 与阶段协调器的事件契约注释 - 更新 PhaseChanged 迁移文档与 analyzer-warning-reduction recovery 记录 --- .../Coroutine/CoroutineSchedulerTests.cs | 2 +- .../Logging/AsyncLogAppenderTests.cs | 17 ++++++++++ GFramework.Core/Architectures/Architecture.cs | 10 +++++- .../ArchitecturePhaseCoordinator.cs | 4 +-- .../Coroutine/CoroutineExceptionEventArgs.cs | 1 + .../Logging/Appenders/AsyncLogAppender.cs | 3 +- .../analyzer-warning-reduction-tracking.md | 24 +++++++++++--- .../analyzer-warning-reduction-trace.md | 32 +++++++++++++++++++ docs/zh-CN/core/architecture.md | 11 +++++++ docs/zh-CN/core/lifecycle.md | 3 ++ 10 files changed, 96 insertions(+), 11 deletions(-) diff --git a/GFramework.Core.Tests/Coroutine/CoroutineSchedulerTests.cs b/GFramework.Core.Tests/Coroutine/CoroutineSchedulerTests.cs index 432eddf8..08b0fb8a 100644 --- a/GFramework.Core.Tests/Coroutine/CoroutineSchedulerTests.cs +++ b/GFramework.Core.Tests/Coroutine/CoroutineSchedulerTests.cs @@ -377,7 +377,7 @@ public class CoroutineSchedulerTests var handle = _scheduler.Run(CreateExceptionCoroutine()); _scheduler.Update(); - var observation = await exceptionSource.Task; + var observation = await exceptionSource.Task.WaitAsync(TimeSpan.FromSeconds(3)); Assert.Multiple(() => { diff --git a/GFramework.Core.Tests/Logging/AsyncLogAppenderTests.cs b/GFramework.Core.Tests/Logging/AsyncLogAppenderTests.cs index cc7a0985..89db303c 100644 --- a/GFramework.Core.Tests/Logging/AsyncLogAppenderTests.cs +++ b/GFramework.Core.Tests/Logging/AsyncLogAppenderTests.cs @@ -103,6 +103,23 @@ public class AsyncLogAppenderTests }); } + [Test] + public void ILogAppender_Flush_Should_Raise_OnFlushCompleted_Only_Once() + { + var innerAppender = new TestAppender(); + using var asyncAppender = new AsyncLogAppender(innerAppender, bufferSize: 10); + ILogAppender logAppender = asyncAppender; + var observedResults = new List(); + + asyncAppender.Append(new LogEntry(DateTime.UtcNow, LogLevel.Info, "TestLogger", "Interface flush check", null, null)); + asyncAppender.OnFlushCompleted += (_, eventArgs) => observedResults.Add(eventArgs.Success); + + logAppender.Flush(); + + Assert.That(observedResults, Has.Count.EqualTo(1)); + Assert.That(observedResults, Has.All.True); + } + [Test] public void Dispose_ShouldProcessRemainingEntries() { diff --git a/GFramework.Core/Architectures/Architecture.cs b/GFramework.Core/Architectures/Architecture.cs index 6a284145..509fd94a 100644 --- a/GFramework.Core/Architectures/Architecture.cs +++ b/GFramework.Core/Architectures/Architecture.cs @@ -99,8 +99,16 @@ public abstract class Architecture : IArchitecture public virtual Action? Configurator => null; /// - /// 阶段变更事件(用于测试和扩展) + /// 在架构生命周期阶段发生变化时触发。 /// + /// + /// + /// 订阅者应通过 读取当前阶段,而不是依赖内部生命周期对象。 + /// + /// + /// 事件委托中的 sender 始终为当前 实例,便于测试与外部扩展保持稳定的发布者契约。 + /// + /// public event EventHandler? PhaseChanged; #endregion diff --git a/GFramework.Core/Architectures/ArchitecturePhaseCoordinator.cs b/GFramework.Core/Architectures/ArchitecturePhaseCoordinator.cs index 51675ac9..42cf9578 100644 --- a/GFramework.Core/Architectures/ArchitecturePhaseCoordinator.cs +++ b/GFramework.Core/Architectures/ArchitecturePhaseCoordinator.cs @@ -39,8 +39,8 @@ internal sealed class ArchitecturePhaseCoordinator( /// /// 进入指定阶段并广播给所有阶段消费者。 - /// 顺序保持为“更新阶段值 → 生命周期钩子 → 容器中的阶段监听器 → 外部事件”, - /// 以兼容既有调用约定。 + /// 顺序保持为“更新阶段值 → 生命周期钩子 → 容器中的阶段监听器”, + /// 以保证框架扩展与运行时组件看到一致的阶段视图。 /// /// 目标阶段。 public void EnterPhase(ArchitecturePhase next) diff --git a/GFramework.Core/Coroutine/CoroutineExceptionEventArgs.cs b/GFramework.Core/Coroutine/CoroutineExceptionEventArgs.cs index cddfc054..dfacd69c 100644 --- a/GFramework.Core/Coroutine/CoroutineExceptionEventArgs.cs +++ b/GFramework.Core/Coroutine/CoroutineExceptionEventArgs.cs @@ -11,6 +11,7 @@ public sealed class CoroutineExceptionEventArgs : EventArgs /// /// 发生异常的协程句柄。 /// 协程执行过程中抛出的异常。 + /// public CoroutineExceptionEventArgs(CoroutineHandle handle, Exception exception) { Handle = handle; diff --git a/GFramework.Core/Logging/Appenders/AsyncLogAppender.cs b/GFramework.Core/Logging/Appenders/AsyncLogAppender.cs index eda3f9a3..c377acce 100644 --- a/GFramework.Core/Logging/Appenders/AsyncLogAppender.cs +++ b/GFramework.Core/Logging/Appenders/AsyncLogAppender.cs @@ -117,8 +117,7 @@ public sealed class AsyncLogAppender : ILogAppender /// void ILogAppender.Flush() { - var success = Flush(); - OnFlushCompleted?.Invoke(this, new AsyncLogFlushCompletedEventArgs(success)); + Flush(); } /// 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 e84369d9..2c4d2b08 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 @@ -7,11 +7,13 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-013` -- 当前阶段:`Phase 13` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-014` +- 当前阶段:`Phase 14` - 当前焦点: - - 当前 `MA0046` 主批次已在 `Architecture`、`AsyncLogAppender` 与 `CoroutineScheduler` 上收口完成 - - 下一轮优先从 `MA0016` 或 `MA0002` 中选择低风险批次继续推进;`MA0015` 与 `MA0077` 继续作为尾项顺手吸收 + - 当前分支 PR #267 的 latest CodeRabbit unresolved threads、outside-diff comment 与 nitpick comment 已完成本地复核 + - 本轮优先收口仍然成立的 follow-up:`AsyncLogAppender` 接口路径重复触发 `OnFlushCompleted`、事件/XML 契约缺口、 + `PhaseChanged` 迁移文档说明与 `ai-plan` 基线表述歧义 + - 下一轮默认恢复到 `MA0016` 或 `MA0002` 低风险批次;`MA0015` 与 `MA0077` 继续作为尾项顺手吸收 - `GFramework.Godot` 的 `Timing.cs` 已同步适配新事件签名,但当前 worktree 的 Godot restore 资产仍受 Windows fallback package folder 干扰,独立 build 需在修复资产后补跑 - 后续继续按 warning 类型和数量批处理,而不是回退到按单文件切片推进 - 当某一轮主类型数量不足时,允许顺手合并其他低冲突 warning 类型,`MA0015` 与 `MA0077` @@ -28,6 +30,8 @@ - 已增强 `gframework-pr-review` 脚本与 skill 文档,降低超长 JSON 直出导致的 review 信号漏看风险 - 已完成 `GFramework.Core` 当前 `MA0046` 批次:将阶段、协程与异步日志事件统一迁移到 `EventHandler` 形状, 并同步更新 `GFramework.Godot` 订阅点、定向测试与 `docs/zh-CN` 示例 +- 已完成当前 PR #267 review follow-up:修复 `AsyncLogAppender` 的 `ILogAppender.Flush()` 双重完成通知,并补齐 + `PhaseChanged` / `CoroutineExceptionEventArgs` XML 文档、`PhaseChanged` 迁移说明和 `ai-plan` 基线注释 - 当前 `GFramework.Core` `net8.0` warnings-only 基线已降到 `9` 条;剩余 warning 集中在 `MA0016` 集合抽象接口、`MA0002` comparer 重载,以及 `MA0015` / `MA0077` 两个低数量尾项 @@ -56,6 +60,8 @@ 让“先落盘、再定向抽取”成为默认可操作路径 - `RP-013` 已完成 `GFramework.Core` 当前 `MA0046` 批次,并以新的事件参数类型替换阶段、协程和异步日志事件的 非标准签名;`GFramework.Core` `net8.0` warnings-only 基线由 `15` 降至 `9` +- `RP-014` 使用 `gframework-pr-review` 复核当前分支 PR #267 的 latest head review threads、outside-diff comment 与 + nitpick comment 后,确认 8 条高信号项中仍成立的是 1 个行为 bug 与 7 个文档/测试/跟踪缺口,并按最小改动收口 - 当前工作树分支 `fix/analyzer-warning-reduction-batch` 已在 `ai-plan/public/README.md` 建立 topic 映射 ## 当前风险 @@ -130,11 +136,19 @@ - 结果:`0 Warning(s)`,`0 Error(s)` - `RP-013` 的定向验证结果: - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release --no-restore -p:TargetFramework=net8.0 -p:RestoreFallbackFolders="" -nologo -clp:"Summary;WarningsOnly"` - - 结果:`9 Warning(s)`,`0 Error(s)`;当前 `GFramework.Core` `net8.0` warnings-only 输出中已不再出现 `MA0046` + - 结果:`9 Warning(s)`,`0 Error(s)`;相对 `RP-009` / `RP-011` 的 warnings-only 基线 `15 Warning(s)` 已降到 `9 Warning(s)`, + 当前 `GFramework.Core` `net8.0` 输出中已不再出现 `MA0046` - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~ArchitectureLifecycleBehaviorTests|FullyQualifiedName~CoroutineSchedulerTests|FullyQualifiedName~AsyncLogAppenderTests" -m:1 -p:RestoreFallbackFolders="" -nologo` - 结果:`50 Passed`,`0 Failed` - `dotnet build GFramework.Godot/GFramework.Godot.csproj -c Release --no-restore -p:RestoreFallbackFolders="" -nologo` - 结果:失败;当前 worktree 的 Godot restore 资产仍引用 Windows fallback package folder,尚未完成独立项目编译验证 +- `RP-014` 的定向验证结果: + - `dotnet restore GFramework.Core.Tests/GFramework.Core.Tests.csproj -p:RestoreFallbackFolders="" -nologo` + - 结果:通过;host Windows `dotnet` 首次验证前补齐了缺失的 `Meziantou.Analyzer 3.0.48` 包 + - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release --no-restore -p:TargetFramework=net8.0 -p:RestoreFallbackFolders="" -nologo` + - 结果:`9 Warning(s)`,`0 Error(s)`;`AsyncLogAppender` 行为修复与 XML / 文档补充未引入新的 `GFramework.Core` `net8.0` 构建错误 + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~CoroutineSchedulerTests.Scheduler_Should_Raise_OnCoroutineException_With_EventArgs|FullyQualifiedName~AsyncLogAppenderTests.Flush_Should_Raise_OnFlushCompleted_With_Sender_And_Result|FullyQualifiedName~AsyncLogAppenderTests.ILogAppender_Flush_Should_Raise_OnFlushCompleted_Only_Once|FullyQualifiedName~ArchitectureLifecycleBehaviorTests.InitializeAsync_Should_Raise_PhaseChanged_With_Sender_And_EventArgs" -m:1 -p:RestoreFallbackFolders="" -nologo` + - 结果:`4 Passed`,`0 Failed` - active 跟踪文件只保留当前恢复点、活跃事实、风险与下一步,不再重复保存已完成阶段的长篇历史 ## 下一步 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 666ab845..369c76ce 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,5 +1,37 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-21 — RP-014 + +### 阶段:PR #267 review follow-up 收口(RP-014) + +- 使用 `gframework-pr-review` 抓取当前分支 PR #267 的 latest head review threads、outside-diff comment、nitpick comment、 + MegaLinter 摘要与测试报告,并确认本轮除了 6 条 open thread 之外,还存在 1 条 outside-diff 与 1 条 nitpick 需要一并复核 +- 本地复核后确认仍成立的项: + - `AsyncLogAppender` 的显式接口实现 `ILogAppender.Flush()` 会在调用 `Flush()` 后再次手动触发 `OnFlushCompleted`, + 导致接口路径重复通知 + - `Architecture.PhaseChanged`、`CoroutineExceptionEventArgs` 与 `ArchitecturePhaseCoordinator.EnterPhase` 的 XML/注释契约仍未完全同步 + - `CoroutineSchedulerTests` 的异常事件测试缺少测试级超时 + - `docs/zh-CN/core/architecture.md` 与 `docs/zh-CN/core/lifecycle.md` 仍缺少明确的 `PhaseChanged` 迁移说明 + - `ai-plan` active tracking 中 `RP-013` 的 `9 Warning(s)` 需要明确是相对 `RP-009` / `RP-011` 的 warnings-only 基线收敛 +- 实施最小修复: + - 删除 `ILogAppender.Flush()` 中重复的完成事件触发,只保留 `Flush(TimeSpan?)` 内的单一通知源 + - 为接口调用路径补充单次完成通知回归测试,并为协程异常事件测试增加 `WaitAsync(TimeSpan.FromSeconds(3))` + - 补齐 `Architecture.PhaseChanged`、`CoroutineExceptionEventArgs` 与 `ArchitecturePhaseCoordinator.EnterPhase` 的契约文档 + - 在 `docs/zh-CN/core/architecture.md` 与 `docs/zh-CN/core/lifecycle.md` 中加入 `phase => ...` 迁移到 `(_, args) => ...` 的说明 + - 更新 `ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md` 的恢复点、基线描述与验证结果 +- 验证结果: + - `dotnet restore GFramework.Core.Tests/GFramework.Core.Tests.csproj -p:RestoreFallbackFolders="" -nologo` + - 结果:通过;host Windows `dotnet` 首次验证前补齐了缺失的 `Meziantou.Analyzer 3.0.48` + - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release --no-restore -p:TargetFramework=net8.0 -p:RestoreFallbackFolders="" -nologo` + - 结果:`9 Warning(s)`,`0 Error(s)` + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~CoroutineSchedulerTests.Scheduler_Should_Raise_OnCoroutineException_With_EventArgs|FullyQualifiedName~AsyncLogAppenderTests.Flush_Should_Raise_OnFlushCompleted_With_Sender_And_Result|FullyQualifiedName~AsyncLogAppenderTests.ILogAppender_Flush_Should_Raise_OnFlushCompleted_Only_Once|FullyQualifiedName~ArchitectureLifecycleBehaviorTests.InitializeAsync_Should_Raise_PhaseChanged_With_Sender_And_EventArgs" -m:1 -p:RestoreFallbackFolders="" -nologo` + - 结果:`4 Passed`,`0 Failed` +- 当前结论: + - PR #267 里当前仍成立的 CodeRabbit 高信号项已在本地收口 + - 修复内容没有改变 `EventHandler` 迁移方向,只是补齐行为、文档与恢复信息 +- 下一步建议: + - 恢复到 `MA0016` / `MA0002` 主批次,默认先看 `LoggingConfiguration`、`FilterConfiguration` 与 `CollectionExtensions` + ## 2026-04-21 — RP-013 ### 阶段:`MA0046` 事件签名批次收口(RP-013) diff --git a/docs/zh-CN/core/architecture.md b/docs/zh-CN/core/architecture.md index 3c54e1bb..9744a844 100644 --- a/docs/zh-CN/core/architecture.md +++ b/docs/zh-CN/core/architecture.md @@ -126,10 +126,21 @@ protected override void OnInitialize() 其中 `PhaseChanged` 现在遵循标准 `EventHandler` 约定, 阶段值通过 `args.Phase` 读取。 +如果你正在从旧版本迁移,需要把单参数写法 `phase => ...` 改成 `(_, args) => ...`, +并通过 `ArchitecturePhaseChangedEventArgs.Phase` 读取阶段值。 + 如果你需要在 `Ready`、`Destroying` 等阶段执行横切逻辑,比起把这类逻辑塞进某个具体 `System`,更适合单独实现 `IArchitectureLifecycleHook`。 ```csharp +architecture.PhaseChanged += (_, args) => +{ + if (args.Phase == ArchitecturePhase.Ready) + { + Console.WriteLine("Architecture ready from event."); + } +}; + public sealed class MetricsHook : IArchitectureLifecycleHook { public void OnPhase(ArchitecturePhase phase, IArchitecture architecture) diff --git a/docs/zh-CN/core/lifecycle.md b/docs/zh-CN/core/lifecycle.md index 4cdaa8fc..f0f4f096 100644 --- a/docs/zh-CN/core/lifecycle.md +++ b/docs/zh-CN/core/lifecycle.md @@ -138,6 +138,9 @@ architecture.RegisterLifecycleHook(new MetricsHook()); 如果你只需要观察阶段变化,也可以直接订阅: +如果你从旧版本的 `PhaseChanged` 迁移过来,需要把旧写法 `phase => ...` 改成 `(_, args) => ...`, +并通过 `ArchitecturePhaseChangedEventArgs.Phase` 读取阶段值。 + ```csharp architecture.PhaseChanged += (_, args) => {