Compare commits

...

197 Commits
v0.1.2 ... main

Author SHA1 Message Date
gewuyou
7ca21af92d
Merge pull request #341 from GeWuYou/feat/cqrs-optimization
Feat/cqrs optimization
2026-05-08 16:12:20 +08:00
gewuyou
769d036434 fix(cqrs): 收口PR341剩余review尾项
- 修复 request faulted ValueTask 回归测试对 pipeline 探测顺序的隐式依赖,补齐 HasRegistration 与 GetAll 的防御性 mock

- 更新 cqrs-rewrite tracking 与 trace,记录 PR #341 latest-head review 的 stale thread 复核结论与本轮验证结果
2026-05-08 15:06:24 +08:00
gewuyou
9bd8c34693 fix(cqrs): 收口PR审查遗留问题
- 修复 benchmark 宿主误激活同程序集其他 generated registry 的接线路径,收窄服务索引与 descriptor 基线

- 恢复 CqrsDispatcher.SendAsync 的 faulted ValueTask 失败语义,并补充相关回归测试

- 补充 legacy runtime alias 的防守式类型检查、stream lifetime 注释与 cqrs-rewrite 恢复文档验证记录
2026-05-08 14:10:06 +08:00
gewuyou
39ac61c095 fix(cqrs): 补齐流式生命周期基准矩阵
- 新增 stream handler 的 Singleton 和 Transient 生命周期 benchmark,并沿用 generated-provider 宿主接线

- 更新 CQRS benchmark README 与 active ai-plan 恢复点,记录 RP-108 的验证结果和下一步建议
2026-05-08 13:03:00 +08:00
gewuyou
24462b0035 perf(cqrs): 收口默认流式基准宿主
- 新增默认 stream benchmark 的 handwritten generated registry,并通过真实程序集注册路径接上 generated stream invoker provider

- 更新 StreamingBenchmarks 宿主接线、README 与 RP-107 recovery 文档,统一 request、pipeline、stream 默认宿主口径

- 更新 gframework-boot 与 gframework-batch-boot 技能,改为以上下文预算接近约 80% 为默认优先停止信号
2026-05-08 12:47:24 +08:00
gewuyou
c82e981b7e perf(cqrs): 收口请求管线基准宿主
- 新增 request pipeline benchmark 的 handwritten generated request registry,并通过真实程序集注册路径接上 generated invoker provider

- 更新 RequestPipelineBenchmarks 宿主接线与 benchmark README,统一默认 request 与 pipeline 场景的 generated-provider 口径

- 更新 CQRS 迁移 tracking 与 trace,记录 RP-106 的基线、验证结果与下一恢复点
2026-05-08 12:38:18 +08:00
gewuyou
d9547dae4b perf(cqrs): 收口默认请求基准宿主
- 新增 handwritten generated request registry,并让默认 RequestBenchmarks 通过真实程序集注册路径接上 generated invoker provider

- 补齐 benchmark 最小宿主所需的 CQRS runtime、registrar 与 registration service 基础设施接线

- 更新 CQRS 迁移 tracking 与 trace,记录 RP-105 的 benchmark 结论和当前恢复点
2026-05-08 12:23:05 +08:00
gewuyou
120a1487f5 perf(cqrs): 收口请求热路径常量开销
- 优化 CqrsDispatcher.SendAsync 的 direct-return ValueTask 路径,移除 dispatcher 自身的异步状态机开销

- 引入 MicrosoftDiContainer 冻结后服务键索引,收敛 HasRegistration(Type) 的重复描述符扫描

- 更新 cqrs-rewrite active tracking 与 trace,记录 RP-104 的基线、验证结果与下一批建议
2026-05-08 11:38:27 +08:00
gewuyou
4d6dbba6a0
Merge pull request #340 from GeWuYou/feat/cqrs-optimization
Feat/cqrs optimization
2026-05-08 11:13:33 +08:00
gewuyou
32eeb41f29 fix(cqrs): 修复 HasRegistration 评审回归
- 修复 HasRegistration(Type) 的服务键判定,避免将仅按具体类型注册的行为误判为接口已注册

- 补充 strict mock 场景与 HasRegistration 回归测试,并修复 PR #340 暴露的 stream context validation 失败

- 更新 IoC 与 benchmark 文档注释,同步 cqrs-rewrite tracking/trace 到 PR #340 / RP-103
2026-05-08 10:54:37 +08:00
gewuyou
5da4a5893b perf(cqrs): 收紧性能回归门槛并忽略基准产物
- 更新 BenchmarkDotNet 生成目录忽略规则,避免本地基准产物污染工作树

- 补充 CQRS benchmark 回归要求与性能目标,要求相关改动后复跑 request 基准

- 更新 cqrs-rewrite 跟踪文档并记录最新 request 基准结果
2026-05-08 10:30:24 +08:00
gewuyou
18018966f9 perf(cqrs): 优化请求分发热路径并补充 Mediator 对照基准
- 优化 dispatcher 在零 pipeline 场景下跳过空行为解析,减少请求热路径分配

- 修复 MicrosoftDiContainer 热路径的无效 debug 字符串构造,并新增非激活注册检测回归测试

- 新增基于 NuGet 的 Mediator 对照基准并更新 CQRS 重写跟踪文档
2026-05-08 09:41:27 +08:00
gewuyou
5dc2dd25b9
Merge pull request #339 from GeWuYou/feat/cqrs-optimization
feat(cqrs): 补齐流式管道行为接缝
2026-05-08 09:08:37 +08:00
gewuyou
e44c56fb46 fix(cqrs): 收口 PR339 流式管道评审问题
- 修复 MicrosoftDiContainer 中 request 与 stream 行为注册逻辑的重复实现并统一校验路径

- 补充流式管道注册入口与 continuation 缓存的 XML 契约说明,明确并发与冻结前调用约束

- 更新 cqrs-rewrite 跟踪文档并修正 ICqrsRequestInvokerProvider 的 XML 缩进格式问题
2026-05-08 08:49:19 +08:00
gewuyou
aebf1e974d feat(cqrs): 补齐流式管道行为接缝
- 新增 stream pipeline 契约、dispatcher executor 缓存与 generated invoker 兼容路径

- 补充 Architecture 与 IOC 的流式管道注册入口及对应回归测试

- 更新 CQRS 文档和 cqrs-rewrite 的 active tracking/trace
2026-05-08 08:20:48 +08:00
gewuyou
02a60df718
Merge pull request #335 from GeWuYou/dependabot/nuget/Meziantou.Analyzer-3.0.72
Bump Meziantou.Analyzer from 3.0.60 to 3.0.72
2026-05-07 22:00:58 +08:00
gewuyou
77820da820
Merge pull request #336 from GeWuYou/dependabot/nuget/Meziantou.Polyfill-1.0.123
Bump Meziantou.Polyfill from 1.0.121 to 1.0.123
2026-05-07 22:00:48 +08:00
gewuyou
55639c559c
Merge pull request #337 from GeWuYou/dependabot/nuget/GFramework.Cqrs.Benchmarks/Microsoft.Extensions.Logging-10.0.7
Bump Microsoft.Extensions.Logging from 10.0.0 to 10.0.7
2026-05-07 22:00:35 +08:00
gewuyou
042b74473f
Merge pull request #338 from GeWuYou/dependabot/nuget/GFramework.Core.Tests/multi-6f1e76e95e
Bump NUnit from 4.5.1 to 4.6.0
2026-05-07 21:59:59 +08:00
dependabot[bot]
55c2a1ae69
Bump NUnit from 4.5.1 to 4.6.0
---
updated-dependencies:
- dependency-name: NUnit
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: NUnit
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: NUnit
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: NUnit
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: NUnit
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: NUnit
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: NUnit
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-07 13:14:14 +00:00
dependabot[bot]
debc9f27ac
Bump Microsoft.Extensions.Logging from 10.0.0 to 10.0.7
---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging
  dependency-version: 10.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-07 13:10:38 +00:00
dependabot[bot]
8f6e6e121e
Bump Meziantou.Polyfill from 1.0.121 to 1.0.123
---
updated-dependencies:
- dependency-name: Meziantou.Polyfill
  dependency-version: 1.0.123
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-07 13:09:51 +00:00
dependabot[bot]
d010026448
Bump Meziantou.Analyzer from 3.0.60 to 3.0.72
---
updated-dependencies:
- dependency-name: Meziantou.Analyzer
  dependency-version: 3.0.72
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-07 13:08:44 +00:00
gewuyou
54b79d99d3
Merge pull request #334 from GeWuYou/feat/cqrs-optimization
Feat/Implement CQRS runtime integration for legacy compatibility
2026-05-07 20:47:08 +08:00
gewuyou
ffb0a8aff5 fix(core): 收窄 legacy bridge 上下文回退异常边界
- 修复 LegacyCqrsDispatchHelper 仅在上下文缺失时回退,避免吞掉真实 InvalidOperationException

- 补充 CommandExecutor 与 QueryExecutor 相关回归测试,覆盖 fallback 与异常冒泡语义

- 更新 cqrs-rewrite 跟踪与追踪文档,记录 PR #334 本轮复核与验证结果
2026-05-07 20:35:47 +08:00
gewuyou
44d1a89a0b test(core): 补强 legacy bridge 上下文断言
- 补充 AsyncQueryExecutor 与 CommandExecutor bridge 测试的上下文保留断言

- 优化 RecordingCqrsRuntime 的 bridge 执行模拟与响应类型诊断

- 更新 cqrs-rewrite active tracking 与 trace 的 RP-097 验证记录
2026-05-07 20:17:46 +08:00
gewuyou
cca413042f chore(cqrs-rewrite): 同步PR334评审复核状态
- 更新 active tracking 与 trace 到 RP-096,记录 latest-head review 的最新权威结论

- 补充 PR #334 当前 stale open thread、CI 测试与 MegaLinter 噪音的本地复核结果
2026-05-07 19:52:14 +08:00
gewuyou
dc3bd3744e fix(core): 收口 legacy bridge 同步评审问题
- 修复 legacy 同步 bridge 的 runtime 等待方式,统一通过共享 helper 隔离同步上下文并收口重复 dispatch-context 解析逻辑

- 补充 legacy async command bridge 的取消可见性,并更新 ICqrsRuntime 与相关入口的契约说明

- 新增 bridge 回归测试并更新 cqrs-rewrite active tracking,覆盖同步上下文隔离、测试容器释放与取消语义
2026-05-07 19:00:49 +08:00
gewuyou
6056159866 fix(core): 收口 legacy cqrs bridge 评审问题
- 修复 legacy bridge 测试装配与清理流程,改用 InternalsVisibleTo 和显式 handler 注册,补齐共享计数器重置与生命周期说明

- 优化 CommandExecutor、QueryExecutor 与相关模块的 runtime 契约,补充 XML 文档、nullable 注解和显式依赖解析

- 更新 legacy 异步 bridge 的取消语义、兼容文档回退边界以及 cqrs-rewrite active tracking/trace
2026-05-07 17:54:05 +08:00
gewuyou
d7293aa475 refactor(core): 统一旧版命令查询到Cqrs运行时
- 重构 Core 兼容命令查询入口,使 legacy SendCommand/SendQuery 通过内部 bridge request 复用统一 CQRS runtime

- 新增 legacy bridge handler 与真实启动路径回归测试,验证默认架构初始化会自动接入统一 pipeline

- 更新 Core 与 CQRS 文档及 cqrs-rewrite 跟踪,记录 Mediator 尚未吸收的能力差距与后续收口方向
2026-05-07 17:20:14 +08:00
gewuyou
017e689abd feat(cqrs): 补齐请求生命周期基准矩阵
- 新增 request handler Singleton 与 Transient 生命周期 benchmark,并说明 Scoped 对照的宿主前置条件

- 更新 benchmark README,补充当前覆盖范围与后续扩展方向

- 更新 cqrs-rewrite active tracking 与 trace,记录 RP-092 验证结果和沙箱外 benchmark 权威结论
2026-05-07 14:20:50 +08:00
gewuyou
2c58d8b69e
Merge pull request #333 from GeWuYou/refactor/single-context-priority
docs(ai-plan): 归档 single-context-priority 主题
2026-05-07 13:24:46 +08:00
GeWuYou
14cd1fc9a0 chore(benchmark): 删除错误的任务 2026-05-07 13:08:55 +08:00
GeWuYou
577c89fdf3 chore(benchmark): 归档已完成任务,删除错误的任务 2026-05-07 12:44:57 +08:00
gewuyou
a692190a77 docs(ai-plan): 归档 single-context-priority 主题
- 更新 public index,只保留仍处于活跃状态的 topic 与分支映射

- 归档 single-context-priority 主题目录到 public archive

- 补充 ai-plan-governance 跟踪与 trace,记录本次归档校正与验证结果
2026-05-07 12:23:18 +08:00
gewuyou
c3df2b2c96
Merge pull request #332 from GeWuYou/refactor/single-context-priority
Refactor/single context priority
2026-05-07 11:34:50 +08:00
gewuyou
ee8b6a4deb fix(core): 修复上下文销毁解绑与并发一致性
- 修复 GameContext 的别名字典与当前活动上下文同步边界,避免解绑与读取路径出现状态漂移
- 修复 Architecture.Destroy() 缺少全局解绑的问题,并补充相关生命周期 XML 文档
- 更新回归测试、CQRS 注册断言与 single-context-priority 跟踪记录
2026-05-07 10:43:07 +08:00
gewuyou
ff04a4fbad fix(core): 补齐架构销毁后的上下文解绑
- 修复 Architecture 销毁后 GameContext 仍保留活动上下文的问题

- 补充生命周期回归测试并验证失败初始化后的解绑路径

- 收口生成器文档中的多架构表述并更新 ai-plan 追踪
2026-05-07 10:03:16 +08:00
gewuyou
e3fa0db992 refactor(core): 收敛单活动上下文与预冻结查询
- 收敛 GameContext 为单活动上下文模型并保留类型别名兼容查找

- 统一 MicrosoftDiContainer 预冻结实例读取路径并补充 CQRS 注册阶段提示

- 更新 Core 测试、上下文文档与 ai-plan 追踪记录
2026-05-07 08:58:09 +08:00
gewuyou
c2d22285ed
Merge pull request #331 from GeWuYou/fix/package-validation-guard
fix(release): 前移发布包清单校验
2026-05-06 21:34:59 +08:00
gewuyou
e3d6aa5111 fix(release): 修复发布校验链路的审查遗留问题
- 修复 PR workflow 中 dotnet pack 重复构建整个 solution 的问题

- 优化 packed modules 校验脚本的 find 实现以兼容 BSD 环境

- 更新 cqrs-rewrite 活跃跟踪与追踪文档中的当前 PR 锚点和审查结论
2026-05-06 21:27:21 +08:00
gewuyou
30ddb841a9 fix(release): 前移发布包清单校验
- 修复 benchmark 项目误入发布面的风险,明确 GFramework.Cqrs.Benchmarks 保持不可打包。

- 新增共享 packed modules 校验脚本,并让 publish 与 CI 工作流复用同一份发布包名单规则。

- 更新 CQRS active tracking 与 trace,记录本轮发布校验前移的恢复点与验证结果。
2026-05-06 21:12:42 +08:00
gewuyou
c65c131d6a
Merge pull request #330 from GeWuYou/fix/microsoft-di-container-disposal
fix(core): 修复容器释放与基准资源泄漏
2026-05-06 20:47:32 +08:00
gewuyou
f0a2978882 fix(core): 修复容器并发释放重复销毁锁
- 修复 MicrosoftDiContainer 在并发 Dispose 场景下可能重复执行底层读写锁销毁的问题

- 补充 IocContainerLifetimeTests 回归用例以覆盖并发释放时的单次锁销毁约束

- 更新 microsoft-di-container-disposal 追踪文档记录剩余 PR review 处理结果
2026-05-06 20:39:38 +08:00
gewuyou
3233151207 fix(ioc): 修复容器释放竞态与清理路径
- 修复 MicrosoftDiContainer 在等待线程与并发 Dispose 场景下泄露底层锁异常的问题
- 更新 IIocContainer 释放契约文档并移除 Clear 中不可达的 provider 释放逻辑
- 新增 benchmark cleanup helper、并发释放回归测试与 ai-plan 恢复入口
2026-05-06 20:23:16 +08:00
gewuyou
0ec8aa076b fix(core): 修复容器释放与基准资源泄漏
- 修复 MicrosoftDiContainer 的 IDisposable 释放逻辑、根 ServiceProvider 清理与释放后访问保护
- 更新 CQRS benchmarks 的容器 cleanup,并补齐 RequestStartupBenchmarks 的冷启动容器释放路径
- 补充 Core 容器生命周期回归测试并归档 issue 327 的 ai-plan topic
2026-05-06 19:08:48 +08:00
gewuyou
588800bb7b
Merge pull request #329 from GeWuYou/chore/archive-completed-ai-plan-topics
chore(ai-plan): 归档已完成专题
2026-05-06 17:22:16 +08:00
gewuyou
ee41206965 chore(ai-plan): 归档已完成专题
- 更新 ai-plan 公共索引,移除 semantic-release-versioning、runtime-generator-boundary 和 github-issue-review-skill 的活跃入口与分支映射
- 归档 三个已完成 topic 的 tracking 与 trace 文档到 ai-plan/public/archive/ 下
2026-05-06 16:59:35 +08:00
gewuyou
db89918333
Merge pull request #328 from GeWuYou/feat/github-issue-review-skill
feat(skills): 新增 GitHub issue 分诊 skill
2026-05-06 16:51:02 +08:00
gewuyou
f25ccccad2 fix(skills): 修复 issue review skill 评审问题
- 修复 issue-review 脚本的代理回退、GitHub Token 认证与 JSON 输出契约

- 调整非 bug issue 的澄清判定并补充 docs、feature 分诊回归测试

- 更新 skill 示例占位符与 ai-plan 跟踪记录,收敛 PR #328 follow-up
2026-05-06 16:25:29 +08:00
gewuyou
ab9829044f feat(skills): 新增 GitHub issue 分诊 skill
- 新增 gframework-issue-review skill,支持抓取 issue 元数据、评论、timeline 与分诊摘要。

- 补充 JSON 输出、唯一 open issue 自动解析与 WSL Linux git 绑定兼容处理。

- 更新 ai-plan 恢复入口并增加脚本级测试与验证记录。
2026-05-06 15:40:48 +08:00
gewuyou
109bce6e9e
Merge pull request #326 from GeWuYou/feat/cqrs-optimization
Test/Add comprehensive CQRS benchmarking suite with reflection and generated invoker paths
2026-05-06 14:29:06 +08:00
gewuyou
6d619b9a1f fix(cqrs): 收敛 benchmark review 收尾问题
- 修复 benchmark workflow 过滤器输入的 shell 注入风险

- 统一 request 与 stream invoker 基准中 MediatR handler 的生命周期基线

- 更新 request pipeline benchmark 的缓存清理与空行为类型声明

- 压缩 cqrs-rewrite active 跟踪与 trace,记录本轮 PR review 收尾结论
2026-05-06 12:57:56 +08:00
gewuyou
2cb6216d05 fix(cqrs): 修复 benchmark 对照宿主与冷启动基线
- 新增 BenchmarkHostFactory 统一 benchmark 最小宿主构建,并限制 MediatR 扫描到当前场景所需类型

- 修复 GFramework benchmark 容器未冻结导致的首次 handler 解析缺口,恢复 RequestStartupBenchmarks 冷启动结果

- 优化 request、pipeline、notification、stream 与 invoker benchmark 的生命周期对齐,减少无关程序集扫描噪音

- 更新 cqrs-rewrite 跟踪与追踪文档,记录 PR #326 benchmark review 收敛、根因和验证结果
2026-05-06 12:09:20 +08:00
gewuyou
f71791ae98 ci(cqrs): 新增手动 benchmark 工作流
- 新增仅支持 workflow_dispatch 的 Benchmark workflow,默认只验证 benchmark 项目 Release build
- 补充可选 benchmark_filter 输入与 BenchmarkDotNet 工件上传,支持按场景手动执行基准测试
- 更新 cqrs-rewrite 跟踪与 trace,记录手动 benchmark workflow 的用途与当前 startup benchmark 残留风险
2026-05-06 11:48:15 +08:00
gewuyou
2ac02c1a6f fix(cqrs): 收敛 benchmark review 修复
- 修复 RequestStartupBenchmarks 的 baseline 分组、初始化阶段对齐与 MediatR 重复注册问题
- 新增共享 dispatcher cache helper,并统一 benchmark 宿主的 MediatR logging/license 过滤配置
- 更新 cqrs-rewrite 跟踪与 trace,记录 PR #326 锚点、验证去重和 startup benchmark 的残留运行风险
2026-05-06 11:07:33 +08:00
gewuyou
449eeb9606 feat(cqrs): 补齐 stream invoker 基准对照
- 新增 stream generated invoker benchmark 与手写 registry,对照 reflection runtime、generated runtime 和 MediatR 的完整枚举开销

- 更新 benchmark README,补充 generated stream invoker provider 的场景说明与后续扩展方向

- 更新 cqrs-rewrite 跟踪与 trace,记录 RP-089 的基线、验证结果和下一批建议
2026-05-06 09:46:52 +08:00
gewuyou
c01abac06e
Merge pull request #325 from GeWuYou/feat/ai-first-config
fix(game-config): 收紧开放对象关键字边界
2026-05-06 09:40:08 +08:00
gewuyou
6e1eaf8f5c test(cqrs): 补充请求调用器生成路径基准
- 新增 request reflection 与 generated invoker provider 的 steady-state 对照基准

- 引入 handwritten generated registry/provider 以走通真实 registrar 与 dispatcher 预热链路

- 更新 benchmark README 与 cqrs-rewrite RP-088 跟踪记录
2026-05-06 09:36:48 +08:00
gewuyou
e0bbf13d88 test(cqrs): 补充请求启动阶段基准
- 新增 request initialization 与 cold-start 基准并对齐当前 runtime 启动口径

- 通过清理 dispatcher 静态缓存隔离 GFramework.Cqrs 首次分发测量结果

- 更新 benchmark README 与 cqrs-rewrite RP-087 跟踪记录
2026-05-06 09:30:17 +08:00
gewuyou
f776d09f68 fix(ai-first-config): 收口开放对象评审跟进
- 修复 Runtime、Generator 与 Tooling 中开放对象关键字校验的不可达 additionalProperties 分支

- 补充 Tooling 对 additionalProperties false 的正向回归测试

- 更新游戏配置接入文档与 ai-plan 跟踪,记录 PR #325 的核验结论和验证结果
2026-05-06 09:25:59 +08:00
gewuyou
a8f98e467d test(cqrs): 补充请求管道数量矩阵基准
- 新增 request pipeline 0/1/4 数量矩阵基准并保持 GFramework.Cqrs 与 MediatR 对照

- 更新 benchmark README 说明当前场景覆盖与后续扩展方向

- 补充 cqrs-rewrite 跟踪与 trace 的 RP-086 恢复点和验证记录
2026-05-06 09:23:07 +08:00
gewuyou
e6f98cb4af test(cqrs): 补充流式请求基准场景
- 新增 StreamingBenchmarks 并对齐 baseline、GFramework.Cqrs 与 MediatR 的完整枚举对照

- 更新 benchmark README 与 CQRS ai-plan 恢复点,记录 stream 场景落地
2026-05-06 09:14:33 +08:00
gewuyou
96729ddcf1 test(cqrs): 补充基准与生成器回归基础设施
- 新增独立的 GFramework.Cqrs.Benchmarks 项目并引入 request、notification 对比场景

- 补充 request 与 stream invoker provider 的 mixed direct/reflected 顺序回归测试

- 更新 solution、meta-package 排除规则与 CQRS ai-plan 恢复点
2026-05-06 08:57:59 +08:00
gewuyou
cb6dd8a510 fix(game-config): 收紧开放对象关键字边界
- 修复 Runtime、Generator 与 Tooling 对 patternProperties、propertyNames、unevaluatedProperties 的静默接受风险

- 补充三端对称回归测试与 reader-facing 文档边界说明

- 更新 ai-plan 恢复点、验证记录与下一步指针
2026-05-06 08:47:42 +08:00
gewuyou
a8c6c11e9e
Merge pull request #324 from GeWuYou/fix/runtime-generator-boundary
fix(game): 剥离运行时模块对生成器依赖
2026-05-05 13:14:24 +08:00
gewuyou
d9ceb83c2c fix(runtime-generator-boundary): 修复边界校验回归问题
- 修复 runtime-generator 边界校验对独立与带参数 attribute 的漏报问题,并过滤注释示例误报

- 新增 Python 回归测试覆盖独立、限定名、多 attribute 与文档示例场景

- 更新贡献文档与 ai-plan 记录,移除面向用户文档中的内部治理段落并补充验证结果
2026-05-05 13:06:18 +08:00
gewuyou
7288114e33 fix(game): 剥离运行时模块对生成器依赖
- 修复 GFramework.Game 对 SourceGenerators.Abstractions 的项目引用并移除未使用的枚举生成 attribute

- 新增 runtime-generator 边界校验脚本并接入 CI 与发布打包校验

- 更新 AGENTS、贡献文档与 ai-plan 跟踪,明确运行时模块禁止依赖生成器能力
2026-05-05 12:39:10 +08:00
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
dependabot[bot]
64c5ecb3ca chore(deps): bump peter-evans/create-pull-request from 7 to 8
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7 to 8.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v7...v8)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-04 09:59:20 +08:00
dependabot[bot]
2ccacb8102 Bump Meziantou.Analyzer from 3.0.58 to 3.0.60
---
updated-dependencies:
- dependency-name: Meziantou.Analyzer
  dependency-version: 3.0.60
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-04 09:59:03 +08:00
dependabot[bot]
ee998503b3 Bump Meziantou.Polyfill from 1.0.120 to 1.0.121
---
updated-dependencies:
- dependency-name: Meziantou.Polyfill
  dependency-version: 1.0.121
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-04 09:58:51 +08:00
gewuyou
69ea92c149
Merge pull request #319 from GeWuYou/build/semantic-release-rules
build(release): 支持依赖与安全提交触发补丁发布
2026-05-04 09:58:33 +08:00
gewuyou
c5ca161cb5 build(release): 修复发布说明类型映射
- 修复 release-notes-generator 的 Conventional Commits 类型映射

- 补充 SEMREL-RP-006 的验证结果与 PR review 恢复点
2026-05-04 08:14:41 +08:00
gewuyou
53f8baf2ef build(release): 支持依赖与安全提交触发补丁发布
- 更新 semantic-release 规则,将 deps 与 security 提交映射为 patch 发布

- 补充 AGENTS 与贡献文档中的提交类型语义

- 记录 SEMREL-RP-005 验证结果与分支恢复入口
2026-05-03 23:00:33 +08:00
gewuyou
fe1a875785
Merge pull request #317 from GeWuYou/chore/license-headers
Chore/license headers
2026-05-03 22:59:31 +08:00
gewuyou
4153ea59b8 docs(agents): 补充文件头治理规则
- 补充 AGENTS.md 中的许可证文件头规则

- 修复新增治理文件自身缺失的文件头
2026-05-03 21:00:03 +08:00
gewuyou
ff553977e3 chore(license): 补齐 Apache-2.0 文件头治理
- 新增许可证文件头检查与修复脚本

- 补充维护者手动修复 PR 工作流和 CI 校验

- 更新贡献指南中的文件头说明

- 补齐仓库维护源码和配置文件的许可证声明
2026-05-03 19:39:49 +08:00
gewuyou
a0591afa18
Merge pull request #316 from GeWuYou/docs/godot-logging-composition-archive
docs(godot): 归档 Godot logging 主题
2026-05-03 19:23:43 +08:00
gewuyou
d5d34a626c docs(godot): 修复日志组合文档示例
- 修复组合 logger 示例重复创建文件 appender 的生命周期问题

- 更新 Core logging 文档中的平台无关路径示例

- 补充 Godot 日志页面的路径解析指引承接
2026-05-03 19:07:20 +08:00
gewuyou
230cd0e5d1 docs(godot): 归档 Godot logging 主题
- 补充 GodotLogAppender 与 Core appender 组合示例

- 更新 Godot logging 文档中的文件输出接入说明

- 归档 godot-logging-core-sink 恢复材料并清理 boot 索引
2026-05-03 18:54:33 +08:00
gewuyou
6fa1c20d75
Merge pull request #315 from GeWuYou/feat/godot-logging-core-sink
Feat/godot logging core sink
2026-05-03 15:14:33 +08:00
gewuyou
64e5d8d11d test(godot): 补强日志反射断言
- 补充 GodotLogger 结构化属性反射目标的显式断言

- 优化 反射返回类型不匹配时的测试失败定位

- 更新 godot logging core sink 跟踪与执行 trace
2026-05-03 14:04:59 +08:00
gewuyou
3ced56be8b chore(godot): 处理 Godot 日志 PR 反馈
- 修复 GodotLogAppender 测试对结构化属性顺序的依赖

- 移除 GodotLogger 未使用的私有格式化包装方法

- 更新 ai-plan 默认索引和 trace 恢复记录,避免归档主题与重复标题干扰 boot
2026-05-03 13:24:24 +08:00
gewuyou
1009fee4a4 feat(godot): 新增 Godot 日志 Appender
新增 GodotLogAppender 作为 Core ILogAppender 的 Godot 控制台落点

重构 GodotLogger 输出路径以复用 appender 管线并保持现有 ILogger 入口

补充 Godot appender 渲染测试、文档说明与 active topic 恢复记录
2026-05-03 11:03:58 +08:00
gewuyou
40cce565e6 docs(ai-plan): 启动 Godot logging Core sink 主题
- 归档 Godot logging 合规收尾主题并保留验证结果

- 新增 Godot logging Core sink active topic 恢复入口

- 更新 public boot 索引和当前分支映射
2026-05-03 10:51:16 +08:00
gewuyou
918a61f3b2
Merge pull request #314 from GeWuYou/feat/godot-logging-compliance-polish
Feat/godot logging compliance polish
2026-05-03 10:07:03 +08:00
gewuyou
c967b4df3d fix(godot): 修复日志 review 反馈
- 修复 DeferredLogger 格式化重载提前 string.Format 的热路径问题

- 修复 GodotLogger 默认 options 分配与结构化属性无效 key 处理

- 补充 Godot logging XML 文档、回归测试和 appsettings 接入示例

- 更新 Godot logging PR review 跟踪与验证记录
2026-05-03 09:00:41 +08:00
gewuyou
b4b3538b21 fix(godot): 收敛日志配置评审问题
- 修复 GodotLog 配置源生命周期、Shutdown 释放与延迟 logger 并发发布问题

- 修复 Godot logger 配置归一化、无效数字级别校验和未知级别颜色回退

- 优化 Godot 日志模板缓存边界、内部文档和 update-namespaces 脚本失败传播

- 补充 Godot logging 回归测试、用户文档与 active ai-plan 恢复记录
2026-05-02 22:43:07 +08:00
gewuyou
a52f3c6fec feat(godot): 补齐 GodotLogger 接入文件
- 新增 GodotLogger 的配置加载、延迟入口和宿主输出适配实现。
- 复制 ai-libs/GodotLogger 的 MIT 许可证到 third-party-licenses/GodotLogger/LICENSE。
- 补充 active tracking 与 trace,保留下一阶段对齐点。
2026-05-02 21:39:26 +08:00
gewuyou
748bb714fb feat(godot): 收敛 GodotLogger 宿主能力
- 新增 GodotLog、DeferredLogger 和配置自动发现、热重载接线。
- 修复已缓存 logger 的级别判定与输出路径,使动态配置生效。
- 更新文档与追踪记录,明确当前收敛边界和恢复点。
2026-05-02 21:33:28 +08:00
GeWuYou
36e1ae5f32 feat(godot): add configurable logger templates 2026-05-02 19:33:00 +08:00
gewuyou
6aa741114f ci: include third-party licenses in compliance bundle 2026-05-02 19:10:44 +08:00
gewuyou
5306c98470 chore(ai-plan): archive release summary notes recovery point 2026-05-02 19:10:18 +08:00
gewuyou
35a62e6bfb
Merge pull request #313 from GeWuYou/feat/release-summary-notes
fix(release): 修复发布说明变更汇总标题
2026-05-01 23:36:13 +08:00
gewuyou
43094fba83 fix(release): 修复发布说明变更汇总标题
- 修复 git-cliff 模板的 What's Changed 归属,让分类变更列表承担完整变更清单语义

- 保留 每条变更末尾的作者与 PR 链接,避免新增独立 PR 索引造成重复展示

- 更新 semantic-release-versioning 恢复文档与当前分支映射
2026-05-01 23:29:04 +08:00
gewuyou
a1b3576b09
Merge pull request #308 from GeWuYou/docs/sdk-update-documentation
Docs/sdk update documentation
2026-05-01 21:58:22 +08:00
gewuyou
e391833615
Merge pull request #312 from GeWuYou/feat/release-summary-notes 2026-05-01 21:02:22 +08:00
gewuyou
a870ea28a8 fix(ci): 修复发布说明输出
- 修复 git-cliff 模板重复输出提交的问题

- 更新 GitHub Release 使用 release notes 文件作为正文

- 补充 PR review 修复记录与验证结果
2026-05-01 20:40:01 +08:00
gewuyou
3cb0177936 feat(ci): add cliff-based release summaries 2026-05-01 20:22:08 +08:00
gewuyou
6983b7ee84 docs(source-generators): 收口最新 PR 审查跟进
- 更新 schema-config-generator 文案并同步 PR review 最新建议

- 修复 documentation-full-coverage-governance active tracking 的 RP-055 验证引用与审查事实

- 更新 active trace 的 latest reviewed commit、线程结论与验证摘要
2026-05-01 19:08:39 +08:00
gewuyou
00ecf6fb10 docs(source-generators): 收口 PR 审查文档跟进
- 更新 source-generators 文档中的 fallback 条件说明与自包含运行时示例

- 压缩 documentation-full-coverage-governance active tracking 的验证摘要并同步最新 PR 审查事实

- 补充 active trace 的显式风险记录与本轮验证结论
2026-05-01 17:02:25 +08:00
dependabot[bot]
52b96ed36f Bump Meziantou.Analyzer from 3.0.52 to 3.0.58
---
updated-dependencies:
- dependency-name: Meziantou.Analyzer
  dependency-version: 3.0.58
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-01 16:52:02 +08:00
dependabot[bot]
85a8b35154 Bump Meziantou.Polyfill from 1.0.116 to 1.0.120
---
updated-dependencies:
- dependency-name: Meziantou.Polyfill
  dependency-version: 1.0.120
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-01 16:51:51 +08:00
dependabot[bot]
9581682231 Bump Microsoft.NET.Test.Sdk from 18.4.0 to 18.5.1
---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-version: 18.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-version: 18.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-version: 18.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-version: 18.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-version: 18.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-version: 18.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-version: 18.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-01 16:51:35 +08:00
gewuyou
896e3efaa9 docs(source-generators): 收口 PR 审查中的文档入口表述
- 更新 source-generators 文档中的测试入口表述,使用语义化链接标签替代源码路径

- 校正 documentation-full-coverage-governance active ai-plan 的 PR 审查事实、验证结果与下一步恢复点
2026-05-01 15:37:26 +08:00
gewuyou
4fdb1e7398 docs(ai-plan): 收口 PR 审查遗留文档问题
- 修复 documentation-full-coverage-governance 归档中的 Markdown 行内代码闭合错误

- 更新 active tracking 与 trace,使其反映 PR #308 review 的本地核验结论

- 补充本轮文档构建验证结果与后续恢复步骤
2026-05-01 13:29:52 +08:00
gewuyou
241c9ffeb3 docs(source-generators): 补充迁移兼容说明并归档恢复历史
- 补充 Schema 配置生成器专题的迁移步骤、兼容边界与回退建议

- 更新 active tracking 与 active trace,只保留当前恢复事实、验证结果与归档指针

- 新增 RP-049 到 RP-052 的状态、验证与时间线归档文件
2026-05-01 13:22:14 +08:00
gewuyou
7e77fee0a5 docs(ai-plan): 更新文档覆盖恢复点
- 更新 committed diff 基线,记录本轮提交后的 8 files / 337 lines 状态

- 补充 documentation-full-coverage-governance 的下一步建议,避免后续按过时工作树峰值恢复
2026-05-01 13:22:13 +08:00
gewuyou
103b961e6f docs(source-generators): 补充生成器专题覆盖并更新进度
- 新增 Schema 配置生成器专题页,补充输入契约、生成物与诊断边界

- 更新 source-generators、API 参考与 CQRS 文档,说明共享支撑层阅读路线与 fallback 分层

- 更新 documentation-full-coverage-governance 的 tracking 和 trace,记录批次指标与验证结果
2026-05-01 13:22:12 +08:00
gewuyou
1c21df1414
Merge pull request #307 from GeWuYou/feat/cqrs-optimization
Feat/Add stream invoker provider support to CQRS handler registry generator
2026-05-01 09:31:00 +08:00
gewuyou
26314dba5e docs(cqrs-rewrite): 收敛恢复入口文档
- 更新 active tracking,仅保留 RP-076、PR #307、活跃风险、权威验证与下一推荐步骤

- 重构 active trace,仅保留当前阶段决策、验证结果与后续恢复方向

- 补充 RP-062 至 RP-076 的 trace 归档,承接迁出的历史阶段上下文
2026-04-30 18:47:55 +08:00
gewuyou
9296def108 test(cqrs): 补齐 stream invoker gate 回归
- 补充 stream invoker descriptor 与 descriptor entry 缺失时整体跳过 provider 元数据的生成器回归

- 优化测试辅助重命名逻辑,精确模拟 metadata name 缺失而不破坏其余合同编译

- 更新 cqrs-rewrite 跟踪与追踪,记录 PR #307 follow-up 的恢复点和验证结果
2026-04-30 17:50:30 +08:00
gewuyou
83528742bb fix(cqrs): 收敛生成调用描述符与PR评审回归
- 修复 request 与 stream generated invoker 描述符的静态方法与空值防御,提前拒绝非法元数据

- 补充 provider 空描述符枚举与非静态 invoker 回退回归,更新相关 XML 注释与中文文档语义

- 更新 cqrs-rewrite 活跃跟踪、执行 trace 与验证归档,记录 PR #307 的当前验证结论
2026-04-30 16:25:59 +08:00
gewuyou
36db7d0929
Merge pull request #306 from GeWuYou/feat/ai-first-config
Feat/ai first config
2026-04-30 16:09:43 +08:00
gewuyou
e671646a74 fix(ai-first-config): 收口 PR 306 审查遗留项
- 新增 Generator 与 Tooling 的 anyOf 和坏形状回归覆盖,补齐组合关键字与未知 type 拒绝

- 修复 VS Code 配置工具的 object-array 直属项收集与 contains 文案一致性问题

- 更新 README、Game 文档与工具说明,明确 additionalProperties 显式 false 边界与最小接入路径

- 补充 ai-plan 跟踪与 trace,记录 PR 306 open threads 收口结果和验证摘要
2026-04-30 15:22:04 +08:00
gewuyou
8b36626266 test(cqrs): 补充 provider fallback 回归
- 新增 non-enumerating request 与 stream provider 回归,锁定 dispatcher 会继续回退到反射路径

- 更新 CQRS 重写恢复点到 RP-074,并记录定向验证结果
2026-04-30 15:04:10 +08:00
gewuyou
040bcb99e4 fix(ai-first-config): 收口当前 PR 审查遗留项
- 新增 anyOf 对称运行时回归测试,覆盖组合关键字拒绝分支

- 更新 Game Abstractions README 的配置系统链接显示名,避免暴露原始路径

- 精简 active tracking 的批次级验证细节并补充恢复指针

- 清理 trace 中重复日期标题,消除 MD024 风险
2026-04-30 15:03:47 +08:00
gewuyou
1091594224 fix(cqrs): 收敛 generated invoker 异常语义
- 修复 request 与 stream generated invoker 签名不兼容时冒出 ArgumentException 的行为,统一包装为 InvalidOperationException

- 补充对应 runtime 回归测试并更新 CQRS 重写恢复点到 RP-073
2026-04-30 14:59:19 +08:00
gewuyou
502f65239c test(cqrs): 补充 provider gate 合同回归
- 新增 request 与 stream gate 回归,锁定 runtime 合同不完整时不会发射 invoker provider 元数据

- 更新 CQRS 重写恢复点到 RP-072,并记录定向验证与 helper 收敛
2026-04-30 14:53:05 +08:00
gewuyou
dc21188c79 test(cqrs): 锁定 precise reflected provider 边界
- 新增 request 与 stream generator 回归,明确 precise reflected 注册不会发射 invoker provider 元数据

- 更新 CQRS 重写恢复点到 RP-071,并记录本轮验证与边界结论
2026-04-30 14:44:51 +08:00
gewuyou
6b5c5d9e2d docs(cqrs): 更新生成式 invoker 恢复点
- 更新 CQRS 重写跟踪到 RP-070,补充 hidden-implementation generated invoker runtime 回归说明

- 补充本轮定向验证结果与当前相对 origin/main 的 branch diff 指标
2026-04-30 14:37:12 +08:00
gewuyou
5a77e2fb33 test(cqrs-tests): 补充 hidden implementation generated invoker 回归
- 新增 hidden implementation request provider runtime 集成回归,验证 registrar 与 dispatcher 会继续消费 generated metadata

- 新增 hidden implementation stream provider runtime 集成回归,覆盖可见 handler interface 下的流式 dispatch 路径

- 补充对应测试替身 registry 与隐藏 handler 容器,保持现有 generated invoker 测试风格
2026-04-30 14:13:09 +08:00
gewuyou
eb30388267 feat(cqrs): 扩大生成式 invoker 发射范围
- 扩大 request 与 stream invoker 发射范围到 reflected-implementation 注册场景

- 补充 hidden implementation 回归测试并更新 CQRS ai-plan 恢复点
2026-04-30 14:07:05 +08:00
gewuyou
172c08176c test(cqrs): 补充 hidden implementation provider 元数据断言
- 新增 hidden implementation 但 visible handler interface 的 request provider 生成断言

- 新增 hidden implementation 但 visible handler interface 的 stream provider 生成断言
2026-04-30 13:34:08 +08:00
gewuyou
ea0b937705 feat(cqrs): 补充生成式 stream invoker 接缝
- 新增 stream invoker provider、descriptor 与 dispatcher/registrar 接线

- 更新 source generator 与回归测试,覆盖 generated stream invoker 发射和消费语义

- 更新 CQRS 文档与 ai-plan 恢复点,补充 stream invoker 的接入与验证记录
2026-04-30 13:26:54 +08:00
gewuyou
85f7c1707e docs(game): 同步场景与宿主入口配置边界
- 补充 Scene 与 UI 入口对配置系统正式边界页的指引

- 明确 oneOf、anyOf 与非 false additionalProperties 不属于默认采用路径

- 更新 Godot storage 入口对 VS Code 工具辅助层与 raw YAML 回退路径的说明
2026-04-30 13:25:29 +08:00
gewuyou
01f1e5fd72 docs(game): 同步数据与设置入口配置边界
- 补充 data 与 setting 入口对 AI-First 配置系统共享 schema 子集的 reader-facing 提示

- 说明 DataRepository、UnifiedSettingsDataRepository 与 SettingsModel 负责持久化和应用而不放宽配置契约

- 更新复杂 schema shape 回到 config-system 与 raw YAML 处理的采用指引
2026-04-30 13:25:29 +08:00
gewuyou
e8203bc76e docs(game): 同步生成器与持久化入口配置边界
- 补充 Game.SourceGenerators 对共享 schema 子集的 reader-facing 采用边界说明

- 更新 serialization 与 storage 页面中的复杂 schema 回退路径提示

- 明确 oneOf、anyOf 与非 false additionalProperties 不属于默认采用路径
2026-04-30 13:25:29 +08:00
gewuyou
7e62313b24 docs(game): 同步总览入口配置采用边界
- 补充首页、入门页与 API 导航对 AI-First 配置工作流正式契约的高层说明

- 更新入口提示以说明 additionalProperties: false 与 oneOf/anyOf 的默认采用边界

- 强调超出共享 schema 子集的复杂 shape 应回到 raw YAML 与 schema 设计处理
2026-04-30 13:25:26 +08:00
gewuyou
74f853bffe docs(game): 同步生成器与抽象层配置边界
- 更新 source-generators 入口,说明 Game.SourceGenerators 面向与 Runtime 对齐的共享 schema 子集

- 补充 abstractions 文档与 README,明确配置契约实现边界仍需回到 GFramework.Game 与 config-system 文档

- 强调 oneOf、anyOf 与非 false 的 additionalProperties 不属于当前 reader-facing 采用路径
2026-04-30 13:23:19 +08:00
gewuyou
56a96b50fd docs(game): 同步配置入口采用边界
- 更新 Game 入口页的静态 YAML 配置接入提示,明确 Runtime 与 Source Generator 的共享契约优先级

- 补充安装入口对 additionalProperties: false 与 oneOf / anyOf 拒绝边界的 reader-facing 提示

- 优化入口页采用建议,说明复杂 shape 应回退到 raw YAML 与 schema 设计本体
2026-04-30 13:23:19 +08:00
gewuyou
0721cafd03 docs(game): 同步配置工作流入口边界
- 更新仓库根 README 的 AI-First 配置接入提示与共享子集边界说明

- 补充 GFramework.Game README 中的配置系统采用约束与 raw YAML 回退路径
2026-04-30 13:23:19 +08:00
gewuyou
fdcb11c92c fix(config-tool): 收紧坏形状 schema 解析边界
- 修复 Tooling 侧 additionalProperties 仅接受 false 的共享边界校验

- 补充数组 items 与 contains 子 schema 必须显式声明 type 的拒绝逻辑

- 更新 ai-plan 恢复摘要与 JS 回归测试验证记录
2026-04-30 13:23:19 +08:00
gewuyou
e8cceac7ae docs(game): 补齐配置工具能力边界说明
- 更新 config system 与 config tool 的 reader-facing 边界说明

- 补充 additionalProperties:false、oneOf/anyOf rejection 与 raw YAML 回退路径

- 记录本批次 Tooling/Docs 收口验证与下一步
2026-04-30 13:23:19 +08:00
gewuyou
7f98cafbfa fix(config-tool): 统一 contains 与本地化提示文案
- 修复 dependentRequired 校验消息键缺失导致的隐式 undefined 文案映射

- 统一 contains 与 dependent schema 相关中文提示措辞并补齐 maxContains hint 输出

- 补充本地化与 contains 摘要测试覆盖新增文案与回归场景
2026-04-30 13:23:19 +08:00
gewuyou
3f335f19d6 docs(game): 收口配置工具说明入口
- 更新 config-tool 文档,承接 VS Code 工具能力、边界与适用场景说明

- 优化 config-system 文档,移除重复工具细节并保留系统级入口说明
2026-04-30 13:23:19 +08:00
gewuyou
13b77eb3fe fix(game-config): 显式声明闭合对象字段边界
- 修复 Runtime 与 Source Generator 对 additionalProperties 的隐式闭合对象语义,统一接受 additionalProperties:false 并拒绝其它开放对象形状

- 补充 Release 回归测试,覆盖生成器诊断与运行时 additionalProperties 边界

- 更新配置工具元数据与 README 说明,使命令、设置和当前能力描述保持一致
2026-04-30 13:23:19 +08:00
gewuyou
eddce21383 docs(ai-first-config-system): 补充 tooling lane 收口验证记录
- 更新 tracking 中的 Tooling lane 收口验证结果,补齐实际的 Release build 结论
2026-04-30 13:23:19 +08:00
gewuyou
fad391e8cf feat(config-tool): 支持对象数组内嵌对象数组编辑
- 新增对象数组编辑器对数组项内嵌对象数组的递归渲染与保存能力

- 补充嵌套对象数组表单模型与 YAML 写回回归测试

- 更新配置系统文档中的 raw YAML 回退边界说明
2026-04-30 13:23:19 +08:00
gewuyou
d6a154726c fix(game-config): 显式拒绝 oneOf 与 anyOf 组合关键字
- 修复 Runtime、Source Generator 与 Tooling 对 oneOf/anyOf 的静默接受,统一改为显式报错

- 补充 JS 与 Release 测试回归,覆盖生成器诊断和运行时拒绝路径

- 更新 ai-plan 跟踪与中文文档,明确后续默认跳过会改变生成类型形状的组合关键字
2026-04-30 13:23:19 +08:00
gewuyou
f17f9f3da6 test(cqrs): 补充 stream invoker provider 生成断言
- 新增 stream invoker provider runtime fixture,复用 request provider 测试风格锁定 descriptor 和静态 invoker 形状

- 补充 Phase 8 stream invoker provider 回归测试骨架,并暂时以 Ignore 挂起等待主线程生成实现落地
2026-04-30 13:14:29 +08:00
gewuyou
98477068d6 docs(cqrs): 补充生成式 stream invoker 文档语义
- 更新 CQRS runtime 与生成器文档,补充 generated stream invoker provider / descriptor 的并列表述。

- 说明 runtime 优先消费 generated request / stream invoker 元数据,未命中时回退到既有反射 binding。

- 调整 request-only 措辞,使 reader-facing 文案与现有 generated request invoker 语义保持一致。
2026-04-30 13:14:11 +08:00
gewuyou
8d6fc74b3d
Merge pull request #305 from GeWuYou/feat/cqrs-optimization
Feat/Add notification publisher seam and request invoker provider for CQRS runtime
2026-04-30 13:05:41 +08:00
gewuyou
0f1e91a499 fix(cqrs): 收口PR审查遗留问题
- 修复并发 CQRS 解析测试的失败路径释放逻辑,并收敛重复 orchestration 以消除新增 analyzer warning

- 更新 generated request invoker provider 相关测试、XML 文档与 generator 注释,明确默认 runtime 的描述符预热契约

- 调整 legacy runtime alias 注册与 generated provider 注册顺序,并同步 cqrs-rewrite 跟踪文档中的 PR #305 triage 结果
2026-04-30 12:58:05 +08:00
gewuyou
0c65cd8e38 feat(cqrs): 前移请求调用器生成注册
- 新增 generated request invoker provider seam,并让 registrar 与 dispatcher 复用编译期请求调用元数据

- 扩展 CQRS source generator 发射 request invoker provider 成员与最小 request invoker 方法

- 补充 runtime 与 source-generator 回归测试,并更新 cqrs-rewrite 追踪到 RP-067
2026-04-30 12:10:25 +08:00
gewuyou
7209fdc32d docs(cqrs): 收口旧版运行时别名说明
- 更新 LegacyICqrsRuntime 兼容层说明,明确旧命名空间别名与正式 CQRS runtime seam 的边界

- 补充容器基础设施回填 legacy alias 的回归测试,并收敛相关 helper 注释

- 更新 cqrs-rewrite 跟踪与 trace,记录 RP-066 的批处理结果和验证
2026-04-30 11:38:52 +08:00
gewuyou
c1dfee3c71 test(core): 补充架构上下文CQRS懒解析回归
- 新增 PublishAsync 与 CreateStream 并发首次访问只解析一次 ICqrsRuntime 的回归测试

- 更新 cqrs-rewrite 跟踪与 trace,记录三份 Mediator 测试命名收口已完成
2026-04-30 11:28:37 +08:00
gewuyou
b015a91e57 test(cqrs): 收口 ArchitectureContext 综合测试命名
- 重命名综合测试类、命名空间与文件路径,使其与 CQRS 和 ArchitectureContext 语义一致

- 更新中文注释与局部变量命名,移除残留的 Mediator 表述且不改变测试行为

- 收窄文件内测试辅助类型可见性,避免额外暴露旧语义类型并保持项目编译通过
2026-04-30 11:26:18 +08:00
gewuyou
f44629deb3 test(cqrs): 统一架构上下文集成测试命名
- 重命名 CQRS 架构上下文集成测试文件、命名空间与测试类以移除 Mediator 语义残留

- 更新嵌套测试类型、局部变量与中文注释为 CQRS 和 ArchitectureContext 一致命名

- 补充公开测试类型与成员的 XML 文档说明而不改变测试断言行为
2026-04-30 11:25:16 +08:00
gewuyou
e1af8ac833 test(cqrs): 收口高级特性测试的CQRS命名
- 重命名高级特性测试类、命名空间与文件路径,统一到 CQRS 与 ArchitectureContext 语义

- 更新测试方法名、中文注释与日志器名称,移除残留的 Mediator 命名

- 补充当前测试文件内辅助类型的 XML 文档,保持测试行为不变
2026-04-30 11:24:22 +08:00
gewuyou
22f608eb4d feat(cqrs): 新增通知发布策略接缝
- 新增 notification publisher seam 与默认顺序发布器,保持零处理器静默完成与首错即停语义

- 调整 dispatcher、runtime factory 与测试基础设施,支持复用容器中预注册的通知发布策略

- 补充 publisher 回归测试并更新 CQRS 文档与 ai-plan 恢复点
2026-04-30 11:07:24 +08:00
gewuyou
a3fe2974f7 docs(cqrs): 归档CQRS与Mediator评估结论
- 新增 CQRS 与 Mediator 的结构化评估归档,明确生产替代完成度与设计吸收差距

- 更新 cqrs-rewrite active tracking 与 trace,提升恢复点到 RP-063 并重排后续优先级

- 补充本轮最小 Release 构建验证结果,保持 ai-plan 恢复入口与实际状态一致
2026-04-30 10:16:35 +08:00
gewuyou
5eea12b5ba
Merge pull request #304 from GeWuYou/feat/cqrs-optimization
Feat/cqrs optimization
2026-04-30 09:43:53 +08:00
gewuyou
72ce0f1199 test(cqrs): 收敛剩余 PR304 review 跟进
- 修复 fallback failure 测试夹具的并行执行与 stream state 文档命名问题

- 归档 cqrs-rewrite 历史 trace 与验证记录并压缩 active 恢复入口

- 更新当前验证结果与下一步,保持 PR304 review follow-up 可恢复
2026-04-30 09:23:01 +08:00
gewuyou
98021f59e7 test(cqrs): 补齐 PR304 测试 XML 注释
- 补齐上下文校验 handler 的 Handle 参数与返回 XML 注释

- 更新带 DispatchId 的测试请求与通知 XML 参数注释

- 记录 cqrs-rewrite 主题的本轮 PR review 跟进
2026-04-30 09:00:24 +08:00
gewuyou
255a6a152e fix(cqrs): 收敛 PR 304 review 跟进
- 修复 CqrsDispatcher 的 pipeline invoker 重复创建,并补齐缓存线程模型文档

- 优化 CQRS 与 generator 回归测试的并发保护和稳定语义断言

- 更新 cqrs-rewrite 跟踪与 trace,记录 RP-062 的 PR review follow-up 验证结果
2026-04-30 07:43:42 +08:00
gewuyou
bc365197e8 docs(cqrs): 刷新批处理恢复点记录
- 更新 cqrs-rewrite 跟踪与 trace,记录 RP-061 的 registrar fallback 失败分支批次

- 同步当前 gframework-batch-boot 50 的分支规模与下一步恢复入口
2026-04-29 23:03:22 +08:00
gewuyou
a445807b83 test(cqrs): 新增 registrar fallback 失败分支测试
- 新增独立测试文件覆盖 fallback 名称无法解析时的 warning 与跳过行为

- 新增 fallback 名称解析抛异常时的 warning 回归断言

- 补充 direct fallback 跨程序集条目被跳过并记录 warning 的验证
2026-04-29 23:03:22 +08:00
gewuyou
52b9ddd4a7 test(cqrs): 补充上下文前置条件失败回归
- 新增 dispatcher 上下文校验测试,锁定非 IArchitectureContext 上下文的 request、notification 与 stream 失败语义

- 通过公开 runtime 工厂与最小容器 mock 覆盖调用前校验路径,不改 runtime 实现

- 更新 cqrs-rewrite 跟踪与 trace,记录 RP-060 的验证结论
2026-04-29 23:03:21 +08:00
gewuyou
57d848546f test(cqrs): 补充非请求分发上下文回归
- 新增 notification 与 stream dispatch binding 上下文刷新回归,锁定缓存复用时仍按当次分发重新注入上下文

- 补充测试替身记录 handler 实例身份与 ArchitectureContext,覆盖重复分发场景

- 更新 cqrs-rewrite 跟踪与 trace,记录 RP-058 和 RP-059 的验证结论
2026-04-29 23:03:21 +08:00
gewuyou
226c0b3b49 test(cqrs): 补充注册服务程序集去重测试
- 新增 DefaultCqrsRegistrationService 的独立测试文件,覆盖同次调用内的重复程序集键去重行为

- 验证跨两次调用重复程序集键时会跳过注册并写入 debug 日志
2026-04-29 23:03:21 +08:00
gewuyou
36596210ff test(cqrs): 新增 ReflectionFallbackAttribute 合同测试
- 新增 CqrsReflectionFallbackAttribute 的叶子级合同测试,覆盖旧版 marker 语义

- 补充字符串与 Type 输入的过滤、去重、排序归一化断言

- 验证空参数数组保护,固定 runtime 可依赖的 attribute 元数据边界
2026-04-29 23:03:21 +08:00
gewuyou
16cd96b94b test(cqrs): 补充 dispatcher 缓存上下文回归
- 新增 cached request pipeline executor 的上下文刷新回归测试与专用测试替身

- 记录 singleton behavior 生命周期语义下的上下文重新注入结论

- 更新 cqrs-rewrite 跟踪与 trace 恢复点到 RP-057
2026-04-29 23:03:21 +08:00
gewuyou
5365f9aec2 refactor(cqrs): 删除 pointer 运行时重建残留
- 重构 CqrsHandlerRegistryGenerator 的运行时类型引用模型,移除不可达的 pointer 子结构

- 删除 SourceEmission 中已失效的 MakePointerType 发射分支,保持 pointer 拒绝语义不变

- 更新 cqrs-rewrite 跟踪与 trace,记录本轮清理和定向验证结果
2026-04-29 23:03:21 +08:00
gewuyou
e51b64f8d5 test(cqrs): 补齐外部隐藏泛型精确注册回归
- 新增外部程序集隐藏泛型定义与可见类型实参的 precise registration 回归

- 更新 CQRS 重写跟踪与 trace,记录本轮覆盖范围和验证结果
2026-04-29 23:03:21 +08:00
gewuyou
7b5efde3bd test(cqrs): 补强数组类型生成回归
- 新增多维数组、交错数组与外部程序集隐藏元素类型的 precise runtime type lookup 回归

- 更新 cqrs-rewrite 跟踪与追踪,记录 RP-053 到 RP-054 的并行批次收口与验证结果
2026-04-29 23:03:21 +08:00
gewuyou
e81a43680d fix(cqrs): 缓存请求管道执行形状
- 优化 CqrsDispatcher 的 request pipeline 路径,按请求类型与行为数量缓存 typed executor 形状并在单次分发中绑定当前 handler 与 behaviors

- 补充 dispatcher 缓存回归测试,覆盖 pipeline executor 的首次创建、后续复用与行为顺序稳定
2026-04-29 23:03:21 +08:00
gewuyou
3b4eb3e40a docs(cqrs): 更新入口与回退语义说明
- 更新 CQRS 入口文档,明确 generated registry 优先与 targeted fallback 的注册顺序

- 修正 README 对 CqrsReflectionFallbackAttribute 的过时描述,补充多实例与 Type 或字符串双合同语义

- 优化 API 参考中的 CQRS 阅读关注点,突出 generated registry 与 targeted fallback contract
2026-04-29 23:03:21 +08:00
gewuyou
79f9cb3706
Merge pull request #303 from GeWuYou/docs/sdk-update-documentation
Docs/sdk update documentation
2026-04-29 22:59:12 +08:00
gewuyou
ddaabd8104
Merge pull request #302 from GeWuYou/feat/cqrs-optimization
Feat/cqrs optimization
2026-04-29 16:32:45 +08:00
gewuyou
8d8b94f608 fix(cqrs): 收敛 fallback 审查跟进
- 修复 generator preamble 的多实例 fallback 特性排版并移除死参数

- 补强 mixed/direct fallback 生成回归断言并拒绝空 marker

- 更新 CQRS 审查跟踪记录与 XML 文档
2026-04-29 16:20:15 +08:00
gewuyou
76fcdb8233 perf(cqrs): 拆分混合 fallback 元数据
- 优化 CqrsReflectionFallbackAttribute 与生成器发射策略,在 mixed 场景下拆分 Type 与字符串 fallback 元数据
- 补充 CQRS runtime 与 SourceGenerators 回归测试,锁定多实例 fallback 特性和定向类型回查行为
- 更新 CQRS 生成器文档与 ai-plan 恢复记录,沉淀 RP-052 的验证结果与下一步
2026-04-29 13:37:05 +08:00
gewuyou
1d5404e206 docs(documentation-governance): 收口数据与 UI 文档措辞
- 更新 Game 数据与存储页面、Godot UI 页面中的 reader-facing 说明,移除内部证据口吻、外部项目指代和生硬导流

- 更新 CQRS 抽象层与 SourceGenerators.Common README 的标签表述,避免暴露源文件路径列表和实现级打包术语

- 补充 documentation-full-coverage-governance 的 RP-050 恢复点、验证结果与 origin/main stop-condition 计量
2026-04-29 13:36:07 +08:00
gewuyou
5fd71f3620 perf(cqrs): 收敛生成器 fallback 元数据发射
- 优化 CqrsHandlerRegistryGenerator 的 fallback 合同探测与元数据发射策略,在可直接引用 handlers 时优先输出 Type 元数据
- 补充 SourceGenerators 回归测试,覆盖字符串合同兼容路径与直接 Type 元数据优先级
- 更新 CQRS 生成器说明与 ai-plan 恢复文档,记录 RP-051 的验证结果与后续方向
2026-04-29 13:25:20 +08:00
gewuyou
e18512f043 docs(documentation-governance): 收口 Game 与 Godot 文档措辞
- 更新 Game / Godot 细页的交叉链接与边界描述,移除内部路径、旧文档对比和命令式跳转

- 更新 GFramework.Godot 与配置工具 README 的公开标签,避免暴露测试路径和原始文档路径

- 补充 documentation-full-coverage-governance 的 RP-049 恢复点、验证结果与 origin/main stop-condition 计量
2026-04-29 13:23:26 +08:00
gewuyou
4557dde631
Merge pull request #301 from GeWuYou/fix/analyzer-warning-reduction-batch
Fix/analyzer warning reduction batch
2026-04-29 11:14:56 +08:00
gewuyou
0ad2ed1761 fix(game): 修复空对象配置比较键并归档 warning reduction 主题
- 修复 YamlConfigAllowedValue 与 YamlConfigConstantValue 对空对象 const 或 enum 比较键的误判,同时继续拒绝非空纯空白输入
- 补充 YamlConfigModelContractTests 对空比较键与纯空白比较键的回归覆盖,并验证空对象 const 场景
- 更新 ai-plan 公共索引并归档 analyzer-warning-reduction 主题,保留最终 PR review 结论与验证记录
2026-04-29 10:27:01 +08:00
GeWuYou
590f2cb516 fix(enum):补充枚举注解 2026-04-29 10:17:02 +08:00
gewuyou
f5f2c251e5 fix(pr-review): 修复当前评审中仍然成立的问题
- 修复 Mediator 集成测试中的阻塞等待、缓存竞态与共享状态原子性问题

- 补充 YamlConfig 运行时模型的构造期约束与 exception XML 文档

- 新增 模型契约回归测试并更新 analyzer warning reduction 恢复文档
2026-04-29 09:19:24 +08:00
gewuyou
7da985947c fix(game): 清理剩余配置 schema warning
- 重构 YamlConfigSchemaValidator 的长方法为语义化 helper,清理剩余 MA0051 warning
- 修复 条件分支 helper 的字符串比较方式,避免新增 MA0006 warning
- 更新 analyzer warning reduction 跟踪与 trace,记录仓库根 clean build 已归零
2026-04-29 08:55:03 +08:00
gewuyou
104ac25dc3 refactor(game): 拆分 schema 校验模型类型
- 拆分 schema model 类型到独立同名文件

- 清理 schema 校验模型的文件命名 analyzer 告警

- 更新 warning reduction 批处理收口状态
2026-04-29 08:38:23 +08:00
gewuyou
1395b84439 refactor(game): 拆分对象 schema 关键字校验方法
- 重构 dependentRequired 与 dependentSchemas 的单项解析流程

- 重构 allOf 与条件 schema 的分支解析流程

- 优化 object-focused 内联 schema 的 properties 与 required 校验拆分
2026-04-29 08:32:04 +08:00
gewuyou
e1c1eb1123 fix(game): 收紧 schema 正则校验边界
- 修复 schema 正则校验缺少超时边界导致的 analyzer 风险

- 更新字符串等值比较为 ordinal 语义

- 补充 warning reduction 批处理恢复状态与验证结果
2026-04-29 08:26:19 +08:00
gewuyou
9109eecea9 test(cqrs): 减少 Mediator 综合测试告警
- 优化测试 helper 类型作用域以消除文件名匹配告警

- 补充异步等待 ConfigureAwait(false) 以满足 analyzer 约束

- 调整集合抽象、字符串比较器和异常参数名用法
2026-04-29 08:19:16 +08:00
gewuyou
121df440c3 test(cqrs): 清理 Mediator 高级测试告警
- 修复 Mediator 高级测试中的异步等待告警

- 修复 验证请求异常参数名告警

- 优化 测试辅助类型区域的文件名告警处理
2026-04-29 08:18:16 +08:00
gewuyou
ed269d4a34 test(cqrs): 清理 Mediator 架构集成测试警告
- 优化 Mediator 架构集成测试的 helper 类型作用域,消除文件名与类型名警告

- 补充异步测试路径的 ConfigureAwait(false),满足 analyzer 要求

- 更新测试集合暴露类型为只读或抽象集合,保留行为不变
2026-04-29 08:16:05 +08:00
1207 changed files with 33975 additions and 4622 deletions

View File

@ -12,6 +12,10 @@ batches until a clear stop condition is met.
Treat `AGENTS.md` as the source of truth. This skill extends `gframework-boot`; it does not replace it. Treat `AGENTS.md` as the source of truth. This skill extends `gframework-boot`; it does not replace it.
Context budget is a first-class stop signal. Do not keep batching merely because a file-count threshold still has
headroom if the active conversation, loaded repo artifacts, validation output, and pending recovery updates suggest the
agent is approaching its safe working-context limit.
## Startup Workflow ## Startup Workflow
1. Execute the normal `gframework-boot` startup sequence first: 1. Execute the normal `gframework-boot` startup sequence first:
@ -28,6 +32,11 @@ Treat `AGENTS.md` as the source of truth. This skill extends `gframework-boot`;
- repeated test refactor pattern - repeated test refactor pattern
- module-by-module documentation refresh - module-by-module documentation refresh
- other repetitive multi-file cleanup - other repetitive multi-file cleanup
4. Before the first implementation batch, estimate whether the current task is likely to stay below roughly 80% of the
agent's safe working-context budget through one more full batch cycle:
- include already loaded `AGENTS.md`, skills, `ai-plan` files, recent command output, active diffs, and expected validation output
- if another batch would probably push the conversation near the limit, plan to stop after the current batch even if
branch-size thresholds still have room
## Baseline Selection ## Baseline Selection
@ -67,8 +76,15 @@ For shorthand numeric thresholds, use a fixed default baseline:
Choose one primary stop condition before the first batch and restate it to the user. Choose one primary stop condition before the first batch and restate it to the user.
When the user does not explicitly override the priority order, use:
1. context-budget safety
2. semantic batch boundary / reviewability
3. the user-requested local metric such as files, lines, warnings, or time
Common stop conditions: Common stop conditions:
- the next batch would likely push the agent above roughly 80% of its safe working-context budget
- branch diff vs baseline approaches a file-count threshold - branch diff vs baseline approaches a file-count threshold
- warnings-only build reaches a target count - warnings-only build reaches a target count
- a specific hotspot list is exhausted - a specific hotspot list is exhausted
@ -76,6 +92,9 @@ Common stop conditions:
If multiple stop conditions exist, rank them and treat one as primary. If multiple stop conditions exist, rank them and treat one as primary.
Treat file-count or line-count thresholds as coarse repository-scope signals, not as a proxy for AI context health.
When they disagree with context-budget safety, context-budget safety wins.
## Shorthand Stop-Condition Syntax ## Shorthand Stop-Condition Syntax
`gframework-batch-boot` may be invoked with shorthand numeric thresholds when the user clearly wants a branch-size stop `gframework-batch-boot` may be invoked with shorthand numeric thresholds when the user clearly wants a branch-size stop
@ -108,6 +127,7 @@ When shorthand is used:
- current branch and active topic - current branch and active topic
- selected baseline - selected baseline
- current stop-condition metric - current stop-condition metric
- current context-budget posture and whether one more batch is safe
- next candidate slices - next candidate slices
2. Keep the critical path local. 2. Keep the critical path local.
3. Delegate only bounded slices with explicit ownership: 3. Delegate only bounded slices with explicit ownership:
@ -128,6 +148,7 @@ When shorthand is used:
- integrate or verify the result - integrate or verify the result
- rerun the required validation - rerun the required validation
- recompute the primary stop-condition metric - recompute the primary stop-condition metric
- reassess whether one more batch would likely push the agent near or beyond roughly 80% context usage
- decide immediately whether to continue or stop - decide immediately whether to continue or stop
7. Do not require the user to manually trigger every round unless: 7. Do not require the user to manually trigger every round unless:
- the next slice is ambiguous - the next slice is ambiguous
@ -158,6 +179,7 @@ For multi-batch work, keep recovery artifacts current.
Stop the loop when any of the following becomes true: Stop the loop when any of the following becomes true:
- the next batch would likely push the agent near or beyond roughly 80% of its safe working-context budget
- the primary stop condition has been reached or exceeded - the primary stop condition has been reached or exceeded
- the remaining slices are no longer low-risk - the remaining slices are no longer low-risk
- validation failures indicate the task is no longer repetitive - validation failures indicate the task is no longer repetitive
@ -165,6 +187,7 @@ Stop the loop when any of the following becomes true:
When stopping, report: When stopping, report:
- whether context budget was the deciding factor
- which baseline was used - which baseline was used
- the exact metric value at stop time - the exact metric value at stop time
- completed batches - completed batches

View File

@ -36,14 +36,18 @@ Treat `AGENTS.md` as the source of truth. Use this skill to enforce a startup se
- `simple`: one concern, one file or module, no parallel discovery required - `simple`: one concern, one file or module, no parallel discovery required
- `medium`: a small number of modules, some read-only exploration helpful, critical path still easy to keep local - `medium`: a small number of modules, some read-only exploration helpful, critical path still easy to keep local
- `complex`: cross-module design, migration, large refactor, or work likely to exceed one context window - `complex`: cross-module design, migration, large refactor, or work likely to exceed one context window
11. Apply the delegation policy from `AGENTS.md`: 11. Estimate the current context-budget posture before substantive execution:
- account for loaded startup artifacts, active `ai-plan` files, visible diffs, open validation output, and likely next-step output volume
- if the task already appears near roughly 80% of a safe working-context budget, prefer closing the current batch,
refreshing recovery artifacts, and stopping at the next natural semantic boundary instead of starting a fresh broad slice
12. Apply the delegation policy from `AGENTS.md`:
- Keep the critical path local - Keep the critical path local
- Use `explorer` with `gpt-5.1-codex-mini` for narrow read-only questions, tracing, inventory, and comparisons - Use `explorer` with `gpt-5.1-codex-mini` for narrow read-only questions, tracing, inventory, and comparisons
- Use `worker` with `gpt-5.4` only for bounded implementation tasks with explicit ownership - Use `worker` with `gpt-5.4` only for bounded implementation tasks with explicit ownership
- Do not delegate purely for ceremony; delegate only when it materially shortens the task or controls context growth - Do not delegate purely for ceremony; delegate only when it materially shortens the task or controls context growth
12. Before editing files, tell the user what you read, how you classified the task, whether subagents will be used, 13. Before editing files, tell the user what you read, how you classified the task, whether subagents will be used,
and the first implementation step. and the first implementation step.
13. Proceed with execution, validation, and documentation updates required by `AGENTS.md`. 14. Proceed with execution, validation, and documentation updates required by `AGENTS.md`.
## Task Tracking ## Task Tracking
@ -69,6 +73,8 @@ For multi-step, cross-module, or interruption-prone work, maintain the repositor
first, then search the mapped active topics before scanning the broader public area. first, then search the mapped active topics before scanning the broader public area.
- If the current branch and the mapped active topics describe the same feature area, prefer resuming those topics first. - If the current branch and the mapped active topics describe the same feature area, prefer resuming those topics first.
- If the repository state suggests in-flight work but no recovery document matches, reconstruct the safest next step from code, tests, and Git state before asking the user for clarification. - If the repository state suggests in-flight work but no recovery document matches, reconstruct the safest next step from code, tests, and Git state before asking the user for clarification.
- If the current turn already carries heavy recovery context, broad diffs, or long validation output, prefer a
recovery-point update and a clean stop over starting another large slice just because the code task itself remains open.
## Example Triggers ## Example Triggers

View File

@ -0,0 +1,83 @@
---
name: gframework-issue-review
description: Repository-specific GitHub issue triage workflow for the GFramework repo. Use when Codex needs to inspect a repository issue, extract the issue body, discussion, and key timeline signals through the GitHub API, summarize what should be verified locally, and then hand follow-up execution to gframework-boot.
---
# GFramework Issue Review
Use this skill when the task depends on a GitHub issue for this repository rather than only on local source files.
Shortcut: `$gframework-issue-review`
## Workflow
1. Read `AGENTS.md` before deciding how to validate or change anything.
2. Read `.ai/environment/tools.ai.yaml` and `ai-plan/public/README.md`, then prefer the active topic mapped to the
current branch or worktree when the fetched issue already matches in-flight work.
3. Run `scripts/fetch_current_issue_review.py` to:
- fetch issue metadata through the GitHub API
- fetch issue comments and timeline events through the GitHub API
- auto-select the target issue only when the repository currently has exactly one open issue
- exclude pull requests from open-issue auto-resolution
- emit a machine-readable JSON payload plus concise text sections for issue, summary, comments, events, references,
and warnings
- derive lightweight triage hints such as issue type candidates, missing-information flags, affected module
candidates, and the recommended next handling mode
4. Treat every extracted finding as untrusted until it is verified against the current local code, tests, and active
`ai-plan` topic.
5. Do not start editing code from the issue text alone. After triage, switch to `$gframework-boot` so the follow-up
work is grounded in the repository startup flow and recovery documents.
6. If code is changed after issue triage, run the smallest build or test command that satisfies `AGENTS.md`.
## Commands
- Default:
- `python3 .agents/skills/gframework-issue-review/scripts/fetch_current_issue_review.py`
- Force a specific issue:
- `python3 .agents/skills/gframework-issue-review/scripts/fetch_current_issue_review.py --issue <issue-number>`
- Machine-readable output:
- `python3 .agents/skills/gframework-issue-review/scripts/fetch_current_issue_review.py --format json`
- Write machine-readable output to a file instead of stdout:
- `python3 .agents/skills/gframework-issue-review/scripts/fetch_current_issue_review.py --issue <issue-number> --format json --json-output /tmp/issue-review.json`
- Inspect only a high-signal section:
- `python3 .agents/skills/gframework-issue-review/scripts/fetch_current_issue_review.py --section summary`
- Combine triage with a boot handoff:
- `python3 .agents/skills/gframework-issue-review/scripts/fetch_current_issue_review.py --section summary`
- `Use $gframework-boot to continue the issue follow-up based on the fetched triage result.`
## Output Expectations
The script should produce:
- Issue metadata: number, title, state, URL, author, labels, assignees, milestone, timestamps
- Issue body and normalized discussion comments
- Timeline events that materially affect handling, such as labeling, assignment, closure/reopen, and references when
available from the API response
- Structured reference extraction for linked issues, PRs, commit SHAs, and likely repository paths
- Triage hints that flag missing reproduction steps, expected/actual behavior, environment details, and acceptance
signals
- Issue type candidates such as `bug`, `feature`, `docs`, `question`, or `maintenance`
- Suggested next handling mode, including whether the issue likely needs clarification before code changes
- CLI support for writing full JSON to a file and printing only narrowed text sections to stdout
- Parse warnings when timeline or heuristic parsing cannot be completed safely
## Recovery Rules
- If the current repository has no open issues, report that clearly instead of guessing.
- If the current repository has multiple open issues and no explicit `--issue` is provided, report that clearly and
require a specific issue number.
- If GitHub access fails because of proxy configuration, rerun the fetch with proxy variables removed.
- Prefer GitHub API results over HTML scraping.
- Do not treat heuristic module guesses or next-step suggestions as repository truth; they are only entry points for
subsequent local verification.
- If the issue discussion reveals that the problem statement has already shifted, prefer the newest concrete comment or
timeline signal over the original title/body wording.
- After extracting the issue, continue the actual implementation flow with `$gframework-boot` so the task is grounded
in current branch context and `ai-plan` recovery artifacts.
## Example Triggers
- `Use $gframework-issue-review on the current repository issue`
- `Check the open GitHub issue and summarize what should be verified locally`
- `Inspect issue <issue-number> and tell me whether this looks like bug triage or a feature request`
- `先用 $gframework-issue-review 看当前 open issue再用 $gframework-boot 继续`

View File

@ -0,0 +1,4 @@
interface:
display_name: "GFramework Issue Review"
short_description: "Inspect the current repository issue and triage next steps"
default_prompt: "Use $gframework-issue-review to inspect the current repository issue through the GitHub API, summarize the issue body, discussion, and key timeline signals, highlight what must be verified locally, and then hand follow-up execution to $gframework-boot."

View File

@ -0,0 +1,858 @@
#!/usr/bin/env python3
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
"""
Fetch the current GFramework GitHub issue and extract the signals needed for
local follow-up work without relying on gh CLI.
"""
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
import urllib.error
import urllib.request
from typing import Any
OWNER = "GeWuYou"
REPO = "GFramework"
WORKTREE_ROOT_DIRECTORY_NAME = "GFramework-WorkTree"
GIT_ENVIRONMENT_KEY = "GFRAMEWORK_WINDOWS_GIT"
GIT_DIR_ENVIRONMENT_KEY = "GFRAMEWORK_GIT_DIR"
WORK_TREE_ENVIRONMENT_KEY = "GFRAMEWORK_WORK_TREE"
REQUEST_TIMEOUT_ENVIRONMENT_KEY = "GFRAMEWORK_ISSUE_REVIEW_TIMEOUT_SECONDS"
GITHUB_TOKEN_ENVIRONMENT_KEYS = ("GFRAMEWORK_GITHUB_TOKEN", "GITHUB_TOKEN", "GH_TOKEN")
PROXY_ENVIRONMENT_KEYS = ("http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "all_proxy")
DEFAULT_REQUEST_TIMEOUT_SECONDS = 60
USER_AGENT = "codex-gframework-issue-review"
DISPLAY_SECTION_CHOICES = (
"issue",
"summary",
"comments",
"events",
"references",
"warnings",
)
ISSUE_TYPE_CANDIDATES = ("bug", "feature", "docs", "question", "maintenance")
ACTIVE_TOPIC_KEYWORDS: dict[str, tuple[str, ...]] = {
"ai-first-config-system": ("config", "configuration", "gameconfig", "settings"),
"coroutine-optimization": ("coroutine", "yield", "await", "scheduler"),
"cqrs-rewrite": ("cqrs", "command", "query", "eventbus", "event bus"),
"data-repository-persistence": ("repository", "serialization", "persistence", "data", "settings"),
"runtime-generator-boundary": ("source generator", "generator", "attribute", "packaging"),
"semantic-release-versioning": ("release", "version", "semantic-release", "tag", "publish"),
"documentation-full-coverage-governance": ("docs", "documentation", "readme", "vitepress", "api reference"),
}
ACTUAL_BEHAVIOR_PATTERNS = (
"actual",
"currently",
"instead",
"but",
"error",
"exception",
"fails",
"failed",
"wrong",
)
EXPECTED_BEHAVIOR_PATTERNS = (
"expected",
"should",
"want",
"would like",
"needs to",
)
REPRODUCTION_PATTERNS = (
"steps to reproduce",
"reproduce",
"reproduction",
"how to reproduce",
"minimal example",
"sample",
"demo",
)
ENVIRONMENT_PATTERNS = (
"windows",
"linux",
"macos",
"wsl",
"godot",
".net",
"sdk",
"version",
"environment",
)
ACCEPTANCE_PATTERNS = (
"acceptance",
"done when",
"definition of done",
"verified by",
"test plan",
)
FILE_PATH_PATTERN = re.compile(r"\b(?:[A-Za-z0-9_.-]+/)+[A-Za-z0-9_.-]+\b")
ISSUE_REFERENCE_PATTERN = re.compile(r"(?:^|\s)#(\d+)\b")
COMMIT_REFERENCE_PATTERN = re.compile(r"\b[0-9a-f]{7,40}\b")
LINE_BREAK_NORMALIZER = re.compile(r"\n{3,}")
def resolve_git_command() -> str:
"""Resolve the git executable to use for this repository."""
candidates = [
os.environ.get(GIT_ENVIRONMENT_KEY),
"git.exe",
"git",
]
for candidate in candidates:
if not candidate:
continue
if os.path.isabs(candidate):
if os.path.exists(candidate):
return candidate
continue
resolved_candidate = shutil.which(candidate)
if resolved_candidate:
return resolved_candidate
raise RuntimeError(f"No usable git executable found. Set {GIT_ENVIRONMENT_KEY} to override it.")
def find_repository_root(start_path: Path) -> Path | None:
"""Locate the repository root by walking parent directories for repo markers."""
for candidate in (start_path, *start_path.parents):
if (candidate / "AGENTS.md").exists() and (candidate / ".ai/environment/tools.ai.yaml").exists():
return candidate
return None
def resolve_worktree_git_dir(repository_root: Path) -> Path | None:
"""Resolve the main-repository worktree gitdir for this WSL worktree layout."""
if repository_root.parent.name != WORKTREE_ROOT_DIRECTORY_NAME:
return None
primary_repository_root = repository_root.parent.parent / REPO
candidate_git_dir = primary_repository_root / ".git" / "worktrees" / repository_root.name
return candidate_git_dir if candidate_git_dir.exists() else None
def resolve_git_invocation() -> list[str]:
"""Resolve the git command arguments, preferring explicit WSL worktree binding."""
configured_git_dir = os.environ.get(GIT_DIR_ENVIRONMENT_KEY)
configured_work_tree = os.environ.get(WORK_TREE_ENVIRONMENT_KEY)
linux_git = shutil.which("git")
if configured_git_dir and configured_work_tree and linux_git:
return [linux_git, f"--git-dir={configured_git_dir}", f"--work-tree={configured_work_tree}"]
repository_root = find_repository_root(Path.cwd())
if repository_root is not None and linux_git:
worktree_git_dir = resolve_worktree_git_dir(repository_root)
if worktree_git_dir is not None:
return [linux_git, f"--git-dir={worktree_git_dir}", f"--work-tree={repository_root}"]
root_git_dir = repository_root / ".git"
if root_git_dir.exists():
return [linux_git, f"--git-dir={root_git_dir}", f"--work-tree={repository_root}"]
return [resolve_git_command()]
def resolve_request_timeout_seconds() -> int:
"""Return the GitHub request timeout in seconds."""
configured_timeout = os.environ.get(REQUEST_TIMEOUT_ENVIRONMENT_KEY)
if not configured_timeout:
return DEFAULT_REQUEST_TIMEOUT_SECONDS
try:
parsed_timeout = int(configured_timeout)
except ValueError as error:
raise RuntimeError(
f"{REQUEST_TIMEOUT_ENVIRONMENT_KEY} must be an integer number of seconds."
) from error
if parsed_timeout <= 0:
raise RuntimeError(f"{REQUEST_TIMEOUT_ENVIRONMENT_KEY} must be greater than zero.")
return parsed_timeout
def run_command(args: list[str]) -> str:
"""Run a command and return stdout, raising on failure."""
process = subprocess.run(args, capture_output=True, text=True, check=False)
if process.returncode != 0:
stderr = process.stderr.strip()
raise RuntimeError(f"Command failed: {' '.join(args)}\n{stderr}")
return process.stdout.strip()
def get_current_branch() -> str:
"""Return the current git branch name."""
return run_command([*resolve_git_invocation(), "rev-parse", "--abbrev-ref", "HEAD"])
def resolve_github_token() -> str | None:
"""Return the first configured GitHub token for authenticated API requests."""
for environment_key in GITHUB_TOKEN_ENVIRONMENT_KEYS:
token = os.environ.get(environment_key)
if token:
return token
return None
def build_request_headers(accept: str) -> dict[str, str]:
"""Build GitHub request headers and include auth when a token is available."""
headers = {"Accept": accept, "User-Agent": USER_AGENT}
token = resolve_github_token()
if token:
headers["Authorization"] = f"Bearer {token}"
return headers
def has_proxy_environment() -> bool:
"""Return whether the current process is configured to use an outbound proxy."""
return any(os.environ.get(environment_key) for environment_key in PROXY_ENVIRONMENT_KEYS)
def perform_request(url: str, headers: dict[str, str], *, disable_proxy: bool) -> tuple[str, Any]:
"""Execute a single HTTP request and return decoded text plus response headers."""
opener = (
urllib.request.build_opener(urllib.request.ProxyHandler({}))
if disable_proxy
else urllib.request.build_opener()
)
request = urllib.request.Request(url, headers=headers)
with opener.open(request, timeout=resolve_request_timeout_seconds()) as response:
return response.read().decode("utf-8", "replace"), response.headers
def open_url(url: str, accept: str) -> tuple[str, Any]:
"""Open a URL, retrying without proxies only when the configured proxy path fails."""
headers = build_request_headers(accept)
try:
return perform_request(url, headers, disable_proxy=False)
except urllib.error.HTTPError:
raise
except (urllib.error.URLError, TimeoutError, OSError):
if not has_proxy_environment():
raise
return perform_request(url, headers, disable_proxy=True)
def fetch_json(url: str, accept: str = "application/vnd.github+json") -> tuple[Any, Any]:
"""Fetch a JSON payload and its response headers from GitHub."""
text, headers = open_url(url, accept=accept)
return json.loads(text), headers
def extract_next_link(headers: Any) -> str | None:
"""Extract the next-page link from GitHub pagination headers."""
link_header = headers.get("Link")
if not link_header:
return None
match = re.search(r'<([^>]+)>;\s*rel="next"', link_header)
return match.group(1) if match else None
def fetch_paged_json(url: str, accept: str = "application/vnd.github+json") -> list[dict[str, Any]]:
"""Fetch every page from a paginated GitHub API endpoint."""
items: list[dict[str, Any]] = []
next_url: str | None = url
while next_url:
payload, headers = fetch_json(next_url, accept=accept)
if not isinstance(payload, list):
raise RuntimeError(f"Expected list payload from GitHub API, got {type(payload).__name__}.")
items.extend(payload)
next_url = extract_next_link(headers)
return items
def collapse_whitespace(text: str) -> str:
"""Collapse repeated whitespace into single spaces while preserving paragraph intent."""
normalized = text.replace("\r\n", "\n").replace("\r", "\n")
normalized = LINE_BREAK_NORMALIZER.sub("\n\n", normalized)
normalized = re.sub(r"[ \t]+", " ", normalized)
normalized = re.sub(r" *\n *", "\n", normalized)
return normalized.strip()
def truncate_text(text: str, max_length: int) -> str:
"""Collapse whitespace and truncate long text for CLI display."""
collapsed = collapse_whitespace(text)
if max_length <= 0 or len(collapsed) <= max_length:
return collapsed
return collapsed[: max_length - 3].rstrip() + "..."
def filter_open_issue_candidates(items: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""Filter GitHub issue list responses down to non-PR issue items."""
return [item for item in items if not item.get("pull_request")]
def select_single_open_issue_number(items: list[dict[str, Any]]) -> int:
"""Resolve the target issue number when the repository has exactly one open issue."""
issues = filter_open_issue_candidates(items)
if not issues:
raise RuntimeError("No open GitHub issues found for this repository. Pass --issue <number> to inspect one.")
if len(issues) > 1:
numbers = ", ".join(str(item.get("number")) for item in issues[:5])
suffix = "" if len(issues) <= 5 else ", ..."
raise RuntimeError(
"Multiple open GitHub issues found for this repository "
f"({len(issues)} total: {numbers}{suffix}). Pass --issue <number> to inspect one."
)
return int(issues[0]["number"])
def resolve_issue_number(issue_number: int | None) -> tuple[int, str]:
"""Resolve the issue number, auto-selecting only when exactly one open issue exists."""
if issue_number is not None:
return issue_number, "explicit"
open_items = fetch_paged_json(f"https://api.github.com/repos/{OWNER}/{REPO}/issues?state=open&per_page=100")
return select_single_open_issue_number(open_items), "auto-single-open-issue"
def fetch_issue_metadata(issue_number: int) -> dict[str, Any]:
"""Fetch normalized metadata for a GitHub issue."""
payload, _ = fetch_json(f"https://api.github.com/repos/{OWNER}/{REPO}/issues/{issue_number}")
if not isinstance(payload, dict):
raise RuntimeError("Failed to fetch GitHub issue metadata.")
if payload.get("pull_request"):
raise RuntimeError(f"Item #{issue_number} is a pull request, not a plain issue.")
labels = []
for label in payload.get("labels", []):
if isinstance(label, dict) and label.get("name"):
labels.append(str(label["name"]))
assignees = []
for assignee in payload.get("assignees", []):
login = assignee.get("login")
if login:
assignees.append(str(login))
milestone_title = None
milestone = payload.get("milestone")
if isinstance(milestone, dict) and milestone.get("title"):
milestone_title = str(milestone["title"])
return {
"number": int(payload["number"]),
"title": str(payload["title"]),
"state": str(payload["state"]).upper(),
"url": str(payload["html_url"]),
"author": str(payload.get("user", {}).get("login") or ""),
"created_at": str(payload.get("created_at") or ""),
"updated_at": str(payload.get("updated_at") or ""),
"labels": labels,
"assignees": assignees,
"milestone": milestone_title,
"body": str(payload.get("body") or ""),
}
def fetch_issue_comments(issue_number: int) -> list[dict[str, Any]]:
"""Fetch issue comments for the selected issue."""
return fetch_paged_json(f"https://api.github.com/repos/{OWNER}/{REPO}/issues/{issue_number}/comments?per_page=100")
def fetch_issue_timeline(issue_number: int) -> list[dict[str, Any]]:
"""Fetch issue timeline events when GitHub exposes them to the current client."""
return fetch_paged_json(
f"https://api.github.com/repos/{OWNER}/{REPO}/issues/{issue_number}/timeline?per_page=100",
accept="application/vnd.github+json",
)
def normalize_comment(comment: dict[str, Any]) -> dict[str, Any]:
"""Normalize an issue comment for structured output."""
return {
"id": int(comment.get("id") or 0),
"author": str(comment.get("user", {}).get("login") or ""),
"created_at": str(comment.get("created_at") or ""),
"updated_at": str(comment.get("updated_at") or ""),
"body": str(comment.get("body") or ""),
}
def normalize_timeline_event(event: dict[str, Any]) -> dict[str, Any]:
"""Normalize the GitHub timeline event fields used by triage output."""
actor = str(event.get("actor", {}).get("login") or "")
created_at = str(event.get("created_at") or event.get("submitted_at") or "")
event_type = str(event.get("event") or event.get("__typename") or "unknown")
label_name = ""
assignee = ""
source_issue_number: int | None = None
source_issue_url = ""
commit_id = ""
label = event.get("label")
if isinstance(label, dict) and label.get("name"):
label_name = str(label["name"])
assignee_payload = event.get("assignee")
if isinstance(assignee_payload, dict) and assignee_payload.get("login"):
assignee = str(assignee_payload["login"])
source = event.get("source")
if isinstance(source, dict):
issue_payload = source.get("issue")
if isinstance(issue_payload, dict):
if issue_payload.get("number"):
source_issue_number = int(issue_payload["number"])
if issue_payload.get("html_url"):
source_issue_url = str(issue_payload["html_url"])
commit_id_value = event.get("commit_id")
if isinstance(commit_id_value, str):
commit_id = commit_id_value
return {
"event": event_type,
"actor": actor,
"created_at": created_at,
"label": label_name,
"assignee": assignee,
"commit_id": commit_id,
"source_issue_number": source_issue_number,
"source_issue_url": source_issue_url,
}
def gather_text_blocks(issue: dict[str, Any], comments: list[dict[str, Any]]) -> list[str]:
"""Return the issue body plus discussion comment bodies for heuristic parsing."""
blocks = [issue.get("body", "")]
blocks.extend(comment.get("body", "") for comment in comments)
return [block for block in blocks if block]
def has_any_pattern(text_blocks: list[str], patterns: tuple[str, ...]) -> bool:
"""Return whether any normalized text block contains any requested pattern."""
lowered_blocks = [collapse_whitespace(block).lower() for block in text_blocks]
return any(pattern in block for block in lowered_blocks for pattern in patterns)
def choose_issue_type_candidates(issue: dict[str, Any], text_blocks: list[str]) -> list[str]:
"""Infer lightweight issue-type candidates from labels and discussion text."""
labels = [label.lower() for label in issue.get("labels", [])]
text = "\n".join(text_blocks).lower()
candidates: list[str] = []
if any(label in {"bug", "regression"} for label in labels) or "bug" in text or "error" in text or "fails" in text:
candidates.append("bug")
if any(label in {"feature", "enhancement"} for label in labels) or "feature" in text or "support" in text:
candidates.append("feature")
if any(label in {"documentation", "docs"} for label in labels) or "documentation" in text or "readme" in text:
candidates.append("docs")
if any(label in {"question", "help wanted"} for label in labels) or "?" in issue.get("title", ""):
candidates.append("question")
if any(label in {"chore", "maintenance", "refactor"} for label in labels) or "cleanup" in text or "refactor" in text:
candidates.append("maintenance")
if not candidates:
candidates.append("question" if issue.get("body", "").strip().endswith("?") else "bug")
ordered_candidates: list[str] = []
for candidate in ISSUE_TYPE_CANDIDATES:
if candidate in candidates:
ordered_candidates.append(candidate)
return ordered_candidates
def extract_references_from_text(text: str) -> dict[str, list[str]]:
"""Extract issue, commit, and file-path references from one text block."""
issue_numbers = sorted({match.group(1) for match in ISSUE_REFERENCE_PATTERN.finditer(text)}, key=int)
commit_shas = sorted({match.group(0) for match in COMMIT_REFERENCE_PATTERN.finditer(text)})
file_paths = sorted({match.group(0) for match in FILE_PATH_PATTERN.finditer(text)})
return {
"issues": [f"#{number}" for number in issue_numbers],
"commit_shas": commit_shas,
"file_paths": file_paths,
}
def merge_reference_values(values: list[dict[str, list[str]]]) -> dict[str, list[str]]:
"""Merge extracted reference lists while preserving sorted unique output."""
merged: dict[str, set[str]] = {"issues": set(), "commit_shas": set(), "file_paths": set()}
for value in values:
for key in merged:
merged[key].update(value.get(key, []))
return {
"issues": sorted(merged["issues"], key=lambda item: int(item[1:])),
"commit_shas": sorted(merged["commit_shas"]),
"file_paths": sorted(merged["file_paths"]),
}
def build_references(issue: dict[str, Any], comments: list[dict[str, Any]], events: list[dict[str, Any]]) -> dict[str, Any]:
"""Build structured references from issue text and timeline context."""
extracted = [extract_references_from_text(issue.get("body", ""))]
extracted.extend(extract_references_from_text(comment.get("body", "")) for comment in comments)
merged = merge_reference_values(extracted)
referenced_by_timeline = sorted(
{
f"#{event['source_issue_number']}"
for event in events
if event.get("source_issue_number") is not None
},
key=lambda item: int(item[1:]),
)
pull_request_references = sorted(
{
issue_reference
for issue_reference in merged["issues"]
if issue_reference != f"#{issue['number']}"
},
key=lambda item: int(item[1:]),
)
return {
"issues": merged["issues"],
"pull_requests_or_issues": pull_request_references,
"commit_shas": merged["commit_shas"],
"file_paths": merged["file_paths"],
"timeline_cross_references": referenced_by_timeline,
}
def build_information_flags(
issue: dict[str, Any],
comments: list[dict[str, Any]],
issue_type_candidates: list[str],
) -> dict[str, bool]:
"""Derive missing-information and readiness flags with issue-type-aware heuristics."""
text_blocks = gather_text_blocks(issue, comments)
has_reproduction_steps = has_any_pattern(text_blocks, REPRODUCTION_PATTERNS)
has_expected_behavior = has_any_pattern(text_blocks, EXPECTED_BEHAVIOR_PATTERNS)
has_actual_behavior = has_any_pattern(text_blocks, ACTUAL_BEHAVIOR_PATTERNS)
has_environment_details = has_any_pattern(text_blocks, ENVIRONMENT_PATTERNS)
has_acceptance_signals = has_any_pattern(text_blocks, ACCEPTANCE_PATTERNS)
primary_issue_type = issue_type_candidates[0] if issue_type_candidates else "bug"
if primary_issue_type == "bug":
needs_clarification = not (
(has_actual_behavior and (has_reproduction_steps or has_environment_details))
or has_acceptance_signals
)
elif primary_issue_type in {"feature", "docs"}:
needs_clarification = not (has_expected_behavior or has_acceptance_signals)
elif primary_issue_type == "maintenance":
needs_clarification = not (has_expected_behavior or has_actual_behavior or has_acceptance_signals)
else:
needs_clarification = not (has_expected_behavior or has_actual_behavior or has_acceptance_signals)
return {
"has_reproduction_steps": has_reproduction_steps,
"has_expected_behavior": has_expected_behavior,
"has_actual_behavior": has_actual_behavior,
"has_environment_details": has_environment_details,
"has_acceptance_signals": has_acceptance_signals,
"needs_clarification": needs_clarification,
}
def choose_affected_topics(issue: dict[str, Any], comments: list[dict[str, Any]]) -> list[str]:
"""Map the issue discussion to likely active topics when obvious keyword matches exist."""
text = "\n".join(gather_text_blocks(issue, comments)).lower()
matches: list[str] = []
for topic, keywords in ACTIVE_TOPIC_KEYWORDS.items():
if any(keyword in text for keyword in keywords):
matches.append(topic)
return matches
def choose_next_action(
information_flags: dict[str, bool],
issue_type_candidates: list[str],
affected_topics: list[str],
) -> str:
"""Choose the next handling mode for boot handoff."""
if information_flags["needs_clarification"]:
return "clarify-issue-before-code"
if affected_topics:
return "resume-existing-topic-with-boot"
if "docs" in issue_type_candidates and issue_type_candidates[0] == "docs":
return "start-new-docs-topic-with-boot"
return "start-new-topic-with-boot"
def build_triage_hints(issue: dict[str, Any], comments: list[dict[str, Any]]) -> dict[str, Any]:
"""Build lightweight, reviewable triage hints for boot follow-up."""
text_blocks = gather_text_blocks(issue, comments)
issue_type_candidates = choose_issue_type_candidates(issue, text_blocks)
information_flags = build_information_flags(issue, comments, issue_type_candidates)
affected_topics = choose_affected_topics(issue, comments)
next_action = choose_next_action(information_flags, issue_type_candidates, affected_topics)
return {
"issue_type_candidates": issue_type_candidates,
"information_flags": information_flags,
"affected_active_topics": affected_topics,
"next_action": next_action,
"boot_handoff": {
"recommended_skill": "gframework-boot",
"mode": "resume" if affected_topics else "new",
"notes": (
"Use gframework-boot to verify the issue against local code and active ai-plan topics."
if not information_flags["needs_clarification"]
else "Use gframework-boot to record a clarification-first task before changing code."
),
},
}
def build_result(issue_number: int, branch: str, resolution_mode: str) -> dict[str, Any]:
"""Build the full issue review payload for the selected issue."""
parse_warnings: list[str] = []
issue = fetch_issue_metadata(issue_number)
raw_comments = fetch_issue_comments(issue_number)
comments = [normalize_comment(comment) for comment in raw_comments]
events: list[dict[str, Any]] = []
try:
raw_events = fetch_issue_timeline(issue_number)
events = [normalize_timeline_event(event) for event in raw_events]
except Exception as error: # noqa: BLE001
parse_warnings.append(f"Issue timeline could not be fetched or parsed: {error}")
references = build_references(issue, comments, events)
triage_hints = build_triage_hints(issue, comments)
return {
"issue": {
**issue,
"resolved_from_branch": branch,
"resolution_mode": resolution_mode,
},
"discussion": {
"comment_count": len(comments),
"comments": comments,
},
"events": {
"count": len(events),
"items": events,
},
"references": references,
"triage_hints": triage_hints,
"parse_warnings": parse_warnings,
}
def write_json_output(result: dict[str, Any], output_path: str) -> str:
"""Write the full JSON result to disk and return the destination path."""
destination_path = Path(output_path).expanduser()
destination_path.parent.mkdir(parents=True, exist_ok=True)
destination_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
return str(destination_path)
def summarize_events(events: list[dict[str, Any]]) -> list[str]:
"""Convert normalized events into concise text lines."""
lines: list[str] = []
for event in events:
summary = f"- {event['event']}"
details: list[str] = []
if event.get("actor"):
details.append(f"actor={event['actor']}")
if event.get("label"):
details.append(f"label={event['label']}")
if event.get("assignee"):
details.append(f"assignee={event['assignee']}")
if event.get("source_issue_number") is not None:
details.append(f"source_issue=#{event['source_issue_number']}")
if event.get("commit_id"):
details.append(f"commit={event['commit_id'][:12]}")
if event.get("created_at"):
details.append(f"at={event['created_at']}")
if details:
summary += " (" + ", ".join(details) + ")"
lines.append(summary)
return lines
def format_text(
result: dict[str, Any],
*,
sections: list[str] | None = None,
max_description_length: int = 400,
json_output_path: str | None = None,
) -> str:
"""Format the result payload into concise text output."""
lines: list[str] = []
selected_sections = set(sections or DISPLAY_SECTION_CHOICES)
issue = result["issue"]
triage_hints = result["triage_hints"]
discussion = result["discussion"]
events = result["events"]
references = result["references"]
if "issue" in selected_sections:
lines.append(f"Issue #{issue['number']}: {issue['title']}")
lines.append(f"State: {issue['state']}")
lines.append(f"Author: {issue['author']}")
lines.append(f"Labels: {', '.join(issue['labels']) if issue['labels'] else '(none)'}")
lines.append(f"Assignees: {', '.join(issue['assignees']) if issue['assignees'] else '(none)'}")
lines.append(f"Milestone: {issue['milestone'] or '(none)'}")
lines.append(f"Created: {issue['created_at']}")
lines.append(f"Updated: {issue['updated_at']}")
lines.append(f"Resolved from branch: {issue['resolved_from_branch'] or '(not branch-based)'}")
lines.append(f"Resolution mode: {issue['resolution_mode']}")
lines.append(f"URL: {issue['url']}")
if issue["body"]:
lines.append("Body:")
lines.append(truncate_text(issue["body"], max_description_length))
if "summary" in selected_sections:
lines.append("")
lines.append("Triage summary:")
lines.append("- Issue type candidates: " + ", ".join(triage_hints["issue_type_candidates"]))
information_flags = triage_hints["information_flags"]
lines.append(
"- Information flags: "
+ ", ".join(
[
f"repro={'yes' if information_flags['has_reproduction_steps'] else 'no'}",
f"expected={'yes' if information_flags['has_expected_behavior'] else 'no'}",
f"actual={'yes' if information_flags['has_actual_behavior'] else 'no'}",
f"environment={'yes' if information_flags['has_environment_details'] else 'no'}",
f"acceptance={'yes' if information_flags['has_acceptance_signals'] else 'no'}",
f"needs_clarification={'yes' if information_flags['needs_clarification'] else 'no'}",
]
)
)
lines.append(
"- Affected active topics: "
+ (", ".join(triage_hints["affected_active_topics"]) if triage_hints["affected_active_topics"] else "(none)")
)
lines.append(f"- Next action: {triage_hints['next_action']}")
lines.append(f"- Boot handoff: {triage_hints['boot_handoff']['notes']}")
if "comments" in selected_sections:
lines.append("")
lines.append(f"Discussion comments: {discussion['comment_count']}")
for comment in discussion["comments"]:
lines.append(f"- {comment['author']} at {comment['created_at']}")
lines.append(f" {truncate_text(comment['body'], max_description_length)}")
if "events" in selected_sections:
lines.append("")
lines.append(f"Timeline events: {events['count']}")
lines.extend(summarize_events(events["items"]))
if "references" in selected_sections:
lines.append("")
lines.append("References:")
lines.append("- Mentioned issues: " + (", ".join(references["issues"]) if references["issues"] else "(none)"))
lines.append(
"- Cross references: "
+ (
", ".join(references["timeline_cross_references"])
if references["timeline_cross_references"]
else "(none)"
)
)
lines.append(
"- Related issue/PR mentions: "
+ (
", ".join(references["pull_requests_or_issues"])
if references["pull_requests_or_issues"]
else "(none)"
)
)
lines.append("- Commit SHAs: " + (", ".join(references["commit_shas"]) if references["commit_shas"] else "(none)"))
lines.append("- File paths: " + (", ".join(references["file_paths"]) if references["file_paths"] else "(none)"))
if result["parse_warnings"] and "warnings" in selected_sections:
lines.append("")
lines.append("Warnings:")
for warning in result["parse_warnings"]:
lines.append(f"- {truncate_text(warning, max_description_length)}")
if json_output_path:
lines.append("")
lines.append(f"Full JSON written to: {json_output_path}")
return "\n".join(lines)
def parse_args() -> argparse.Namespace:
"""Parse CLI arguments."""
parser = argparse.ArgumentParser()
parser.add_argument("--branch", help="Override the current branch name.")
parser.add_argument("--issue", type=int, help="Fetch a specific issue number instead of auto-selecting one.")
parser.add_argument("--format", choices=("text", "json"), default="text")
parser.add_argument(
"--json-output",
help="Write the full JSON result to a file. When used with --format text, stdout stays concise and points to the file.",
)
parser.add_argument(
"--section",
action="append",
choices=DISPLAY_SECTION_CHOICES,
help="Limit text output to specific sections. Can be passed multiple times.",
)
parser.add_argument(
"--max-description-length",
type=int,
default=400,
help="Truncate long text bodies in text output to this many characters.",
)
return parser.parse_args()
def main() -> None:
"""Run the CLI entry point."""
args = parse_args()
branch = args.branch or get_current_branch()
issue_number, resolution_mode = resolve_issue_number(args.issue)
result = build_result(issue_number, branch, resolution_mode)
json_output_path: str | None = None
if args.json_output:
json_output_path = write_json_output(result, args.json_output)
if args.format == "json":
print(json.dumps(result, ensure_ascii=False, indent=2))
return
print(
format_text(
result,
sections=args.section,
max_description_length=args.max_description_length,
json_output_path=json_output_path,
)
)
if __name__ == "__main__":
try:
main()
except Exception as error: # noqa: BLE001
print(str(error), file=sys.stderr)
sys.exit(1)

View File

@ -0,0 +1,94 @@
#!/usr/bin/env python3
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
"""Regression tests for the GFramework issue review fetch helper."""
from __future__ import annotations
import importlib.util
from pathlib import Path
import unittest
SCRIPT_PATH = Path(__file__).with_name("fetch_current_issue_review.py")
MODULE_SPEC = importlib.util.spec_from_file_location("fetch_current_issue_review", SCRIPT_PATH)
if MODULE_SPEC is None or MODULE_SPEC.loader is None:
raise RuntimeError(f"Unable to load module from {SCRIPT_PATH}.")
MODULE = importlib.util.module_from_spec(MODULE_SPEC)
MODULE_SPEC.loader.exec_module(MODULE)
class SelectSingleOpenIssueNumberTests(unittest.TestCase):
"""Cover auto-resolution rules for open GitHub issues."""
def test_select_single_open_issue_number_filters_pull_requests(self) -> None:
"""Pull requests in the issues API must not block the single-open-issue path."""
selected = MODULE.select_single_open_issue_number(
[
{"number": 10, "pull_request": {"url": "https://example.test/pr/10"}},
{"number": 11},
]
)
self.assertEqual(selected, 11)
def test_select_single_open_issue_number_rejects_multiple_plain_issues(self) -> None:
"""Auto-resolution must stop when more than one plain issue is open."""
with self.assertRaisesRegex(RuntimeError, "Multiple open GitHub issues found"):
MODULE.select_single_open_issue_number([{"number": 11}, {"number": 12}])
class ExtractReferencesFromTextTests(unittest.TestCase):
"""Cover lightweight reference extraction used by the text and JSON output."""
def test_extract_references_from_text_finds_issue_commit_and_path_mentions(self) -> None:
"""The helper should retain the high-signal references needed for follow-up triage."""
references = MODULE.extract_references_from_text(
"See #123, commit abcdef1234567890, and GFramework.Core/Systems/Runner.cs for the failing path."
)
self.assertEqual(references["issues"], ["#123"])
self.assertEqual(references["commit_shas"], ["abcdef1234567890"])
self.assertEqual(references["file_paths"], ["GFramework.Core/Systems/Runner.cs"])
class BuildTriageHintsTests(unittest.TestCase):
"""Cover next-action classification for non-bug issue flows."""
def test_build_triage_hints_routes_docs_issue_to_docs_topic_without_bug_style_clarification(self) -> None:
"""Docs issues with a clear requested change should not be forced through bug-style clarification."""
triage_hints = MODULE.build_triage_hints(
{
"title": "Update documentation landing page",
"labels": ["docs"],
"body": "The guide should explain the landing-page layout for new contributors.",
},
[],
)
self.assertEqual(triage_hints["issue_type_candidates"][0], "docs")
self.assertEqual(triage_hints["affected_active_topics"], [])
self.assertFalse(triage_hints["information_flags"]["needs_clarification"])
self.assertEqual(triage_hints["next_action"], "start-new-docs-topic-with-boot")
def test_build_triage_hints_routes_feature_issue_to_new_topic_when_request_is_clear(self) -> None:
"""Feature requests with explicit desired behavior should stay actionable without fake bug repro gates."""
triage_hints = MODULE.build_triage_hints(
{
"title": "Support release note previews",
"labels": ["enhancement"],
"body": "The workflow should support previewing generated notes before completion.",
},
[],
)
self.assertEqual(triage_hints["issue_type_candidates"][0], "feature")
self.assertEqual(triage_hints["affected_active_topics"], [])
self.assertFalse(triage_hints["information_flags"]["needs_clarification"])
self.assertEqual(triage_hints["next_action"], "start-new-topic-with-boot")
if __name__ == "__main__":
unittest.main()

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: "zh-CN" language: "zh-CN"
early_access: false early_access: false

View File

@ -1,4 +1,7 @@
license_overrides: # Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
license_overrides:
NETStandard.Library: MIT NETStandard.Library: MIT
Microsoft.NETCore.Platforms: MIT Microsoft.NETCore.Platforms: MIT
System.Buffers: MIT System.Buffers: MIT

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
name: "Bug Report / 缺陷报告" name: "Bug Report / 缺陷报告"
description: "Report a reproducible defect in GFramework. / 报告可稳定复现的 GFramework 缺陷。" description: "Report a reproducible defect in GFramework. / 报告可稳定复现的 GFramework 缺陷。"
title: "[Bug]: " title: "[Bug]: "

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
name: "Feature Request / 功能建议" name: "Feature Request / 功能建议"
description: "Suggest a new capability or an API improvement. / 提出新能力或 API 改进建议。" description: "Suggest a new capability or an API improvement. / 提出新能力或 API 改进建议。"
title: "[Feature]: " title: "[Feature]: "

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
name: "Documentation / 文档改进" name: "Documentation / 文档改进"
description: "Report missing, outdated, or unclear documentation. / 报告缺失、过期或不清晰的文档。" description: "Report missing, outdated, or unclear documentation. / 报告缺失、过期或不清晰的文档。"
title: "[Docs]: " title: "[Docs]: "

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
name: "Question / 使用咨询" name: "Question / 使用咨询"
description: "Ask for guidance about usage, behavior, or adoption. / 询问用法、行为或接入方式。" description: "Ask for guidance about usage, behavior, or adoption. / 询问用法、行为或接入方式。"
title: "[Question]: " title: "[Question]: "

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: "Search Existing Issues / 搜索现有 Issues" - name: "Search Existing Issues / 搜索现有 Issues"

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
name: Validate PAT name: Validate PAT
description: Validate that the release PAT can access the repository and push tags. description: Validate that the release PAT can access the repository and push tags.

99
.github/cliff.toml vendored Normal file
View File

@ -0,0 +1,99 @@
[remote.github]
owner = "GeWuYou"
repo = "GFramework"
[changelog]
header = ""
body = """
{%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
{%- endmacro -%}
{% macro has_release_highlight(commit) -%}
{%- set highlighted = false -%}
{%- if commit.remote and commit.remote.pr_labels -%}
{%- for label in commit.remote.pr_labels -%}
{%- if label == "release-highlight" or label == "highlight" -%}
{%- set highlighted = true -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- if not highlighted and commit.footers -%}
{%- for footer in commit.footers -%}
{%- if footer.token == "Release-Highlight" and footer.value | trim == "true" -%}
{%- set highlighted = true -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{{ highlighted }}
{%- endmacro %}
{% macro print_commit(commit) -%}
- {{ commit.message | split(pat="\n") | first | trim | upper_first }}{% if commit.remote and commit.remote.username %} by @{{ commit.remote.username }}{% elif commit.author.name %} by {{ commit.author.name }}{% endif %}{% if commit.remote and commit.remote.pr_number %} in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}){% endif %}
{%- endmacro %}
{% if version -%}
## {{ version }} ({{ timestamp | date(format="%Y-%m-%d") }})
{% else -%}
## 未发布
{% endif %}
{% set highlights = commits | filter(attribute="breaking", value=true) %}
{% for commit in commits -%}
{% if self::has_release_highlight(commit=commit) == "true" -%}
{% set_global highlights = highlights | concat(with=commit) -%}
{% endif -%}
{% endfor -%}
{% if highlights | length > 0 -%}
## 重点条目
{% for commit in highlights -%}
{{ self::print_commit(commit=commit) }}
{% endfor %}
{% endif -%}
{% if commits | length > 0 -%}
## What's Changed
{% for group, commits in commits | group_by(attribute="group") -%}
### {{ group | striptags | trim }}
{% for commit in commits -%}
{{ self::print_commit(commit=commit) }}
{% endfor %}
{% endfor -%}
{% endif -%}
{% if previous and previous.version and version -%}
Full Changelog: [{{ previous.version }}...{{ version }}]({{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }})
{% endif -%}
"""
footer = ""
[git]
conventional_commits = true
filter_unconventional = true
split_commits = false
protect_breaking_commits = false
sort_commits = "oldest"
commit_parsers = [
{ message = ".*\\[skip changelog\\].*", skip = true },
{ body = ".*\\[skip changelog\\].*", skip = true },
{ message = "^feat", group = "<!-- 0 -->✨ 新功能" },
{ message = "^fix", group = "<!-- 1 -->🐛 Bug 修复" },
{ message = "^perf", group = "<!-- 2 -->⚡ 优化" },
{ message = "^refactor", group = "<!-- 2 -->⚡ 优化" },
{ message = "^docs", group = "<!-- 3 -->📝 文档/其他" },
{ message = "^test", group = "<!-- 3 -->📝 文档/其他" },
{ message = "^chore", group = "<!-- 3 -->📝 文档/其他" },
{ message = "^build", group = "<!-- 3 -->📝 文档/其他" },
{ message = "^ci", group = "<!-- 3 -->📝 文档/其他" },
{ message = "^style", group = "<!-- 3 -->📝 文档/其他" }
]
[git.github]
commits = true

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
version: 2 version: 2
updates: updates:
# ===== NuGet 依赖(所有项目)===== # ===== NuGet 依赖(所有项目)=====

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
name: Semantic Release Version and Tag name: Semantic Release Version and Tag
on: on:
@ -14,6 +17,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
pull-requests: read
outputs: outputs:
published: ${{ steps.semantic_release.outputs.new_release_published }} published: ${{ steps.semantic_release.outputs.new_release_published }}
last_tag: ${{ steps.semantic_release.outputs.last_release_git_tag }} last_tag: ${{ steps.semantic_release.outputs.last_release_git_tag }}
@ -56,13 +60,27 @@ jobs:
echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}" echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}"
echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}"
- name: Generate preview release notes
if: ${{ steps.semantic_release.outputs.new_release_published == 'true' }}
id: cliff_preview
uses: orhun/git-cliff-action@v4
with:
config: .github/cliff.toml
args: >-
-vv --unreleased --strip header
--tag "${{ steps.semantic_release.outputs.new_release_git_tag }}"
env:
OUTPUT: PREVIEW_RELEASE_NOTES.md
GITHUB_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ github.token }}
- name: Write preview summary - name: Write preview summary
env: env:
RELEASE_PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }} RELEASE_PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }}
RELEASE_NOTES: ${{ steps.semantic_release.outputs.new_release_notes }} CLIFF_RELEASE_NOTES: ${{ steps.cliff_preview.outputs.content }}
run: | run: |
{ {
echo "## Semantic Release Preview" echo "## Release Preview"
echo echo
echo "- Commit: \`${{ github.sha }}\`" echo "- Commit: \`${{ github.sha }}\`"
echo "- Release needed: \`${{ steps.semantic_release.outputs.new_release_published }}\`" echo "- Release needed: \`${{ steps.semantic_release.outputs.new_release_published }}\`"
@ -71,13 +89,11 @@ jobs:
echo "- Next tag: \`${{ steps.semantic_release.outputs.new_release_git_tag }}\`" echo "- Next tag: \`${{ steps.semantic_release.outputs.new_release_git_tag }}\`"
echo "- Preview auth: uses \`PAT_TOKEN\` because semantic-release dry-run still performs a remote push permission probe." echo "- Preview auth: uses \`PAT_TOKEN\` because semantic-release dry-run still performs a remote push permission probe."
echo "- Snapshot semantics: this preview is pinned to dispatch SHA \`${{ github.sha }}\`; commits added to \`main\` after the run starts are not included." echo "- Snapshot semantics: this preview is pinned to dispatch SHA \`${{ github.sha }}\`; commits added to \`main\` after the run starts are not included."
if [ "${RELEASE_PUBLISHED}" = "true" ] && [ -n "${RELEASE_NOTES}" ]; then if [ "${RELEASE_PUBLISHED}" = "true" ] && [ -n "${CLIFF_RELEASE_NOTES}" ]; then
echo echo
echo "<details><summary>Preview release notes</summary>" echo "### 候选发布说明"
echo echo
printf '%s\n' "${RELEASE_NOTES}" printf '%s\n' "${CLIFF_RELEASE_NOTES}"
echo
echo "</details>"
fi fi
echo echo
echo "If the version looks correct, approve the \`release-approval\` environment to continue." echo "If the version looks correct, approve the \`release-approval\` environment to continue."
@ -93,6 +109,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: write contents: write
pull-requests: read
environment: environment:
name: release-approval name: release-approval
steps: steps:
@ -131,13 +148,26 @@ jobs:
echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}" echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}"
echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}"
- name: Generate published release notes
if: ${{ steps.semantic_release.outputs.new_release_published == 'true' }}
id: cliff_release
uses: orhun/git-cliff-action@v4
with:
config: .github/cliff.toml
args: >-
-vv --latest --strip header
env:
OUTPUT: PUBLISHED_RELEASE_NOTES.md
GITHUB_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ github.token }}
- name: Write release summary - name: Write release summary
env: env:
RELEASE_PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }} RELEASE_PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }}
RELEASE_NOTES: ${{ steps.semantic_release.outputs.new_release_notes }} CLIFF_RELEASE_NOTES: ${{ steps.cliff_release.outputs.content }}
run: | run: |
{ {
echo "## Semantic Release Publish" echo "## Release Publish"
echo echo
echo "- Commit: \`${{ github.sha }}\`" echo "- Commit: \`${{ github.sha }}\`"
echo "- Preview last tag: \`${{ needs.preview.outputs.last_tag }}\`" echo "- Preview last tag: \`${{ needs.preview.outputs.last_tag }}\`"
@ -148,12 +178,10 @@ jobs:
echo "- Next version: \`${{ steps.semantic_release.outputs.new_release_version }}\`" echo "- Next version: \`${{ steps.semantic_release.outputs.new_release_version }}\`"
echo "- Next tag: \`${{ steps.semantic_release.outputs.new_release_git_tag }}\`" echo "- Next tag: \`${{ steps.semantic_release.outputs.new_release_git_tag }}\`"
echo "- Snapshot semantics: this publish run still uses dispatch SHA \`${{ github.sha }}\`; commits added to \`main\` after the preview started are excluded." echo "- Snapshot semantics: this publish run still uses dispatch SHA \`${{ github.sha }}\`; commits added to \`main\` after the preview started are excluded."
if [ "${RELEASE_PUBLISHED}" = "true" ] && [ -n "${RELEASE_NOTES}" ]; then if [ "${RELEASE_PUBLISHED}" = "true" ] && [ -n "${CLIFF_RELEASE_NOTES}" ]; then
echo echo
echo "<details><summary>Published release notes</summary>" echo "### 已发布说明"
echo echo
printf '%s\n' "${RELEASE_NOTES}" printf '%s\n' "${CLIFF_RELEASE_NOTES}"
echo
echo "</details>"
fi fi
} >> "${GITHUB_STEP_SUMMARY}" } >> "${GITHUB_STEP_SUMMARY}"

71
.github/workflows/benchmark.yml vendored Normal file
View File

@ -0,0 +1,71 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
name: Benchmark
on:
workflow_dispatch:
inputs:
benchmark_filter:
description: '可选的 BenchmarkDotNet 过滤器;留空时仅执行 benchmark 项目 Release build'
required: false
default: ''
type: string
permissions:
contents: read
jobs:
benchmark:
name: Benchmark Build Or Run
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET 10
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: |
~/.nuget/packages
~/.local/share/NuGet
key: ${{ runner.os }}-nuget-benchmarks-${{ hashFiles('GFramework.Cqrs.Benchmarks/*.csproj', 'GFramework.Cqrs/*.csproj', 'GFramework.Cqrs.Abstractions/*.csproj', 'GFramework.Core/*.csproj', 'GFramework.Core.Abstractions/*.csproj', '**/nuget.config') }}
- name: Restore benchmark project
run: dotnet restore GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj
- name: Build benchmark project
run: dotnet build GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-restore
- name: Report build-only mode
if: ${{ inputs.benchmark_filter == '' }}
run: |
echo "No benchmark filter provided."
echo "Workflow completed after validating the benchmark project build."
- name: Run filtered benchmarks
if: ${{ inputs.benchmark_filter != '' }}
env:
BENCHMARK_FILTER: ${{ inputs.benchmark_filter }}
run: |
set -euo pipefail
dotnet run --project GFramework.Cqrs.Benchmarks/GFramework.Cqrs.Benchmarks.csproj -c Release --no-build -- \
--filter "$BENCHMARK_FILTER"
- name: Upload BenchmarkDotNet artifacts
if: ${{ always() && inputs.benchmark_filter != '' }}
uses: actions/upload-artifact@v7
with:
name: benchmark-artifacts
path: |
BenchmarkDotNet.Artifacts/**
GFramework.Cqrs.Benchmarks/bin/Release/net10.0/BenchmarkDotNet.Artifacts/**
if-no-files-found: ignore

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
# CI/CD工作流配置构建和测试.NET项目 # CI/CD工作流配置构建和测试.NET项目
# 该工作流仅在创建或更新面向任意分支的 pull request 时触发 # 该工作流仅在创建或更新面向任意分支的 pull request 时触发
name: CI - Build & Test name: CI - Build & Test
@ -28,6 +31,13 @@ jobs:
- name: Validate C# naming - name: Validate C# naming
run: bash scripts/validate-csharp-naming.sh run: bash scripts/validate-csharp-naming.sh
# 校验仓库维护源码是否包含 Apache-2.0 文件头声明
- name: Validate license headers
run: python3 scripts/license-header.py --check
- name: Validate runtime-generator boundaries
run: python3 scripts/validate-runtime-generator-boundaries.py
# 缓存MegaLinter # 缓存MegaLinter
- name: Cache MegaLinter - name: Cache MegaLinter
uses: actions/cache@v5 uses: actions/cache@v5
@ -145,6 +155,19 @@ jobs:
- name: Build - name: Build
run: dotnet build GFramework.sln -c Release --no-restore run: dotnet build GFramework.sln -c Release --no-restore
- name: Pack published modules
run: |
rm -rf ./packages
dotnet pack GFramework.sln \
-c Release \
--no-build \
--no-restore \
-o ./packages \
-p:IncludeSymbols=false
- name: Validate packed modules
run: bash scripts/validate-packed-modules.sh ./packages
# 运行单元测试输出TRX格式结果到TestResults目录 # 运行单元测试输出TRX格式结果到TestResults目录
# 顺序执行各测试项目,避免并发 dotnet test 进程导致“TRX 全绿但 step 仍返回失败”的假红状态 # 顺序执行各测试项目,避免并发 dotnet test 进程导致“TRX 全绿但 step 仍返回失败”的假红状态
- name: Test All Projects - name: Test All Projects

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
# GitHub Actions工作流配置CodeQL静态代码分析 # GitHub Actions工作流配置CodeQL静态代码分析
# 该工作流用于对C#项目进行安全漏洞和代码质量分析 # 该工作流用于对C#项目进行安全漏洞和代码质量分析
name: "CodeQL" name: "CodeQL"

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
name: License Compliance (Feluda) name: License Compliance (Feluda)
on: on:
@ -62,6 +65,7 @@ jobs:
# with: 配置上传的具体内容 # with: 配置上传的具体内容
# name: 工件名称,用于标识上传的文件集合 # name: 工件名称,用于标识上传的文件集合
# path: 指定需要上传的文件路径列表(支持多行格式) # path: 指定需要上传的文件路径列表(支持多行格式)
# third-party-licenses/**: 手工维护的参考源码许可证原文
- name: Upload compliance artifacts - name: Upload compliance artifacts
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
@ -69,6 +73,7 @@ jobs:
path: | path: |
NOTICE NOTICE
THIRD_PARTY_LICENSES.md THIRD_PARTY_LICENSES.md
third-party-licenses/**
sbom.spdx.json sbom.spdx.json
sbom.cyclonedx.json sbom.cyclonedx.json
sbom-spdx-validation.txt sbom-spdx-validation.txt
@ -79,15 +84,17 @@ jobs:
# 压缩包中包含以下文件: # 压缩包中包含以下文件:
# - NOTICE: 项目声明文件 # - NOTICE: 项目声明文件
# - THIRD_PARTY_LICENSES.md: 第三方许可证列表 # - THIRD_PARTY_LICENSES.md: 第三方许可证列表
# - third-party-licenses/: 手工维护的参考源码许可证原文
# - sbom.spdx.json: SPDX 格式的软件物料清单 # - sbom.spdx.json: SPDX 格式的软件物料清单
# - sbom.cyclonedx.json: CycloneDX 格式的软件物料清单 # - sbom.cyclonedx.json: CycloneDX 格式的软件物料清单
# - sbom-spdx-validation.txt: SPDX 格式验证结果 # - sbom-spdx-validation.txt: SPDX 格式验证结果
# - sbom-cyclonedx-validation.txt: CycloneDX 格式验证结果 # - sbom-cyclonedx-validation.txt: CycloneDX 格式验证结果
- name: Package compliance bundle - name: Package compliance bundle
run: | run: |
zip license-compliance.zip \ zip -r license-compliance.zip \
NOTICE \ NOTICE \
THIRD_PARTY_LICENSES.md \ THIRD_PARTY_LICENSES.md \
third-party-licenses \
sbom.spdx.json \ sbom.spdx.json \
sbom.cyclonedx.json \ sbom.cyclonedx.json \
sbom-spdx-validation.txt \ sbom-spdx-validation.txt \

View File

@ -0,0 +1,54 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
# 维护者手动触发的 Apache-2.0 文件头修复流程。
name: License Header Fix
on:
workflow_dispatch:
inputs:
base_branch:
description: Branch to fix and target with the generated pull request.
required: true
default: main
permissions:
contents: write
pull-requests: write
jobs:
fix-license-headers:
name: Create license header fix PR
runs-on: ubuntu-latest
steps:
- name: Checkout target branch
uses: actions/checkout@v6
with:
ref: ${{ inputs.base_branch }}
- name: Add missing license headers
run: python3 scripts/license-header.py --fix
- name: Create pull request
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
base: ${{ inputs.base_branch }}
branch: chore/license-headers-${{ github.run_id }}
delete-branch: true
commit-message: |
chore(license): 补齐 Apache-2.0 文件头
- 补充缺失源文件许可证声明
- 更新文件头治理校验结果
title: "chore(license): 补齐 Apache-2.0 文件头"
body: |
## Summary
- 补齐仓库维护源码和配置文件缺失的 Apache-2.0 文件头
- 使用 `scripts/license-header.py --fix` 生成本次修复
## Validation
- `python3 scripts/license-header.py --check`

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
# 工作流名称Publish Docs # 工作流名称Publish Docs
# 该工作流用于在推送标签或手动触发时构建并部署文档到 GitHub Pages # 该工作流用于在推送标签或手动触发时构建并部署文档到 GitHub Pages

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
name: Publish VS Code Extension name: Publish VS Code Extension
on: on:

View File

@ -1,3 +1,6 @@
# Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
# 发布工作流NuGet + GitHub Packages + GitHub Release # 发布工作流NuGet + GitHub Packages + GitHub Release
# #
# 功能:当推送标签时自动构建、打包,并将相同产物并发发布到 NuGet.org 与 GitHub Packages # 功能:当推送标签时自动构建、打包,并将相同产物并发发布到 NuGet.org 与 GitHub Packages
@ -79,41 +82,10 @@ jobs:
-p:IncludeSymbols=false -p:IncludeSymbols=false
- name: Validate packed modules - name: Validate packed modules
run: | run: bash scripts/validate-packed-modules.sh ./packages
set -euo pipefail
expected_packages=( - name: Validate runtime-generator package boundaries
"GeWuYou.GFramework" run: python3 scripts/validate-runtime-generator-boundaries.py --package-dir ./packages
"GeWuYou.GFramework.Core"
"GeWuYou.GFramework.Core.Abstractions"
"GeWuYou.GFramework.Core.SourceGenerators"
"GeWuYou.GFramework.Cqrs"
"GeWuYou.GFramework.Cqrs.Abstractions"
"GeWuYou.GFramework.Cqrs.SourceGenerators"
"GeWuYou.GFramework.Ecs.Arch"
"GeWuYou.GFramework.Ecs.Arch.Abstractions"
"GeWuYou.GFramework.Game"
"GeWuYou.GFramework.Game.Abstractions"
"GeWuYou.GFramework.Game.SourceGenerators"
"GeWuYou.GFramework.Godot"
"GeWuYou.GFramework.Godot.SourceGenerators"
)
mapfile -t actual_packages < <(
find ./packages -maxdepth 1 -type f -name '*.nupkg' -printf '%f\n' \
| sed -E 's/\.[0-9][0-9A-Za-z.-]*\.nupkg$//' \
| sort -u
)
printf '%s\n' "${expected_packages[@]}" | sort > expected-packages.txt
printf '%s\n' "${actual_packages[@]}" | sort > actual-packages.txt
echo "Expected packages:"
cat expected-packages.txt
echo "Actual packages:"
cat actual-packages.txt
diff -u expected-packages.txt actual-packages.txt
- name: Show packages - name: Show packages
run: ls -la ./packages || true run: ls -la ./packages || true
@ -240,27 +212,56 @@ jobs:
permissions: permissions:
contents: write contents: write
packages: read packages: read
pull-requests: read
steps: steps:
- name: Checkout repository (at tag)
uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: true
- name: Download package artifacts - name: Download package artifacts
uses: actions/download-artifact@v8 uses: actions/download-artifact@v8
with: with:
name: packages name: packages
path: ./packages path: ./packages
- name: Generate release notes
id: cliff_release
uses: orhun/git-cliff-action@v4
with:
config: .github/cliff.toml
args: >-
-vv --latest --strip header
env:
OUTPUT: RELEASE_NOTES.md
GITHUB_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ github.token }}
# 无论某一侧包源发布是否失败,都继续创建 Release。 # 无论某一侧包源发布是否失败,都继续创建 Release。
# 合规工件由独立 workflow 生成,当前发布流不再假设这些文件在同一次运行中可用。 # 合规工件由独立 workflow 生成,当前发布流不再假设这些文件在同一次运行中可用。
- name: Create GitHub Release and Upload Assets - name: Create GitHub Release and Upload Assets
uses: softprops/action-gh-release@v3 uses: softprops/action-gh-release@v3
with: with:
generate_release_notes: true
name: "Release ${{ github.ref_name }}" name: "Release ${{ github.ref_name }}"
body: | body_path: RELEASE_NOTES.md
Release created by CI for tag ${{ github.ref_name }}
Package version: ${{ needs.build-pack.outputs.package_version }}
draft: false draft: false
prerelease: false prerelease: false
files: | files: |
./packages/*.nupkg ./packages/*.nupkg
env: env:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
- name: Write publish summary
env:
CLIFF_RELEASE_NOTES: ${{ steps.cliff_release.outputs.content }}
run: |
{
echo "## GitHub Release"
echo
echo "- Tag: \`${{ github.ref_name }}\`"
echo "- Package version: \`${{ needs.build-pack.outputs.package_version }}\`"
echo
printf '%s\n' "${CLIFF_RELEASE_NOTES}"
} >> "${GITHUB_STEP_SUMMARY}"

1
.gitignore vendored
View File

@ -26,3 +26,4 @@ ai-libs/
.codex .codex
# tool # tool
.venv/ .venv/
BenchmarkDotNet.Artifacts/

View File

@ -1,4 +1,7 @@
# 配置文件用于设置代码质量检查工具的各项参数和规则 # Copyright (c) 2025-2026 GeWuYou
# SPDX-License-Identifier: Apache-2.0
# 配置文件用于设置代码质量检查工具的各项参数和规则
# 包含全局排除目录、启用/禁用的检查器、特定语言配置等设置 # 包含全局排除目录、启用/禁用的检查器、特定语言配置等设置
APPLY_FIXES: none APPLY_FIXES: none

View File

@ -33,6 +33,14 @@
"type": "refactor", "type": "refactor",
"release": "patch" "release": "patch"
}, },
{
"type": "deps",
"release": "patch"
},
{
"type": "security",
"release": "patch"
},
{ {
"type": "docs", "type": "docs",
"release": false "release": false
@ -70,6 +78,45 @@
"@semantic-release/release-notes-generator", "@semantic-release/release-notes-generator",
{ {
"preset": "conventionalcommits", "preset": "conventionalcommits",
"presetConfig": {
"types": [
{
"type": "feat",
"section": "Features",
"hidden": false
},
{
"type": "fix",
"section": "Bug Fixes",
"hidden": false
},
{
"type": "perf",
"section": "Performance Improvements",
"hidden": false
},
{
"type": "refactor",
"section": "Refactoring",
"hidden": false
},
{
"type": "deps",
"section": "Dependency Updates",
"hidden": false
},
{
"type": "security",
"section": "Security Fixes",
"hidden": false
},
{
"type": "revert",
"section": "Reverts",
"hidden": false
}
]
},
"parserOpts": { "parserOpts": {
"noteKeywords": [ "noteKeywords": [
"BREAKING CHANGE", "BREAKING CHANGE",

View File

@ -60,6 +60,10 @@ All AI agents and contributors must follow these rules when writing, reviewing,
`minor` segment. `minor` segment.
- Use `fix` for behavior corrections, `perf` for observable performance improvements, and `refactor` only for - Use `fix` for behavior corrections, `perf` for observable performance improvements, and `refactor` only for
non-feature code restructuring; these should raise the next released version's `patch` segment. non-feature code restructuring; these should raise the next released version's `patch` segment.
- Use `deps` for dependency version updates, dependency lockfile refreshes, and package maintenance that should raise
the next released version's `patch` segment.
- Use `security` for vulnerability fixes, dependency security mitigations, and security configuration corrections
that should raise the next released version's `patch` segment.
- Use `docs``test``chore``build``ci``style` for their literal categories; do not encode these changes as - Use `docs``test``chore``build``ci``style` for their literal categories; do not encode these changes as
`feat` just because they feel important. These categories MUST NOT trigger a release. `feat` just because they feel important. These categories MUST NOT trigger a release.
- Use `BREAKING CHANGE` in the commit footer or `!` after the type / scope header (for example `feat!:` or - Use `BREAKING CHANGE` in the commit footer or `!` after the type / scope header (for example `feat!:` or
@ -79,6 +83,23 @@ All AI agents and contributors must follow these rules when writing, reviewing,
- The branch naming rule for a new task branch is `<type>/<topic-or-scope>`, where `<type>` should match the intended - The branch naming rule for a new task branch is `<type>/<topic-or-scope>`, where `<type>` should match the intended
Conventional Commit category as closely as practical. Conventional Commit category as closely as practical.
## License Header Rules
- Repository-maintained source and configuration files that are supported by `scripts/license-header.py` MUST include an
Apache-2.0 file header before the task is considered complete.
- When creating or modifying supported files, contributors MUST preserve an existing compliant header or add the SPDX
header generated by `python3 scripts/license-header.py --fix`.
- Before committing changes that add or modify supported source/configuration files, contributors MUST run
`python3 scripts/license-header.py --check` and resolve any missing or misplaced headers.
- For files with shebang lines, keep the shebang as the first line and place the license header immediately after it.
- For XML/MSBuild files with an XML declaration, keep the XML declaration as the first node and place the license header
immediately after it.
- Do not add project license headers to excluded or third-party areas such as `.agents/**`, `ai-libs/**`,
`third-party-licenses/**`, generated snapshots, binary assets, lock files, and generated build output. Treat
`scripts/license-header.py` as the authoritative include/exclude policy for this check.
- If CI reports a license-header failure, either fix it locally with `python3 scripts/license-header.py --fix` or, for
maintainer-owned cleanup, use the manual `License Header Fix` GitHub Actions workflow to create a reviewed repair PR.
## Repository Boot Skill ## Repository Boot Skill
- The repository-maintained Codex boot skill lives at `.codex/skills/gframework-boot/`. - The repository-maintained Codex boot skill lives at `.codex/skills/gframework-boot/`.
@ -191,6 +212,9 @@ All generated or modified code MUST include clear and meaningful comments where
- Private fields: `_camelCase` - Private fields: `_camelCase`
- Keep abstractions projects free of implementation details and engine-specific dependencies. - Keep abstractions projects free of implementation details and engine-specific dependencies.
- Preserve existing module boundaries. Do not introduce new cross-module dependencies without clear architectural need. - Preserve existing module boundaries. Do not introduce new cross-module dependencies without clear architectural need.
- Framework runtime, abstractions, and meta-package projects MUST NOT reference `*.SourceGenerators*` projects or packages,
and MUST NOT use source-generator attributes such as `GenerateEnumExtensions` or `ContextAware`. Those capabilities are
reserved for consumer projects, generator projects, examples explicitly meant to demonstrate generator usage, and related tests.
### Formatting ### Formatting

View File

@ -1,11 +1,16 @@
<!--
Copyright (c) 2025-2026 GeWuYou
SPDX-License-Identifier: Apache-2.0
-->
<Project> <Project>
<!-- Keep repository-wide analyzer behavior consistent while allowing only selected projects to opt into polyfills. --> <!-- Keep repository-wide analyzer behavior consistent while allowing only selected projects to opt into polyfills. -->
<ItemGroup> <ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="3.0.52"> <PackageReference Include="Meziantou.Analyzer" Version="3.0.72">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Update="Meziantou.Polyfill" Version="1.0.116"> <PackageReference Update="Meziantou.Polyfill" Version="1.0.123">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -1,3 +1,8 @@
<!--
Copyright (c) 2025-2026 GeWuYou
SPDX-License-Identifier: Apache-2.0
-->
<Project> <Project>
<!-- <!--

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace GFramework.Core.Abstractions.Architectures; namespace GFramework.Core.Abstractions.Architectures;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Enums; using GFramework.Core.Abstractions.Enums;
namespace GFramework.Core.Abstractions.Architectures; namespace GFramework.Core.Abstractions.Architectures;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.Reflection; using System.Reflection;
using GFramework.Core.Abstractions.Lifecycle; using GFramework.Core.Abstractions.Lifecycle;
using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Model;
@ -81,6 +84,20 @@ public interface IArchitecture : IAsyncInitializable, IAsyncDestroyable, IInitia
void RegisterCqrsPipelineBehavior<TBehavior>() void RegisterCqrsPipelineBehavior<TBehavior>()
where TBehavior : class; where TBehavior : class;
/// <summary>
/// 注册 CQRS 流式请求管道行为。
/// 既支持实现 <c>IStreamPipelineBehavior&lt;,&gt;</c> 的开放泛型行为类型,
/// 也支持绑定到单一流式请求/响应对的封闭行为类型。
/// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
/// <exception cref="InvalidOperationException">当前架构的底层容器已冻结,无法继续注册流式管道行为。</exception>
/// <exception cref="ObjectDisposedException">当前架构的底层容器已释放,无法继续注册流式管道行为。</exception>
/// <remarks>
/// 该入口应在架构初始化冻结容器之前调用;具体开放泛型或封闭行为类型的校验逻辑由底层容器负责。
/// </remarks>
void RegisterCqrsStreamPipelineBehavior<TBehavior>()
where TBehavior : class;
/// <summary> /// <summary>
/// 从指定程序集显式注册 CQRS 处理器。 /// 从指定程序集显式注册 CQRS 处理器。
/// 当处理器位于默认架构程序集之外的模块或扩展程序集中时,可在初始化阶段调用该入口接入对应程序集。 /// 当处理器位于默认架构程序集之外的模块或扩展程序集中时,可在初始化阶段调用该入口接入对应程序集。

View File

@ -1,4 +1,7 @@
using GFramework.Core.Abstractions.Properties; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Properties;
namespace GFramework.Core.Abstractions.Architectures; namespace GFramework.Core.Abstractions.Architectures;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Command; using GFramework.Core.Abstractions.Command;
using GFramework.Core.Abstractions.Environment; using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Events; using GFramework.Core.Abstractions.Events;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Architectures; namespace GFramework.Core.Abstractions.Architectures;
/// <summary> /// <summary>

View File

@ -1,4 +1,7 @@
using GFramework.Core.Abstractions.Enums; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Enums;
namespace GFramework.Core.Abstractions.Architectures; namespace GFramework.Core.Abstractions.Architectures;

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Architectures; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Architectures;
/// <summary> /// <summary>
/// 架构模块接口,继承自架构生命周期接口。 /// 架构模块接口,继承自架构生命周期接口。

View File

@ -1,4 +1,7 @@
using GFramework.Core.Abstractions.Enums; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Enums;
namespace GFramework.Core.Abstractions.Architectures; namespace GFramework.Core.Abstractions.Architectures;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Command; using GFramework.Core.Abstractions.Command;
using GFramework.Core.Abstractions.Events; using GFramework.Core.Abstractions.Events;
using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Ioc;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Lifecycle; using GFramework.Core.Abstractions.Lifecycle;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Ioc;
namespace GFramework.Core.Abstractions.Architectures; namespace GFramework.Core.Abstractions.Architectures;

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Bases; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Bases;
/// <summary> /// <summary>
/// 表示键值对的接口,定义了通用的键值对数据结构契约 /// 表示键值对的接口,定义了通用的键值对数据结构契约

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Bases; namespace GFramework.Core.Abstractions.Bases;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Bases; namespace GFramework.Core.Abstractions.Bases;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
namespace GFramework.Core.Abstractions.Command; namespace GFramework.Core.Abstractions.Command;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
namespace GFramework.Core.Abstractions.Command; namespace GFramework.Core.Abstractions.Command;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Command; namespace GFramework.Core.Abstractions.Command;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Events; using GFramework.Core.Abstractions.Events;
using GFramework.Core.Abstractions.Utility; using GFramework.Core.Abstractions.Utility;

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Controller; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Controller;
/// <summary> /// <summary>
/// 控制器标记接口,用于标识控制器组件 /// 控制器标记接口,用于标识控制器组件

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Coroutine; namespace GFramework.Core.Abstractions.Coroutine;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Coroutine; namespace GFramework.Core.Abstractions.Coroutine;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Coroutine; namespace GFramework.Core.Abstractions.Coroutine;
/// <summary> /// <summary>

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Coroutine; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Coroutine;
/// <summary> /// <summary>
/// 表示协程的执行状态枚举 /// 表示协程的执行状态枚举

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Coroutine; namespace GFramework.Core.Abstractions.Coroutine;
/// <summary> /// <summary>

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Coroutine; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Coroutine;
/// <summary> /// <summary>
/// 时间源接口,提供当前时间、时间增量以及更新功能 /// 时间源接口,提供当前时间、时间增量以及更新功能

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Coroutine; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Coroutine;
/// <summary> /// <summary>
/// 定义一个可等待指令的接口,用于协程系统中的异步操作控制 /// 定义一个可等待指令的接口,用于协程系统中的异步操作控制

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.ComponentModel; using System.ComponentModel;
namespace GFramework.Core.Abstractions.Cqrs; namespace GFramework.Core.Abstractions.Cqrs;

View File

@ -1,3 +1,8 @@
<!--
Copyright (c) 2025-2026 GeWuYou
SPDX-License-Identifier: Apache-2.0
-->
<Project> <Project>
<!-- import parent: https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build --> <!-- import parent: https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build -->
<PropertyGroup> <PropertyGroup>

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Enums; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Enums;
/// <summary> /// <summary>
/// 架构阶段枚举,定义了系统架构初始化和运行过程中的各个关键阶段 /// 架构阶段枚举,定义了系统架构初始化和运行过程中的各个关键阶段

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Environment; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Environment;
/// <summary> /// <summary>
/// 定义环境接口,提供应用程序运行环境的相关信息 /// 定义环境接口,提供应用程序运行环境的相关信息

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Events; namespace GFramework.Core.Abstractions.Events;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Events; namespace GFramework.Core.Abstractions.Events;
/// <summary> /// <summary>

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Events; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Events;
/// <summary> /// <summary>
/// 事件接口,定义了事件注册的基本功能 /// 事件接口,定义了事件注册的基本功能

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Events; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Events;
/// <summary> /// <summary>
/// 事件总线接口,提供事件的发送、注册和注销功能 /// 事件总线接口,提供事件的发送、注册和注销功能

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Events; namespace GFramework.Core.Abstractions.Events;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Events; namespace GFramework.Core.Abstractions.Events;
/// <summary> /// <summary>

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Events; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Events;
/// <summary> /// <summary>
/// 提供注销功能的接口 /// 提供注销功能的接口

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Events; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Events;
/// <summary> /// <summary>
/// 提供统一注销功能的接口,用于管理需要注销的对象列表 /// 提供统一注销功能的接口,用于管理需要注销的对象列表

View File

@ -1,4 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <!--
Copyright (c) 2025-2026 GeWuYou
SPDX-License-Identifier: Apache-2.0
-->
<Project Sdk="Microsoft.NET.Sdk">
<!-- <!--
配置项目构建属性 配置项目构建属性

View File

@ -1,4 +1,7 @@
// IsExternalInit.cs // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
// IsExternalInit.cs
// This type is required to support init-only setters and record types // This type is required to support init-only setters and record types
// when targeting netstandard2.0 or older frameworks. // when targeting netstandard2.0 or older frameworks.

View File

@ -1,13 +1,22 @@
using System.Reflection; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.Reflection;
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Systems;
namespace GFramework.Core.Abstractions.Ioc; namespace GFramework.Core.Abstractions.Ioc;
/// <summary> /// <summary>
/// 依赖注入容器接口,定义了服务注册、解析和管理的基本操作 /// 依赖注入容器接口,定义服务注册、解析与生命周期管理的统一入口。
/// </summary> /// </summary>
public interface IIocContainer : IContextAware /// <remarks>
/// 实现者必须在 <see cref="IDisposable.Dispose" /> 中释放容器拥有的根 <see cref="IServiceProvider" /> 及其
/// 关联同步资源,并保证释放操作幂等。
/// 容器一旦释放,后续任何注册、解析、查询或作用域创建调用都必须抛出
/// <see cref="ObjectDisposedException" />,避免消费者继续访问失效的运行时状态。
/// </remarks>
public interface IIocContainer : IContextAware, IDisposable
{ {
#region Register Methods #region Register Methods
@ -96,6 +105,20 @@ public interface IIocContainer : IContextAware
void RegisterCqrsPipelineBehavior<TBehavior>() void RegisterCqrsPipelineBehavior<TBehavior>()
where TBehavior : class; where TBehavior : class;
/// <summary>
/// 注册 CQRS 流式请求管道行为。
/// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
/// <exception cref="InvalidOperationException">容器已冻结,无法继续注册流式管道行为。</exception>
/// <exception cref="ObjectDisposedException">容器已释放,无法继续注册流式管道行为。</exception>
/// <remarks>
/// 该入口既支持实现 <c>IStreamPipelineBehavior&lt;,&gt;</c> 的开放泛型行为类型,
/// 也支持绑定到单一流式请求/响应对的封闭行为类型。
/// 应在容器冻结前的注册阶段调用;具体可注册形态由实现容器负责校验。
/// </remarks>
void RegisterCqrsStreamPipelineBehavior<TBehavior>()
where TBehavior : class;
/// <summary> /// <summary>
/// 从指定程序集显式注册 CQRS 处理器。 /// 从指定程序集显式注册 CQRS 处理器。
/// 该入口适用于处理器不位于默认架构程序集中的场景,例如扩展包、模块程序集或拆分后的业务程序集。 /// 该入口适用于处理器不位于默认架构程序集中的场景,例如扩展包、模块程序集或拆分后的业务程序集。
@ -132,6 +155,10 @@ public interface IIocContainer : IContextAware
/// </summary> /// </summary>
/// <typeparam name="T">期望获取的实例类型</typeparam> /// <typeparam name="T">期望获取的实例类型</typeparam>
/// <returns>找到的第一个实例;如果未找到则返回 null</returns> /// <returns>找到的第一个实例;如果未找到则返回 null</returns>
/// <remarks>
/// 在 <see cref="Freeze" /> 之前,该查询只保证返回已经物化为实例绑定的服务。
/// 仅通过工厂或实现类型注册的服务在预冻结阶段可能不可见;若需要完整激活语义,请先冻结容器。
/// </remarks>
T? Get<T>() where T : class; T? Get<T>() where T : class;
/// <summary> /// <summary>
@ -140,6 +167,10 @@ public interface IIocContainer : IContextAware
/// </summary> /// </summary>
/// <param name="type">期望获取的实例类型</param> /// <param name="type">期望获取的实例类型</param>
/// <returns>找到的第一个实例;如果未找到则返回 null</returns> /// <returns>找到的第一个实例;如果未找到则返回 null</returns>
/// <remarks>
/// 在 <see cref="Freeze" /> 之前,该查询只保证返回已经物化为实例绑定的服务。
/// 仅通过工厂或实现类型注册的服务在预冻结阶段可能不可见;若需要完整激活语义,请先冻结容器。
/// </remarks>
object? Get(Type type); object? Get(Type type);
@ -165,6 +196,9 @@ public interface IIocContainer : IContextAware
/// </summary> /// </summary>
/// <typeparam name="T">期望获取的实例类型</typeparam> /// <typeparam name="T">期望获取的实例类型</typeparam>
/// <returns>所有符合条件的实例列表;如果没有则返回空数组</returns> /// <returns>所有符合条件的实例列表;如果没有则返回空数组</returns>
/// <remarks>
/// 在 <see cref="Freeze" /> 之前,该查询只会枚举当前已经可见的实例绑定,不会主动执行工厂或创建实现类型。
/// </remarks>
IReadOnlyList<T> GetAll<T>() where T : class; IReadOnlyList<T> GetAll<T>() where T : class;
/// <summary> /// <summary>
@ -172,6 +206,9 @@ public interface IIocContainer : IContextAware
/// </summary> /// </summary>
/// <param name="type">期望获取的实例类型</param> /// <param name="type">期望获取的实例类型</param>
/// <returns>所有符合条件的实例列表;如果没有则返回空数组</returns> /// <returns>所有符合条件的实例列表;如果没有则返回空数组</returns>
/// <remarks>
/// 在 <see cref="Freeze" /> 之前,该查询只会枚举当前已经可见的实例绑定,不会主动执行工厂或创建实现类型。
/// </remarks>
IReadOnlyList<object> GetAll(Type type); IReadOnlyList<object> GetAll(Type type);
@ -210,8 +247,26 @@ public interface IIocContainer : IContextAware
/// </summary> /// </summary>
/// <typeparam name="T">要检查的类型</typeparam> /// <typeparam name="T">要检查的类型</typeparam>
/// <returns>如果容器中包含指定类型的实例则返回true否则返回false</returns> /// <returns>如果容器中包含指定类型的实例则返回true否则返回false</returns>
/// <remarks>
/// 在 <see cref="Freeze" /> 之前,该方法更接近“是否存在对应注册”的检查,而不是完整的 DI 可解析性判断。
/// </remarks>
bool Contains<T>() where T : class; bool Contains<T>() where T : class;
/// <summary>
/// 检查容器中是否存在可赋值给指定服务类型的注册项,而不要求解析出实例。
/// </summary>
/// <param name="type">要检查的服务类型。</param>
/// <returns>若存在显式注册或开放泛型映射可满足该服务类型,则返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
/// <exception cref="ArgumentNullException">当 <paramref name="type" /> 为 <see langword="null" /> 时抛出。</exception>
/// <exception cref="ObjectDisposedException">当调用 <see cref="HasRegistration(Type)" /> 时容器已被释放时抛出。</exception>
/// <remarks>
/// 该入口面向“先判断是否值得解析实例”的热路径优化场景。
/// 与 <see cref="Contains{T}" /> 不同,它不会为了判断结果而激活服务实例,因此可避免把瞬态对象创建、
/// 多服务枚举或日志分配混入仅需存在性判断的调用链中。
/// 该方法按服务键与开放泛型映射判断可见性,不会把“仅以实现类型自身注册”的实例误判成其所有可赋值接口都已注册。
/// </remarks>
bool HasRegistration(Type type);
/// <summary> /// <summary>
/// 判断容器中是否包含某个具体的实例对象 /// 判断容器中是否包含某个具体的实例对象
/// </summary> /// </summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Lifecycle; namespace GFramework.Core.Abstractions.Lifecycle;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Lifecycle; namespace GFramework.Core.Abstractions.Lifecycle;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Lifecycle; namespace GFramework.Core.Abstractions.Lifecycle;
/// <summary> /// <summary>

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Lifecycle; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Lifecycle;
/// <summary> /// <summary>
/// 可销毁接口,为需要资源清理的组件提供标准销毁能力 /// 可销毁接口,为需要资源清理的组件提供标准销毁能力

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Lifecycle; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Lifecycle;
/// <summary> /// <summary>
/// 可初始化接口,为需要初始化的组件提供标准初始化能力 /// 可初始化接口,为需要初始化的组件提供标准初始化能力

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Lifecycle; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Lifecycle;
/// <summary> /// <summary>
/// 完整生命周期接口,组合了初始化和销毁能力 /// 完整生命周期接口,组合了初始化和销毁能力

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Localization; namespace GFramework.Core.Abstractions.Localization;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.Globalization; using System.Globalization;
using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Systems;

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Localization; namespace GFramework.Core.Abstractions.Localization;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Localization; namespace GFramework.Core.Abstractions.Localization;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Localization; namespace GFramework.Core.Abstractions.Localization;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Localization; namespace GFramework.Core.Abstractions.Localization;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Localization; namespace GFramework.Core.Abstractions.Localization;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Localization; namespace GFramework.Core.Abstractions.Localization;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging; namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging; namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging; namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging; namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Logging; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>
/// 定义日志记录接口,提供日志记录和级别检查功能 /// 定义日志记录接口,提供日志记录和级别检查功能

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Logging; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>
/// 定义日志工厂接口,用于创建日志记录器实例 /// 定义日志工厂接口,用于创建日志记录器实例

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Logging; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>
/// 定义日志工厂提供者的接口,用于创建具有指定名称和最小日志级别的日志记录器 /// 定义日志工厂提供者的接口,用于创建具有指定名称和最小日志级别的日志记录器

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging; namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging; namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging; namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>

View File

@ -1,4 +1,7 @@
namespace GFramework.Core.Abstractions.Logging; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>
/// 定义日志级别的枚举,用于标识不同严重程度的日志消息 /// 定义日志级别的枚举,用于标识不同严重程度的日志消息

View File

@ -1,3 +1,6 @@
// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
namespace GFramework.Core.Abstractions.Logging; namespace GFramework.Core.Abstractions.Logging;
/// <summary> /// <summary>

View File

@ -1,4 +1,7 @@
using GFramework.Core.Abstractions.Architectures; // Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Lifecycle; using GFramework.Core.Abstractions.Lifecycle;
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;

Some files were not shown because too many files have changed in this diff Show More