Compare commits

..

11 Commits

Author SHA1 Message Date
gewuyou
c69942d66e
Merge pull request #323 from GeWuYou/feat/cqrs-optimization
Feat/cqrs optimization
2026-05-04 21:03:25 +08:00
gewuyou
212d5b1cce docs(cqrs): 同步 PR 恢复锚点
- 更新 CQRS active tracking 的当前 PR 锚点为 PR #323
- 补充 PR review 收敛 trace 与最新验证结果
2026-05-04 20:56:19 +08:00
gewuyou
b1f406ad99 test(cqrs): 补齐 request handler gate 回归
- 新增缺少 IRequestHandler 合同时的 generator 静默跳过覆盖

- 更新 CQRS 恢复文档与本轮验证记录
2026-05-04 19:17:48 +08:00
gewuyou
61cc1be1e5 test(cqrs): 补齐外部 contract gate 回归
- 新增缺少 ILogger 合同时的 generator 静默跳过覆盖

- 新增缺少 IServiceCollection 合同时的 generator 静默跳过覆盖

- 更新 CQRS 恢复文档与本轮验证记录
2026-05-04 19:15:32 +08:00
gewuyou
915d93d06d test(cqrs): 扩展 registry gate 回归
- 新增缺少 notification handler 合同时的 generator 静默跳过覆盖

- 新增缺少 stream handler 合同时的 generator 静默跳过覆盖

- 新增缺少 registry attribute 合同时的 generator 静默跳过覆盖

- 更新 CQRS 恢复文档与本轮验证记录
2026-05-04 19:12:49 +08:00
gewuyou
e17fa15a01 test(cqrs): 补齐 registry gate 回归
- 新增缺少 ICqrsHandlerRegistry 时的 generator 静默跳过覆盖

- 更新 CQRS 恢复文档与本轮验证记录
2026-05-04 19:01:47 +08:00
gewuyou
857ce08edb test(cqrs): 补齐 fallback 元数据回归
- 新增 mixed fallback 禁用多实例 attribute 时的字符串回退覆盖

- 补充 runtime AttributeUsage 变体测试辅助方法

- 更新 CQRS 恢复文档与本轮验证记录
2026-05-04 18:55:04 +08:00
gewuyou
0ac53a4cee test(cqrs): 补齐 request invoker 合同回归
- 新增 request invoker descriptor 缺失时的 generator 回归覆盖

- 新增 request invoker descriptor entry 缺失时的 generator 回归覆盖

- 更新 CQRS 恢复文档与本轮验证记录
2026-05-04 18:49:26 +08:00
gewuyou
ac95202f9c
Merge pull request #322 from GeWuYou/fix/release-notes-pr-links 2026-05-04 16:05:33 +08:00
gewuyou
478072acc3 fix(release): 修复 git-cliff PR 元数据令牌
- 修复 auto-tag 中 git-cliff 使用 PAT_TOKEN 导致 PR 读取权限不受 job permissions 约束的问题

- 修复 semantic-release trace 中重复日期标题触发 MD024 的问题

- 更新 SEMREL-RP-007 跟踪记录,说明发布说明生成的 token 分工与后续恢复点
2026-05-04 14:19:40 +08:00
gewuyou
53870c1f92 fix(release): 修复发布说明 PR 链接缺失
- 修复 release notes 生成 job 缺少 PR 读取权限的问题

- 更新 semantic-release 主题恢复点与验证记录

- 补充当前修复分支到 ai-plan 启动映射
2026-05-04 10:19:58 +08:00
9 changed files with 508 additions and 29 deletions

View File

@ -17,6 +17,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
published: ${{ steps.semantic_release.outputs.new_release_published }}
last_tag: ${{ steps.semantic_release.outputs.last_release_git_tag }}
@ -71,7 +72,7 @@ jobs:
env:
OUTPUT: PREVIEW_RELEASE_NOTES.md
GITHUB_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
GITHUB_TOKEN: ${{ github.token }}
- name: Write preview summary
env:
@ -108,6 +109,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
environment:
name: release-approval
steps:
@ -157,7 +159,7 @@ jobs:
env:
OUTPUT: PUBLISHED_RELEASE_NOTES.md
GITHUB_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
GITHUB_TOKEN: ${{ github.token }}
- name: Write release summary
env:

View File

@ -243,6 +243,7 @@ jobs:
permissions:
contents: write
packages: read
pull-requests: read
steps:
- name: Checkout repository (at tag)

View File

@ -79,8 +79,8 @@ public sealed partial class CqrsHandlerRegistryGenerator
/// 从可直接表达 handler 接口的注册描述中提取 request invoker 发射计划。
/// </summary>
/// <param name="supportsRequestInvokerProvider">
/// 指示当前 runtime 是否同时暴露 <c>ICqrsRequestInvokerProvider</c> 与
/// <c>IEnumeratesCqrsRequestInvokerDescriptors</c> 契约;若不支持,则本方法必须返回空结果并让后续发射路径整体跳过。
/// 指示当前 runtime 是否完整暴露 request invoker provider、descriptor 与 descriptor entry 契约;
/// 若不支持,则本方法必须返回空结果并让后续发射路径整体跳过。
/// </param>
/// <param name="registrations">已按稳定顺序整理完成的 handler 注册描述。</param>
/// <returns>
@ -136,8 +136,8 @@ public sealed partial class CqrsHandlerRegistryGenerator
/// 从可直接表达 handler 接口的注册描述中提取 stream invoker 发射计划。
/// </summary>
/// <param name="supportsStreamInvokerProvider">
/// 指示当前 runtime 是否同时暴露 <c>ICqrsStreamInvokerProvider</c> 与
/// <c>IEnumeratesCqrsStreamInvokerDescriptors</c> 契约;若不支持,则本方法必须返回空结果并让后续发射路径整体跳过。
/// 指示当前 runtime 是否完整暴露 stream invoker provider、descriptor 与 descriptor entry 契约;
/// 若不支持,则本方法必须返回空结果并让后续发射路径整体跳过。
/// </param>
/// <param name="registrations">已按稳定顺序整理完成的 handler 注册描述。</param>
/// <returns>

View File

@ -2332,6 +2332,66 @@ public class CqrsHandlerRegistryGeneratorTests
("CqrsHandlerRegistry.g.cs", AssemblyLevelCqrsHandlerRegistryExpected));
}
/// <summary>
/// 验证当 runtime 缺少 generated registry 需要依赖的基础合同时,
/// 生成器会整体跳过发射,避免产出无法承载运行时注册合同的半成品源码。
/// </summary>
/// <param name="startMarker">待移除 runtime 合同块的起始标记。</param>
/// <param name="endMarker">待移除 runtime 合同块之后的下一个稳定标记。</param>
[TestCase(
"public interface ICqrsHandlerRegistry",
"[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]")]
[TestCase(
"public interface INotificationHandler",
"public interface IStreamRequestHandler")]
[TestCase(
"public interface IRequestHandler",
"rename:MissingIRequestHandler")]
[TestCase(
"public interface IStreamRequestHandler",
"rename:MissingIStreamRequestHandler")]
[TestCase(
"[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]",
"[AttributeUsage(AttributeTargets.Assembly)]")]
[TestCase(
"public interface ILogger",
"rename:MissingILogger")]
[TestCase(
"public interface IServiceCollection",
"rename:MissingServiceCollection")]
public void Does_Not_Generate_Registry_When_Runtime_Lacks_Required_Generation_Contract(
string startMarker,
string endMarker)
{
var source = endMarker.StartsWith("rename:", StringComparison.Ordinal)
? RenameTypeIdentifier(
HiddenNestedHandlerSelfRegistrationSource,
startMarker.Replace("public interface ", string.Empty, StringComparison.Ordinal),
endMarker["rename:".Length..])
: RemoveBlock(
HiddenNestedHandlerSelfRegistrationSource,
startMarker,
endMarker);
var execution = ExecuteGenerator(source);
var inputCompilationErrors = execution.InputCompilationDiagnostics
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.ToArray();
var generatedCompilationErrors = execution.GeneratedCompilationDiagnostics
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.ToArray();
var generatorErrors = execution.GeneratorDiagnostics
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.ToArray();
Assert.Multiple(() =>
{
Assert.That(inputCompilationErrors, Is.Empty);
Assert.That(generatedCompilationErrors, Is.Empty);
Assert.That(generatorErrors, Is.Empty);
Assert.That(execution.GeneratedSources, Is.Empty);
});
}
/// <summary>
/// 验证当程序集包含生成代码无法合法引用的私有嵌套处理器时,生成器会在生成注册器内部执行定向反射注册,
/// 不再依赖程序集级 fallback marker。
@ -2870,6 +2930,50 @@ public class CqrsHandlerRegistryGeneratorTests
});
}
/// <summary>
/// 验证当 runtime 同时支持直接 <see cref="Type" /> 与字符串 fallback 元数据、但不允许多个 fallback 特性实例时,
/// mixed 场景会整体回退到单个字符串特性,避免生成会违反 runtime attribute usage 的多实例元数据。
/// </summary>
[Test]
public void
Emits_String_Fallback_Metadata_For_Mixed_Fallback_When_Runtime_Disallows_Multiple_Fallback_Attributes()
{
var source = ReplaceAttributeUsageForType(
AssemblyLevelMixedFallbackMetadataSource,
"CqrsReflectionFallbackAttribute",
"[AttributeUsage(AttributeTargets.Assembly)]");
var execution = ExecuteGenerator(
source,
allowUnsafe: true);
var inputCompilationErrors = execution.InputCompilationDiagnostics
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.ToArray();
var generatedCompilationErrors = execution.GeneratedCompilationDiagnostics
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.ToArray();
var generatorErrors = execution.GeneratorDiagnostics
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
.ToArray();
Assert.Multiple(() =>
{
Assert.That(inputCompilationErrors.Select(static diagnostic => diagnostic.Id), Does.Contain("CS0306"));
Assert.That(generatedCompilationErrors, Is.Empty);
Assert.That(generatorErrors, Is.Empty);
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
var generatedSource = execution.GeneratedSources[0].content;
Assert.That(
generatedSource,
Does.Contain(
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(\"TestApp.Container+AlphaHandler\", \"TestApp.Container+BetaHandler\")]"));
Assert.That(generatedSource, Does.Not.Contain("CqrsReflectionFallbackAttribute(typeof("));
Assert.That(
CountOccurrences(
generatedSource,
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute"),
Is.EqualTo(1));
});
}
/// <summary>
/// 验证当 runtime 暴露 request invoker provider 契约时,生成器会让 generated registry 同时发射
/// request invoker 描述符与对应的开放静态 invoker 方法。
@ -2998,6 +3102,58 @@ public class CqrsHandlerRegistryGeneratorTests
});
}
/// <summary>
/// 验证当 runtime 缺少 <c>CqrsRequestInvokerDescriptor</c> 时,
/// 生成器不会继续发射依赖描述符类型的 request provider 元数据。
/// </summary>
[Test]
public void Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Type()
{
var source = RenameTypeIdentifier(
RequestInvokerProviderSource,
"CqrsRequestInvokerDescriptor",
"MissingCqrsRequestInvokerDescriptor");
var generatedSource = RunGenerator(source);
Assert.Multiple(() =>
{
Assert.That(
generatedSource,
Does.Contain(
"internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry"));
Assert.That(generatedSource, Does.Not.Contain("ICqrsRequestInvokerProvider"));
Assert.That(generatedSource, Does.Not.Contain("IEnumeratesCqrsRequestInvokerDescriptors"));
Assert.That(generatedSource, Does.Not.Contain("CqrsRequestInvokerDescriptorEntry("));
Assert.That(generatedSource, Does.Not.Contain("InvokeRequestHandler0"));
});
}
/// <summary>
/// 验证当 runtime 缺少 <c>CqrsRequestInvokerDescriptorEntry</c> 时,
/// 生成器不会继续保留 request provider 的枚举接口或静态 invoker 元数据。
/// </summary>
[Test]
public void Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Entry_Type()
{
var source = RenameTypeIdentifier(
RequestInvokerProviderSource,
"CqrsRequestInvokerDescriptorEntry",
"MissingCqrsRequestInvokerDescriptorEntry");
var generatedSource = RunGenerator(source);
Assert.Multiple(() =>
{
Assert.That(
generatedSource,
Does.Contain(
"internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry"));
Assert.That(generatedSource, Does.Not.Contain("ICqrsRequestInvokerProvider"));
Assert.That(generatedSource, Does.Not.Contain("IEnumeratesCqrsRequestInvokerDescriptors"));
Assert.That(generatedSource, Does.Not.Contain("CqrsRequestInvokerDescriptorEntry("));
Assert.That(generatedSource, Does.Not.Contain("InvokeRequestHandler0"));
});
}
/// <summary>
/// 验证当 request handler 仍需走 precise reflected 注册时,
/// 生成器即使检测到 request invoker provider runtime 合同,也不会错误发射无法稳定表达隐藏请求/响应类型的 provider 元数据。
@ -3373,6 +3529,47 @@ public class CqrsHandlerRegistryGeneratorTests
return !char.IsLetterOrDigit(character) && character != '_';
}
/// <summary>
/// 替换指定测试类型紧邻的 <c>AttributeUsage</c> 声明,用于构造 runtime contract 的 attribute usage 变体。
/// </summary>
/// <param name="source">原始测试源码。</param>
/// <param name="typeName">需要定位的类型名。</param>
/// <param name="replacementAttributeUsage">替换后的完整 <c>AttributeUsage</c> 声明。</param>
/// <returns>完成 attribute usage 替换后的源码。</returns>
private static string ReplaceAttributeUsageForType(
string source,
string typeName,
string replacementAttributeUsage)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(typeName);
ArgumentNullException.ThrowIfNull(replacementAttributeUsage);
var typeIndex = source.IndexOf($"public sealed class {typeName}", StringComparison.Ordinal);
if (typeIndex < 0)
{
throw new InvalidOperationException("The requested type declaration was not found in the generator test input.");
}
const string attributeUsagePrefix = "[AttributeUsage(";
var attributeUsageStartIndex = source.LastIndexOf(attributeUsagePrefix, typeIndex, StringComparison.Ordinal);
if (attributeUsageStartIndex < 0)
{
throw new InvalidOperationException("The requested AttributeUsage declaration was not found in the generator test input.");
}
var attributeUsageEndIndex = source.IndexOf(']', attributeUsageStartIndex);
if (attributeUsageEndIndex < 0 || attributeUsageEndIndex > typeIndex)
{
throw new InvalidOperationException("The requested AttributeUsage declaration is malformed.");
}
return source.Remove(
attributeUsageStartIndex,
attributeUsageEndIndex - attributeUsageStartIndex + 1)
.Insert(attributeUsageStartIndex, replacementAttributeUsage);
}
/// <summary>
/// 统计生成源码中某个固定片段的出现次数,用于锁定程序集级 fallback 特性的发射个数。
/// </summary>

View File

@ -65,6 +65,9 @@ help the current worktree land on the right recovery documents without scanning
- Branch: `build/semantic-release-rules`
- Worktree hint: `GFramework`
- Priority 1: `semantic-release-versioning`
- Branch: `fix/release-notes-pr-links`
- Worktree hint: `GFramework`
- Priority 1: `semantic-release-versioning`
- Branch: `docs/sdk-update-documentation`
- Worktree hint: `GFramework-update-documentation`
- Priority 1: `documentation-full-coverage-governance`

View File

@ -7,19 +7,24 @@ CQRS 迁移与收敛。
## 当前恢复点
- 恢复点编号:`CQRS-REWRITE-RP-076`
- 恢复点编号:`CQRS-REWRITE-RP-082`
- 当前阶段:`Phase 8`
- 当前 PR 锚点:`PR #307`
- 当前 PR 锚点:`PR #323`
- 当前结论:
- `GFramework.Cqrs` 已完成对外部 `Mediator` 的生产级替代,当前主线已从“是否可替代”转向“仓库内部收口与能力深化顺序”
- `dispatch/invoker` 生成前移已扩展到 request / stream 路径,当前 `RP-076` 已补齐 stream invoker provider gate 的四项 runtime 合同分支
- `ai-plan` active 入口现以 `PR #307``RP-076` 为唯一权威恢复锚点;更早 PR 与阶段细节均以下方归档为准
- `dispatch/invoker` 生成前移已扩展到 request / stream 路径,`RP-077` 已补齐 request invoker provider gate 与 stream gate 对称的 descriptor / descriptor entry runtime 合同回归
- `RP-078` 已补齐 mixed fallback metadata 在 runtime 不允许多个 fallback attribute 实例时的单字符串 attribute 回退回归
- `RP-079` 已补齐 runtime 缺少 generated handler registry interface 时的 generator 静默跳过回归
- `RP-080` 已将基础 generation gate 回归扩展到 notification handler interface、stream handler interface 与 registry attribute 缺失分支
- `RP-081` 已继续补齐基础 generation gate 的 logging 与 DI runtime contract 缺失分支
- 当前 `RP-082` 已补齐基础 generation gate 的 request handler runtime contract 缺失分支
- `ai-plan` active 入口现以 `PR #323``RP-082` 为唯一权威恢复锚点;`PR #307`、其他更早 PR 与阶段细节均以下方归档或说明为准
## 当前活跃事实
- 当前分支对应 `PR #307`,状态为 `OPEN`
- 当前分支对应 `PR #323`,状态为 `OPEN`
- latest-head review 仍以 `ai-plan` 恢复文档收敛为主要待闭环项;代码与测试侧的本地有效问题已收敛
- 远端 `CTRF` 最新汇总为 `2247/2247` passed
- 远端 `CTRF` 最新汇总为 `2274/2274` passed
- `MegaLinter` 当前只暴露 `dotnet-format``Restore operation failed` 环境噪音,尚未提供本地仍成立的文件级格式诊断
## 当前风险
@ -30,18 +35,65 @@ CQRS 迁移与收敛。
## 最近权威验证
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output /tmp/current-pr-review.json`
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output <temporary-json-output>`
- 结果:通过
- 备注:确认当前分支对应 `PR #307`,本轮剩余 open AI feedback 主要集中在 `ai-plan` 收敛
- 备注:确认当前分支对应 `PR #323`,本轮剩余 open AI feedback 主要集中在 `ai-plan` PR 锚点收敛
- `git diff --check`
- 结果:通过
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Descriptor_Enumerator|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Descriptor_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Descriptor_Entry_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
- 结果:通过,`5/5` passed
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Entry_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Enumerator"`
- 结果:通过,`4/4` passed
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行,避免脚本内部 plain `git ls-files` 误判仓库上下文
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_String_Fallback_Metadata_For_Mixed_Fallback_When_Runtime_Disallows_Multiple_Fallback_Attributes"`
- 结果:通过,`1/1` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Generate_Registry_When_Runtime_Lacks_Handler_Registry_Interface"`
- 结果:通过,`1/1` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Generate_Registry_When_Runtime_Lacks_Required_Generation_Contract"`
- 结果:通过,`4/4` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Generate_Registry_When_Runtime_Lacks_Required_Generation_Contract"`
- 结果:通过,`6/6` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Generate_Registry_When_Runtime_Lacks_Required_Generation_Contract"`
- 结果:通过,`7/7` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
## 下一推荐步骤
1. 继续处理 `PR #307` 的剩余 review 收尾,优先保持 `ai-plan` active 入口与 trace 的单一锚点一致
2. 若继续推进代码切片,优先复核 request 侧是否存在与 stream gate 对称的生成合同遗漏,再决定是否补同批 generator 回归
1. 继续处理 `PR #323` 的剩余 review 收尾,优先保持 `ai-plan` active 入口与 trace 的单一锚点一致
2. 若继续推进代码切片,优先复核基础 generation gate 之外的 runtime contract 或 fallback selection 分支;基础 gate 的可安全构造缺失分支已覆盖
3. 在进入下一批 runtime / generator 收敛前,保持最小 Release build 或 targeted test 作为权威验证
## 活跃文档
@ -56,5 +108,5 @@ CQRS 迁移与收敛。
## 说明
- `PR #261``PR #302``PR #305` 及更早阶段的详细过程已不再作为 active 恢复入口;如需追溯,以对应归档文件为准
- `PR #261``PR #302``PR #305``PR #307` 及更早阶段的详细过程已不再作为 active 恢复入口;如需追溯,以对应归档文件或历史 trace 段落为准
- active tracking 仅保留当前恢复点、当前风险、最近权威验证与下一推荐步骤,避免 `boot` 落到历史阶段细节

View File

@ -2,11 +2,11 @@
## 2026-04-30
### 阶段PR #307 active 入口收敛CQRS-REWRITE-RP-076
### 阶段:历史 PR #307 active 入口收敛CQRS-REWRITE-RP-076
- 继续沿用 `$gframework-pr-review``PR #307` 做 latest-head triage本轮只处理仍成立的 `ai-plan` 恢复入口问题
- 主线程确认当前远端权威信号:
- 当分支对应 `PR #307`,状态为 `OPEN`
- 当分支对应 `PR #307`,状态为 `OPEN`
- 远端 `CTRF` 最新汇总为 `2247/2247` passed
- `MegaLinter` 仅剩 `dotnet-format``Restore operation failed` 环境噪音
- 仍未闭环的 review 重点集中在 `cqrs-rewrite` active tracking / trace 仍保留过多历史锚点,而非新的运行时代码缺陷
@ -17,7 +17,7 @@
### 验证RP-076
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output /tmp/current-pr-review.json`
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output <temporary-json-output>`
- 结果:通过
- 备注:确认 `PR #307` 的当前 review 重点已收敛到 `ai-plan` 文档收尾
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
@ -27,6 +27,188 @@
### 当前下一步RP-076
1. 继续按 `PR #307` 的 latest-head review 收尾,优先保持 active tracking 与 active trace 的单一锚点一致
1. 当时继续按 `PR #307` 的 latest-head review 收尾,优先保持 active tracking 与 active trace 的单一锚点一致
2. 若继续推进代码切片,先复核 request 侧是否仍存在与 stream invoker gate 对称的生成合同遗漏
3. 进入下一批前继续使用最小 Release build 或 targeted test 作为权威验证,避免把环境噪音误判为代码问题
## 2026-05-04
### 阶段request invoker provider gate 对称回归CQRS-REWRITE-RP-077
- 使用 `$gframework-batch-boot 25` 继续 `feat/cqrs-optimization` 的 CQRS 收口批次
- 批次目标:在 branch diff 相对 `origin/main` 接近 `25` 个文件前,补齐低风险的 generator 合同回归切片
- 本轮先确认当前 worktree 已无 `local-plan` 遗留恢复入口,随后转入 `cqrs-rewrite` 的 request / stream invoker provider gate 对称性复核
- 结论:
- 生产代码已经同时检查 request provider、enumerator、descriptor 与 descriptor entry 四项 runtime 合同
- request 侧测试只覆盖缺少 provider / enumerator缺少 descriptor / descriptor entry 的回归覆盖落后于 stream 侧
- 已补齐:
- `Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Type`
- `Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Entry_Type`
- source emission XML 文档同步说明 provider gate 依赖完整 descriptor / descriptor entry 合同
### 验证RP-077
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Entry_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Enumerator"`
- 结果:通过,`4/4` passed
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行,避免脚本内部 plain `git ls-files` 误判仓库上下文
- `git diff --check`
- 结果:通过
### 当前下一步RP-077
1. 继续使用 `origin/main` 作为 `$gframework-batch-boot 25` 的基线,复算 branch diff 后决定是否还能接下一批
2. 若继续推进代码切片,优先查找 request / stream invoker provider runtime 合同之外的同类对称测试缺口
### 阶段mixed fallback attribute usage 回归CQRS-REWRITE-RP-078
- 继续沿用 `$gframework-batch-boot 25`,当前 branch diff 仍低于阈值
- 复核 fallback metadata runtime contract 后确认:
- mixed fallback 在 runtime 允许多个 fallback attribute 实例时已有直接 `Type` + 字符串拆分回归
- runtime 同时支持 `params Type[]` / `params string[]` 但不允许多个 fallback attribute 实例时,缺少锁定“整体回退到单个字符串 attribute”的回归
- 已补齐:
- `Emits_String_Fallback_Metadata_For_Mixed_Fallback_When_Runtime_Disallows_Multiple_Fallback_Attributes`
- `ReplaceAttributeUsageForType` 测试辅助方法,用于构造 runtime attribute usage 变体而不复制大型 source fixture
### 验证RP-078
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_String_Fallback_Metadata_For_Mixed_Fallback_When_Runtime_Disallows_Multiple_Fallback_Attributes"`
- 结果:通过,`1/1` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
### 当前下一步RP-078
1. 继续复算 branch diff vs `origin/main`,若仍低于 `25` 个文件可继续下一批
2. 下一批优先查看 fallback metadata 与 generated invoker provider 之外是否还有同类 runtime contract gate 回归缺口
### 阶段:基础 generated registry contract gate 回归CQRS-REWRITE-RP-079
- 继续沿用 `$gframework-batch-boot 25`,当前 branch diff 仍低于阈值
- 复核 generator 基础启用条件后确认:缺少 `ICqrsHandlerRegistry`runtime 不具备承载 generated registry 的基础接口合同,应整体跳过发射
- 已补齐:
- `Does_Not_Generate_Registry_When_Runtime_Lacks_Handler_Registry_Interface`
### 验证RP-079
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Generate_Registry_When_Runtime_Lacks_Handler_Registry_Interface"`
- 结果:通过,`1/1` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
### 当前下一步RP-079
1. 继续复算 branch diff vs `origin/main`,若仍低于 `25` 个文件可继续下一批
2. 下一批优先复核基础 generation gate 中其他必需 runtime contracts 是否也需要同类回归覆盖
### 阶段:基础 generated registry contract gate 扩展回归CQRS-REWRITE-RP-080
- 将 `RP-079` 的单一 handler registry interface 缺失回归扩展为基础 generation gate 参数化测试
- 已补齐缺失分支:
- `ICqrsHandlerRegistry`
- `INotificationHandler<TNotification>`
- `IStreamRequestHandler<TRequest, TResponse>`
- `CqrsHandlerRegistryAttribute`
- stream handler interface 变体采用类型重命名构造 runtime metadata miss避免删除命名空间尾部单行接口时引入输入编译错误
### 验证RP-080
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Generate_Registry_When_Runtime_Lacks_Required_Generation_Contract"`
- 结果:通过,`4/4` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
### 当前下一步RP-080
1. 继续复算 branch diff vs `origin/main`,若仍低于 `25` 个文件可继续下一批
2. 下一批优先复核基础 generation gate 中 logging / DI 依赖是否已有合适的输入编译安全回归覆盖方式
### 阶段:基础 generated registry external contract gate 回归CQRS-REWRITE-RP-081
- 延续 `RP-080` 的参数化基础 generation gate 测试,将外部 logging / DI 依赖也纳入同一组静默跳过回归
- 已补齐缺失分支:
- `GFramework.Core.Abstractions.Logging.ILogger`
- `Microsoft.Extensions.DependencyInjection.IServiceCollection`
- 两个变体均通过类型重命名构造 runtime metadata miss保持输入源码可编译避免把依赖缺失测试误写成编译失败测试
### 验证RP-081
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Generate_Registry_When_Runtime_Lacks_Required_Generation_Contract"`
- 结果:通过,`6/6` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
### 当前下一步RP-081
1. 继续复算 branch diff vs `origin/main`,若仍低于 `25` 个文件可继续下一批
2. 下一批优先复核基础 generation gate 中 request handler contract 与 handler registry attribute 以外是否还有可安全构造的缺失分支
### 阶段:基础 generated registry request handler gate 回归CQRS-REWRITE-RP-082
- 延续 `RP-081` 的基础 generation gate 参数化测试,补齐 `IRequestHandler<TRequest,TResponse>` 缺失分支
- 该变体同样通过类型重命名构造 runtime metadata miss保持输入源码可编译
- 至此基础 generation gate 中可安全构造的缺失分支已覆盖:
- request handler interface
- notification handler interface
- stream handler interface
- handler registry interface
- handler registry attribute
- logging interface
- DI service collection interface
### 验证RP-082
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Generate_Registry_When_Runtime_Lacks_Required_Generation_Contract"`
- 结果:通过,`7/7` passed
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `git diff --check`
- 结果:通过
### 当前下一步RP-082
1. 继续复算 branch diff vs `origin/main`,若仍低于 `25` 个文件可继续下一批
2. 下一批优先复核基础 generation gate 之外的 runtime contract 或 fallback selection 分支;基础 gate 的可安全构造缺失分支已覆盖
### 阶段PR #323 review 锚点收敛CQRS-REWRITE-RP-082
- 使用 `$gframework-pr-review` 重新拉取当前分支 PR review payload确认当前分支对应 `PR #323`,状态为 `OPEN`
- 本轮 latest-head open AI thread 仅指出 active tracking 中仍保留 `PR #307` 作为当前 PR 锚点;本地复核后确认该反馈仍成立
- 已将 active tracking 的当前 PR 锚点、活跃事实、最近 PR review 备注和下一推荐步骤统一到 `PR #323`
- `PR #307` 仅保留为历史 PR 说明和较早 trace 段落,不再作为 active 恢复入口
### 验证PR #323 review
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output <temporary-json-output>`
- 结果:通过
- 备注:确认 `PR #323` 只有 1 个 CodeRabbit open thread指向 active tracking 的 PR 锚点漂移
- 远端 `CTRF` 最新汇总为 `2274/2274` passed
- `MegaLinter` 当前仅报告 `dotnet-format``Restore operation failed` 环境噪音,未提供本地仍成立的文件级格式诊断
- `git diff --check`
- 结果:通过
- `python3 scripts/license-header.py --check`
- 结果:通过
- 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
- 结果:通过,`0 warning / 0 error`
### 当前下一步PR #323 review
1. 若 review 重新触发后仍有 latest-head open thread继续以 `PR #323` 为当前唯一 PR 恢复锚点复核
2. 后续若继续推进代码切片,优先复核基础 generation gate 之外的 runtime contract 或 fallback selection 分支

View File

@ -13,13 +13,15 @@
## 当前恢复点
- 恢复点编号SEMREL-RP-006
- 当前阶段:处理 PR review 中的 release notes 类型映射漂移
- 恢复点编号SEMREL-RP-007
- 当前阶段:修复 git-cliff 发布说明 PR 链接缺失
- 当前焦点:
- `.releaserc.json``release-notes-generator` 增加 `presetConfig.types`
- 让 `refactor``deps``security` 这类 patch 级发布原因出现在 semantic-release 生成的 notes 中
- `AGENTS.md``docs/zh-CN/contributing.md` 同步提交类型说明
- `build/semantic-release-rules` 分支映射到当前 active topic
- `.github/workflows/auto-tag.yml` 的 preview / release job 增加 `pull-requests: read`
- `.github/workflows/auto-tag.yml``git-cliff-action` 改用 `${{ github.token }}` 读取 PR 元数据,`PAT_TOKEN`
只保留给 `semantic-release` 的 dry-run push 探测与真实打 tag
- `.github/workflows/publish.yml` 的 GitHub Release job 增加 `pull-requests: read`
- 保持 `.github/cliff.toml``by @user in #PR` 模板不变,只补足 GitHub PR 元数据读取权限
- `fix/release-notes-pr-links` 分支映射到当前 active topic
### 已知风险
@ -33,6 +35,10 @@
以保证 `conventionalcommits` preset 在 GitHub Actions 中可解析
- `git-cliff-action``OUTPUT` 文件需要在 `softprops/action-gh-release` 执行时保留在当前工作目录,后续如调整
working-directory 或 artifact 路径,需要同步复查 `body_path`
- `git-cliff-action` 依赖 GitHub API 补充 `commit.remote.pr_number`;生成 release notes 的 workflow job 必须具备
`pull-requests: read`,否则模板只能稳定输出作者,不能稳定输出 `in #PR`
- `auto-tag.yml` 中 job 级 `permissions` 只约束 `${{ github.token }}`,不约束 `${{ secrets.PAT_TOKEN }}`;生成
release notes 时必须使用 `${{ github.token }}` 才能让 `pull-requests: read` 声明真正生效
## 已完成
@ -46,6 +52,8 @@
`ai-plan/public/semantic-release-versioning/archive/todos/semantic-release-versioning-rp004-2026-05-02.md`
- `SEMREL-RP-005` 已扩展 `deps` / `security` 的 patch 发布规则,并同步提交规范文档
- `SEMREL-RP-006` 已根据 PR review 复核结果补齐 release notes 类型映射,避免 patch 发布原因只触发版本而不进入 notes
- `SEMREL-RP-007` 已为所有 `git-cliff-action` release notes 生成 job 补齐 PR 读取权限,并让 `auto-tag.yml`
`git-cliff-action` 改用 `${{ github.token }}`,避免未来 GitHub Release 正文缺失 PR 链接
## 验证
@ -60,10 +68,16 @@
- `semantic-release --dry-run --no-ci` 已成功加载 `commit-analyzer``release-notes-generator`,随后因远端 tag
fetch 会 clobber 本地既有 tags 而终止,未暴露 `presetConfig.types` 配置解析错误
- `dotnet build GFramework.sln -c Release` 通过,`0 warning / 0 error`
- `SEMREL-RP-007` 已完成本地验证:
- workflow 权限静态检查通过,所有 `git-cliff-action` 所在 job 均使用具备 `pull-requests: read`
`${{ github.token }}`
- `.github/cliff.toml` 通过 Python `tomllib` 解析
- `python3 scripts/license-header.py --check` 通过
- `dotnet build GFramework.sln -c Release` 通过,`0 warning / 0 error`
- 更早阶段的 dry-run / tag /抽象项目验证已归档到
`ai-plan/public/semantic-release-versioning/archive/todos/semantic-release-versioning-2026-04-26.md`
## 下一步
1. 提交 `SEMREL-RP-006` 的 PR review 修复
2. 如后续需要完整 semantic-release 版本预览,先处理本地 tag 与远端 tag 的 clobber 冲突
1. 推送 `SEMREL-RP-007` 的 PR review 修复,并重新抓取 PR review 确认重复标题线程和 PAT token 说明已收敛
2. 如后续需要回填当前 GitHub Release 正文,使用带 PR read 权限的 GitHub CLI 或 API token 重新生成并更新 notes

View File

@ -2,6 +2,34 @@
## 2026-05-04
### 发布说明 PR 链接权限修复SEMREL-RP-007
- 触发原因:
- v0.3.0 GitHub Release 中多数条目只显示 `by @GeWuYou`,没有 `in #xxx`
- `.github/cliff.toml``print_commit` 只有在 `commit.remote.pr_number` 存在时才追加 PR 链接
- `auto-tag.yml``publish.yml``git-cliff-action` job 只声明了 `contents` / `packages` 权限,没有显式
`pull-requests: read`
- PR review 补充指出 `auto-tag.yml` 里的 `git-cliff-action` 实际接收 `PAT_TOKEN`job 级 `pull-requests: read`
不会约束该 token
- 本地复核结论:
- 模板本身已经包含 `by @user in #PR` 输出,不需要改 release notes 格式
- `publish.yml` 已对 `git-cliff-action` 使用 `${{ github.token }}`job 级 `pull-requests: read` 能直接生效
- `auto-tag.yml` 应仅让 `semantic-release` 继续使用 `PAT_TOKEN`,让 `git-cliff-action` 改用带 job 权限的
`${{ github.token }}`,避免 PR 元数据读取能力取决于 PAT 创建时的额外 scope
- 当前环境未安装 `git-cliff``gh`,无法在本地直接重渲染并回填已发布的 GitHub Release 正文
- 已应用修复:
- `.github/workflows/auto-tag.yml` 的 preview / release job 增加 `pull-requests: read`
- `.github/workflows/auto-tag.yml` 的 preview / release `git-cliff-action` 改用 `${{ github.token }}`
- `.github/workflows/publish.yml``create-release` job 增加 `pull-requests: read`
- `ai-plan/public/README.md` 新增 `fix/release-notes-pr-links``semantic-release-versioning` 的 active topic 映射
- 验证:
- workflow 权限静态检查通过,所有 `git-cliff-action` 所在 job 均使用具备 `pull-requests: read``${{ github.token }}`
- `.github/cliff.toml` 通过 Python `tomllib` 解析
- `python3 scripts/license-header.py --check` 通过
- `dotnet build GFramework.sln -c Release` 通过,`0 warning / 0 error`
- 下一步是推送本轮 PR review 修复并重新抓取 PR review确认重复标题线程和 PAT token 说明已收敛;如需回填
v0.3.0 Release 正文,需要在具备 `git-cliff` / `gh` 或 GitHub release API 能力的环境中执行。
### PR review notes 类型映射修复SEMREL-RP-006
- 通过 `$gframework-pr-review` 抓取当前分支 PR #319