fix(pr-review): 收口当前 PR 仍有效的 review 建议

- 修复 AGENTS 中 latest-head review thread 指向的英文标点一致性问题

- 删除 MediatorAdvancedFeaturesTests 中未使用的 TestLoggingBehavior 测试基础设施

- 重构 VersionedMigrationRunner 的迁移执行上下文传递并补充对应 XML 文档

- 更新 analyzer warning reduction 的 active tracking 与 trace,记录 PR #291 复核结果和 639 条根构建基线
This commit is contained in:
gewuyou 2026-04-25 17:18:01 +08:00
parent 4edfe53cd9
commit a75194337e
5 changed files with 91 additions and 58 deletions

View File

@ -33,7 +33,7 @@ All AI agents and contributors must follow these rules when writing, reviewing,
baseline from a non-incremental repository-root build by running `dotnet clean` and then `dotnet build`. baseline from a non-incremental repository-root build by running `dotnet clean` and then `dotnet build`.
- Contributors MUST NOT treat a repeated incremental `dotnet build` result as authoritative for warning inspection when - Contributors MUST NOT treat a repeated incremental `dotnet build` result as authoritative for warning inspection when
a clean baseline has not been captured in the same round. a clean baseline has not been captured in the same round.
- If a direct `dotnet clean``dotnet build`or `dotnet test` command fails inside the agent sandbox with missing - If a direct `dotnet clean`, `dotnet build`, or `dotnet test` command fails inside the agent sandbox with missing
diagnostics, `Permission denied`, MSBuild pipe/socket errors, or other environment-only noise that does not match a diagnostics, `Permission denied`, MSBuild pipe/socket errors, or other environment-only noise that does not match a
normal shell invocation, contributors MUST request permission and rerun the same direct command outside the sandbox normal shell invocation, contributors MUST request permission and rerun the same direct command outside the sandbox
before concluding that the repository or toolchain is broken. before concluding that the repository or toolchain is broken.

View File

@ -366,14 +366,6 @@ public sealed class TestBehaviorRequestHandler : IRequestHandler<TestBehaviorReq
} }
} }
public static class TestLoggingBehavior
{
/// <summary>
/// 记录测试行为写入的日志消息,同时避免向调用方暴露具体集合实现。
/// </summary>
public static IList<string> LoggedMessages { get; set; } = new List<string>();
}
public sealed record TestValidatedRequest : IRequest<string> public sealed record TestValidatedRequest : IRequest<string>
{ {
public int Value { get; init; } public int Value { get; init; }

View File

@ -23,6 +23,19 @@ namespace GFramework.Game.Internal;
/// </remarks> /// </remarks>
internal static class VersionedMigrationRunner internal static class VersionedMigrationRunner
{ {
/// <summary>
/// 复用一次迁移链执行期间不会变化的上下文,避免多个 helper 重复传递同一组委托和消息元数据。
/// </summary>
/// <typeparam name="TData">迁移数据类型。</typeparam>
/// <typeparam name="TMigration">迁移描述类型。</typeparam>
private readonly record struct MigrationExecutionContext<TData, TMigration>(
Func<TData, int> GetVersion,
Func<TMigration, int> GetToVersion,
Func<TMigration, TData, TData> ApplyMigration,
string SubjectName,
string MigrationKind)
where TData : class;
/// <summary> /// <summary>
/// 校验迁移注册是否表示一次有效的前向升级。 /// 校验迁移注册是否表示一次有效的前向升级。
/// </summary> /// </summary>
@ -99,21 +112,24 @@ internal static class VersionedMigrationRunner
return data; return data;
} }
var context = new MigrationExecutionContext<TData, TMigration>(
getVersion,
getToVersion,
applyMigration,
subjectName,
migrationKind);
var current = data; var current = data;
while (currentVersion < targetVersion) while (currentVersion < targetVersion)
{ {
var migration = GetRequiredMigration(resolveMigration, currentVersion, subjectName, migrationKind); var migration = GetRequiredMigration(resolveMigration, currentVersion, in context);
var result = ApplyMigrationStep( var result = ApplyMigrationStep(
migration, migration,
current, current,
currentVersion, currentVersion,
targetVersion, targetVersion,
getVersion, in context);
getToVersion,
applyMigration,
subjectName,
migrationKind);
current = result.Data; current = result.Data;
currentVersion = result.Version; currentVersion = result.Version;
} }
@ -140,24 +156,24 @@ internal static class VersionedMigrationRunner
/// <summary> /// <summary>
/// 解析当前版本必须存在的下一步迁移器,避免在调用循环中重复拼接相同错误。 /// 解析当前版本必须存在的下一步迁移器,避免在调用循环中重复拼接相同错误。
/// </summary> /// </summary>
/// <typeparam name="TData">迁移数据类型。</typeparam>
/// <typeparam name="TMigration">迁移描述类型。</typeparam> /// <typeparam name="TMigration">迁移描述类型。</typeparam>
/// <param name="resolveMigration">迁移解析委托。</param> /// <param name="resolveMigration">迁移解析委托。</param>
/// <param name="currentVersion">当前版本。</param> /// <param name="currentVersion">当前版本。</param>
/// <param name="subjectName">迁移主体名称。</param> /// <param name="context">本轮迁移链共享的执行上下文。</param>
/// <param name="migrationKind">迁移类别名称。</param>
/// <returns>已解析的迁移器。</returns> /// <returns>已解析的迁移器。</returns>
/// <exception cref="InvalidOperationException">缺少迁移器时抛出。</exception> /// <exception cref="InvalidOperationException">缺少迁移器时抛出。</exception>
private static TMigration GetRequiredMigration<TMigration>( private static TMigration GetRequiredMigration<TData, TMigration>(
Func<int, TMigration?> resolveMigration, Func<int, TMigration?> resolveMigration,
int currentVersion, int currentVersion,
string subjectName, in MigrationExecutionContext<TData, TMigration> context)
string migrationKind) where TData : class
{ {
var migration = resolveMigration(currentVersion); var migration = resolveMigration(currentVersion);
if (migration is null) if (migration is null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"No {migrationKind} is registered for {subjectName} from version {currentVersion}."); $"No {context.MigrationKind} is registered for {context.SubjectName} from version {currentVersion}.");
} }
return migration; return migration;
@ -172,11 +188,7 @@ internal static class VersionedMigrationRunner
/// <param name="current">迁移前数据。</param> /// <param name="current">迁移前数据。</param>
/// <param name="currentVersion">迁移前版本。</param> /// <param name="currentVersion">迁移前版本。</param>
/// <param name="targetVersion">运行时目标版本。</param> /// <param name="targetVersion">运行时目标版本。</param>
/// <param name="getVersion">数据版本读取委托。</param> /// <param name="context">本轮迁移链共享的执行上下文。</param>
/// <param name="getToVersion">迁移器目标版本读取委托。</param>
/// <param name="applyMigration">迁移执行委托。</param>
/// <param name="subjectName">迁移主体名称。</param>
/// <param name="migrationKind">迁移类别名称。</param>
/// <returns>迁移后的数据与新版本号。</returns> /// <returns>迁移后的数据与新版本号。</returns>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// 迁移结果为空、声明目标版本不匹配、版本未前进或超出运行时版本时抛出。 /// 迁移结果为空、声明目标版本不匹配、版本未前进或超出运行时版本时抛出。
@ -186,64 +198,60 @@ internal static class VersionedMigrationRunner
TData current, TData current,
int currentVersion, int currentVersion,
int targetVersion, int targetVersion,
Func<TData, int> getVersion, in MigrationExecutionContext<TData, TMigration> context)
Func<TMigration, int> getToVersion,
Func<TMigration, TData, TData> applyMigration,
string subjectName,
string migrationKind)
where TData : class where TData : class
{ {
var migratedData = applyMigration(migration, current) var migratedData = context.ApplyMigration(migration, current)
?? throw new InvalidOperationException( ?? throw new InvalidOperationException(
$"{migrationKind} for {subjectName} from version {currentVersion} returned null."); $"{context.MigrationKind} for {context.SubjectName} from version {currentVersion} returned null.");
var migratedVersion = getVersion(migratedData); var migratedVersion = context.GetVersion(migratedData);
ValidateMigrationResult( ValidateMigrationResult(
currentVersion, currentVersion,
targetVersion, targetVersion,
migratedVersion, migratedVersion,
getToVersion(migration), context.GetToVersion(migration),
subjectName, in context);
migrationKind);
return (migratedData, migratedVersion); return (migratedData, migratedVersion);
} }
/// <summary> /// <summary>
/// 校验单步迁移结果与声明目标版本一致,并确保版本严格单调递增且不越过运行时版本。 /// 校验单步迁移结果与声明目标版本一致,并确保版本严格单调递增且不越过运行时版本。
/// </summary> /// </summary>
/// <typeparam name="TData">迁移数据类型。</typeparam>
/// <typeparam name="TMigration">迁移描述类型。</typeparam>
/// <param name="currentVersion">迁移前版本。</param> /// <param name="currentVersion">迁移前版本。</param>
/// <param name="targetVersion">运行时目标版本。</param> /// <param name="targetVersion">运行时目标版本。</param>
/// <param name="migratedVersion">迁移后实际版本。</param> /// <param name="migratedVersion">迁移后实际版本。</param>
/// <param name="declaredTargetVersion">迁移器声明的目标版本。</param> /// <param name="declaredTargetVersion">迁移器声明的目标版本。</param>
/// <param name="subjectName">迁移主体名称。</param> /// <param name="context">本轮迁移链共享的执行上下文。</param>
/// <param name="migrationKind">迁移类别名称。</param>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// 声明目标版本不匹配、版本未前进或超出运行时版本时抛出。 /// 声明目标版本不匹配、版本未前进或超出运行时版本时抛出。
/// </exception> /// </exception>
private static void ValidateMigrationResult( private static void ValidateMigrationResult<TData, TMigration>(
int currentVersion, int currentVersion,
int targetVersion, int targetVersion,
int migratedVersion, int migratedVersion,
int declaredTargetVersion, int declaredTargetVersion,
string subjectName, in MigrationExecutionContext<TData, TMigration> context)
string migrationKind) where TData : class
{ {
if (declaredTargetVersion != migratedVersion) if (declaredTargetVersion != migratedVersion)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"{migrationKind} for {subjectName} declared target version {declaredTargetVersion} " + $"{context.MigrationKind} for {context.SubjectName} declared target version {declaredTargetVersion} " +
$"but returned version {migratedVersion}."); $"but returned version {migratedVersion}.");
} }
if (migratedVersion <= currentVersion) if (migratedVersion <= currentVersion)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"{migrationKind} for {subjectName} must advance beyond version {currentVersion}."); $"{context.MigrationKind} for {context.SubjectName} must advance beyond version {currentVersion}.");
} }
if (migratedVersion > targetVersion) if (migratedVersion > targetVersion)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
$"{migrationKind} for {subjectName} produced version {migratedVersion}, " + $"{context.MigrationKind} for {context.SubjectName} produced version {migratedVersion}, " +
$"which exceeds the current runtime version {targetVersion}."); $"which exceeds the current runtime version {targetVersion}.");
} }
} }

View File

@ -6,14 +6,14 @@
## 当前恢复点 ## 当前恢复点
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-069` - 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-070`
- 当前阶段:`Phase 69` - 当前阶段:`Phase 70`
- 当前焦点: - 当前焦点:
- `2026-04-25` 主线程已提交 `58ba6c0` `test(cqrs-tests): 收敛处理器注册缓存测试 warning` - `2026-04-25` 主线程`$gframework-pr-review` 复核当前分支 PR `#291` 的 latest-head AI review threads、nitpick 与 MegaLinter 信号
- 当前并行双文件批次聚焦 `GFramework.Cqrs.Tests/Logging/TestLogger.cs``GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs` - 当前批次只吸收本地仍成立且修复成本明确的 3 项:`AGENTS.md` 英文标点一致性、`MediatorAdvancedFeaturesTests.cs` 中未使用的 `TestLoggingBehavior``VersionedMigrationRunner.cs` 的迁移上下文参数对象化
- 主线程收敛 `TestLogger.Logs` 的集合抽象暴露问题worker 则在 `MediatorAdvancedFeaturesTests.cs` 内收敛一组低风险 `MA0016` - `dotnet clean` + `dotnet build` 的直接仓库根基线已从 `640 Warning(s)` 降至 `639 Warning(s)`,说明本轮 PR review follow-up 继续有效
- 提权后的直接仓库根基线已从 `645 Warning(s)` 降至 `640 Warning(s)`,说明本轮双文件 `Cqrs.Tests` 批次继续有效 - `GFramework.Game` 的直接受影响 `Release` build 当前为 `326 Warning(s)``0 Error(s)``GFramework.Cqrs.Tests``149 Warning(s)``0 Error(s)`
- `GFramework.Cqrs.Tests` 的直接受影响 `Release` build 当前为 `0 Warning(s)``0 Error(s)`branch diff 仍显著低于 `$gframework-batch-boot 50` 阈值 - CodeRabbit 提到的 `TestLogger` 重复实现与 `YamlConfigLoaderTests.cs` 常量位置仅属于可选整理,本轮未纳入修复写集
## 当前活跃事实 ## 当前活跃事实
@ -22,7 +22,7 @@
- `dotnet clean` - `dotnet clean`
- 结果:成功;此前沙箱内 “Build FAILED but 0 errors” 的 clean 结果不是仓库真值 - 结果:成功;此前沙箱内 “Build FAILED but 0 errors” 的 clean 结果不是仓库真值
- `dotnet build` - `dotnet build`
- 最新结果:成功;`640 Warning(s)`、`0 Error(s)` - 最新结果:成功;`639 Warning(s)`、`0 Error(s)`
- 已提交的低风险批次文件: - 已提交的低风险批次文件:
- `AGENTS.md` - `AGENTS.md`
- `GFramework.Core.Tests/Logging/LogContextTests.cs` - `GFramework.Core.Tests/Logging/LogContextTests.cs`
@ -39,8 +39,10 @@
- `ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md` - `ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md`
- `ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md` - `ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md`
- 当前批次验证结果: - 当前批次验证结果:
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
- 最新主线程结果:成功;`326 Warning(s)``0 Error(s)`
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
- 最新主线程结果:成功;`0 Warning(s)``0 Error(s)` - 最新主线程结果:成功;`149 Warning(s)`、`0 Error(s)`
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 上一轮主线程结果:成功;`0 Warning(s)``0 Error(s)` - 上一轮主线程结果:成功;`0 Warning(s)``0 Error(s)`
@ -70,14 +72,16 @@
- `dotnet clean` - `dotnet clean`
- 当前结果:成功;在提权后的直接 shell 中可正常完成仓库根 clean - 当前结果:成功;在提权后的直接 shell 中可正常完成仓库根 clean
- `dotnet build` - `dotnet build`
- 当前结果:成功;`640 Warning(s)``0 Error(s)` - 当前结果:成功;`639 Warning(s)``0 Error(s)`
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
- 当前结果:成功;`326 Warning(s)``0 Error(s)`
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
- 当前结果:成功;`0 Warning(s)``0 Error(s)` - 当前结果:成功;`149 Warning(s)`、`0 Error(s)`
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
- 当前结果:成功;`0 Warning(s)``0 Error(s)` - 当前结果:成功;`0 Warning(s)``0 Error(s)`
## 下一步建议 ## 下一步建议
1. 以当前 `640 Warning(s)` 根基线为新恢复点,继续按 `$gframework-batch-boot 50` 规则重算 branch diff并挑选下一个 1-3 文件的低风险热点。 1. 以当前 `639 Warning(s)` 根基线为新恢复点,继续按 `$gframework-batch-boot 50` 规则重算 branch diff并挑选下一个 1-3 文件的低风险热点。
2. 下一轮优先从 `GFramework.Cqrs.Tests`、`GFramework.Core.Tests``GFramework.Game` 中继续选择单文件 `MA0051``MA0016` 或测试噪音切片,避免过早推高 review 范围 2. 下一轮优先从 `GFramework.Game` 或 `GFramework.Cqrs.Tests` 中继续选择单文件 `MA0051``MA0016` 或 review 新暴露的低风险 warning 切片,避免把“可选整理”与 warning 收敛混成大改
3. 后续凡是沙箱内 `.NET` 验证再次出现无诊断失败、pipe/socket 权限问题或与普通 shell 不一致的结果,直接申请沙箱外重跑同一命令,不再扩散 workaround 型命令噪音。 3. 后续凡是沙箱内 `.NET` 验证再次出现无诊断失败、pipe/socket 权限问题或与普通 shell 不一致的结果,直接申请沙箱外重跑同一命令,不再扩散 workaround 型命令噪音。

View File

@ -1,5 +1,34 @@
# Analyzer Warning Reduction 追踪 # Analyzer Warning Reduction 追踪
## 2026-04-25 — RP-070
### 阶段:按 PR #291 latest-head review 收口仍有效的小批次,并刷新新的仓库根基线
- 触发背景:
- 用户显式要求执行 `$gframework-pr-review`,当前分支对应 PR `#291`
- 抓取结果显示 latest-head 只有 1 条未解决 review thread 指向 `AGENTS.md` 英文标点不一致;同时最新 CodeRabbit review body 还包含 `VersionedMigrationRunner.cs` 参数过多与 `MediatorAdvancedFeaturesTests.cs` 未使用测试基础设施这两条本地仍成立的建议
- MegaLinter 仅报出 `dotnet-format` restore 失败test report 为 `2156 passed / 0 failed`,因此本轮重点改为“只吸收仍有效且低风险的 review 建议”
- 主线程实施:
- 将 `AGENTS.md` 中英文规则段的 `dotnet clean` / `dotnet build` / `dotnet test` 列表标点改为英文逗号,直接消化 latest-head open thread
- 删除 `GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs` 中未被任何测试引用的 `TestLoggingBehavior` 静态类型,移除无收益的可变测试基础设施
- 在 `GFramework.Game/Internal/VersionedMigrationRunner.cs` 内引入私有 `MigrationExecutionContext<TData, TMigration>`,把多处 helper 共享的不变迁移上下文收口为参数对象,并同步补齐新增泛型 helper 的 XML 文档
- 明确拒绝把 `TestLogger` 重复实现与 `YamlConfigLoaderTests.cs` 常量位置这类“可选整理”混入本轮 warning 收敛批次
- 验证里程碑:
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json`
- 结果:成功;确认 PR `#291` latest-head open review threads 为 `1`MegaLinter 仅有 `dotnet-format` restore 失败tests 为 `2156 passed`
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
- 结果:成功;`326 Warning(s)``0 Error(s)`
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
- 结果:成功;`149 Warning(s)``0 Error(s)`
- `dotnet clean`
- 结果:成功
- `dotnet build`
- 结果:成功;`639 Warning(s)``0 Error(s)`,相较 `RP-069``640` 再下降 `1`
- 当前结论:
- PR #291 当前最有价值的 review follow-up 已被主线程吸收,且没有把“可选整理”误当成必须修复项
- 当前仓库根 warning 基线继续下降到 `639`,说明这轮 review 驱动的小批次仍符合 analyzer warning reduction 主题
- 下一轮可继续围绕 `GFramework.Game``GFramework.Cqrs.Tests` 选择新的单文件低风险热点,或在新 head 推送后重新抓取 PR review 判断是否还有剩余有效线程
## 2026-04-25 — RP-069 ## 2026-04-25 — RP-069
### 阶段:继续收口 Cqrs.Tests 双文件集合抽象 warning并刷新新的仓库根基线 ### 阶段:继续收口 Cqrs.Tests 双文件集合抽象 warning并刷新新的仓库根基线