feat(skills): 完善审查能力并强化报告契约
This commit is contained in:
parent
a9d0848676
commit
29ef9b342d
|
|
@ -5,7 +5,7 @@ description: "GitLink CLI 契约专项审查:检查 flags 与默认值、命
|
|||
|
||||
## 已合并功能的增量证据
|
||||
|
||||
前置 PR #426 和 #427 同时扩展了 workflow 命令的参数与可选 JSON 字段。本 Skill 应把它们作为契约变更样本,核对旧调用是否保持兼容、新开关默认值、`changes`/`commits`/`ci_builds` 字段的可选性,以及 JSON 输出没有 ANSI、HTML 或敏感值:
|
||||
配套基础能力 PR #429 和 #430 扩展了 workflow 命令的参数与可选 JSON 字段。本 Skill 应把它们作为待验证的契约变更样本,而不是已合并前置;命令可用时核对旧调用兼容性、新开关默认值、`changes`/`commits`/`ci_builds` 字段可选性,以及 JSON 不含 ANSI、HTML 或敏感值:
|
||||
|
||||
```bash
|
||||
gitlink-cli workflow +review-context --help
|
||||
|
|
@ -107,7 +107,7 @@ gitlink-cli workflow +review-queue --from queue.json --previous queue-previous.j
|
|||
|
||||
下面这些不属于这个 skill 的职责:
|
||||
|
||||
- PR 是否值得合并:交给 `gitlink-pr-assessor`
|
||||
- PR 是否值得合并:由 `gitlink-code-review` 和 `gitlink-pr-integrator` 提供价值与集成依据
|
||||
- PR 是否适合集成主线:交给 `gitlink-pr-integrator`
|
||||
- commit message、分支命名、PR 模板质量:交给 `gitlink-commit-quality`
|
||||
- 维护者今日值班优先级:交给 `gitlink-maintainer-radar`
|
||||
|
|
|
|||
|
|
@ -1,55 +1,73 @@
|
|||
---
|
||||
name: gitlink-code-review
|
||||
description: "GitLink Pull Request 专项代码审查:只分析代码质量、逻辑正确性、测试覆盖、可维护性和代码级安全风险,生成带 CR 稳定编号、精确文件行号、证据、修复建议和验证限制的只读 Markdown 报告。用户只需点名 gitlink-code-review 并提供仓库与一个或多个 PR 编号即可使用;默认不调用其他 Skill、不评论或修改远端。"
|
||||
description: "GitLink 社区智能审查:审查一个或多个 PR 的贡献价值、变更范围、Review 修改履约、实现可行性、代码质量、逻辑、测试、维护性、性能、兼容性和安全性,并可执行仓库代码健康扫描与批量 Issue 分诊。生成关键结论前置、证据完整的只读 Markdown 报告和待人工审核的 Review 建议;默认不调用其他 Skill、不评论或修改远端。"
|
||||
---
|
||||
|
||||
# GitLink PR 代码审查
|
||||
# GitLink 社区智能审查
|
||||
|
||||
只负责代码层审查,不扩展到仓库健康度、Issue 分拣、PR 价值判断、PR 关系、合并门禁或维护者 SLA。
|
||||
以 PR 审查为主线,保留仓库健康扫描和 Issue 分诊。优化信息顺序,不缩减原有分析能力。
|
||||
|
||||
## 默认调用契约
|
||||
|
||||
用户只需说“使用 `gitlink-code-review` 审查 `<owner>/<repo>` 的 PR `#<number>`”;多个 PR 可以直接列出多个编号。除非仓库或 PR 无法确定,不要求用户重复说明输出格式、安全边界或报告路径。
|
||||
用户只需点名 `gitlink-code-review` 并提供仓库以及一个或多个 PR 编号。没有 PR 编号但明确要求仓库健康扫描或 Issue 分诊时,执行对应模式;同时要求多项能力时,生成一份综合报告。
|
||||
|
||||
点名本 Skill 后,默认自动执行以下要求:
|
||||
默认遵守以下规则:
|
||||
|
||||
- **范围固定**:只审查代码质量、逻辑正确性、测试覆盖、可维护性和代码级安全。
|
||||
- **独立运行**:不调用其他 Skill;发现契约、拓扑、集成或维护问题时只写“超出本次范围”,不替它们下结论。
|
||||
- **只读运行**:不提交 Review、不发表评论、不 approve、不合并、不关闭、不分配、不修改远端。
|
||||
- **稳定发现**:问题使用 `CR-001` 起的稳定编号,标明严重性、文件/行号、证据、影响、修复建议和验证限制。
|
||||
- **高效首屏**:报告开头先给会直接影响评审结果的结论、门禁和最多 5 项动作;blocking/high 使用颜色和粗体,同时保留纯文本标签。
|
||||
- **单文件落盘**:一次运行只生成一份供人阅读的 Markdown,保存到 `reports/skill-runs/gitlink-code-review/<owner>-<repo>-<scope>-<yyyyMMdd-HHmmssZ>.md`。多个 PR 放在同一份报告中,但每个 PR 的发现和结论必须分开。
|
||||
- **完整审查**:不限制为固定五个维度;按改动实际风险选择价值、可行性、Review 履约、逻辑、质量、测试、维护性、性能、兼容性、安全、文档和协作等维度。
|
||||
- **Review 闭环**:存在既有 Review 时,逐条判断作者是否修改、修改是否满足要求、证据是否充分以及是否引入回归。
|
||||
- **只读远端**:可以生成 Review 结论、整体评论草稿和内联评论草稿,但不提交 Review、不评论、不 approve、不合并、不关闭、不分配、不改标签。
|
||||
- **独立运行**:不调用其他 Skill。遇到需要专项判断的内容,可以注明验证限制,但仍完成本 Skill 能够完成的分析。
|
||||
- **结论前置**:首屏先显示评审建议、阻断项、Review 履约结果和最多 5 项关键动作;blocking/high 使用颜色和粗体,并保留纯文本标签。
|
||||
- **证据可追溯**:发现使用 `CR-`,Review 履约项使用 `RV-`,健康项使用 `RH-`,Issue 分诊项使用 `IT-` 稳定编号。
|
||||
- **单文件落盘**:一次运行生成一份 UTF-8 Markdown,保存到 `reports/skill-runs/gitlink-code-review/<owner>-<repo>-<scope>-<yyyyMMdd-HHmmssZ>.md`。
|
||||
|
||||
Markdown 使用 UTF-8 保存,不写 ANSI。无法写入工作区时,在回复中输出完整 Markdown,并明确标记“未落盘”;不能只给聊天摘要而丢失完整报告。
|
||||
无法写入工作区时,在最终回复中输出完整 Markdown 并标记“未落盘”。Markdown 不写 ANSI;凭据、cookie、token 和敏感值必须脱敏。
|
||||
|
||||
## 运行模式
|
||||
|
||||
### PR 审查模式
|
||||
|
||||
给出 PR 编号时默认启用,包含 PR 变更、Review 履约、完整代码审查、运行验证和 Review 建议。
|
||||
|
||||
### 仓库健康模式
|
||||
|
||||
用户要求仓库级检查时启用;综合报告中放在 PR 详细审查之后。检查文档、许可证、CI 配置、代码规范、测试结构、依赖管理、安全基线和 Issue 治理状态。
|
||||
|
||||
### Issue 分诊模式
|
||||
|
||||
用户要求 Issue 扫描或综合社区审查时启用。首屏只列各优先级的数量和 Issue 编号,详细分类统一放在报告最后。
|
||||
|
||||
## 首屏固定结构
|
||||
|
||||
首屏必须在详细分析之前,保持在维护者无需滚动或少量滚动即可读完的长度:
|
||||
首屏只保留直接改变维护者决策的信息:
|
||||
|
||||
```markdown
|
||||
# PR #<number> 代码审查摘要
|
||||
# GitLink 社区审查摘要
|
||||
|
||||
**结论:** <span style="color:#B42318"><strong>需要修改</strong></span> **[action_required]**
|
||||
**关键门禁:** 代码质量 `failed` | 逻辑 `partial` | 测试 `failed` | 可维护性 `passed` | 安全 `passed`
|
||||
**发现:** blocking 1 | high 1 | medium 2 | low 0
|
||||
**Review 建议:** <span style="color:#B42318"><strong>修改后再审</strong></span> **[action_required]**
|
||||
**PR 门禁:** 价值 `passed` | Review 履约 `failed` | 实现 `partial` | 测试 `failed` | 安全 `passed`
|
||||
**Review 履约:** 8 条 | 已完成 6 | 部分完成 1 | 未完成 1 | 引入回归 0
|
||||
**代码发现:** blocking 0 | high 2 | medium 3 | low 4
|
||||
**Issue 待办:** P0 1 条(#81)| P1 3 条(#72、#76、#89)| P2 5 条 | P3 8 条
|
||||
|
||||
## 先处理这 2 项
|
||||
## 先处理这 3 项
|
||||
|
||||
1. <span style="color:#B42318"><strong>[CR-001][blocking] 修复</strong></span> `path/file.go:42` 的越权路径;责任:作者;证据:`E-CR-001`。
|
||||
2. <span style="color:#B54708"><strong>[CR-002][high] 补测</strong></span> 非法输入与失败路径;责任:作者;证据:`E-CR-002`。
|
||||
1. <span style="color:#B42318"><strong>[RV-003][high] 补全 Review 要求</strong></span>:失败路径仍未返回可诊断错误。
|
||||
2. <span style="color:#B54708"><strong>[CR-002][high] 增加回归测试</strong></span>:复杂分支名未覆盖 URL 编码。
|
||||
3. <span style="color:#B54708"><strong>[CR-004][high] 收紧权限边界</strong></span>:写操作缺少资源归属校验。
|
||||
```
|
||||
|
||||
颜色仅用于最终结论、blocking/high、关键门禁和最优先动作。必须同时保留 `[blocking]`、`[high]` 等纯文本回退,避免渲染器清除 HTML 后丢失含义。
|
||||
Issue 首屏摘要不得展开标题、原因、标签或负责人。颜色只用于最终结论、blocking/high 和关键动作;始终保留 `[action_required]`、`[high]` 等文本回退。
|
||||
|
||||
## 输入和批量模式
|
||||
## 证据采集
|
||||
|
||||
单 PR 优先获取统一上下文:
|
||||
优先获取统一上下文:
|
||||
|
||||
```bash
|
||||
gitlink-cli workflow +review-context --owner <owner> --repo <repo> --number <number> --include-commits=true --include-ci=true --format json
|
||||
```
|
||||
|
||||
接口不可用时再分别获取:
|
||||
接口不可用时分别获取:
|
||||
|
||||
```bash
|
||||
gitlink-cli pr +view --owner <owner> --repo <repo> --id <pull_request_id> --format json
|
||||
|
|
@ -58,87 +76,135 @@ gitlink-cli pr +diff --owner <owner> --repo <repo> --id <pull_request_id> --form
|
|||
gitlink-cli pr +reviews --owner <owner> --repo <repo> --id <pull_request_id> --format json
|
||||
```
|
||||
|
||||
`id` 使用 API 的 `pull_request_id`,报告标题使用用户可见的 PR 编号。两者无法确认映射时停止深审并记录限制,不能猜测。
|
||||
记录 owner、repo、用户可见 PR 编号、`pull_request_id`、base、head、head SHA、采集时间和数据来源。详情、Diff、Review、CI 和本地检出必须对应同一快照;不一致时标记 `stale`,不能写成通过。
|
||||
|
||||
多个 PR 按编号逐条建立独立上下文、独立 `CR-` 发现和独立结论,然后在报告最前面增加批量摘要。不得把多个 Diff 混合成一个代码结论,也不得为了节省篇幅省略某个 PR 的验证限制。
|
||||
多个 PR 必须建立独立上下文、独立结论和独立编号空间,不得混合 Diff 或证据。批量摘要只合并数量和优先级,不合并具体判断。
|
||||
|
||||
## 审查流程
|
||||
## PR 审查流程
|
||||
|
||||
### 1. 固定证据快照
|
||||
### 1. 理解目标和贡献价值
|
||||
|
||||
记录 owner、repo、PR 编号、`pull_request_id`、base、head、head SHA、采集时间和数据来源。同一报告内的详情、Diff、Review、CI 和本地检出必须对应同一 head SHA;不一致时标记 `stale`,不得写成通过。
|
||||
对照标题、描述、关联 Issue、提交和实际 Diff,回答:
|
||||
|
||||
### 2. 确认变更声明与影响面
|
||||
- 解决的问题是否真实、常用并适合仓库定位。
|
||||
- 实际改动是否覆盖声明功能,是否存在未说明的范围扩张。
|
||||
- 是否重复现有能力,或是否提供更完整、兼容、可维护的实现。
|
||||
- 对用户、维护者、自动化脚本和后续扩展有什么实际影响。
|
||||
|
||||
先阅读标题、描述、提交和文件列表,提取作者声明的行为。只将声明用作检查目标,不将它当作已验证事实。标记核心代码、测试、权限、文件、网络、命令执行、依赖和敏感输出的变化。
|
||||
作者声明只能作为验证目标,不能直接作为通过证据。
|
||||
|
||||
### 3. 检查五个固定维度
|
||||
### 2. 分析 PR 变更
|
||||
|
||||
- **代码质量**:错误处理、资源释放、并发、复杂度、重复实现、API 使用和明显性能退化。
|
||||
- **逻辑正确性**:正常路径、边界条件、空值、状态转换、分页、排序、编码、错误分支和平台差异。
|
||||
- **测试覆盖**:正常、失败、边界、兼容和回归路径;断言是否验证行为而非只验证返回成功。
|
||||
- **可维护性**:是否符合现有架构、职责是否清晰、命名和注释是否解释复杂逻辑、维护成本是否无必要上升。
|
||||
- **代码级安全**:注入、路径遍历、越权、反序列化、凭据泄露、资源耗尽、危险外联和依赖风险。
|
||||
列出新增、删除、重构和行为变化,标明核心文件、测试、文档、依赖、权限、文件、网络、命令执行和敏感输出变化。区分:
|
||||
|
||||
- PR 初始实现包含的改动。
|
||||
- Review 后新增的修复提交。
|
||||
- 与 Review 无关的新范围。
|
||||
- 修复过程中被删除或退化的既有能力。
|
||||
|
||||
只有当前完整 Diff 时,可以分析最终变更,但不得声称已经完成 Review 前后比较。
|
||||
|
||||
### 3. 验证 Review 修改履约
|
||||
|
||||
读取所有有效 Review、普通评论中的代码问题和后续提交。对每条可执行意见建立 `RV-` 项:
|
||||
|
||||
- `resolved`:当前实现满足要求,并有代码或测试证据。
|
||||
- `partially_resolved`:只覆盖部分条件或缺少关键验证。
|
||||
- `unresolved`:未修改,或修改与要求不一致。
|
||||
- `regressed`:处理意见时引入新的行为、安全或兼容问题。
|
||||
- `outdated`:目标代码已删除或结构变化使原意见不再适用。
|
||||
- `not_verifiable`:缺少 Review 基线、提交映射或运行条件。
|
||||
|
||||
每项记录 reviewer、原意见摘要、原位置或时间、对应提交、当前位置、判断、证据和剩余动作。优先使用 Review 对应 commit SHA 与当前 head SHA 的增量 diff;无法建立基线时明确降低置信度。
|
||||
|
||||
不要把“代码发生变化”当作“已经满足 Review”,必须核对意见中的行为要求和边界条件。
|
||||
|
||||
### 4. 执行完整代码审查
|
||||
|
||||
按改动风险选择并覆盖相关维度:
|
||||
|
||||
- 逻辑正确性和边界条件。
|
||||
- 错误处理、资源释放、并发和状态一致性。
|
||||
- 测试的正常、失败、边界、兼容和回归路径。
|
||||
- 架构一致性、职责划分、复杂度、重复实现和长期维护成本。
|
||||
- 性能退化、批量复杂度、分页、缓存和资源耗尽风险。
|
||||
- CLI/API/JSON/帮助/i18n/UTF-8/跨平台兼容性。
|
||||
- 注入、路径遍历、越权、凭据泄露、危险外联、依赖和供应链风险。
|
||||
- 文档、示例、错误提示和迁移说明是否与实现一致。
|
||||
- 实现亮点、测试亮点和已经正确吸收的 Review 意见。
|
||||
|
||||
安全检查读取 [`../gitlink-shared/references/security-review-matrix.md`](../gitlink-shared/references/security-review-matrix.md)。疑似密钥只报告类型和位置,不复制值。
|
||||
|
||||
### 4. 执行与改动相匹配的验证
|
||||
### 5. 执行功能与回归验证
|
||||
|
||||
优先使用仓库 README、CI、Makefile 或现有测试中的命令。至少覆盖本次改动的正常路径、失败路径和兼容路径;安全敏感改动补恶意输入或权限边界测试。记录命令、工作目录、实际检出 SHA、退出码和输出摘要。
|
||||
优先使用仓库 README、CI、Makefile 和现有测试定义的环境。验证 PR 描述中的关键功能、Review 涉及路径、正常路径、失败路径、兼容路径和安全边界。
|
||||
|
||||
未执行、无法执行、CI 未关联当前 head 或测试命令不存在时,分别写 `not_run`、`partial` 或 `stale`,不能写成通过。CI 只统计匹配当前 PR head SHA 的构建;分支匹配只能作为低置信度回退。
|
||||
记录命令、工作目录、检出 SHA、退出码、耗时和输出摘要。CI 只统计匹配当前 head SHA 的构建;分支匹配只能作为低置信度回退。未执行或证据过期时写 `not_run`、`partial` 或 `stale`,不能写成通过。
|
||||
|
||||
### 5. 生成发现和动作
|
||||
### 6. 生成 Review 建议
|
||||
|
||||
每条 `CR-` 发现必须包含:
|
||||
给出 `建议通过`、`修改后再审`、`暂缓合并` 或 `需要人工判断`,并生成可供维护者编辑的整体 Review 草稿;需要精确定位时生成内联评论草稿。草稿应包含正向评价、阻断问题、证据和最小修复建议。
|
||||
|
||||
- 严重性:`blocking`、`high`、`medium` 或 `low`
|
||||
- 事实类型:`observed`、`derived` 或 `candidate`
|
||||
- 精确位置:文件和行号,或可复现命令
|
||||
- 触发条件与实际影响
|
||||
- 主证据 ID
|
||||
- 最小可执行修复建议
|
||||
- 验证状态和限制
|
||||
无论结论为何,都不得调用远端写接口。最终回复必须明确说明“Review 草稿尚未提交,需人工审核”。
|
||||
|
||||
没有精确证据的内容只能是 `candidate`,不能升级为 blocking。纯格式、个人偏好和 linter 可自动修复的问题默认不进入首屏。
|
||||
## 仓库健康扫描
|
||||
|
||||
## 严重性规则
|
||||
仓库模式至少检查:
|
||||
|
||||
- `blocking`:可导致漏洞、数据损坏、核心行为错误、明显回归,或核心功能完全无法验证。
|
||||
- `high`:高概率影响真实用户、关键失败路径或重要兼容行为,应在本轮修复。
|
||||
- README、CONTRIBUTING、CHANGELOG、LICENSE 和安全政策。
|
||||
- CI、格式化、lint、静态检查和跨平台配置。
|
||||
- 测试目录、关键模块覆盖、fixture 质量和失败路径测试。
|
||||
- 依赖锁文件、已知风险、更新策略和供应链边界。
|
||||
- 代码组织、重复热点、复杂模块和维护者可理解性。
|
||||
- Issue 分类、响应状态和长期未处理风险。
|
||||
|
||||
健康项使用 `RH-` 编号,标明检查范围、事实证据、影响和建议。不能仅根据文件是否存在给出高分;无法读取内容或执行工具时明确限制。
|
||||
|
||||
## 批量 Issue 分诊
|
||||
|
||||
扫描 open、未分类、近期新增或长期未处理的 Issue,按以下维度建立 `IT-` 项:
|
||||
|
||||
- 类型:bug、feature、documentation、question、performance、security 或 maintenance。
|
||||
- 优先级:`P0` 立即处置、`P1` 本轮处理、`P2` 计划处理、`P3` 可延后。
|
||||
- 所属模块和影响范围。
|
||||
- 复现信息、环境、日志和预期行为是否完整。
|
||||
- 是否疑似重复、依赖其他事项或需要关联 PR。
|
||||
- 当前等待作者、维护者、负责人还是平台。
|
||||
- 推荐标签、负责人、下一动作和回复草稿。
|
||||
|
||||
P0/P1 必须有证据,安全问题避免在报告中复制利用细节或敏感值。默认只生成建议;标签、分配、回复、关闭和其他写操作必须经过人工审核和新的明确授权。
|
||||
|
||||
## 发现与严重性
|
||||
|
||||
每条 `CR-` 发现必须包含严重性、事实类型、文件与行号或复现命令、触发条件、影响、证据、最小修复建议和验证限制。
|
||||
|
||||
- `blocking`:漏洞、数据损坏、核心行为错误、明显回归,或核心声明完全无法验证。
|
||||
- `high`:高概率影响真实用户、关键失败路径、Review 要求或重要兼容行为。
|
||||
- `medium`:存在边界、测试或维护缺口,但没有证据表明立即阻断。
|
||||
- `low`:不影响当前正确性的可选改进,合并显示并放入附录。
|
||||
- `low`:不影响当前正确性的可选改进,合并展示并放入详细部分。
|
||||
|
||||
## 单一 Markdown 报告结构
|
||||
没有精确证据的内容只能标记 `candidate`,不能升级为 blocking。纯格式偏好和可自动修复的低价值问题不得进入首屏。
|
||||
|
||||
报告按以下顺序组织:
|
||||
## 单一 Markdown 报告顺序
|
||||
|
||||
1. 评审决策和五维门禁
|
||||
2. 最多五项关键动作
|
||||
3. 验证结果摘要
|
||||
4. 完整 `CR-` 发现
|
||||
5. 正向证据
|
||||
6. 限制和未验证项
|
||||
7. 证据附录
|
||||
1. 首屏审查结论、Review 建议、门禁、Review 履约统计、Issue 优先级计数和最多五项动作。
|
||||
2. PR 目标、贡献价值和变更概览。
|
||||
3. `RV-` Review 修改履约明细。
|
||||
4. 完整 `CR-` 代码审查、正向证据和 Review 草稿。
|
||||
5. 构建、测试、功能验证和验证限制。
|
||||
6. `RH-` 仓库代码健康度。
|
||||
7. `IT-` Issue 分诊详细结果。
|
||||
8. 证据账本和附录。
|
||||
|
||||
完整发现示例:
|
||||
|
||||
```markdown
|
||||
### CR-001:非法路径可逃逸目标目录
|
||||
|
||||
- **严重性:** blocking
|
||||
- **位置:** `internal/files/write.go:42`
|
||||
- **证据:** `E-CR-001`,恶意路径 fixture 使目标落到工作目录之外
|
||||
- **影响:** 有写权限的调用者可以覆盖非目标文件
|
||||
- **修复建议:** 清理并解析路径后,验证最终绝对路径仍位于允许根目录
|
||||
- **验证限制:** Windows junction 场景尚未执行
|
||||
```
|
||||
没有启用的模式在报告中注明“本次未请求”,不虚构结果。Issue 具体说明始终位于 PR、验证和健康度内容之后。
|
||||
|
||||
## 完成前自检
|
||||
|
||||
- 首屏是否先于完整发现,且只保留最多五项会改变评审结果的动作。
|
||||
- 是否只覆盖五个固定维度,没有调用其他 Skill 或扩展到其他治理任务。
|
||||
- 每条 blocking/high 是否有位置、证据、影响和修复建议。
|
||||
- 未执行的测试是否明确标为 `not_run`,旧 head 证据是否标为 `stale`。
|
||||
- 是否没有执行任何远端写操作。
|
||||
- 是否已生成一份 UTF-8 Markdown,并在最终回复中给出其绝对路径和一句话结论。
|
||||
- 首屏是否先给结论,且最多只有五项会改变维护者决策的动作。
|
||||
- 是否完整分析实际相关维度,而不是机械限制为五项。
|
||||
- 是否区分完整 PR Diff 与 Review 后增量 Diff。
|
||||
- 每条有效 Review 是否有 `RV-` 状态、证据和剩余动作。
|
||||
- Review 建议和草稿是否只写入报告,没有提交远端。
|
||||
- 仓库健康扫描和 Issue 分诊是否在请求时保留,Issue 详情是否位于报告最后。
|
||||
- blocking/high 是否有可复现证据,未执行测试是否标为 `not_run`。
|
||||
- 是否生成一份 UTF-8 Markdown,并在最终回复中给出绝对路径和一句话结论。
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
interface:
|
||||
display_name: "PR 代码审查"
|
||||
short_description: "基于 Diff、测试和安全证据生成精确、可执行的代码 Review。"
|
||||
default_prompt: "使用 $gitlink-code-review 审查指定 GitLink PR。按 Skill 默认契约只做五个代码维度的只读审查,并生成关键结论前置的单一 Markdown 报告。"
|
||||
display_name: "社区智能审查"
|
||||
short_description: "验证 PR、Review 修改、仓库健康和 Issue 分诊,生成决策优先报告。"
|
||||
default_prompt: "使用 $gitlink-code-review 审查指定 GitLink PR,验证已有 Review 的修改情况,并生成结论前置、详情完整且不回写远端的 Markdown 报告。"
|
||||
|
|
|
|||
|
|
@ -33,4 +33,4 @@ $context | Set-Content .\pr-123-context.json -Encoding utf8
|
|||
2. **[CR-002][medium] 复看** 错误输出中的敏感字段脱敏(责任:作者;证据:`diff:internal/client/client.go:42`)。
|
||||
```
|
||||
|
||||
完整 Diff、命令输出、未匹配构建和正向反馈放入附录。默认只生成这份报告或评论草稿;只有用户明确授权并满足共享运行协议,才允许发布普通 `COMMENT`,不得自动 `APPROVE` 或 `MERGE`。
|
||||
完整 Diff、命令输出、未匹配构建和正向反馈放入附录。本 Skill 只生成报告、整体 Review 草稿和内联评论草稿,不发布 `COMMENT`、`APPROVE` 或 `MERGE`;维护者人工审核后可在独立操作中决定是否发布。
|
||||
|
|
|
|||
|
|
@ -60,9 +60,12 @@ gitlink-cli pr +files --id 42 --format json
|
|||
|
||||
```bash
|
||||
gitlink-cli pr +diff --id 42 --format json
|
||||
gitlink-cli pr +reviews --id 42 --format json
|
||||
```
|
||||
|
||||
### Step 4:逐文件审查
|
||||
### Step 4:验证 Review 修改并逐文件审查
|
||||
|
||||
如果 PR 已有 Review,先把每条可执行意见映射到 Review 后的提交和当前代码,标记为 `resolved`、`partially_resolved`、`unresolved`、`regressed`、`outdated` 或 `not_verifiable`。代码发生变化本身不能证明意见已经解决。
|
||||
|
||||
对每个变更文件,分析代码质量。以下是审查结果示例:
|
||||
|
||||
|
|
@ -113,15 +116,9 @@ gitlink-cli pr +diff --id 42 --format json
|
|||
- 有类型注解,代码可读性好
|
||||
```
|
||||
|
||||
### Step 5:提交 Review
|
||||
### Step 5:生成待人工审核的 Review 草稿
|
||||
|
||||
```bash
|
||||
# 提交整体 Review 评论
|
||||
gitlink-cli pr +review --id 42 --owner Gitlink --repo forgeplus --body '{
|
||||
"body": "## PR #42 代码审查报告\n\n### 🔴 Critical\n\n1. **JWT Secret 硬编码** — `src/config.py:15`\n JWT_SECRET 硬编码在源码中。建议使用 `os.getenv(\"JWT_SECRET\")`。\n\n2. **SQL 注入风险** — `src/auth/login.py:42`\n 直接拼接用户输入到 SQL 查询。建议使用参数化查询。\n\n### 🟡 Warning\n\n1. **密码明文存储** — 建议使用 bcrypt 哈希处理。\n\n### 总体评价\n\n代码整体结构清晰,测试覆盖良好。建议修复 Critical 问题后合并。",
|
||||
"event": "COMMENT"
|
||||
}'
|
||||
```
|
||||
在 Markdown 报告中生成整体 Review 和必要的内联评论草稿,不调用 `pr +review` 或任何远端写接口。维护者审核、编辑并明确决定发布后,再在本次 Skill 之外执行提交。
|
||||
|
||||
### Step 6:输出审查摘要
|
||||
|
||||
|
|
@ -159,6 +156,8 @@ gitlink-cli pr +files --id <id> --format json
|
|||
# 获取 Diff
|
||||
gitlink-cli pr +diff --id <id> --format json
|
||||
|
||||
# 提交 Review
|
||||
gitlink-cli pr +review --body '{"body":"...","event":"COMMENT"}'
|
||||
# 获取已有 Review,用于验证后续修改
|
||||
gitlink-cli pr +reviews --id <id> --format json
|
||||
```
|
||||
|
||||
本工作流只生成报告和 Review 草稿,不提交远端。
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ description: "GitLink 维护者队列专项雷达:扫描 open PR、open Issue
|
|||
|
||||
## 已合并功能的增量证据
|
||||
|
||||
前置 PR #427 的队列差异是本 Skill 的增量输入。值班扫描应优先展示 `new`、`priority_changed`、`risk_changed` 和 `resolved`,把未变化项压缩为数量;需要深入某个 PR 时,再用前置 PR #426 的证据包补齐 Review、提交和 CI 状态:
|
||||
配套 PR #430 的队列差异可作为本 Skill 的增量输入;#430 未合并时只使用当前队列,不得虚构变化。值班扫描优先展示 `new`、`priority_changed`、`risk_changed` 和 `resolved`,把未变化项压缩为数量;需要深入某个 PR 时,可在 PR #429 可用后用证据包补齐 Review、提交和 CI 状态:
|
||||
|
||||
```bash
|
||||
gitlink-cli workflow +review-queue --owner <owner> --repo <repo> --previous queue-previous.json --format json
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"security_gate": "passed",
|
||||
"verification": "partial",
|
||||
"findings": [
|
||||
{"id": "CR-001", "severity": "high", "status": "open", "summary": "错误路径缺少回归测试", "evidence": ["shortcuts/example/example_test.go:42"], "related_ids": []}
|
||||
{"id": "CR-001", "severity": "high", "status": "open", "summary": "错误路径缺少回归测试", "evidence": ["E-CR-001"], "related_ids": []}
|
||||
],
|
||||
"top_actions": [
|
||||
{"id": "CR-001", "owner": "author", "severity": "high", "action": "补充错误路径回归测试", "evidence": ["shortcuts/example/example_test.go:42"]}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"security_gate": "passed",
|
||||
"verification": "complete",
|
||||
"findings": [
|
||||
{"id": "TP-001", "severity": "medium", "status": "open", "summary": "与 PR #124 修改同一命令入口,建议合并顺序复核", "evidence": ["shortcuts/example/example.go", "pr:124"], "related_ids": ["PR-124"]}
|
||||
{"id": "TP-001", "severity": "medium", "status": "open", "summary": "与 PR #124 修改同一命令入口,建议合并顺序复核", "evidence": ["E-TP-001"], "related_ids": ["PR-124"]}
|
||||
],
|
||||
"top_actions": [
|
||||
{"id": "TP-001", "owner": "maintainer", "severity": "medium", "action": "复核 PR #123 与 PR #124 的合并顺序", "evidence": ["shortcuts/example/example.go"]}
|
||||
|
|
|
|||
|
|
@ -284,6 +284,9 @@ function New-FinalReport {
|
|||
}
|
||||
Write-JsonFile -Path (Join-Path $Path 'final-report.json') -Value $report
|
||||
Write-MarkdownReport -Path $Path -Report $report
|
||||
$validator = Join-Path $PSScriptRoot '..\..\gitlink-shared\examples\validate-maintenance-report.ps1'
|
||||
if (-not (Test-Path -LiteralPath $validator)) { throw "missing maintenance report validator: $validator" }
|
||||
& $validator -Path (Join-Path $Path 'final-report.json') | Out-Null
|
||||
return $report
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
---
|
||||
name: gitlink-pr-integrator
|
||||
description: "GitLink PR 集成专项验证:检查当前 head 对最新主线的合并态、构建、测试、契约、安全、冲突与发布影响,生成带 IN 编号和门禁证据的只读 Markdown 报告。用户只需点名 gitlink-pr-integrator 并提供一个或多个 PR;默认独立运行、不调用其他 Skill、不评论或合并远端。"
|
||||
description: "GitLink PR 价值与集成验证:评估贡献价值及其详细依据,并检查当前 head 对最新主线的合并态、构建、测试、契约、安全、冲突与发布影响,生成结论前置、带 IN 编号和可追溯证据的只读 Markdown 报告。用户只需点名 gitlink-pr-integrator 并提供一个或多个 PR;默认独立运行、不调用其他 Skill、不评论或合并远端。"
|
||||
---
|
||||
|
||||
## 已合并功能的增量证据
|
||||
|
||||
前置 PR #426 合并后,先复用统一 PR 证据包,再进入本 Skill 的独立 worktree 验证。重点读取 `commits`、`ci_builds`、`sections` 和 `notes`,将 CI 状态与本地构建、测试结果一起纳入集成门禁:
|
||||
配套基础能力 PR #429 合并后可复用统一 PR 证据包,再进入本 Skill 的独立 worktree 验证。#429 未合并或命令不可用时必须回退到现有只读接口并标记限制,不能假定证据包已经存在。重点读取 `commits`、`ci_builds`、`sections` 和 `notes`:
|
||||
|
||||
```bash
|
||||
gitlink-cli workflow +review-context --owner <owner> --repo <repo> --number <number> --include-commits=true --include-ci=true --format json
|
||||
```
|
||||
|
||||
本 Skill 只负责合并态、构建、测试、契约和发布影响,不代替代码审查。已有队列快照时可读取前置 PR #427 的 `changes`,但它只能辅助排序,不能跳过本地验证。
|
||||
本 Skill 独立评估贡献价值,并负责合并态、构建、测试、契约、安全、冲突和发布影响。配套 PR #430 可提供 `changes`;#430 未合并时继续使用现有队列接口。队列变化只能作为价值和排序证据,不能跳过仓库现状核对或本地验证。
|
||||
|
||||
CI 门禁必须读取 `ci_summary`:`match_mode=sha` 优先,`branch` 只能作为回退;`matched=0` 时 CI 为 `not_run`,不能给出 `ready_to_merge`。只要匹配构建中存在 `failed`,集成结论至少为 `action_required`;`unmatched` 构建只进入限制说明。队列的 `waiting_on` 仅用于安排下一动作,不改变合并门禁。
|
||||
|
||||
|
|
@ -29,10 +29,10 @@ CI 门禁必须读取 `ci_summary`:`match_mode=sha` 优先,`branch` 只能
|
|||
|
||||
点名后默认自动执行:
|
||||
|
||||
- 只判断集成就绪度,不调用其他 Skill,不重新做完整代码审查、不判断替代关系或维护者 SLA。
|
||||
- 独立判断贡献价值和集成就绪度,不调用其他 Skill;不重新做完整代码审查,不输出完整 PR 替代图谱,也不判断维护者 SLA。
|
||||
- 只读远端;允许在隔离 worktree 中执行本地验证,但不评论、不 approve、不合并、不关闭、不修改远端。
|
||||
- 使用 `IN-001` 起的稳定编号,记录门禁、当前 head SHA、命令、退出码、证据、下一动作和限制。
|
||||
- 首屏先直接回答“现在能否进入 merge queue”,显示六项门禁和最多 5 项动作;失败或高风险使用颜色和粗体。
|
||||
- 首屏先给出“贡献是否值得合入”和“现在能否进入 merge queue”,显示贡献价值与六项技术门禁及最多 5 项动作;失败或高风险使用颜色和粗体。
|
||||
- 一次运行只生成一份 UTF-8 Markdown,保存到 `reports/skill-runs/gitlink-pr-integrator/<owner>-<repo>-<scope>-<yyyyMMdd-HHmmssZ>.md`;多个 PR 先给队列摘要,再分别给每条 PR 的门禁。
|
||||
|
||||
无法写入工作区时输出完整 Markdown 并标记“未落盘”。最终回复给出报告绝对路径、可进入队列数量、阻断数量和第一下一动作。
|
||||
|
|
@ -43,7 +43,8 @@ CI 门禁必须读取 `ci_summary`:`match_mode=sha` 优先,`branch` 只能
|
|||
# PR #<number> 集成摘要
|
||||
|
||||
**结论:** <span style="color:#B54708"><strong>需补验证</strong></span> **[action_required]**
|
||||
**门禁:** 合并态 `passed` | 构建 `passed` | 测试 `not_run` | 契约 `partial` | 安全 `not_run` | 冲突 `low`
|
||||
**贡献价值:** <span style="color:#067647"><strong>值得合入</strong></span> **[passed]**:需求明确、增量有效,维护成本可接受
|
||||
**门禁:** 价值 `passed` | 合并态 `passed` | 构建 `passed` | 测试 `not_run` | 契约 `partial` | 安全 `not_run` | 冲突 `low`
|
||||
|
||||
## 先处理这 2 项
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ CI 门禁必须读取 `ci_summary`:`match_mode=sha` 优先,`branch` 只能
|
|||
2. <span style="color:#B54708"><strong>[IN-002][high] 复查</strong></span> 权限边界;责任:reviewer。
|
||||
```
|
||||
|
||||
这个 Skill 解决的是“这个 PR 现在能不能安全并入主线”,不是“这个 PR 有没有价值”。遇到贡献价值、功能可行性或完整代码审查问题时,只将其记录为超出范围或限制,不自动切换到其他 Skill。
|
||||
这个 Skill 同时回答“这个贡献是否值得进入社区”和“当前实现能否安全并入主线”。价值判断必须有仓库事实、需求和差异证据,不能根据标题、代码量、作者身份或主观新颖感下结论。完整代码缺陷审查仍不在本 Skill 内重复执行,但集成验证中发现的明确阻断问题必须记录并影响结论。
|
||||
|
||||
执行命令前,按需读取 [`references/api_reference.md`](./references/api_reference.md)。其中包含 GitLink CLI 命令、Windows 调用方式、独立 worktree 验证方法和报告字段约定。
|
||||
|
||||
|
|
@ -62,7 +63,7 @@ CI 门禁必须读取 `ci_summary`:`match_mode=sha` 优先,`branch` 只能
|
|||
默认遵循 [`../gitlink-shared/references/maintenance-report-contract.md`](../gitlink-shared/references/maintenance-report-contract.md),先回答“现在能否进入 merge queue”,再展开证据。首屏只保留:
|
||||
|
||||
- `decision`:`merge`、`action_required` 或 `blocked`
|
||||
- 合并态、构建、测试、契约、安全和冲突六个门禁
|
||||
- 贡献价值、合并态、构建、测试、契约、安全和冲突七个门禁
|
||||
- 最多 5 项下一动作,明确等待作者、reviewer、维护者还是平台
|
||||
- 仅列会改变排序的冲突和影响面,其余放附录
|
||||
|
||||
|
|
@ -73,28 +74,52 @@ CI 门禁必须读取 `ci_summary`:`match_mode=sha` 优先,`branch` 只能
|
|||
```markdown
|
||||
# PR #<number> 集成摘要
|
||||
**结论:** <span style="color:#B54708"><strong>需补验证</strong></span> **[action_required]**
|
||||
**门禁:** 合并态通过 | 构建通过 | 测试未执行 | 安全未验证 | 冲突低
|
||||
**贡献价值:** <span style="color:#067647"><strong>值得合入</strong></span> **[passed]**:解决高频维护问题,现有命令没有等价能力
|
||||
**门禁:** 价值通过 | 合并态通过 | 构建通过 | 测试未执行 | 安全未验证 | 冲突低
|
||||
|
||||
## 先做这 2 件事
|
||||
1. **[IN-001][high] 验证** `go test ./...`(责任:作者/维护者确认命令)。
|
||||
2. **[IN-002][high] 复查** `internal/auth/` 的权限边界(责任:reviewer)。
|
||||
```
|
||||
|
||||
只有六项门禁全部有充分证据且无 `blocking/high` 未解决项,才可使用 `merge`。大型 PR 先做文件/目录重叠和安全热点筛选,只有高风险候选才进入独立 worktree 的完整合并验证,避免批量扫描浪费维护者时间。
|
||||
只有贡献价值和六项技术门禁都有充分证据且无 `blocking/high` 未解决项,才可使用 `merge`。大型 PR 先做价值证据、文件/目录重叠和安全热点筛选,低价值或高度重复候选先交维护者判断,高风险候选再进入独立 worktree 的完整合并验证,避免批量扫描浪费维护者时间。
|
||||
|
||||
## 贡献价值门禁
|
||||
|
||||
贡献价值是正式门禁,不依赖其他未安装或未合并的 Skill。先核对仓库事实,再从以下七个方面形成依据:
|
||||
|
||||
1. **需求真实性**:是否有 Issue、用户反馈、现有命令缺口、重复人工步骤、错误记录或文档限制等直接证据。
|
||||
2. **社区适配度**:是否符合仓库定位、维护方向、现有架构和公开协作规范,而不是仅对作者私有场景有用。
|
||||
3. **功能增量**:相对默认分支、已合并实现和 open PR,具体增加、修复或简化了什么;不得把代码量当成功能价值。
|
||||
4. **使用频率与受益面**:是否覆盖常用流程,影响普通用户、维护者、自动化调用方还是极少数边缘场景。
|
||||
5. **实现完整性**:代码、失败路径、测试、帮助、文档和兼容处理是否足以交付,而非只有演示路径。
|
||||
6. **维护成本**:新增 API、依赖、配置、平台分支、长期兼容和支持成本是否与收益匹配。
|
||||
7. **风险收益比**:安全、兼容、性能和回归风险是否可控,是否存在更小且同样有效的实现。
|
||||
|
||||
每个维度标记 `strong`、`moderate`、`weak` 或 `unknown`,并至少引用一个证据 ID。详细价值结论必须回答:解决了什么已证实的问题、比仓库现状多了什么、谁会受益、代价是什么、为什么值得或不值得现在合入。
|
||||
|
||||
价值门禁使用:
|
||||
|
||||
- `passed`:需求和增量有直接证据,适配社区,交付完整度与维护成本合理。
|
||||
- `partial`:价值方向成立,但重复关系、受益范围、完整性或维护代价仍需确认。
|
||||
- `failed`:有充分证据表明没有有效增量、与仓库定位冲突,或维护风险明显高于收益。
|
||||
- `not_run`:仓库现状、需求来源或相关实现无法获取,不能判断。
|
||||
|
||||
价值为 `partial` 或 `not_run` 时,机器决策使用协议内的 `observe`,人读结论显示“需要维护者判断”;价值为 `failed` 时不得建议进入 merge queue。禁止仅凭 star、作者历史、PR 描述措辞或变更行数给分。
|
||||
|
||||
## 集成验证的刷新与停机规则
|
||||
|
||||
集成报告的幂等键必须包含 PR head SHA。验证开始后若远端 head SHA 变化,立即停止剩余门禁并标记 `stale`,不要把旧 commit 的构建结果套到新代码上。每项门禁都登记实际检出 SHA、命令、退出码和时间;缺少这些信息只能是 `not_run` 或 `partial`。
|
||||
|
||||
门禁决策按以下顺序收敛:先确认 base/head 和 merge-base,再确认冲突与文件影响面,然后执行仓库规定的构建/测试,最后合并 `CR-`、`CG-`、`TP-` 的未解决发现。`ci_summary.match_mode=none/unavailable` 时 CI 门禁不通过;`unmatched` 构建不能计入失败,但必须进入限制说明。安全、构建、测试或契约任一关键门禁为 `failed`,结论不得为 `merge`。
|
||||
门禁决策按以下顺序收敛:先确认需求和仓库现状,形成贡献价值依据;再确认 base/head、merge-base、冲突和文件影响面;然后执行仓库规定的构建/测试,最后合并已有 `CR-`、`CG-`、`TP-` 发现。`ci_summary.match_mode=none/unavailable` 时 CI 门禁不通过;`unmatched` 构建不能计入失败,但必须进入限制说明。价值、安全、构建、测试或契约任一关键门禁为 `failed`,结论不得为 `merge`。
|
||||
|
||||
集成器可以生成 merge queue 顺序和合并后动作,但不得自动 merge。只有维护者明确授权且所有门禁仍针对同一个 head SHA 时,才可以生成可执行的合并命令草稿。
|
||||
|
||||
输出必须携带统一协议的 `run`、`evidence`、`limitations` 和 `next_run`;维护者首先看六项门禁和最多五项动作,完整命令、merge-base 和测试日志放入附录。
|
||||
输出必须携带统一协议的 `run`、`evidence`、`limitations` 和 `next_run`;维护者首先看价值结论、六项技术门禁和最多五项动作,完整价值矩阵、命令、merge-base 和测试日志放入后文或附录。
|
||||
|
||||
## 职责边界与组合协同
|
||||
|
||||
独立运行时,本 Skill 只判断一个 PR 是否具备进入合并队列的条件;它不重新做完整代码审查、不判断 PR 之间的替代关系,也不按 SLA 排维护者任务。组合运行时读取 `CR-xxx`、`CG-xxx` 和 `TP-xxx` 结果,使用 `IN-xxx` 记录集成阻断和门禁,不改写专项发现。安全专项未运行时,安全门禁必须保持 `not_run`,不能因构建通过而推断安全通过。
|
||||
独立运行时,本 Skill 判断贡献价值并验证 PR 是否具备进入合并队列的条件。为确认功能增量,可以识别明显重复和已存在实现,但不生成完整替代关系图谱;它也不重新做完整代码审查或按 SLA 排维护者任务。组合运行时可读取 `CR-xxx`、`CG-xxx` 和 `TP-xxx` 结果,使用 `IN-xxx` 记录价值和集成门禁,不改写专项发现。专项结果不存在时必须独立采集价值证据;安全未验证时保持 `not_run`,不能因构建通过而推断安全通过。
|
||||
|
||||
## Windows 前置
|
||||
|
||||
|
|
@ -126,11 +151,13 @@ go run . pr --help
|
|||
- `ready_to_merge`:合并态干净,官方构建/测试通过,冲突和发布风险可接受。
|
||||
- `ready_after_rebase`:主要阻塞是基线已漂移,rebase 或重新合并后大概率可继续。
|
||||
- `ready_after_followups`:代码本身接近可合并,但还缺文档、帮助文本、测试、changelog 或发布动作。
|
||||
- `observe`:技术上可能可集成,但贡献价值、重复程度、受益范围或维护成本缺少足够证据,需要维护者决策。
|
||||
- `not_integration_ready`:当前无法安全并入主线,存在冲突、失败验证、较高回归风险或明显的集成阻塞。
|
||||
|
||||
同时给出以下评级:
|
||||
|
||||
- `merge_readiness`: `high` / `medium` / `low`
|
||||
- `contribution_value`: `passed` / `partial` / `failed` / `not_run`
|
||||
- `integration_risk`: `low` / `medium` / `high`
|
||||
- `conflict_risk`: `low` / `medium` / `high`
|
||||
- `release_impact`: `none` / `patch` / `minor` / `major`
|
||||
|
|
@ -154,11 +181,27 @@ gitlink-cli ci +builds --owner <owner> --repo <repo> --format json
|
|||
至少提取:
|
||||
|
||||
- base 分支、head 分支、head 来源仓库
|
||||
- PR 声明解决的问题、关联 Issue、用户反馈和使用场景
|
||||
- 变更文件、核心目录、是否涉及 CLI 命令入口、帮助文本、文档、测试
|
||||
- 当前 review 结论、是否已有 maintainer 明确阻塞项
|
||||
- 仓库默认分支、语言、CI 是否开启、项目推荐的验证命令
|
||||
|
||||
### Step 2: 准备独立的集成验证环境
|
||||
### Step 2: 建立贡献价值证据
|
||||
|
||||
先检查默认分支、文档、命令帮助、相关 Issue、已合并实现和 open PR,建立“当前仓库已经具有什么、仍缺什么”的基线。然后将 PR 的每项声明映射到具体 Diff、测试和文档,输出七维价值矩阵。
|
||||
|
||||
至少形成以下证据:
|
||||
|
||||
- `E-IN-VALUE-01`:需求来源或仓库缺口,例如关联 Issue、可复现限制、重复人工步骤或缺失命令。
|
||||
- `E-IN-VALUE-02`:相对默认分支的实际功能增量及对应文件、命令或行为。
|
||||
- `E-IN-VALUE-03`:与已合并实现及 open PR 的重复、互补或差异证据。
|
||||
- `E-IN-VALUE-04`:测试、帮助、文档和失败路径体现的交付完整性。
|
||||
- `E-IN-VALUE-05`:新增依赖、API、配置、兼容层和长期维护成本。
|
||||
- `E-IN-VALUE-06`:受益对象、使用频率依据和风险收益判断。
|
||||
|
||||
无法访问 Issue、历史实现或真实使用证据时,将对应维度标记 `unknown`,不得用 PR 描述补齐。发现疑似重复时可以影响价值门禁,但只有证据充分时才能写“无有效增量”;复杂替代关系应记录为需要维护者进一步比较。
|
||||
|
||||
### Step 3: 准备独立的集成验证环境
|
||||
|
||||
集成验证必须隔离执行。优先顺序如下:
|
||||
|
||||
|
|
@ -168,7 +211,7 @@ gitlink-cli ci +builds --owner <owner> --repo <repo> --format json
|
|||
|
||||
禁止直接在用户当前脏工作树里 `merge` 或 `rebase`。如果仓库里已经有未提交改动,只把它当信息源,不把它当验证环境。
|
||||
|
||||
### Step 3: 做合并态验证
|
||||
### Step 4: 做合并态验证
|
||||
|
||||
目标不是只看 PR 自己能不能编译,而是回答“把它并到最新主线后还能不能工作”。
|
||||
|
||||
|
|
@ -197,7 +240,7 @@ git merge --no-ff --no-commit FETCH_HEAD
|
|||
|
||||
验证命令必须优先使用项目文档、CI 配置、`Makefile` 或仓库惯例,不要发明一套项目从未使用过的检查方式。
|
||||
|
||||
### Step 4: 扫描与其他 open PR 的冲突风险
|
||||
### Step 5: 扫描与其他 open PR 的冲突风险
|
||||
|
||||
集成就绪度不是单 PR 视角,还要考虑队列里的其他候选项。
|
||||
|
||||
|
|
@ -222,7 +265,7 @@ gitlink-cli pr +list --owner <owner> --repo <repo> --state open --page 1 --limit
|
|||
|
||||
如果发现明显的先后依赖,给出建议合并顺序。
|
||||
|
||||
### Step 5: 输出集成影响矩阵
|
||||
### Step 6: 输出集成影响矩阵
|
||||
|
||||
不要只写“测试通过”。要明确主线在什么面上会被改变。
|
||||
|
||||
|
|
@ -237,7 +280,7 @@ gitlink-cli pr +list --owner <owner> --repo <repo> --state open --page 1 --limit
|
|||
|
||||
如果代码改了,但帮助文本、README、示例或测试没有同步,直接记为集成跟进项,而不是轻描淡写地放过。
|
||||
|
||||
### Step 6: 给出发布与回移建议
|
||||
### Step 7: 给出发布与回移建议
|
||||
|
||||
把改动归入以下类型之一:
|
||||
|
||||
|
|
@ -253,7 +296,7 @@ gitlink-cli pr +list --owner <owner> --repo <repo> --state open --page 1 --limit
|
|||
- 是否需要迁移说明或兼容性提示
|
||||
- 是否适合回移到维护分支
|
||||
|
||||
### Step 7: 形成合并后动作清单
|
||||
### Step 8: 形成合并后动作清单
|
||||
|
||||
如果 PR 代码已经接近可合并,但还差最后几步,明确写成动作清单:
|
||||
|
||||
|
|
@ -263,7 +306,7 @@ gitlink-cli pr +list --owner <owner> --repo <repo> --state open --page 1 --limit
|
|||
- 调整 milestone / 看板状态
|
||||
- 合并后立即跟进的 issue 或回归验证
|
||||
|
||||
### Step 8: 可选回写
|
||||
### Step 9: 可选回写
|
||||
|
||||
只有用户明确要求时,才把结论回写到远端。回写前先生成本地 Markdown 报告,并优先 `dry-run`。
|
||||
|
||||
|
|
@ -280,38 +323,56 @@ gitlink-cli pr +list --owner <owner> --repo <repo> --state open --page 1 --limit
|
|||
<!-- gitlink-pr-integrator:report v1 -->
|
||||
## PR #<id> 集成就绪报告
|
||||
|
||||
**结论:** ready_after_followups
|
||||
**结论:** <span style="color:#B54708"><strong>需要维护者判断</strong></span> **[observe]**
|
||||
**贡献价值:** <span style="color:#B54708"><strong>证据不完整</strong></span> **[partial]**
|
||||
**门禁:** 价值 `partial` | 合并态 `passed` | 构建 `passed` | 测试 `passed` | 契约 `passed` | 安全 `passed` | 冲突 `low`
|
||||
**merge_readiness:** medium
|
||||
**integration_risk:** medium
|
||||
**conflict_risk:** high
|
||||
**release_impact:** minor
|
||||
|
||||
### 1. 合并态验证
|
||||
### 先处理
|
||||
1. **[IN-001][high] 确认** 是否已有等价批量能力;责任:维护者;证据:`E-IN-VALUE-03`。
|
||||
|
||||
### 1. 贡献价值依据
|
||||
| 维度 | 评级 | 依据 | 证据 |
|
||||
|------|------|------|------|
|
||||
| 需求真实性 | strong | 关联 Issue 描述了可复现的高频人工步骤 | E-IN-VALUE-01 |
|
||||
| 社区适配度 | strong | 能力落在现有命令体系和维护方向内 | E-IN-VALUE-01 |
|
||||
| 功能增量 | unknown | 尚未完成已合并 PR 与 open PR 的等价能力核对 | E-IN-VALUE-03 |
|
||||
| 使用频率与受益面 | moderate | 维护者和脚本调用方可复用,但缺少使用数据 | E-IN-VALUE-06 |
|
||||
| 实现完整性 | strong | 代码、测试、help 和失败路径均有对应变更 | E-IN-VALUE-04 |
|
||||
| 维护成本 | moderate | 新增一个 API 面,需要长期保持兼容 | E-IN-VALUE-05 |
|
||||
| 风险收益比 | moderate | 收益明确,但重复程度确认前不能建议合入 | E-IN-VALUE-03 |
|
||||
|
||||
**价值结论依据:** <说明解决的问题、仓库当前缺口、实际增量、受益对象、维护代价和当前为何值得或不值得合入>
|
||||
|
||||
### 2. 合并态验证
|
||||
- 基线:`<base_branch>`
|
||||
- 结果:可合并 / 需 rebase / 存在冲突
|
||||
- 构建:通过 / 失败 / 未执行
|
||||
- 测试:通过 / 失败 / 未执行
|
||||
- 备注:<只在合并态暴露的问题>
|
||||
|
||||
### 2. 与 open PR 的冲突分析
|
||||
### 3. 与 open PR 的冲突分析
|
||||
| PR | 风险 | 原因 | 建议顺序 |
|
||||
|----|------|------|----------|
|
||||
| #123 | high | 同时修改 `shortcuts/pr/pr.go` | 先合并对方 |
|
||||
|
||||
### 3. 集成影响矩阵
|
||||
### 4. 集成影响矩阵
|
||||
| 面向 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| CLI 行为 | changed | 新增 `...` |
|
||||
| Help / docs | follow-up needed | 命令帮助已更新,README 未同步 |
|
||||
| Tests | changed | 新增单测,但缺少回归场景 |
|
||||
|
||||
### 4. 发布建议
|
||||
### 5. 发布建议
|
||||
- 类型:feature
|
||||
- 版本影响:minor
|
||||
- 是否需要 release notes:是
|
||||
- 是否建议回移:否
|
||||
|
||||
### 5. 合并后动作
|
||||
### 6. 合并后动作
|
||||
1. <动作 1>
|
||||
2. <动作 2>
|
||||
3. <动作 3>
|
||||
|
|
@ -323,11 +384,11 @@ gitlink-cli pr +list --owner <owner> --repo <repo> --state open --page 1 --limit
|
|||
|
||||
1. 列出 open PR。
|
||||
2. 过滤掉已经 merged、closed 或已经明确被维护者拒绝的项。
|
||||
3. 按最近活动时间、冲突密度和合并态风险排序。
|
||||
4. 对前 N 条候选 PR 逐条生成集成就绪报告。
|
||||
3. 先按需求证据、功能增量、重复风险和受益面形成轻量价值门禁。
|
||||
4. 再按价值、最近活动时间、冲突密度和合并态风险排序,对前 N 条候选 PR 生成集成就绪报告。
|
||||
5. 再输出一份队列总览,包含建议合并顺序和需要先处理的冲突热点文件。
|
||||
|
||||
批量模式下,仍然不要默认对全部 PR 执行高成本本地构建。先做元信息和冲突雷达,只有用户指定或风险较高时再进入本地合并验证。
|
||||
批量模式下,不默认对全部 PR 执行高成本本地构建。先做价值证据、元信息和冲突雷达;价值为 `failed` 的项不进入构建队列,`partial/not_run` 的项进入维护者确认队列,价值通过且风险较高的候选再进入本地合并验证。
|
||||
|
||||
## 示例请求
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
interface:
|
||||
display_name: "PR 集成检查"
|
||||
short_description: "评估 PR 是否能安全并入主线,分析冲突、发布影响和合并后动作。"
|
||||
default_prompt: "使用 $gitlink-pr-integrator 检查指定 GitLink PR。按 Skill 默认契约只读验证六项集成门禁,并生成关键结论前置的单一 Markdown 报告。"
|
||||
display_name: "PR 价值与集成检查"
|
||||
short_description: "以详细证据评估贡献价值,并验证 PR 能否安全并入主线。"
|
||||
default_prompt: "使用 $gitlink-pr-integrator 评估指定 GitLink PR 的贡献价值和集成条件,生成结论前置、依据完整且不修改远端的 Markdown 报告。"
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ gitlink-cli ci +builds --owner Gitlink --repo gitlink-cli --format json
|
|||
```markdown
|
||||
# PR #123 集成摘要
|
||||
**结论:** <span style="color:#067647"><strong>可进入合并队列</strong></span> **[merge]**
|
||||
**门禁:** 合并态通过 | 构建通过 | 测试通过 | 契约通过 | 安全通过 | 冲突低
|
||||
**贡献价值:** <span style="color:#067647"><strong>值得合入</strong></span> **[passed]**:解决高频批量操作缺口,默认分支无等价能力
|
||||
**门禁:** 价值通过 | 合并态通过 | 构建通过 | 测试通过 | 契约通过 | 安全通过 | 冲突低
|
||||
|
||||
## 需要记录的动作
|
||||
1. **[IN-001][low] 更新** 发布说明(责任:维护者)。
|
||||
```
|
||||
|
||||
如果构建、测试或安全门禁是 `not_run`,结论必须降级为 `action_required` 或 `blocked`。高风险 PR 需要在独立 worktree 中验证,且报告记录真实 base、head 和命令。
|
||||
详细部分必须列出需求来源、仓库现状、功能增量、受益对象、完整性、维护成本和风险收益证据。如果价值为 `partial`/`not_run`,机器决策降级为 `observe` 并显示“需要维护者判断”;如果构建、测试或安全门禁是 `not_run`,结论必须降级为 `action_required` 或 `blocked`。高风险 PR 需要在独立 worktree 中验证,且报告记录真实 base、head 和命令。
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ description: "GitLink open PR 队列关系专项分析:识别依赖、继承
|
|||
|
||||
## 已合并功能的增量证据
|
||||
|
||||
前置 PR #426 提供统一的 PR 文件、提交和 Review 证据;前置 PR #427 提供队列变化。拓扑分析可以先取一次队列快照,再为候选关系补取证据,减少对整条 open PR 队列的重复请求:
|
||||
配套基础能力 PR #429 提供统一的 PR 文件、提交和 Review 证据,PR #430 提供队列变化。它们未合并或命令不可用时,必须回退到现有只读接口并标记限制;可用时先取一次队列快照,再为候选关系补取证据:
|
||||
|
||||
```bash
|
||||
gitlink-cli workflow +review-queue --owner <owner> --repo <repo> --previous queue-previous.json --format json
|
||||
|
|
@ -115,7 +115,7 @@ gitlink-cli workflow +review-context --owner <owner> --repo <repo> --number <num
|
|||
|
||||
下面这些不属于本 skill 的职责:
|
||||
|
||||
- 单条 PR 的贡献价值、可行性、执行验证:交给 `gitlink-pr-assessor`
|
||||
- 单条 PR 的贡献价值和实现审查:交给 `gitlink-code-review`;集成价值与执行验证交给 `gitlink-pr-integrator`
|
||||
- 单条 PR 是否已经具备并入主线的条件:交给 `gitlink-pr-integrator`
|
||||
- 维护者值班、SLA、review 负载和停滞治理:交给 `gitlink-maintainer-radar`
|
||||
|
||||
|
|
@ -273,7 +273,7 @@ gitlink-cli repo +info --owner <owner> --repo <repo> --format json
|
|||
4. 明显替代关系
|
||||
5. 冲突热点文件/模块
|
||||
6. 建议处理顺序
|
||||
7. 需要进一步切换到 `gitlink-pr-assessor` 或 `gitlink-pr-integrator` 深挖的对象
|
||||
7. 需要进一步切换到 `gitlink-code-review` 或 `gitlink-pr-integrator` 深挖的对象
|
||||
|
||||
## 报告模板
|
||||
|
||||
|
|
|
|||
|
|
@ -85,5 +85,5 @@ open PR 总数:156
|
|||
### 5. 需要深挖的对象
|
||||
|
||||
- 用 `gitlink-pr-integrator`:#281、#282、#272、#274、#283/#284/#285,重点做合并态和冲突验证。
|
||||
- 用 `gitlink-pr-assessor`:#276、#259,重点判断是否应拆分、暂缓或拒绝。
|
||||
- 用 `gitlink-code-review`:#276、#259,重点判断是否应拆分、暂缓或拒绝。
|
||||
- 人工重点比较:#272 vs #76、#274 vs #72/#263、#262 vs #238/#70。
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ metadata:
|
|||
cliHelp: "gitlink-cli --help"
|
||||
---
|
||||
|
||||
五个维护 Skill 的证据复用和职责边界见 [`references/maintenance-evidence-workflow.md`](references/maintenance-evidence-workflow.md)。前置 PR #426 提供单 PR 证据包,前置 PR #427 提供队列快照差异;二者只提供只读基础数据,不合并五个 Skill 的职责。
|
||||
五个维护 Skill 的证据复用和职责边界见 [`references/maintenance-evidence-workflow.md`](references/maintenance-evidence-workflow.md)。配套基础能力 PR #429 提供单 PR 证据包,PR #430 提供队列快照差异;二者未合并或命令不可用时必须标记限制并降级,不能假定能力已经存在。
|
||||
|
||||
可直接照着 [`examples/maintenance-evidence-v2.md`](examples/maintenance-evidence-v2.md) 演示一次固定时间点的队列扫描、单 PR CI 证据关联和五个 Skill 的最短交接路径。
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
每次执行先建立运行键,例如:
|
||||
|
||||
```text
|
||||
gitlink-pr-assessor:Gitlink/gitlink-cli:123:abcdef1:executive
|
||||
gitlink-code-review:Gitlink/gitlink-cli:123:abcdef1:executive
|
||||
```
|
||||
|
||||
同一运行键只生成一次报告;只有 PR head、Review、CI 或维护者策略变化时才重新评估。
|
||||
|
|
@ -42,7 +42,7 @@ gitlink-cli workflow +review-context `
|
|||
|
||||
| Skill | 首先读取 | 产生的动作 |
|
||||
|---|---|---|
|
||||
| `gitlink-pr-assessor` | `files`、`reviews`、`ci_summary`、`notes` | `CR-` 代码、测试和安全发现 |
|
||||
| `gitlink-code-review` | `files`、`reviews`、`ci_summary`、`notes` | `CR-` 代码、Review 履约、测试和安全发现 |
|
||||
| `gitlink-pr-integrator` | `ci_summary`、本地验证、`changes` | `IN-` 集成门禁 |
|
||||
| `gitlink-pr-topology` | `changes`、候选 PR 的文件和分支 | `TP-` 依赖与重叠关系 |
|
||||
| `gitlink-maintainer-radar` | `waiting_hours`、`stale`、`waiting_on`、reviewer 数量 | `MR-` 今日待办 |
|
||||
|
|
@ -50,11 +50,11 @@ gitlink-cli workflow +review-context `
|
|||
|
||||
组合报告只在首屏展示最多 5 个动作,原始响应、完整 diff、未匹配构建和未执行项放入附录。任何 Skill 单独运行时,都必须把未纳入的其他维度标为“未检查”。
|
||||
|
||||
## 5. 自动 Review 的安全边界
|
||||
## 4. 自动 Review 的安全边界
|
||||
|
||||
只有报告证据完整、PR 仍为 open、当前运行键没有已发布报告、没有 blocking/高风险安全发现,并且评论只包含事实和建议时,才可以由外层 runner 自动发布建议性 Review。以下情况只生成草稿:CI 未关联当前 head、工作树 SHA 不一致、关键测试未执行、数据过期或责任方不明确。五个 Skill 都不能自动合并、关闭、拒绝或修改权限。
|
||||
|
||||
## 4. 最小自检
|
||||
## 5. 最小自检
|
||||
|
||||
```powershell
|
||||
$json = Get-Content -Raw -Encoding utf8 .\pr-123-context.json | ConvertFrom-Json
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"verification": "partial",
|
||||
"scope": {"owner": "Gitlink", "repo": "gitlink-cli", "items": 1},
|
||||
"run": {
|
||||
"run_id": "gitlink-pr-assessor:Gitlink/gitlink-cli:42:abcdef1:executive",
|
||||
"run_id": "gitlink-code-review:Gitlink/gitlink-cli:42:abcdef1:executive",
|
||||
"trigger": "schedule",
|
||||
"started_at": "2026-07-20T12:00:00Z",
|
||||
"as_of": "2026-07-20T12:01:10Z",
|
||||
|
|
@ -33,7 +33,22 @@
|
|||
"evidence": ["shortcuts/example/example_test.go:42"]
|
||||
}
|
||||
],
|
||||
"findings": [],
|
||||
"findings": [
|
||||
{
|
||||
"id": "CR-001",
|
||||
"severity": "high",
|
||||
"status": "open",
|
||||
"summary": "失败路径缺少回归测试",
|
||||
"evidence": ["E-CR-001"]
|
||||
},
|
||||
{
|
||||
"id": "CR-002",
|
||||
"severity": "medium",
|
||||
"status": "open",
|
||||
"summary": "平台 CI 证据尚未关联当前 head",
|
||||
"evidence": ["E-CR-001"]
|
||||
}
|
||||
],
|
||||
"limitations": ["平台 CI 结果未提供"],
|
||||
"next_run": {"reason": "等待作者提交新 commit", "after_minutes": 60}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$validator = Join-Path $PSScriptRoot 'validate-maintenance-report.ps1'
|
||||
$fixture = Join-Path $PSScriptRoot 'maintenance-report.fixture.json'
|
||||
$tempRoot = Join-Path ([IO.Path]::GetTempPath()) ("gitlink-maintenance-validator-" + [Guid]::NewGuid().ToString('N'))
|
||||
New-Item -ItemType Directory -Path $tempRoot | Out-Null
|
||||
|
||||
function Write-Case {
|
||||
param([string]$Name, [object]$Report)
|
||||
$path = Join-Path $tempRoot "$Name.json"
|
||||
[IO.File]::WriteAllText($path, ($Report | ConvertTo-Json -Depth 100), (New-Object Text.UTF8Encoding($false)))
|
||||
return $path
|
||||
}
|
||||
|
||||
function Read-Fixture {
|
||||
return (Get-Content -Raw -Encoding utf8 $fixture | ConvertFrom-Json)
|
||||
}
|
||||
|
||||
function Assert-Rejected {
|
||||
param([string]$Name, [scriptblock]$Mutate)
|
||||
$report = Read-Fixture
|
||||
& $Mutate $report
|
||||
$path = Write-Case $Name $report
|
||||
$rejected = $false
|
||||
try { & $validator -Path $path | Out-Null } catch { $rejected = $true }
|
||||
if (-not $rejected) { throw "validator accepted invalid case: $Name" }
|
||||
}
|
||||
|
||||
try {
|
||||
& $validator -Path $fixture | Out-Null
|
||||
|
||||
Assert-Rejected 'invalid-decision' { param($r) $r.decision = 'ship_it' }
|
||||
Assert-Rejected 'negative-count' { param($r) $r.counts.high = -1 }
|
||||
Assert-Rejected 'count-mismatch' { param($r) $r.counts.low = 1 }
|
||||
Assert-Rejected 'count-distribution-mismatch' { param($r) $r.findings[1].severity = 'high' }
|
||||
Assert-Rejected 'severity-mismatch' { param($r) $r.severity = 'low' }
|
||||
Assert-Rejected 'unsafe-merge' {
|
||||
param($r)
|
||||
$r.decision = 'merge'
|
||||
$r.security_gate = 'failed'
|
||||
}
|
||||
Assert-Rejected 'duplicate-evidence' {
|
||||
param($r)
|
||||
$r.evidence = @($r.evidence[0], $r.evidence[0])
|
||||
}
|
||||
Assert-Rejected 'invalid-evidence-kind' { param($r) $r.evidence[0].kind = 'guess' }
|
||||
Assert-Rejected 'unknown-evidence-reference' { param($r) $r.findings[0].evidence = @('E-MISSING') }
|
||||
Assert-Rejected 'non-utc-run' { param($r) $r.run.as_of = '2026-07-20T20:01:10+08:00' }
|
||||
|
||||
Write-Output 'maintenance report validator tests passed: 1 valid, 10 invalid cases'
|
||||
} finally {
|
||||
if (Test-Path -LiteralPath $tempRoot) {
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force
|
||||
}
|
||||
}
|
||||
|
|
@ -4,53 +4,194 @@ param(
|
|||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$raw = Get-Content -Raw -Encoding utf8 $Path
|
||||
$report = $raw | ConvertFrom-Json
|
||||
|
||||
$required = @('schema_version', 'mode', 'decision', 'severity', 'counts', 'security_gate', 'verification', 'top_actions', 'findings', 'limitations')
|
||||
foreach ($name in $required) {
|
||||
if ($null -eq $report.PSObject.Properties[$name]) {
|
||||
throw "missing report field: $name"
|
||||
function Has-Property {
|
||||
param([object]$Object, [string]$Name)
|
||||
return $null -ne $Object -and ($Object.PSObject.Properties.Name -contains $Name)
|
||||
}
|
||||
|
||||
function Require-Property {
|
||||
param([object]$Object, [string]$Name, [string]$Context)
|
||||
if (-not (Has-Property $Object $Name)) { throw "$Context missing field: $Name" }
|
||||
return $Object.$Name
|
||||
}
|
||||
|
||||
function Require-NonEmptyString {
|
||||
param([object]$Object, [string]$Name, [string]$Context)
|
||||
$value = Require-Property $Object $Name $Context
|
||||
if ($value -isnot [string] -or [string]::IsNullOrWhiteSpace($value)) { throw "$Context.$Name must be a non-empty string" }
|
||||
return $value
|
||||
}
|
||||
|
||||
function Require-Array {
|
||||
param([object]$Object, [string]$Name, [string]$Context)
|
||||
$value = Require-Property $Object $Name $Context
|
||||
return @($value)
|
||||
}
|
||||
|
||||
function Require-NonNegativeInteger {
|
||||
param([object]$Object, [string]$Name, [string]$Context)
|
||||
$value = Require-Property $Object $Name $Context
|
||||
if ($value -is [bool] -or $value -isnot [ValueType]) { throw "$Context.$Name must be a non-negative integer" }
|
||||
$number = [double]$value
|
||||
if ($number -lt 0 -or [Math]::Truncate($number) -ne $number) { throw "$Context.$Name must be a non-negative integer" }
|
||||
return [int64]$number
|
||||
}
|
||||
|
||||
function Require-Enum {
|
||||
param([object]$Object, [string]$Name, [string[]]$Allowed, [string]$Context)
|
||||
$value = Require-NonEmptyString $Object $Name $Context
|
||||
if ($value -notin $Allowed) { throw "invalid $Context.$Name`: $value" }
|
||||
return $value
|
||||
}
|
||||
|
||||
function Require-Rfc3339Utc {
|
||||
param([string]$Value, [string]$Context)
|
||||
try { $parsed = [DateTimeOffset]::Parse($Value) } catch { throw "$Context must be RFC3339" }
|
||||
if ($parsed.Offset -ne [TimeSpan]::Zero) { throw "$Context must use UTC" }
|
||||
}
|
||||
|
||||
$resolvedPath = (Resolve-Path -LiteralPath $Path).Path
|
||||
$raw = [IO.File]::ReadAllText($resolvedPath, (New-Object Text.UTF8Encoding($false, $true)))
|
||||
if ($raw.Contains([char]0xfffd) -or $raw.Contains([char]0)) { throw 'JSON contains encoding control characters' }
|
||||
Add-Type -AssemblyName System.Web.Extensions
|
||||
try { $shape = (New-Object Web.Script.Serialization.JavaScriptSerializer).DeserializeObject($raw) } catch { throw "invalid JSON report: $($_.Exception.Message)" }
|
||||
if ($shape -isnot [Collections.IDictionary]) { throw 'report root must be an object' }
|
||||
foreach ($name in @('top_actions', 'findings', 'limitations')) {
|
||||
if (-not $shape.ContainsKey($name) -or $shape[$name] -isnot [array]) { throw "report.$name must be an array" }
|
||||
}
|
||||
if (-not $shape.ContainsKey('counts') -or $shape['counts'] -isnot [Collections.IDictionary]) { throw 'report.counts must be an object' }
|
||||
if (-not $shape.ContainsKey('scope') -or $shape['scope'] -isnot [Collections.IDictionary]) { throw 'report.scope must be an object' }
|
||||
if ($shape.ContainsKey('evidence') -and $shape['evidence'] -isnot [array]) { throw 'report.evidence must be an array' }
|
||||
foreach ($item in @($shape['top_actions'])) {
|
||||
if ($item -isnot [Collections.IDictionary] -or -not $item.ContainsKey('evidence') -or $item['evidence'] -isnot [array]) { throw 'top_action.evidence must be an array' }
|
||||
}
|
||||
foreach ($item in @($shape['findings'])) {
|
||||
if ($item -isnot [Collections.IDictionary] -or -not $item.ContainsKey('evidence') -or $item['evidence'] -isnot [array]) { throw 'finding.evidence must be an array' }
|
||||
}
|
||||
try { $report = $raw | ConvertFrom-Json } catch { throw "invalid JSON report: $($_.Exception.Message)" }
|
||||
if ($null -eq $report -or $report -is [array] -or $report -isnot [psobject]) { throw 'report root must be an object' }
|
||||
|
||||
$schemaVersion = Require-NonEmptyString $report 'schema_version' 'report'
|
||||
if ($schemaVersion -ne '1.0') { throw "unsupported schema_version: $schemaVersion" }
|
||||
$mode = Require-Enum $report 'mode' @('executive', 'standard', 'full') 'report'
|
||||
$decision = Require-Enum $report 'decision' @('merge', 'action_required', 'reorder', 'observe', 'blocked') 'report'
|
||||
$severity = Require-Enum $report 'severity' @('blocking', 'high', 'medium', 'low') 'report'
|
||||
$securityGate = Require-Enum $report 'security_gate' @('passed', 'failed', 'partial', 'not_run', 'not_applicable') 'report'
|
||||
$verification = Require-Enum $report 'verification' @('complete', 'passed', 'failed', 'partial', 'not_run', 'stale', 'not_applicable') 'report'
|
||||
|
||||
$counts = Require-Property $report 'counts' 'report'
|
||||
if ($null -eq $counts -or $counts -is [array] -or $counts -isnot [psobject]) { throw 'report.counts must be an object' }
|
||||
$countValues = [ordered]@{}
|
||||
foreach ($name in @('blocking', 'high', 'medium', 'low')) {
|
||||
$countValues[$name] = Require-NonNegativeInteger $counts $name 'report.counts'
|
||||
}
|
||||
|
||||
$scope = Require-Property $report 'scope' 'report'
|
||||
if ($null -eq $scope -or $scope -is [array] -or $scope -isnot [psobject]) { throw 'report.scope must be an object' }
|
||||
Require-NonEmptyString $scope 'owner' 'report.scope' | Out-Null
|
||||
Require-NonEmptyString $scope 'repo' 'report.scope' | Out-Null
|
||||
Require-NonNegativeInteger $scope 'items' 'report.scope' | Out-Null
|
||||
|
||||
$topActions = Require-Array $report 'top_actions' 'report'
|
||||
$findings = Require-Array $report 'findings' 'report'
|
||||
$limitations = Require-Array $report 'limitations' 'report'
|
||||
if ($mode -eq 'executive' -and $topActions.Count -gt 5) { throw 'executive report has more than five top actions' }
|
||||
foreach ($item in $limitations) {
|
||||
if ($item -isnot [string] -or [string]::IsNullOrWhiteSpace($item)) { throw 'report.limitations entries must be non-empty strings' }
|
||||
}
|
||||
|
||||
$expectedFindingCount = $countValues.blocking + $countValues.high + $countValues.medium + $countValues.low
|
||||
if ($expectedFindingCount -ne $findings.Count) { throw "report.counts total $expectedFindingCount does not match findings count $($findings.Count)" }
|
||||
$expectedSeverity = if ($countValues.blocking -gt 0) { 'blocking' } elseif ($countValues.high -gt 0) { 'high' } elseif ($countValues.medium -gt 0) { 'medium' } else { 'low' }
|
||||
if ($severity -ne $expectedSeverity) { throw "report.severity $severity does not match highest finding severity $expectedSeverity" }
|
||||
|
||||
$findingIds = @{}
|
||||
$actualFindingCounts = @{ blocking = 0; high = 0; medium = 0; low = 0 }
|
||||
foreach ($finding in $findings) {
|
||||
$id = Require-NonEmptyString $finding 'id' 'finding'
|
||||
if ($findingIds.ContainsKey($id)) { throw "duplicate finding id: $id" }
|
||||
$findingIds[$id] = $true
|
||||
$findingSeverity = Require-Enum $finding 'severity' @('blocking', 'high', 'medium', 'low') "finding[$id]"
|
||||
$actualFindingCounts[$findingSeverity]++
|
||||
Require-Enum $finding 'status' @('open', 'resolved', 'accepted', 'candidate', 'stale') "finding[$id]" | Out-Null
|
||||
Require-NonEmptyString $finding 'summary' "finding[$id]" | Out-Null
|
||||
$findingEvidence = Require-Array $finding 'evidence' "finding[$id]"
|
||||
if ($findingEvidence.Count -eq 0) { throw "finding has no evidence: $id" }
|
||||
if ($countValues[$findingSeverity] -le 0) { throw "finding severity $findingSeverity is not represented in report.counts" }
|
||||
}
|
||||
foreach ($name in @('blocking', 'high', 'medium', 'low')) {
|
||||
if ($actualFindingCounts[$name] -ne $countValues[$name]) {
|
||||
throw "report.counts.$name $($countValues[$name]) does not match $($actualFindingCounts[$name]) findings"
|
||||
}
|
||||
}
|
||||
|
||||
if ($report.mode -notin @('executive', 'standard', 'full')) { throw "invalid report mode" }
|
||||
if ($report.decision -notin @('merge', 'action_required', 'reorder', 'observe', 'blocked')) { throw "invalid report decision" }
|
||||
if ($report.top_actions.Count -gt 5) { throw "executive report has more than five top actions" }
|
||||
if ($raw -match "`e\[|<span|</span>") { throw "JSON contains presentation markers" }
|
||||
if ($raw.Contains([char]0xfffd) -or $raw.Contains([char]0)) { throw "JSON contains encoding control characters" }
|
||||
if ($raw -match '(?i)(authorization|bearer)\s+[A-Za-z0-9._-]{20,}') { throw "JSON contains a credential-like value" }
|
||||
|
||||
foreach ($action in @($report.top_actions)) {
|
||||
foreach ($name in @('id', 'owner', 'action', 'evidence')) {
|
||||
if ($null -eq $action.PSObject.Properties[$name]) { throw "top action missing field: $name" }
|
||||
$actionIds = @{}
|
||||
foreach ($action in $topActions) {
|
||||
$id = Require-NonEmptyString $action 'id' 'top_action'
|
||||
if ($actionIds.ContainsKey($id)) { throw "duplicate top action id: $id" }
|
||||
$actionIds[$id] = $true
|
||||
Require-NonEmptyString $action 'owner' "top_action[$id]" | Out-Null
|
||||
Require-NonEmptyString $action 'action' "top_action[$id]" | Out-Null
|
||||
$actionEvidence = Require-Array $action 'evidence' "top_action[$id]"
|
||||
if ($actionEvidence.Count -eq 0) { throw "top action has no evidence: $id" }
|
||||
if (Has-Property $action 'severity') {
|
||||
Require-Enum $action 'severity' @('blocking', 'high', 'medium', 'low') "top_action[$id]" | Out-Null
|
||||
}
|
||||
if ($action.evidence.Count -eq 0) { throw "top action has no evidence: $($action.id)" }
|
||||
}
|
||||
|
||||
if ($null -ne $report.PSObject.Properties['run']) {
|
||||
foreach ($name in @('run_id', 'trigger', 'as_of')) {
|
||||
if ($null -eq $report.run.PSObject.Properties[$name]) { throw "run missing field: $name" }
|
||||
if (Has-Property $report 'run') {
|
||||
$run = $report.run
|
||||
Require-NonEmptyString $run 'run_id' 'report.run' | Out-Null
|
||||
Require-Enum $run 'trigger' @('pull_request_opened', 'pull_request_synchronized', 'review_submitted', 'schedule', 'manual') 'report.run' | Out-Null
|
||||
$asOf = Require-NonEmptyString $run 'as_of' 'report.run'
|
||||
Require-Rfc3339Utc $asOf 'report.run.as_of'
|
||||
if (Has-Property $run 'started_at') {
|
||||
Require-Rfc3339Utc (Require-NonEmptyString $run 'started_at' 'report.run') 'report.run.started_at'
|
||||
}
|
||||
if ($report.run.trigger -notin @('pull_request_opened', 'pull_request_synchronized', 'review_submitted', 'schedule', 'manual')) { throw "invalid run trigger" }
|
||||
try { [DateTimeOffset]::Parse($report.run.as_of) | Out-Null } catch { throw "invalid run as_of" }
|
||||
}
|
||||
|
||||
if ($null -ne $report.PSObject.Properties['evidence']) {
|
||||
if (Has-Property $report 'evidence') {
|
||||
$evidence = Require-Array $report 'evidence' 'report'
|
||||
$evidenceIds = @{}
|
||||
foreach ($item in @($report.evidence)) {
|
||||
foreach ($name in @('id', 'kind', 'status', 'ref')) {
|
||||
if ($null -eq $item.PSObject.Properties[$name]) { throw "evidence missing field: $name" }
|
||||
foreach ($item in $evidence) {
|
||||
$id = Require-NonEmptyString $item 'id' 'evidence'
|
||||
if ($evidenceIds.ContainsKey($id)) { throw "duplicate evidence id: $id" }
|
||||
$evidenceIds[$id] = $true
|
||||
Require-Enum $item 'kind' @('pr_api', 'diff', 'review', 'ci', 'local_checkout', 'test_output', 'contract_test', 'integration_test', 'cli_help', 'queue_snapshot', 'human_policy') "evidence[$id]" | Out-Null
|
||||
Require-Enum $item 'status' @('complete', 'partial', 'failed', 'not_run', 'stale') "evidence[$id]" | Out-Null
|
||||
Require-NonEmptyString $item 'source' "evidence[$id]" | Out-Null
|
||||
Require-NonEmptyString $item 'ref' "evidence[$id]" | Out-Null
|
||||
Require-NonEmptyString $item 'scope' "evidence[$id]" | Out-Null
|
||||
if (Has-Property $item 'observed_at') {
|
||||
Require-Rfc3339Utc (Require-NonEmptyString $item 'observed_at' "evidence[$id]") "evidence[$id].observed_at"
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($finding in $findings) {
|
||||
foreach ($reference in @(Require-Array $finding 'evidence' "finding[$($finding.id)]")) {
|
||||
if ($reference -isnot [string] -or -not $evidenceIds.ContainsKey($reference)) {
|
||||
throw "finding[$($finding.id)] references unknown evidence: $reference"
|
||||
}
|
||||
}
|
||||
if ($item.status -notin @('complete', 'partial', 'failed', 'not_run', 'stale')) { throw "invalid evidence status: $($item.id)" }
|
||||
if ($evidenceIds.ContainsKey($item.id)) { throw "duplicate evidence id: $($item.id)" }
|
||||
$evidenceIds[$item.id] = $true
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -ne $report.PSObject.Properties['next_run']) {
|
||||
if ($null -eq $report.next_run.PSObject.Properties['reason'] -or $null -eq $report.next_run.PSObject.Properties['after_minutes']) { throw "next_run requires reason and after_minutes" }
|
||||
if ([int]$report.next_run.after_minutes -lt 0) { throw "next_run.after_minutes must be non-negative" }
|
||||
if (Has-Property $report 'next_run') {
|
||||
Require-NonEmptyString $report.next_run 'reason' 'report.next_run' | Out-Null
|
||||
Require-NonNegativeInteger $report.next_run 'after_minutes' 'report.next_run' | Out-Null
|
||||
}
|
||||
|
||||
Write-Output "maintenance report contract passed: $Path"
|
||||
if ($decision -eq 'merge') {
|
||||
if ($securityGate -ne 'passed') { throw 'merge decision requires security_gate=passed' }
|
||||
if ($verification -notin @('complete', 'passed')) { throw 'merge decision requires completed verification' }
|
||||
if ($countValues.blocking -gt 0 -or $countValues.high -gt 0) { throw 'merge decision cannot contain blocking or high findings' }
|
||||
}
|
||||
if (($securityGate -eq 'failed' -or $verification -eq 'failed' -or $countValues.blocking -gt 0) -and $decision -eq 'merge') {
|
||||
throw 'failed gate cannot produce merge decision'
|
||||
}
|
||||
|
||||
if ($raw -match "`e\[|<span|</span>") { throw 'JSON contains presentation markers' }
|
||||
if ($raw -match '(?i)(authorization|bearer)\s+[A-Za-z0-9._-]{20,}') { throw 'JSON contains a credential-like value' }
|
||||
|
||||
Write-Output "maintenance report contract passed: $resolvedPath"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## 单个 PR 证据包
|
||||
|
||||
前置 PR #426 合并后,优先使用以下只读命令:
|
||||
配套 PR #429 合并后可优先使用以下只读命令;未合并或命令不可用时回退到现有 PR 只读接口,并把缺失字段标记为 `not_run`/`partial`:
|
||||
|
||||
```bash
|
||||
gitlink-cli workflow +review-context \
|
||||
|
|
@ -21,7 +21,7 @@ gitlink-cli workflow +review-context \
|
|||
|
||||
## 队列变化快照
|
||||
|
||||
前置 PR #427 合并后,先保存 JSON 基线,再在下一轮比较:
|
||||
配套 PR #430 合并后可保存 JSON 基线并在下一轮比较;未合并时只生成当前队列快照,不得虚构 `changes`:
|
||||
|
||||
```bash
|
||||
gitlink-cli workflow +review-queue \
|
||||
|
|
@ -48,7 +48,7 @@ gitlink-cli workflow +review-queue \
|
|||
| `gitlink-maintainer-radar` | Review 和 PR 元数据 | 重点消费新增、风险变化和已解决项 | SLA、Reviewer 负载、责任停滞和今日待办 |
|
||||
| `gitlink-cli-contract-guard` | 文件、帮助、JSON 和错误证据 | 只在涉及 workflow flags/JSON 时消费 | CLI 参数、帮助、输出、错误和编码契约 |
|
||||
|
||||
补充:`gitlink-pr-assessor` 是五个核心 Skill 之前的可选初筛层,负责价值、声明可行性和执行验证,不计入核心五个 Skill 的最终职责矩阵。
|
||||
贡献价值和声明可行性由 `gitlink-code-review` 与 `gitlink-pr-integrator` 独立保留,不依赖未合并的 assessor。
|
||||
|
||||
## 组合运行规则
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ Markdown 和 JSON 的结论必须一致。推荐使用以下字段:
|
|||
"decision": "action_required",
|
||||
"severity": "high",
|
||||
"counts": {"blocking": 1, "high": 2, "medium": 3, "low": 0},
|
||||
"security_gate": "fail",
|
||||
"security_gate": "failed",
|
||||
"verification": "partial",
|
||||
"scope": {"owner": "Gitlink", "repo": "gitlink-cli", "items": 12},
|
||||
"run": {"run_id": "producer:repo:scope:head:executive", "trigger": "schedule", "as_of": "2026-07-20T12:01:10Z"},
|
||||
|
|
@ -62,6 +62,8 @@ Markdown 和 JSON 的结论必须一致。推荐使用以下字段:
|
|||
|
||||
允许的 `decision`:`merge`、`action_required`、`reorder`、`observe`、`blocked`。没有足够证据时必须使用 `observe` 或 `blocked`,不能猜测为通过。
|
||||
|
||||
允许的 `security_gate`:`passed`、`failed`、`partial`、`not_run`、`not_applicable`。允许的 `verification`:`complete`、`passed`、`failed`、`partial`、`not_run`、`stale`、`not_applicable`。`counts` 必须是非负整数,各严重性数量及总和都要与 `findings` 一致;`severity` 必须等于最高发现级别。存在全局 `evidence` 时,每条 finding 的 `evidence` 必须引用真实存在的证据 ID;`top_actions.evidence` 可以引用证据 ID、finding ID 或精确定位符。`merge` 要求安全门禁通过、验证完成,并且不存在 blocking/high 发现。
|
||||
|
||||
## 严重性和稳定编号
|
||||
|
||||
- `blocking`:阻止合并、会泄露凭据、破坏兼容性或无法证明核心行为可用。
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
```json
|
||||
{
|
||||
"run": {
|
||||
"run_id": "gitlink-pr-assessor:Gitlink/gitlink-cli:123:abcdef1:executive",
|
||||
"run_id": "gitlink-code-review:Gitlink/gitlink-cli:123:abcdef1:executive",
|
||||
"trigger": "pull_request_synchronized",
|
||||
"started_at": "2026-07-20T12:00:00Z",
|
||||
"as_of": "2026-07-20T12:01:10Z",
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
}
|
||||
```
|
||||
|
||||
允许的 `kind`:`pr_api`、`diff`、`review`、`ci`、`local_checkout`、`test_output`、`cli_help`、`queue_snapshot`、`human_policy`。允许的 `status`:`complete`、`partial`、`failed`、`not_run`、`stale`。`findings[].evidence` 必须引用台账 ID 或明确的文件/命令证据;没有证据的发现只能是 `candidate`,不能是 blocking。
|
||||
允许的 `kind`:`pr_api`、`diff`、`review`、`ci`、`local_checkout`、`test_output`、`contract_test`、`integration_test`、`cli_help`、`queue_snapshot`、`human_policy`。允许的 `status`:`complete`、`partial`、`failed`、`not_run`、`stale`。`findings[].evidence` 必须引用台账 ID 或明确的文件/命令证据;没有证据的发现只能是 `candidate`,不能是 blocking。
|
||||
|
||||
事实分为 `observed`、`derived` 和 `unknown`:文件/命令/API 直接返回的是 `observed`,规则计算得到的是 `derived`,没有可靠来源的是 `unknown`。`derived` 可以改变排序和建议,但不能单独产生 blocking;`unknown` 必须进入 `limitations`。
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
| `gitlink-pr-integrator` | 合并态、rebase、构建、测试、契约、安全门禁和发布影响 | 不重新进行完整代码审查或维护者值班排序 | 单 PR 证据、其他 Skill 结论、主线和 CI 状态 | `IN-xxx` 集成门禁、决策和合并后动作 |
|
||||
| `gitlink-maintainer-radar` | 首响 SLA、reviewer 负载、责任停滞、等待方和队列变化 | 不判断代码漏洞、CLI 兼容性或 PR 功能优劣 | 队列快照、review 状态、评论时间、分配关系和安全优先级 | `MR-xxx` 维护动作、责任调整和催办建议 |
|
||||
|
||||
`gitlink-pr-assessor` 是五个核心 Skill 之外的前置评估器:它判断 PR 的贡献价值、作者声明可行性和执行验证结果,适合批量筛选未形成维护者结论的 open PR;它不能替代 `gitlink-code-review` 的逐行代码审查,也不能替代 `gitlink-pr-integrator` 的合并门禁。组合运行时可把 assessor 的证据和限制交给核心五个 Skill,但不得把“价值明确”改写成“代码已通过”。
|
||||
核心五个 Skill 不依赖额外前置评估器。`gitlink-code-review` 保留贡献价值、声明可行性和代码审查,`gitlink-pr-integrator` 独立复核贡献价值并执行合并门禁;两者可以共享事实,但不得把“价值明确”改写成“代码已通过”。
|
||||
|
||||
## 允许的功能重叠
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue