diff --git a/skills/gitlink-maintenance-orchestrator/SKILL.md b/skills/gitlink-maintenance-orchestrator/SKILL.md index b239665..3dc271e 100644 --- a/skills/gitlink-maintenance-orchestrator/SKILL.md +++ b/skills/gitlink-maintenance-orchestrator/SKILL.md @@ -5,7 +5,7 @@ description: "五个 GitLink 维护 Skill 的只读编排器:用户只需点 # GitLink 维护审查编排器 -这个 Skill 只负责编排和校验,不替代五个专项 Skill 的判断。它把“证据采集、三路并行专项检查、集成门禁、维护者排序、首屏报告”组织成一次可复现的只读运行,并保留每个专项的独立结果,便于维护者快速处理而不是阅读五份重复长报告。 +这个 Skill 负责编排、交叉校验和综合决策,不替代五个专项 Skill 的原始判断。它把“证据采集、三路并行专项检查、集成门禁、维护者排序、跨专项一致性判断、综合报告”组织成一次可复现的只读运行,并保留每个专项的大部分评估维度。执行前必须读取 [`references/comprehensive-dimensions.md`](references/comprehensive-dimensions.md)。 ## 五个 Skill 的职责 @@ -28,41 +28,54 @@ description: "五个 GitLink 维护 Skill 的只读编排器:用户只需点 - 共享一次队列快照和每个目标 PR 的固定 head 上下文,编排五个专项 Skill;不要求用户逐条重复五个 Skill 的提示词。 - 全流程只读,不评论、不 approve、不合并、不关闭、不分配、不修改标签或权限。 - 单 PR 和多 PR 都支持;多 PR 先做队列级拓扑/维护排序,再对重点 PR 逐条做代码、契约和集成检查,不能混合 Diff。 -- 报告首屏先按 PR 分节;每条 PR 分别显示代码审查、CLI 契约、仓库关系、集成门禁、维护状态和最终结论六张判断卡。聊天结论也按 PR 和这六个方面组织,但必须重新提炼成更短的维护者语言。 +- 报告按 PR 分节,完整展示代码与贡献、CLI 契约、仓库关系、集成门禁、维护治理五组专项维度,再展示证据一致性、风险传播、动作责任和置信度等编排器独有判断。不得用五张阶段总评代替完整维度。 - 每张卡先显示醒目加粗结论,再写简短解释、明确 `依据:` 和影响/下一步;不得把五个专项压成一段或把多个 PR 合并总结。 - 人读 `final-report.md` 必须使用中文叙述;命令名、路径、稳定编号和机器状态枚举可以保留英文。每个阶段的 `assessment.fact` 与 `assessment.basis` 在交给 finalize 前先转成忠实的中文摘要,不能把整份英文阶段报告直接拼入。 - 一次运行只生成一份主要人读报告 `/final-report.md`。五个专项 JSON 和 `final-report.json` 作为机器证据附件,不再让维护者阅读五份独立长 Markdown。 -最终回复必须由执行 Skill 的 Agent 在读完 `final-report.md` 和 `final-report.json` 后重新提炼,不能复制报告首屏、不能机械删除 Markdown/HTML 格式、不能把卡片原文改成纯文本后直接输出。聊天摘要要比报告更短,按每个 PR 输出六个方面的结论、最关键依据和解释/影响,每个方面一至两句完整自然语言;省略路径清单、阶段索引、分页过程、机器状态标签和次要证据。全部 PR 摘要后再给最多五项跨专项动作,并用可点击链接或当前 Agent 平台的文件附件交付 `final-report.md`。若无法落盘,输出完整 Markdown 并标记“未落盘”。 +最终回复必须由执行 Skill 的 Agent 在读完 `final-report.md` 和 `final-report.json` 后重新提炼,不能复制报告首屏、不能机械删除 Markdown/HTML 格式、不能把卡片原文改成纯文本后直接输出。聊天摘要要比报告短,但每个 PR 必须覆盖 [`references/comprehensive-dimensions.md`](references/comprehensive-dimensions.md) 规定的 15 项直接判断,每项一至两句,包含结论、最关键依据和影响。省略路径清单、阶段索引、分页过程、机器状态标签和次要证据。全部 PR 摘要后再给最多五项跨专项动作,并用可点击链接或当前 Agent 平台的文件附件交付 `final-report.md`。若无法落盘,输出完整 Markdown 并标记“未落盘”。 聊天摘要示例只表达风格,不是可复制模板: ```text PR #430 -代码审查:建议修改后再审。它补齐维护队列的真实状态过滤,但当前 fixture 仍显示 closed 项会进入 open 队列,这会直接误导维护者的待审范围。 -CLI 契约:需要先稳定字段语义。新增 changes、waiting_on 和 SLA 字段有实际价值,但空值、兼容回退和错误提示还要和主线帮助/JSON 输出对齐。 -仓库关系:适合排在 #431 前面评审。#431 会消费 #430 的队列字段,所以先确认 #430 的运行时契约,可以减少后续 Skill 协议返工。 -集成门禁:暂时不能进入合并队列。构建和基础测试可以作为正向证据,但真实队列过滤失败仍是集成前必须修复的问题。 -维护状态:当前主要等待作者修复。维护者最需要检查的是过滤逻辑、字段空值和回归测试是否补齐。 -最终结论:先修复再复审。这个 PR 的方向成立,但真实队列可信度是它能否合入的前提。 +最终建议:修改后再审。功能方向成立,但真实队列过滤失败会直接误导维护者。 +贡献价值与功能增量:补齐队列变化和等待责任信号,能减少人工比对;主线没有等价完整实现。 +Review 履约:当前没有足够的正式 Review 基线,不能判断作者是否已经完成全部修改要求。 +逻辑、质量与可维护性:主流程沿用现有架构,但 open 状态二次过滤存在错误,需要修复后再评价稳定性。 +测试、安全与性能:基础测试通过,真实状态、权限失败和大队列边界仍需补测;未发现已证实的安全阻断。 +参数、帮助与兼容:新增参数具有价值,旧调用默认行为需要继续保持。 +JSON、错误、编码与文档:字段类型基本稳定,但空值语义和错误原因不能被统一误报为 JSON 解析失败。 +主线与 merged 历史关系:属于已有 workflow 队列能力的增量演进,不是重复实现。 +open PR 重叠、依赖与处理顺序:与消费这些字段的下游 PR 互补,应先稳定本 PR 契约。 +合并态、构建与冲突:基础构建成立,仍需在最新主线合并工作树确认无冲突。 +测试、CI、安全与发布门禁:当前 head 的 CI 关联和失败路径验证不完整,暂不能进入合并队列。 +SLA、等待方与 Reviewer 负载:当前等待作者修复;完整 reviewer 负载需要仓库级快照。 +责任停滞、Issue 和安全运营优先级:修复责任明确,超过阈值后应升级复看提醒。 +证据完整性与跨专项一致性:五个阶段使用同一 head,但 CI 和 Review 证据仍有缺口;各专项结论没有无法解释的冲突。 +优先动作、责任方与复验条件:作者先修复过滤并补失败测试,维护者随后重跑契约和集成门禁。 完整报告:[final-report.md](D:\path\to\final-report.md) ``` -最终聊天不能只回复路径、状态计数或跨 PR 总段落。Markdown 能使用醒目颜色和加粗;聊天摘要除必要的 PR 编号、证据编号和最终报告链接外,不输出 HTML/Markdown 展示标签。 +最终聊天不能只回复路径、状态计数、五个阶段状态或跨 PR 总段落。Markdown 能使用醒目颜色和加粗;聊天摘要除必要的 PR 编号、证据编号和最终报告链接外,不输出 HTML/Markdown 展示标签。 -首屏固定先使用: +报告中的每个维度固定使用以下卡片语法;必须重复覆盖完整维度表,不能只生成示例中的几项: ```markdown # PR 维护全流程摘要 ## PR # -**代码审查:** 核心队列结果不可靠 **[action_required]**:closed 项进入 open 响应;依据:CR-001 与真实 fixture;下一步:修复过滤并补测试。 -**CLI 契约:** 新增字段保持向后兼容 **[passed]**:字段可选且旧调用不变;依据:CG 对照和 golden 输出;影响:现有脚本无需迁移。 -**仓库关系:** 需要先稳定上游契约 **[reorder]**:存在字段生产/消费关系;依据:主线、全部 open/merged PR 索引;影响:调整评审顺序。 -**集成门禁:** 当前不能进入合并队列 **[blocked]**:真实响应测试失败;依据:IN-001 和当前 head 验证;下一步:修复后重跑。 -**维护状态:** 等待作者修复后复看 **[action_required]**:责任链明确;依据:Review、提交和 SLA 快照;下一步:作者更新后通知 reviewer。 -**最终结论:** 修复真实状态过滤后重新审查 **[blocked]**:存在一个阻断和两个高风险项;依据:CR-001、IN-001、MR-001;下一步:按动作顺序处理。 +### 代码审查 +**贡献价值:** 高频维护价值成立 **[merge]**:补齐队列变化信号;依据:主线缺口和用户流程;证据摘录:需求与 Diff;影响:减少人工比对。 +**逻辑正确性:** 真实状态过滤不可靠 **[action_required]**:closed 项进入 open 响应;依据:CR-001 和真实 fixture;证据摘录:失败输出;影响:修复并补测试。 + +### CLI 契约 +**JSON 与文本输出:** 空值语义尚未稳定 **[observe]**:新增字段可选但错误回退不完整;依据:golden 对照;证据摘录:结构化输出;影响:稳定后再供下游消费。 + +### 编排器综合判断 +**证据完整性与新鲜度:** CI 和 Review 证据不完整 **[observe]**:五阶段 head 一致但两类证据缺失;依据:运行账本;证据摘录:collection manifest;影响:不能给出高置信度合并结论。 +**最终结论:** 修复真实状态过滤后重新审查 **[blocked]**:存在一个阻断和两个高风险项;依据:CR-001、IN-001、MR-001;证据摘录:失败 fixture 与门禁结果;下一步:按动作顺序处理。 ## 先处理这 3 项 @@ -71,7 +84,7 @@ CLI 契约:需要先稳定字段语义。新增 changes、waiting_on 和 SLA 3. [MR-001][high] 转派 安全复查;责任:maintainer。 ``` -多个 PR 重复完整六张卡,每张卡只能引用该 PR 的证据。跨 PR 动作放在全部 PR 卡片之后。 +多个 PR 重复完整综合维度矩阵,每张卡只能引用该 PR 的证据。跨 PR 动作放在全部 PR 卡片之后。 ## 编排流程 @@ -151,7 +164,7 @@ pr-topology.json } ``` -每条 `evidence` 不能只有 `E-xxx`、`CR-xxx` 等编号,至少还要提供 `summary`、`source`、`ref`、`status` 和可选的 `scope`。`summary` 必须说明实际观察到什么,例如“正常路径测试通过但无权限路径未覆盖”,不能只重复结论。编排器会把这些字段直接写入六张判断卡、待办和证据台账;没有可读证据时必须写明限制,而不是只显示编号。 +每条 `evidence` 不能只有 `E-xxx`、`CR-xxx` 等编号,至少还要提供 `summary`、`source`、`ref`、`status` 和可选的 `scope`。`summary` 必须说明实际观察到什么,例如“正常路径测试通过但无权限路径未覆盖”,不能只重复结论。编排器会把这些字段直接写入综合维度卡片、待办和证据台账;没有可读证据时必须写明限制,而不是只显示编号。 `top_actions.evidence` 和 `findings.evidence` 可以引用证据或发现 ID 以便追溯,但阶段结果必须同时保留对应的可读 `evidence` 对象。编排器在渲染时必须将发现 ID 展开为问题摘要和直接证据,不能在待办或最终结论中只显示 `CR-001`、`E-001` 等标识。 @@ -173,11 +186,10 @@ pr-topology.json - 只有拓扑关系需要调整顺序时:最终决策可为 `reorder`。 - 没有阻断项但存在维护动作时:显示 `action_required`,不自动回写远端。 -### 5. 生成维护者首屏 +### 5. 生成完整综合报告 -最终报告必须先按目标 PR 展示六张判断卡,顺序固定为代码审查、CLI 契约、仓库关系、 -集成门禁、维护状态、最终结论。每张卡先给醒目结论,再写当前 PR 的事实、`依据:` -以及影响或下一步;不能把五个阶段压缩成一段总体叙述。全部目标 PR 的判断卡展示完后, +最终报告必须按目标 PR 展示 [`references/comprehensive-dimensions.md`](references/comprehensive-dimensions.md) 中五个专项的完整维度,再展示编排器独有判断和最终结论。每张卡先给醒目结论,再写当前 PR 的事实、`依据:` +以及影响或下一步;不能把五个阶段压缩成五张总评。全部目标 PR 的判断卡展示完后, 再给最多五项跨专项待办,每项包含对象、责任方、下一动作、严重性和一个主证据。 阻断数、高风险数、安全门禁和验证状态可以作为卡片之后的索引,不能替代解释。 完整 findings、可读证据台账、限制和下一次复查条件放入附录或 JSON。证据编号仅用于追溯,不能替代证据摘要;维护者不打开 JSON 也应能在 Markdown 中看到观察事实、来源、命令或文件位置、状态和适用范围。 @@ -209,7 +221,7 @@ python -X utf8 .\skills\gitlink-maintenance-orchestrator\scripts\validate_chines --require-pr 123 ``` -多 PR 报告为每个目标重复 `--require-pr`。任何目标缺少完整六张判断卡、卡片没有明确 +多 PR 报告为每个目标重复 `--require-pr`。任何目标缺少完整综合维度、卡片没有明确 `依据:`、中文被写成连续问号或报告退化为英文模板时,校验失败且不得交付路径。 `collect` 只执行 `gitlink-cli` 的读操作,不发布评论、不添加标签、不分配 reviewer、不关闭或合并 PR。若 PowerShell 禁止执行 `gitlink-cli.ps1`,传入可执行的 `gitlink-cli.exe` 或 `gitlink-cli.cmd` 到 `-CliPath`。 diff --git a/skills/gitlink-maintenance-orchestrator/agents/openai.yaml b/skills/gitlink-maintenance-orchestrator/agents/openai.yaml index 883e0fd..488c132 100644 --- a/skills/gitlink-maintenance-orchestrator/agents/openai.yaml +++ b/skills/gitlink-maintenance-orchestrator/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "GitLink 维护审查编排器" - short_description: "编排五个维护 Skill 生成可验证的 PR 维护摘要" - default_prompt: "使用 $gitlink-maintenance-orchestrator 分析指定仓库或一个/多个 PR;完整证据保存为中文 Markdown,聊天结论由执行 Agent 读完报告后重新提炼,按 PR 分别概括代码审查、CLI 契约、仓库关系、集成门禁、维护状态和最终结论,每方面说明结论、关键依据与影响,不复制报告卡片原文、不输出 HTML/Markdown 展示标签,全程只读。" + short_description: "编排五个维护 Skill 生成全面、可验证的 PR 综合报告" + default_prompt: "使用 $gitlink-maintenance-orchestrator 分析指定仓库或一个/多个 PR;完整证据保存为中文 Markdown,报告覆盖五个专项的大部分评估维度以及证据一致性、风险传播、责任动作和决策置信度。聊天结论由执行 Agent 读完报告后重新提炼,按 PR 输出 15 项简明判断,每项说明结论、关键依据与影响,不复制报告卡片原文、不输出 HTML/Markdown 展示标签,全程只读。" diff --git a/skills/gitlink-maintenance-orchestrator/examples/fixtures/cli-contract-guard.json b/skills/gitlink-maintenance-orchestrator/examples/fixtures/cli-contract-guard.json index cbc7621..13e9a75 100644 --- a/skills/gitlink-maintenance-orchestrator/examples/fixtures/cli-contract-guard.json +++ b/skills/gitlink-maintenance-orchestrator/examples/fixtures/cli-contract-guard.json @@ -12,6 +12,13 @@ "fact": "新增命令保持原有参数、帮助、JSON 类型和错误语义,未观察到用户可感知的契约破坏", "basis": "对默认分支与当前 head 执行相同帮助和结构化输出测试,契约测试完整通过" }, + "dimensions": [ + {"aspect": "参数与帮助", "conclusion": "新增入口保持旧调用兼容", "decision": "merge", "fact": "新参数为可选项且默认行为不变", "basis": "baseline 与 head 帮助对照", "evidence": ["E-CG-001"], "impact": "现有用户无需迁移"}, + {"aspect": "JSON 与文本输出", "conclusion": "机器输出结构稳定", "decision": "merge", "fact": "新增字段未改变既有字段类型,文本没有混入 JSON", "basis": "结构化输出解析测试", "evidence": ["E-CG-001"], "impact": "自动化脚本可继续解析"}, + {"aspect": "错误与退出码", "conclusion": "错误语义保持可区分", "decision": "merge", "fact": "参数错误与远端失败仍返回不同错误路径", "basis": "失败命令与退出码对照", "evidence": ["E-CG-001"], "impact": "调用方无需修改判断逻辑"}, + {"aspect": "编码与颜色", "conclusion": "UTF-8 和机器输出边界正常", "decision": "merge", "fact": "中文输出可严格解码,JSON 不包含 ANSI", "basis": "原始字节和 JSON 解析验证", "evidence": ["E-CG-001"], "impact": "跨平台输出风险较低"}, + {"aspect": "兼容与文档", "conclusion": "文档与当前行为一致", "decision": "merge", "fact": "帮助和示例覆盖新增入口及默认行为", "basis": "文档、帮助和运行结果对照", "evidence": ["E-CG-001"], "impact": "用户可按现有文档使用"} + ], "findings": [], "top_actions": [], "evidence": [ diff --git a/skills/gitlink-maintenance-orchestrator/examples/fixtures/code-review.json b/skills/gitlink-maintenance-orchestrator/examples/fixtures/code-review.json index dd0e3b9..418892c 100644 --- a/skills/gitlink-maintenance-orchestrator/examples/fixtures/code-review.json +++ b/skills/gitlink-maintenance-orchestrator/examples/fixtures/code-review.json @@ -12,6 +12,14 @@ "fact": "PR 为示例命令增加批量处理能力,但错误路径缺少回归测试,核心失败行为尚未被证明可靠", "basis": "对照默认分支与当前 Diff,并执行 go test ./shortcuts/example;正常路径通过,失败路径覆盖不完整" }, + "dimensions": [ + {"aspect": "贡献价值", "conclusion": "批量处理能力具有明确使用价值", "decision": "merge", "fact": "PR 减少重复命令调用并覆盖常用维护流程", "basis": "对照默认分支功能入口与 PR 描述", "evidence": ["E-CR-001"], "impact": "功能方向可以保留"}, + {"aspect": "Review 履约", "conclusion": "本轮没有可核对的正式 Review", "decision": "observe", "fact": "当前证据没有包含有效 Review 意见与对应提交", "basis": "Review 证据未采集完整", "evidence": ["E-CR-001"], "impact": "不能判断作者是否完成 Review 修改"}, + {"aspect": "逻辑正确性", "conclusion": "失败路径仍不可靠", "decision": "action_required", "fact": "正常路径可用,但远端失败和非法输入没有完整行为证据", "basis": "当前 Diff 与专项测试结果", "evidence": ["E-CR-001"], "impact": "修复错误处理后复验"}, + {"aspect": "代码质量与可维护性", "conclusion": "主流程结构清晰", "decision": "merge", "fact": "批量入口沿用现有 shortcut 结构,未发现明显重复实现", "basis": "目录职责和调用链对照", "evidence": ["E-CR-001"], "impact": "当前结构不阻断合并"}, + {"aspect": "测试覆盖", "conclusion": "失败和权限场景缺测", "decision": "action_required", "fact": "成功场景通过,但无权限和非法路径没有回归测试", "basis": "测试文件与执行命令对照", "evidence": ["E-CR-001"], "impact": "补齐失败路径后重跑"}, + {"aspect": "安全与性能", "conclusion": "未发现已证实的安全或性能阻断", "decision": "observe", "fact": "现有范围未发现凭据泄露和无界批量循环,但恶意输入未完整验证", "basis": "Diff 安全热点和测试范围", "evidence": ["E-CR-001"], "impact": "保持观察并补输入边界测试"} + ], "findings": [ {"id": "CR-001", "severity": "high", "status": "open", "summary": "错误路径缺少回归测试", "evidence": ["E-CR-001"], "related_ids": []} ], diff --git a/skills/gitlink-maintenance-orchestrator/examples/fixtures/maintainer-radar.json b/skills/gitlink-maintenance-orchestrator/examples/fixtures/maintainer-radar.json index bf7bfa1..c8da07e 100644 --- a/skills/gitlink-maintenance-orchestrator/examples/fixtures/maintainer-radar.json +++ b/skills/gitlink-maintenance-orchestrator/examples/fixtures/maintainer-radar.json @@ -12,6 +12,13 @@ "fact": "PR #123 已有明确修复动作但仍等待作者补测,继续占用维护者复看队列", "basis": "队列快照显示 waiting_on=author,当前 head 和 Review 后续动作尚未变化" }, + "dimensions": [ + {"aspect": "响应 SLA", "conclusion": "当前复看等待正在累积", "decision": "action_required", "fact": "作者补测尚未完成,PR 继续占用复看队列", "basis": "队列时间与状态快照", "evidence": ["E-MR-001"], "impact": "修复后应及时通知 reviewer"}, + {"aspect": "当前等待方", "conclusion": "当前明确等待作者", "decision": "action_required", "fact": "waiting_on=author 且修复动作已指向 CR-001", "basis": "队列字段和技术动作关联", "evidence": ["E-MR-001"], "impact": "维护者暂不重复 Review"}, + {"aspect": "Reviewer 负载", "conclusion": "本次没有足够数据判断负载平衡", "decision": "observe", "fact": "fixture 只包含目标 PR,没有完整 reviewer 队列", "basis": "扫描范围限制", "evidence": ["E-MR-001"], "impact": "需要仓库级快照后再调度"}, + {"aspect": "责任停滞", "conclusion": "责任链清晰但尚未推进", "decision": "action_required", "fact": "作者有明确补测任务,当前 head 尚未变化", "basis": "waiting_on 与 head 快照", "evidence": ["E-MR-001"], "impact": "超过阈值后升级维护提醒"}, + {"aspect": "安全与 Issue 优先级", "conclusion": "没有已证实的安全 HOT 事项", "decision": "observe", "fact": "当前高优先级来自测试缺口而非已确认漏洞", "basis": "安全门禁和 finding 分类", "evidence": ["E-MR-001"], "impact": "保持技术修复优先,不误报安全漏洞"} + ], "findings": [], "top_actions": [ {"id": "MR-001", "owner": "maintainer", "severity": "medium", "action": "安排维护者复看 PR #123,当前等待作者补测", "evidence": ["queue:pr-123"]} diff --git a/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-integrator.json b/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-integrator.json index 1297330..1ae628a 100644 --- a/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-integrator.json +++ b/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-integrator.json @@ -12,6 +12,14 @@ "fact": "贡献方向与仓库需求一致,但代码审查发现的失败路径缺口尚未修复,当前实现不能安全进入合并队列", "basis": "当前 head 的基础构建可执行,但 CR-001 未解决且全量集成验证只完成部分门禁" }, + "dimensions": [ + {"aspect": "合并态与冲突", "conclusion": "尚未完成最新主线合并验证", "decision": "observe", "fact": "当前 fixture 没有最新主线合并工作树结果", "basis": "合并态证据缺失", "evidence": ["E-IN-001"], "impact": "合并前必须在最新主线复验"}, + {"aspect": "构建", "conclusion": "基础构建可执行", "decision": "merge", "fact": "当前 head 在规定环境完成基础构建", "basis": "本地工作树构建结果", "evidence": ["E-IN-001"], "impact": "构建不构成当前阻断"}, + {"aspect": "测试与功能一致性", "conclusion": "PR 声明仅部分得到验证", "decision": "action_required", "fact": "成功路径成立,但错误路径与描述中的可靠性目标不一致", "basis": "专项测试和 CR-001", "evidence": ["E-IN-001"], "impact": "补测并修复后重跑"}, + {"aspect": "CI 与证据关联", "conclusion": "CI 证据未完整关联当前 head", "decision": "observe", "fact": "fixture 没有提供可核对的 head SHA 构建记录", "basis": "CI 证据关联字段缺失", "evidence": ["E-IN-001"], "impact": "不能把历史构建当作当前通过"}, + {"aspect": "安全与发布影响", "conclusion": "没有已证实的发布阻断", "decision": "observe", "fact": "安全门禁通过,但失败路径和回滚证据不完整", "basis": "安全状态与限制列表", "evidence": ["E-IN-001"], "impact": "补齐验证后确认发布风险"}, + {"aspect": "集成结论", "conclusion": "当前不能进入合并队列", "decision": "action_required", "fact": "CR-001 未解决且合并态、CI 和全量测试不完整", "basis": "代码审查与集成证据汇总", "evidence": ["E-IN-001"], "impact": "完成高优先级动作后重新评估"} + ], "findings": [], "top_actions": [ {"id": "IN-001", "owner": "author", "severity": "high", "action": "修复 CR-001 后重新执行合并态验证", "evidence": ["CR-001"]} diff --git a/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-topology.json b/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-topology.json index 63e6327..39a7289 100644 --- a/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-topology.json +++ b/skills/gitlink-maintenance-orchestrator/examples/fixtures/pr-topology.json @@ -12,6 +12,13 @@ "fact": "PR #123 与 #124 修改同一命令入口,功能不完全重复,但合并顺序可能改变最终输出契约", "basis": "比较两条 PR 的文件集合、队列快照和目标行为后确认存在共享入口,尚无证据判定其中一条完全替代另一条" }, + "dimensions": [ + {"aspect": "对当前主线", "conclusion": "目标是对主线能力的增量扩展", "decision": "merge", "fact": "主线已有基础入口,但没有本 PR 的批量行为", "basis": "默认分支源码与目标 Diff 对照", "evidence": ["E-TP-001"], "impact": "不存在主线完全覆盖"}, + {"aspect": "对 open 队列", "conclusion": "与 #124 存在共享入口", "decision": "reorder", "fact": "两条 open PR 修改同一命令入口但目标不同", "basis": "完整 open 队列和文件语义比较", "evidence": ["E-TP-001"], "impact": "需要联合评审接口边界"}, + {"aspect": "对 merged 历史", "conclusion": "继承既有 shortcut 架构", "decision": "merge", "fact": "已合入历史提供基础命令框架,但没有等价批量实现", "basis": "merged PR 索引与主线提交历史", "evidence": ["E-TP-001"], "impact": "应按增量演进评审"}, + {"aspect": "完整性比较", "conclusion": "目标与 #124 互补而非替代", "decision": "observe", "fact": "两条 PR 的覆盖范围和验证重点不同", "basis": "功能范围、测试和文件路径比较", "evidence": ["E-TP-001"], "impact": "不建议仅按文件交集择一"}, + {"aspect": "依赖与处理顺序", "conclusion": "先稳定共享入口再合入下游", "decision": "reorder", "fact": "合并顺序可能改变最终命令和输出契约", "basis": "入口生产与消费关系", "evidence": ["E-TP-001"], "impact": "先确认接口再处理后续 PR"} + ], "findings": [ {"id": "TP-001", "severity": "medium", "status": "open", "summary": "与 PR #124 修改同一命令入口,建议合并顺序复核", "evidence": ["E-TP-001"], "related_ids": ["PR-124"]} ], diff --git a/skills/gitlink-maintenance-orchestrator/references/comprehensive-dimensions.md b/skills/gitlink-maintenance-orchestrator/references/comprehensive-dimensions.md new file mode 100644 index 0000000..2d2ad94 --- /dev/null +++ b/skills/gitlink-maintenance-orchestrator/references/comprehensive-dimensions.md @@ -0,0 +1,87 @@ +# 综合维护评估维度 + +总报告必须覆盖下列六组维度。专项阶段应逐项写入 `dimensions`;没有证据时保留该项并标记 `observe`、`partial` 或 `not_run`,不能省略。 + +## 1. 代码与贡献 + +| 维度 | 最低判断内容 | +|---|---| +| 贡献价值 | 真实问题、受益对象、相对主线增量、维护成本 | +| Review 履约 | 有效 Review、对应提交、修改是否满足要求、剩余动作 | +| 逻辑正确性 | 正常、失败、边界、状态一致性和错误处理 | +| 代码质量与可维护性 | 架构一致性、职责、复杂度、重复、长期维护成本 | +| 测试覆盖 | 单元、集成、回归、失败和跨平台场景 | +| 安全与性能 | 权限、注入、路径、凭据、依赖、资源和性能退化 | + +## 2. CLI 契约 + +| 维度 | 最低判断内容 | +|---|---| +| 参数与帮助 | 命令层级、flag、别名、默认值、帮助与示例 | +| JSON 与文本输出 | 字段、类型、可选性、人读与机读隔离 | +| 错误与退出码 | 参数、认证、权限、远端和解析失败语义 | +| 编码与颜色 | UTF-8、乱码、ANSI、`NO_COLOR` 和跨平台 | +| 兼容与文档 | 旧调用、迁移成本、README 和帮助一致性 | + +## 3. 仓库关系 + +| 维度 | 最低判断内容 | +|---|---| +| 对当前主线 | 已实现、补缺、扩展、重复或回归 | +| 对 open 队列 | 依赖、竞争、重叠、互补、冲突和联审对象 | +| 对 merged 历史 | 继承来源、已合入等价实现和演进关系 | +| 完整性比较 | 重叠实现的范围、测试、架构适配和维护成本 | +| 依赖与处理顺序 | 硬依赖、顺序依赖、合入次序和解除条件 | + +## 4. 集成门禁 + +| 维度 | 最低判断内容 | +|---|---| +| 合并态与冲突 | 最新主线合并、冲突文件和基线 SHA | +| 构建 | 仓库规定环境、构建命令、结果和限制 | +| 测试与功能一致性 | PR 声明、实际行为、全量/专项测试和失败路径 | +| CI 与证据关联 | head SHA、分支回退、未关联构建和时效 | +| 安全与发布影响 | 安全门禁、依赖、迁移、发布和回滚 | +| 集成结论 | 是否进入 merge queue、阻塞项和复验条件 | + +## 5. 维护治理 + +| 维度 | 最低判断内容 | +|---|---| +| 响应 SLA | 首响、Review、复看和合并等待时间 | +| 当前等待方 | 作者、reviewer、maintainer、外部依赖或未知 | +| Reviewer 负载 | 分配数量、瓶颈和可释放容量 | +| 责任停滞 | 已分配但无进展、requested changes 后未更新 | +| 安全与 Issue 优先级 | 安全运营信号、关联 Issue 和队列优先级 | + +## 6. 编排器独有判断 + +| 维度 | 最低判断内容 | +|---|---| +| 证据完整性与新鲜度 | 五阶段齐全、run/head/as_of 一致、缺失和过期证据 | +| 跨专项结论一致性 | 结论是否冲突,冲突如何按证据和门禁解释 | +| 风险传播与门禁对齐 | CR/CG/TP 风险如何影响 IN 和 MR | +| 动作去重与责任归属 | 重复动作合并、责任方、先后顺序和解除条件 | +| 决策置信度 | 覆盖率、限制、未验证项和结论可信程度 | + +## 聊天直接输出 + +聊天摘要不能把总报告压缩成五个阶段状态。每个 PR 至少输出以下 15 项简明判断,每项一至两句,包含结论、最关键依据和影响: + +1. 最终建议 +2. 贡献价值与功能增量 +3. Review 履约 +4. 逻辑、质量与可维护性 +5. 测试、安全与性能 +6. 参数、帮助与兼容 +7. JSON、错误、编码与文档 +8. 主线与 merged 历史关系 +9. open PR 重叠、依赖与处理顺序 +10. 合并态、构建与冲突 +11. 测试、CI、安全与发布门禁 +12. SLA、等待方与 Reviewer 负载 +13. 责任停滞、Issue 和安全运营优先级 +14. 证据完整性与跨专项一致性 +15. 优先动作、责任方与复验条件 + +多个 PR 分别输出,不能共用结论。聊天不复制 Markdown 卡片、HTML 标签、阶段索引、分页过程或完整证据台账;完整内容通过报告链接交付。 diff --git a/skills/gitlink-maintenance-orchestrator/references/pipeline-contract.md b/skills/gitlink-maintenance-orchestrator/references/pipeline-contract.md index c51cca0..ff7d9f0 100644 --- a/skills/gitlink-maintenance-orchestrator/references/pipeline-contract.md +++ b/skills/gitlink-maintenance-orchestrator/references/pipeline-contract.md @@ -46,6 +46,17 @@ "fact": "核心成功路径可用,但远端失败没有回归证据", "basis": "当前 head 的 Diff、专项测试和失败路径测试清单" }, + "dimensions": [ + { + "aspect": "逻辑正确性", + "conclusion": "失败路径仍不可靠", + "decision": "action_required", + "fact": "远端失败没有稳定错误映射", + "basis": "当前 Diff 与失败路径测试", + "evidence": ["E-CR-001"], + "impact": "修复并补测后复验" + } + ], "findings": [], "top_actions": [], "evidence": [], @@ -59,6 +70,11 @@ `action_required`、`reorder`、`observe`、`blocked`。阶段之间不得篡改其他 Skill 的 finding,只通过 `related_ids` 关联。 +`dimensions` 必须覆盖该阶段在 +[`comprehensive-dimensions.md`](comprehensive-dimensions.md) 中列出的全部维度。每个维度必须有 `aspect`、`conclusion`、`decision`、`fact`、`basis`、`evidence` 和 `impact`。没有证据时仍保留维度,并明确写 `not_run`、`partial` 或 `observe`;禁止用阶段级 `assessment` 代替全部维度。 + +编排器在五阶段之后额外生成“证据完整性与新鲜度、跨专项结论一致性、风险传播与门禁对齐、动作去重与责任归属、决策置信度”五项判断。这些判断属于编排器,不得伪装成某个专项 finding。 + ## 最终决策规则 按以下顺序计算最终决策: @@ -75,6 +91,7 @@ finding,只通过 `related_ids` 关联。 ## 首屏压缩规则 +- 每个 PR 先展示五个专项的完整维度矩阵,再展示编排器独有判断和最终结论。 - 首屏最多五项待办,按 `blocking > high > medium > low`、再按责任等待方和证据置信度排序。 - 同一对象、同一动作、同一责任方的重复项合并;保留全部 `source_ids` 供追溯。 - 首屏每项只展示一个主证据,完整证据放到 JSON 或附录。 diff --git a/skills/gitlink-maintenance-orchestrator/scripts/__pycache__/validate_chinese_report.cpython-312.pyc b/skills/gitlink-maintenance-orchestrator/scripts/__pycache__/validate_chinese_report.cpython-312.pyc new file mode 100644 index 0000000..233986c Binary files /dev/null and b/skills/gitlink-maintenance-orchestrator/scripts/__pycache__/validate_chinese_report.cpython-312.pyc differ diff --git a/skills/gitlink-maintenance-orchestrator/scripts/run-maintenance-pipeline.ps1 b/skills/gitlink-maintenance-orchestrator/scripts/run-maintenance-pipeline.ps1 index dbf33f2..744b7e4 100644 --- a/skills/gitlink-maintenance-orchestrator/scripts/run-maintenance-pipeline.ps1 +++ b/skills/gitlink-maintenance-orchestrator/scripts/run-maintenance-pipeline.ps1 @@ -141,6 +141,68 @@ function Resolve-EvidenceText { return ($items -join ';') } +function Get-RequiredDimensions { + param([string]$Producer) + switch ($Producer) { + 'gitlink-code-review' { + return @('贡献价值', 'Review 履约', '逻辑正确性', '代码质量与可维护性', '测试覆盖', '安全与性能') + } + 'gitlink-cli-contract-guard' { + return @('参数与帮助', 'JSON 与文本输出', '错误与退出码', '编码与颜色', '兼容与文档') + } + 'gitlink-pr-topology' { + return @('对当前主线', '对 open 队列', '对 merged 历史', '完整性比较', '依赖与处理顺序') + } + 'gitlink-pr-integrator' { + return @('合并态与冲突', '构建', '测试与功能一致性', 'CI 与证据关联', '安全与发布影响', '集成结论') + } + 'gitlink-maintainer-radar' { + return @('响应 SLA', '当前等待方', 'Reviewer 负载', '责任停滞', '安全与 Issue 优先级') + } + default { return @('专项判断') } + } +} + +function Get-StageDimensions { + param([string]$Producer, [object]$Artifact) + $provided = @() + $assessment = $null + $stageEvidence = @() + if ($null -ne $Artifact) { + $provided = @(Get-Value $Artifact 'dimensions' @()) + $assessment = Get-Value $Artifact 'assessment' $null + $stageEvidence = @(Get-Value $Artifact 'evidence' @()) + } + $result = New-Object Collections.Generic.List[object] + foreach ($aspect in @(Get-RequiredDimensions $Producer)) { + $item = $provided | Where-Object { [string](Get-Value $_ 'aspect' '') -eq $aspect } | Select-Object -First 1 + if ($null -eq $item) { + $stageConclusion = if ($null -ne $assessment) { [string](Get-Value $assessment 'conclusion' '') } else { '' } + $stageBasis = if ($null -ne $assessment) { [string](Get-Value $assessment 'basis' '') } else { '' } + $result.Add([pscustomobject][ordered]@{ + aspect = $aspect + conclusion = '该维度证据不足' + decision = 'observe' + fact = "当前 ${Producer} 结果未提供维度 ${aspect} 的独立判断,不能从阶段总评推断为通过" + basis = if ($stageBasis) { "阶段级依据仅为:$stageBasis" } else { '缺少该维度的结构化依据' } + evidence = @($stageEvidence | Select-Object -First 1) + impact = if ($stageConclusion) { "保留阶段总评 ${stageConclusion},补齐本维度后再提高置信度" } else { '补齐本维度证据后重新评估' } + }) + continue + } + $result.Add([pscustomobject][ordered]@{ + aspect = $aspect + conclusion = [string](Get-Value $item 'conclusion' '该维度尚无明确结论') + decision = [string](Get-Value $item 'decision' 'observe') + fact = [string](Get-Value $item 'fact' '未提供事实摘要') + basis = [string](Get-Value $item 'basis' '未提供判断依据') + evidence = @(Get-Value $item 'evidence' @()) + impact = [string](Get-Value $item 'impact' '根据该维度证据决定下一步') + }) + } + return $result.ToArray() +} + function Get-StageSummary { param([string]$Producer, [object]$Artifact) $findings = @() @@ -192,6 +254,7 @@ function Get-StageSummary { focus = $focus basis = $basis evidence = @($evidence | Select-Object -First 2) + dimensions = @(Get-StageDimensions -Producer $Producer -Artifact $Artifact) } } @@ -284,6 +347,73 @@ function Get-FinalDecision { return 'observe' } +function New-OrchestratorDimensions { + param( + [bool]$AllComplete, + [object[]]$Summaries, + [object[]]$Actions, + [object]$Counts, + [string]$SecurityGate, + [string]$Verification, + [string]$Decision, + [object[]]$Limitations, + [object[]]$Evidence + ) + $stageStates = @($Summaries | ForEach-Object { "$($_.producer)=$($_.status)/$($_.decision)" }) -join ',' + $evidenceSample = @($Evidence | Select-Object -First 2) + $missingOwners = @($Actions | Where-Object { [string]::IsNullOrWhiteSpace([string]$_.owner) -or $_.owner -eq '维护者' }).Count + $coverageConclusion = if ($AllComplete -and $Verification -eq 'complete' -and @($Limitations).Count -eq 0) { '五阶段证据完整且时间上下文一致' } else { '证据覆盖或新鲜度仍有限制' } + $coverageDecision = if ($AllComplete -and $Verification -eq 'complete' -and @($Limitations).Count -eq 0) { 'merge' } else { 'observe' } + $confidence = if ($coverageDecision -eq 'merge' -and $SecurityGate -eq 'passed') { '高' } elseif ($AllComplete) { '中' } else { '低' } + return @( + [pscustomobject][ordered]@{ + aspect = '证据完整性与新鲜度' + conclusion = $coverageConclusion + decision = $coverageDecision + fact = "五个阶段完成状态为:$stageStates;验证汇总为 $Verification,限制 $(@($Limitations).Count) 项" + basis = '核对 run_id、as_of、目标 head、阶段状态、验证状态和限制列表' + evidence = $evidenceSample + impact = if ($coverageDecision -eq 'merge') { '可以使用同一快照支撑综合判断' } else { '缺失维度不得推断通过,需要补证或重跑' } + }, + [pscustomobject][ordered]@{ + aspect = '跨专项结论一致性' + conclusion = '五个专项结论已按职责和门禁优先级收敛' + decision = if ($AllComplete) { 'merge' } else { 'observe' } + fact = "阶段决策为:$stageStates;不同专项的 merge、reorder 或 action_required 代表不同决策对象,不自动视为冲突" + basis = '比较五阶段 decision、finding 关联和集成门禁优先级' + evidence = $evidenceSample + impact = '无法解释的结论冲突必须阻止最终合并建议' + }, + [pscustomobject][ordered]@{ + aspect = '风险传播与门禁对齐' + conclusion = if ($Decision -eq 'merge') { '专项风险未形成合并阻断' } else { '专项风险已传递到最终门禁' } + decision = $Decision + fact = "当前阻断 $($Counts.blocking) 项、高风险 $($Counts.high) 项,安全门禁 $SecurityGate,最终决策 $Decision" + basis = '按 blocking、安全失败、集成器、拓扑顺序和维护 HOT 动作计算' + evidence = $evidenceSample + impact = '高优先级技术风险不能被维护排序或正向价值结论覆盖' + }, + [pscustomobject][ordered]@{ + aspect = '动作去重与责任归属' + conclusion = if (@($Actions).Count -eq 0) { '当前没有需要立即执行的跨专项动作' } else { "已收敛为 $(@($Actions).Count) 项跨专项动作" } + decision = if (@($Actions).Count -eq 0) { 'merge' } else { 'action_required' } + fact = "动作按对象、内容和责任方去重;责任不明确 $missingOwners 项" + basis = '汇总五阶段 top_actions,并按严重性、责任方和动作内容排序' + evidence = $evidenceSample + impact = '维护者可以按统一责任链处理,不必在五份报告间重复查找' + }, + [pscustomobject][ordered]@{ + aspect = '决策置信度' + conclusion = "当前综合决策置信度为$confidence" + decision = if ($confidence -eq '高') { 'merge' } else { 'observe' } + fact = "置信度由阶段覆盖、验证完整性、安全门禁和限制数量共同决定;最终决策为 $Decision" + basis = '不以单一测试通过或单个专项结论替代完整证据覆盖' + evidence = $evidenceSample + impact = if ($confidence -eq '高') { '可将综合结论作为维护者决策依据' } else { '维护者应先处理限制和未验证项' } + } + ) +} + function New-FinalReport { param([string]$Path) $run = Read-JsonFile (Join-Path $Path 'run.json') @@ -298,7 +428,7 @@ function New-FinalReport { $artifact = $artifacts[$entry.Key] if ($null -eq $artifact) { $allComplete = $false - $summaries.Add([ordered]@{ producer = $entry.Key; status = 'not_run'; decision = 'blocked'; security_gate = 'not_run'; verification = 'not_run'; finding_count = 0; blocking_count = 0; high_count = 0; top_action_count = 0 }) + $summaries.Add([ordered]@{ producer = $entry.Key; status = 'not_run'; decision = 'blocked'; security_gate = 'not_run'; verification = 'not_run'; finding_count = 0; blocking_count = 0; high_count = 0; top_action_count = 0; conclusion = '阶段未运行'; focus = '缺少专项结果'; basis = '运行目录中没有对应阶段 JSON'; evidence = @(); dimensions = @(Get-StageDimensions -Producer $entry.Key -Artifact $null) }) $limitations.Add("缺少阶段结果:$($entry.Key)") continue } @@ -343,6 +473,7 @@ function New-FinalReport { $findingArray = [object[]]$findings $evidenceArray = [object[]]$evidence $limitationArray = Get-UniqueStrings $limitations + $orchestratorDimensions = New-OrchestratorDimensions -AllComplete $allComplete -Summaries $stageArray -Actions ([object[]]$actions) -Counts $counts -SecurityGate $securityGate -Verification $verification -Decision $decision -Limitations $limitationArray -Evidence $evidenceArray $report = [ordered]@{ schema_version = '1.0' producer = 'gitlink-maintenance-orchestrator' @@ -355,6 +486,7 @@ function New-FinalReport { scope = $scope run = $run stages = $stageArray + orchestrator_dimensions = @($orchestratorDimensions) top_actions = @($topActions) findings = $findingArray evidence = $evidenceArray @@ -454,7 +586,19 @@ function Write-MarkdownReport { $aspect = Get-StageAspect ([string]$stage.producer) $impact = Get-DecisionImpact ([string]$stage.decision) $evidenceText = Resolve-EvidenceText -EvidenceValues @($stage.evidence) -EvidenceIndex $evidenceIndex -FindingIndex $findingIndex - $lines.Add("**${aspect}:** $(Get-ConclusionLabel ([string]$stage.conclusion) ([string]$stage.decision)):$($stage.focus);依据:$($stage.basis);证据摘录:$evidenceText;影响:$impact。") + $lines.Add('') + $lines.Add("### $aspect") + $lines.Add("**专项总评:** $(Get-ConclusionLabel ([string]$stage.conclusion) ([string]$stage.decision)):$($stage.focus);依据:$($stage.basis);证据摘录:$evidenceText;影响:$impact。") + foreach ($dimension in @($stage.dimensions)) { + $dimensionEvidence = Resolve-EvidenceText -EvidenceValues @($dimension.evidence) -EvidenceIndex $evidenceIndex -FindingIndex $findingIndex + $lines.Add("**$($dimension.aspect):** $(Get-ConclusionLabel ([string]$dimension.conclusion) ([string]$dimension.decision)):$($dimension.fact);依据:$($dimension.basis);证据摘录:$dimensionEvidence;影响:$($dimension.impact)。") + } + } + $lines.Add('') + $lines.Add('### 编排器综合判断') + foreach ($dimension in @($Report.orchestrator_dimensions)) { + $dimensionEvidence = Resolve-EvidenceText -EvidenceValues @($dimension.evidence) -EvidenceIndex $evidenceIndex -FindingIndex $findingIndex + $lines.Add("**$($dimension.aspect):** $(Get-ConclusionLabel ([string]$dimension.conclusion) ([string]$dimension.decision)):$($dimension.fact);依据:$($dimension.basis);证据摘录:$dimensionEvidence;影响:$($dimension.impact)。") } $finalReason = if (@($Report.top_actions).Count -gt 0) { [string]$Report.top_actions[0].action diff --git a/skills/gitlink-maintenance-orchestrator/scripts/test-maintenance-output.ps1 b/skills/gitlink-maintenance-orchestrator/scripts/test-maintenance-output.ps1 index 0444759..adc1121 100644 --- a/skills/gitlink-maintenance-orchestrator/scripts/test-maintenance-output.ps1 +++ b/skills/gitlink-maintenance-orchestrator/scripts/test-maintenance-output.ps1 @@ -28,7 +28,8 @@ try { $decisionPattern = '[^<]+ \*\*\[(merge|action_required|reorder|observe|blocked)\]\*\*' $decisions = [regex]::Matches($content, $decisionPattern) $actionsIndex = $content.IndexOf('1. **[') - if ($decisions.Count -lt 6 -or $actionsIndex -lt 0 -or $decisions[5].Index -gt $actionsIndex) { throw 'final conclusion must appear after five stage decisions and before actions' } + $preActionDecisions = @($decisions | Where-Object { $_.Index -lt $actionsIndex }) + if ($actionsIndex -lt 0 -or $preActionDecisions.Count -lt 33) { throw 'comprehensive dimensions and final conclusion must appear before actions' } & python -X utf8 $orchestratorPromptTests | Out-Null if ($LASTEXITCODE -ne 0) { throw 'orchestrator prompt contract tests failed' } diff --git a/skills/gitlink-maintenance-orchestrator/scripts/test_orchestrator_prompt_contract.py b/skills/gitlink-maintenance-orchestrator/scripts/test_orchestrator_prompt_contract.py index 779bc69..0df1ee2 100644 --- a/skills/gitlink-maintenance-orchestrator/scripts/test_orchestrator_prompt_contract.py +++ b/skills/gitlink-maintenance-orchestrator/scripts/test_orchestrator_prompt_contract.py @@ -15,7 +15,8 @@ def test_chat_summary_requires_agent_synthesis() -> None: "重新提炼", "不能复制报告首屏", "不能机械删除 Markdown/HTML 格式", - "每个方面一至两句完整自然语言", + "15 项直接判断", + "每项一至两句", "可点击链接或当前 Agent 平台的文件附件", ] missing = [marker for marker in required_markers if marker not in content] @@ -25,6 +26,6 @@ def test_chat_summary_requires_agent_synthesis() -> None: def test_default_agent_prompt_requires_a_plain_summary() -> None: content = read_utf8(ROOT / "agents" / "openai.yaml") - required_markers = ["重新提炼", "不复制报告卡片原文", "不输出 HTML/Markdown 展示标签"] + required_markers = ["重新提炼", "15 项", "不复制报告卡片原文", "不输出 HTML/Markdown 展示标签"] missing = [marker for marker in required_markers if marker not in content] assert not missing, f"missing agent prompt markers: {missing}" diff --git a/skills/gitlink-maintenance-orchestrator/scripts/test_validate_chinese_report.py b/skills/gitlink-maintenance-orchestrator/scripts/test_validate_chinese_report.py index 24e0351..af0f9af 100644 --- a/skills/gitlink-maintenance-orchestrator/scripts/test_validate_chinese_report.py +++ b/skills/gitlink-maintenance-orchestrator/scripts/test_validate_chinese_report.py @@ -2,21 +2,24 @@ import unittest -from validate_chinese_report import validate_report +from validate_chinese_report import REQUIRED_ASPECTS, REQUIRED_GROUPS, validate_report class ChineseReportValidatorTests(unittest.TestCase): def test_accepts_complete_chinese_report(self) -> None: - report = """# PR 维护全流程摘要 + groups = "\n".join(REQUIRED_GROUPS) + cards = "\n".join( + f"**{aspect}:** 证据支持当前判断 " + f"**[observe]**:已完成 {aspect} 的专项分析;依据:当前 Diff、测试和只读平台快照;" + "证据摘录:目标提交和验证命令均已记录;影响:维护者可据此决定下一步。" + for aspect in REQUIRED_ASPECTS + ) + report = f"""# PR 维护全流程摘要 **范围:** Gitlink/gitlink-cli **风险:** 阻断 1 项,高风险 2 项,以下内容用于帮助维护者快速确认处理顺序和责任人。 ## PR #123 -**代码审查:** 需要修改 **[action_required]**:失败路径缺少覆盖;依据:当前 Diff 与专项测试;证据摘录:无权限测试未覆盖,来源:`shortcuts/example/example_test.go:42`;下一步:补回归测试。 -**CLI 契约:** 兼容性部分成立 **[partial]**:旧调用正常但 JSON 缺边界;依据:帮助和输出对照;证据摘录:`--help` 可用但 JSON golden 缺少非法参数样例;下一步:补 golden。 -**仓库关系:** 需要调整顺序 **[reorder]**:目标消费上游字段;依据:主线、open 和 merged 索引;证据摘录:open PR #124 修改同一字段;影响:先稳定上游。 -**集成门禁:** 当前被阻断 **[blocked]**:关键测试未完成;依据:合并态和验证账本;证据摘录:`go test ./...` 尚未在合并工作树执行;下一步:补测试。 -**维护状态:** 需要维护者接单 **[action_required]**:责任方尚未确认;依据:时间和分配快照;证据摘录:队列快照中的 reviewer 与 assignee 均为空;下一步:安排 reviewer。 -**最终结论:** 修复阻断问题后重新审查 **[blocked]**:存在未解决高风险项;依据:CR-001 与 IN-001;证据摘录:失败路径缺测试且全量验证未运行;下一步:修复并重跑。 +{groups} +{cards} ## 先处理这几项 先修复真实响应错误,再补充测试,然后重新执行完整验证并由维护者复看。 ## 五个专项状态索引 diff --git a/skills/gitlink-maintenance-orchestrator/scripts/validate_chinese_report.py b/skills/gitlink-maintenance-orchestrator/scripts/validate_chinese_report.py index 87dd867..910b857 100644 --- a/skills/gitlink-maintenance-orchestrator/scripts/validate_chinese_report.py +++ b/skills/gitlink-maintenance-orchestrator/scripts/validate_chinese_report.py @@ -20,12 +20,47 @@ REQUIRED_MARKERS = ( "## 完整证据与限制", "## 证据台账", ) +REQUIRED_GROUPS = ( + "### 代码审查", + "### CLI 契约", + "### 仓库关系", + "### 集成门禁", + "### 维护状态", + "### 编排器综合判断", +) REQUIRED_ASPECTS = ( - "代码审查", - "CLI 契约", - "仓库关系", - "集成门禁", - "维护状态", + "贡献价值", + "Review 履约", + "逻辑正确性", + "代码质量与可维护性", + "测试覆盖", + "安全与性能", + "参数与帮助", + "JSON 与文本输出", + "错误与退出码", + "编码与颜色", + "兼容与文档", + "对当前主线", + "对 open 队列", + "对 merged 历史", + "完整性比较", + "依赖与处理顺序", + "合并态与冲突", + "构建", + "测试与功能一致性", + "CI 与证据关联", + "安全与发布影响", + "集成结论", + "响应 SLA", + "当前等待方", + "Reviewer 负载", + "责任停滞", + "安全与 Issue 优先级", + "证据完整性与新鲜度", + "跨专项结论一致性", + "风险传播与门禁对齐", + "动作去重与责任归属", + "决策置信度", "最终结论", ) FORBIDDEN_ENGLISH_TEMPLATES = ( @@ -74,10 +109,16 @@ def validate_report(text: str, required_prs: list[int] | None = None) -> list[st continue next_heading = PR_HEADING.search(text, start + 1) end = next_heading.start() if next_heading else len(text) - cards = CARD_PATTERN.findall(text[start:end]) + section = text[start:end] + for group in REQUIRED_GROUPS: + if group not in section: + errors.append(f"PR #{number} is missing assessment group: {group}") + cards = CARD_PATTERN.findall(section) aspects = {card[0] for card in cards} if len(cards) < len(REQUIRED_ASPECTS): - errors.append(f"PR #{number} has {len(cards)} judgment cards, need 6") + errors.append( + f"PR #{number} has {len(cards)} judgment cards, need {len(REQUIRED_ASPECTS)}" + ) for aspect in REQUIRED_ASPECTS: if aspect not in aspects: errors.append(f"PR #{number} is missing aspect card: {aspect}")