diff --git a/skills/gitlink-cli-contract-guard/SKILL.md b/skills/gitlink-cli-contract-guard/SKILL.md index 061e02a..32fd264 100644 --- a/skills/gitlink-cli-contract-guard/SKILL.md +++ b/skills/gitlink-cli-contract-guard/SKILL.md @@ -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` diff --git a/skills/gitlink-code-review/SKILL.md b/skills/gitlink-code-review/SKILL.md index 1f8b3cf..6b7f939 100644 --- a/skills/gitlink-code-review/SKILL.md +++ b/skills/gitlink-code-review/SKILL.md @@ -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` 审查 `/` 的 PR `#`”;多个 PR 可以直接列出多个编号。除非仓库或 PR 无法确定,不要求用户重复说明输出格式、安全边界或报告路径。 +用户只需点名 `gitlink-code-review` 并提供仓库以及一个或多个 PR 编号。没有 PR 编号但明确要求仓库健康扫描或 Issue 分诊时,执行对应模式;同时要求多项能力时,生成一份综合报告。 -点名本 Skill 后,默认自动执行以下要求: +默认遵守以下规则: -- **范围固定**:只审查代码质量、逻辑正确性、测试覆盖、可维护性和代码级安全。 -- **独立运行**:不调用其他 Skill;发现契约、拓扑、集成或维护问题时只写“超出本次范围”,不替它们下结论。 -- **只读运行**:不提交 Review、不发表评论、不 approve、不合并、不关闭、不分配、不修改远端。 -- **稳定发现**:问题使用 `CR-001` 起的稳定编号,标明严重性、文件/行号、证据、影响、修复建议和验证限制。 -- **高效首屏**:报告开头先给会直接影响评审结果的结论、门禁和最多 5 项动作;blocking/high 使用颜色和粗体,同时保留纯文本标签。 -- **单文件落盘**:一次运行只生成一份供人阅读的 Markdown,保存到 `reports/skill-runs/gitlink-code-review/---.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/---.md`。 -Markdown 使用 UTF-8 保存,不写 ANSI。无法写入工作区时,在回复中输出完整 Markdown,并明确标记“未落盘”;不能只给聊天摘要而丢失完整报告。 +无法写入工作区时,在最终回复中输出完整 Markdown 并标记“未落盘”。Markdown 不写 ANSI;凭据、cookie、token 和敏感值必须脱敏。 + +## 运行模式 + +### PR 审查模式 + +给出 PR 编号时默认启用,包含 PR 变更、Review 履约、完整代码审查、运行验证和 Review 建议。 + +### 仓库健康模式 + +用户要求仓库级检查时启用;综合报告中放在 PR 详细审查之后。检查文档、许可证、CI 配置、代码规范、测试结构、依赖管理、安全基线和 Issue 治理状态。 + +### Issue 分诊模式 + +用户要求 Issue 扫描或综合社区审查时启用。首屏只列各优先级的数量和 Issue 编号,详细分类统一放在报告最后。 ## 首屏固定结构 -首屏必须在详细分析之前,保持在维护者无需滚动或少量滚动即可读完的长度: +首屏只保留直接改变维护者决策的信息: ```markdown -# PR # 代码审查摘要 +# GitLink 社区审查摘要 -**结论:** 需要修改 **[action_required]** -**关键门禁:** 代码质量 `failed` | 逻辑 `partial` | 测试 `failed` | 可维护性 `passed` | 安全 `passed` -**发现:** blocking 1 | high 1 | medium 2 | low 0 +**Review 建议:** 修改后再审 **[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. [CR-001][blocking] 修复 `path/file.go:42` 的越权路径;责任:作者;证据:`E-CR-001`。 -2. [CR-002][high] 补测 非法输入与失败路径;责任:作者;证据:`E-CR-002`。 +1. [RV-003][high] 补全 Review 要求:失败路径仍未返回可诊断错误。 +2. [CR-002][high] 增加回归测试:复杂分支名未覆盖 URL 编码。 +3. [CR-004][high] 收紧权限边界:写操作缺少资源归属校验。 ``` -颜色仅用于最终结论、blocking/high、关键门禁和最优先动作。必须同时保留 `[blocking]`、`[high]` 等纯文本回退,避免渲染器清除 HTML 后丢失含义。 +Issue 首屏摘要不得展开标题、原因、标签或负责人。颜色只用于最终结论、blocking/high 和关键动作;始终保留 `[action_required]`、`[high]` 等文本回退。 -## 输入和批量模式 +## 证据采集 -单 PR 优先获取统一上下文: +优先获取统一上下文: ```bash gitlink-cli workflow +review-context --owner --repo --number --include-commits=true --include-ci=true --format json ``` -接口不可用时再分别获取: +接口不可用时分别获取: ```bash gitlink-cli pr +view --owner --repo --id --format json @@ -58,87 +76,135 @@ gitlink-cli pr +diff --owner --repo --id --form gitlink-cli pr +reviews --owner --repo --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,并在最终回复中给出绝对路径和一句话结论。 diff --git a/skills/gitlink-code-review/agents/openai.yaml b/skills/gitlink-code-review/agents/openai.yaml index 14f54be..631bf78 100644 --- a/skills/gitlink-code-review/agents/openai.yaml +++ b/skills/gitlink-code-review/agents/openai.yaml @@ -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 报告。" diff --git a/skills/gitlink-code-review/examples/evidence-first-review.md b/skills/gitlink-code-review/examples/evidence-first-review.md index b71d665..6617a9f 100644 --- a/skills/gitlink-code-review/examples/evidence-first-review.md +++ b/skills/gitlink-code-review/examples/evidence-first-review.md @@ -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`;维护者人工审核后可在独立操作中决定是否发布。 diff --git a/skills/gitlink-code-review/examples/pr-review-workflow.md b/skills/gitlink-code-review/examples/pr-review-workflow.md index 5409630..98740a8 100644 --- a/skills/gitlink-code-review/examples/pr-review-workflow.md +++ b/skills/gitlink-code-review/examples/pr-review-workflow.md @@ -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 --format json # 获取 Diff gitlink-cli pr +diff --id --format json -# 提交 Review -gitlink-cli pr +review --body '{"body":"...","event":"COMMENT"}' +# 获取已有 Review,用于验证后续修改 +gitlink-cli pr +reviews --id --format json ``` + +本工作流只生成报告和 Review 草稿,不提交远端。 diff --git a/skills/gitlink-maintainer-radar/SKILL.md b/skills/gitlink-maintainer-radar/SKILL.md index 78dc231..afb9348 100644 --- a/skills/gitlink-maintainer-radar/SKILL.md +++ b/skills/gitlink-maintainer-radar/SKILL.md @@ -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 --repo --previous queue-previous.json --format json diff --git a/skills/gitlink-maintenance-orchestrator/examples/fixtures/code-review.json b/skills/gitlink-maintenance-orchestrator/examples/fixtures/code-review.json index 4a9faa2..1a89bc1 100644 --- a/skills/gitlink-maintenance-orchestrator/examples/fixtures/code-review.json +++ b/skills/gitlink-maintenance-orchestrator/examples/fixtures/code-review.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"]} diff --git a/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-topology.json b/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-topology.json index 7d89358..14b9e31 100644 --- a/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-topology.json +++ b/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-topology.json @@ -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"]} diff --git a/skills/gitlink-maintenance-orchestrator/scripts/run-maintenance-pipeline.ps1 b/skills/gitlink-maintenance-orchestrator/scripts/run-maintenance-pipeline.ps1 index fe70381..e6afc83 100644 --- a/skills/gitlink-maintenance-orchestrator/scripts/run-maintenance-pipeline.ps1 +++ b/skills/gitlink-maintenance-orchestrator/scripts/run-maintenance-pipeline.ps1 @@ -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 } diff --git a/skills/gitlink-pr-integrator/SKILL.md b/skills/gitlink-pr-integrator/SKILL.md index e23d6c1..2adb86a 100644 --- a/skills/gitlink-pr-integrator/SKILL.md +++ b/skills/gitlink-pr-integrator/SKILL.md @@ -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 --repo --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/---.md`;多个 PR 先给队列摘要,再分别给每条 PR 的门禁。 无法写入工作区时输出完整 Markdown 并标记“未落盘”。最终回复给出报告绝对路径、可进入队列数量、阻断数量和第一下一动作。 @@ -43,7 +43,8 @@ CI 门禁必须读取 `ci_summary`:`match_mode=sha` 优先,`branch` 只能 # PR # 集成摘要 **结论:** 需补验证 **[action_required]** -**门禁:** 合并态 `passed` | 构建 `passed` | 测试 `not_run` | 契约 `partial` | 安全 `not_run` | 冲突 `low` +**贡献价值:** 值得合入 **[passed]**:需求明确、增量有效,维护成本可接受 +**门禁:** 价值 `passed` | 合并态 `passed` | 构建 `passed` | 测试 `not_run` | 契约 `partial` | 安全 `not_run` | 冲突 `low` ## 先处理这 2 项 @@ -51,7 +52,7 @@ CI 门禁必须读取 `ci_summary`:`match_mode=sha` 优先,`branch` 只能 2. [IN-002][high] 复查 权限边界;责任: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 # 集成摘要 **结论:** 需补验证 **[action_required]** -**门禁:** 合并态通过 | 构建通过 | 测试未执行 | 安全未验证 | 冲突低 +**贡献价值:** 值得合入 **[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 --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 --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 --repo --state open --page 1 --limit 如果发现明显的先后依赖,给出建议合并顺序。 -### Step 5: 输出集成影响矩阵 +### Step 6: 输出集成影响矩阵 不要只写“测试通过”。要明确主线在什么面上会被改变。 @@ -237,7 +280,7 @@ gitlink-cli pr +list --owner --repo --state open --page 1 --limit 如果代码改了,但帮助文本、README、示例或测试没有同步,直接记为集成跟进项,而不是轻描淡写地放过。 -### Step 6: 给出发布与回移建议 +### Step 7: 给出发布与回移建议 把改动归入以下类型之一: @@ -253,7 +296,7 @@ gitlink-cli pr +list --owner --repo --state open --page 1 --limit - 是否需要迁移说明或兼容性提示 - 是否适合回移到维护分支 -### Step 7: 形成合并后动作清单 +### Step 8: 形成合并后动作清单 如果 PR 代码已经接近可合并,但还差最后几步,明确写成动作清单: @@ -263,7 +306,7 @@ gitlink-cli pr +list --owner --repo --state open --page 1 --limit - 调整 milestone / 看板状态 - 合并后立即跟进的 issue 或回归验证 -### Step 8: 可选回写 +### Step 9: 可选回写 只有用户明确要求时,才把结论回写到远端。回写前先生成本地 Markdown 报告,并优先 `dry-run`。 @@ -280,38 +323,56 @@ gitlink-cli pr +list --owner --repo --state open --page 1 --limit ## PR # 集成就绪报告 -**结论:** ready_after_followups +**结论:** 需要维护者判断 **[observe]** +**贡献价值:** 证据不完整 **[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. 合并态验证 - 基线:`` - 结果:可合并 / 需 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 --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` 的项进入维护者确认队列,价值通过且风险较高的候选再进入本地合并验证。 ## 示例请求 diff --git a/skills/gitlink-pr-integrator/agents/openai.yaml b/skills/gitlink-pr-integrator/agents/openai.yaml index 4547f46..7b3e174 100644 --- a/skills/gitlink-pr-integrator/agents/openai.yaml +++ b/skills/gitlink-pr-integrator/agents/openai.yaml @@ -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 报告。" diff --git a/skills/gitlink-pr-integrator/examples/executive-integration.md b/skills/gitlink-pr-integrator/examples/executive-integration.md index 8573688..d64d6c6 100644 --- a/skills/gitlink-pr-integrator/examples/executive-integration.md +++ b/skills/gitlink-pr-integrator/examples/executive-integration.md @@ -10,10 +10,11 @@ gitlink-cli ci +builds --owner Gitlink --repo gitlink-cli --format json ```markdown # PR #123 集成摘要 **结论:** 可进入合并队列 **[merge]** -**门禁:** 合并态通过 | 构建通过 | 测试通过 | 契约通过 | 安全通过 | 冲突低 +**贡献价值:** 值得合入 **[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 和命令。 diff --git a/skills/gitlink-pr-topology/SKILL.md b/skills/gitlink-pr-topology/SKILL.md index 6e69964..4af609b 100644 --- a/skills/gitlink-pr-topology/SKILL.md +++ b/skills/gitlink-pr-topology/SKILL.md @@ -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 --repo --previous queue-previous.json --format json @@ -115,7 +115,7 @@ gitlink-cli workflow +review-context --owner --repo --number --repo --format json 4. 明显替代关系 5. 冲突热点文件/模块 6. 建议处理顺序 -7. 需要进一步切换到 `gitlink-pr-assessor` 或 `gitlink-pr-integrator` 深挖的对象 +7. 需要进一步切换到 `gitlink-code-review` 或 `gitlink-pr-integrator` 深挖的对象 ## 报告模板 diff --git a/skills/gitlink-pr-topology/examples/codex-validation-2026-06-26.md b/skills/gitlink-pr-topology/examples/codex-validation-2026-06-26.md index 6d28ca8..62ae806 100644 --- a/skills/gitlink-pr-topology/examples/codex-validation-2026-06-26.md +++ b/skills/gitlink-pr-topology/examples/codex-validation-2026-06-26.md @@ -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。 diff --git a/skills/gitlink-shared/SKILL.md b/skills/gitlink-shared/SKILL.md index a054a11..bd5fe46 100644 --- a/skills/gitlink-shared/SKILL.md +++ b/skills/gitlink-shared/SKILL.md @@ -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 的最短交接路径。 diff --git a/skills/gitlink-shared/examples/maintenance-evidence-v2.md b/skills/gitlink-shared/examples/maintenance-evidence-v2.md index 495a58f..e1a6f0c 100644 --- a/skills/gitlink-shared/examples/maintenance-evidence-v2.md +++ b/skills/gitlink-shared/examples/maintenance-evidence-v2.md @@ -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 diff --git a/skills/gitlink-shared/examples/maintenance-report.fixture.json b/skills/gitlink-shared/examples/maintenance-report.fixture.json index 76e2361..6858154 100644 --- a/skills/gitlink-shared/examples/maintenance-report.fixture.json +++ b/skills/gitlink-shared/examples/maintenance-report.fixture.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} } diff --git a/skills/gitlink-shared/examples/test-maintenance-report-validator.ps1 b/skills/gitlink-shared/examples/test-maintenance-report-validator.ps1 new file mode 100644 index 0000000..8c38c80 --- /dev/null +++ b/skills/gitlink-shared/examples/test-maintenance-report-validator.ps1 @@ -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 + } +} diff --git a/skills/gitlink-shared/examples/validate-maintenance-report.ps1 b/skills/gitlink-shared/examples/validate-maintenance-report.ps1 index 2bdb770..cc60d7f 100644 --- a/skills/gitlink-shared/examples/validate-maintenance-report.ps1 +++ b/skills/gitlink-shared/examples/validate-maintenance-report.ps1 @@ -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\[|") { 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\[|") { 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" diff --git a/skills/gitlink-shared/references/maintenance-evidence-workflow.md b/skills/gitlink-shared/references/maintenance-evidence-workflow.md index a6944f1..eef53b8 100644 --- a/skills/gitlink-shared/references/maintenance-evidence-workflow.md +++ b/skills/gitlink-shared/references/maintenance-evidence-workflow.md @@ -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。 ## 组合运行规则 diff --git a/skills/gitlink-shared/references/maintenance-report-contract.md b/skills/gitlink-shared/references/maintenance-report-contract.md index 427757b..1178682 100644 --- a/skills/gitlink-shared/references/maintenance-report-contract.md +++ b/skills/gitlink-shared/references/maintenance-report-contract.md @@ -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`:阻止合并、会泄露凭据、破坏兼容性或无法证明核心行为可用。 diff --git a/skills/gitlink-shared/references/maintenance-run-protocol.md b/skills/gitlink-shared/references/maintenance-run-protocol.md index 169ad46..30f3676 100644 --- a/skills/gitlink-shared/references/maintenance-run-protocol.md +++ b/skills/gitlink-shared/references/maintenance-run-protocol.md @@ -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`。 diff --git a/skills/gitlink-shared/references/skill-scope-and-handoff.md b/skills/gitlink-shared/references/skill-scope-and-handoff.md index 5764734..91b89d9 100644 --- a/skills/gitlink-shared/references/skill-scope-and-handoff.md +++ b/skills/gitlink-shared/references/skill-scope-and-handoff.md @@ -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` 独立复核贡献价值并执行合并门禁;两者可以共享事实,但不得把“价值明确”改写成“代码已通过”。 ## 允许的功能重叠