Chainlink Flaky Test 修复技能:JIRA 工单语义状态转移协议(transition-ticket)深度解析

原创2026-09-15 11:03:47174 阅读
文章标签:区块链Web3后端

Chainlink Flaky Test 修复技能:JIRA 工单语义状态转移协议(transition-ticket)深度解析

Chainlink 仓库的 tools/test 模块内置了一个面向 AI Agent 的 flaky test 修复技能(fix-flaky-tests),其中 transition-ticket.md 定义了工单状态流转的核心操作:接收一个语义化目标状态(如 In Review、Open),解析为 JIRA 工作流中实际存在的 transition 名称后应用。本文完整继承该协议的操作步骤、别名映射表与指派人策略,并结合仓库中的技能主文件、JIRA 操作索引与子代理契约,讲清它为什么是"语义到实际"的翻译层、FIXED 工单为什么绝不能取消指派,以及它与 claim / abandon 等操作如何拼成完整闭环。

1. 背景:fix-flaky-tests 技能与 JIRA 工单生命周期

tools/test 是基于 testrig 的 Go 测试 harness,提供 make test ARGS="diagnose ..." 用于复现 flake、race、timeout。SKILL.md 是 Agent 执行 flaky test 诊断-修复循环的技能入口,当用户指定了 JIRA 工单(或 N 个符合条件的 flaky-test 工单)作为目标时,整个工作流就变成:

领取工单(Open → In Progress)→ 诊断修复(diagnose 循环)→ 按结果流转工单(FIXED → In Review / ABANDONED、MISMATCH → Open)→ 写调查更新评论。

其中"流转工单"这一步由 jira.md 统一调度,而具体的状态转移动作委托给本文主角 transition-ticket.md。它被定位为一个可包含引用(includable reference):自声明输入、步骤与输出,供 claim-ticket、abandon-ticket 等操作文件复用。

设计动机在于一个工程现实:不同团队的 JIRA 工作流命名习惯差异极大——有的团队用 "In Development",有的用 "Active",有的把收尾状态叫 "Closed",有的叫 "Resolve"。如果协议硬编码某个 transition 名称,换一个项目就会直接失败。因此 transition-ticket 采用"语义目标 + 别名表"的翻译层设计。

2. transition-ticket 核心操作步骤(完整继承)

原文档定义的操作输入是 jira_key(工单键)、target(语义目标状态),可选 accountId(当前用户的 Atlassian 账号 ID)与 original_assignee(slim record 中记录的原始指派人)。步骤如下:

步骤 1:查询可用 transition 调用 mcp__atlassian__getTransitionsForJiraIssue,传入 jira_key,拿到该工单在当前状态下所有可执行的 transition 列表。

步骤 2:别名匹配 将 target 与返回的可用 transition 做匹配,使用第 3 节的别名表,并取响应中出现的第一个匹配别名(Pick the first alias that appears in the response)。

步骤 3:执行转移 调用 mcp__atlassian__transitionJiraIssue,传入匹配到的 transition ID。

  • 对关闭类目标("Won't Do"、"Done"):如果该 transition 支持 resolution 字段,则设置 resolution = "Won't Do"(回退值 "Won't Fix")或对应的完成类解决码。

步骤 4:FIXED 交接特例(target = "In Review" 且提供了 accountId) 这是"修复者拥有评审"的交接语义:

  • 调用 mcp__atlassian__editJiraIssue,设置 assignee.accountId = accountId(即修复该 flake 的 investigator);
  • 不要取消指派。指派人必须保持为当前用户,使工单在 PR 合并前一直挂在对方的看板上;
  • 对 target = "In Review" 而言,original_assignee 仅作信息性字段,不恢复、不清空原始指派人。

失败路径同样被显式约束:claim-ticket 流程规定,若转移失败必须回滚取消指派并把失败原因写入 slim record 的 skip_reason(见 claim-ticket.md 步骤 3),保证工单不会停留在半认领状态。

3. 语义目标状态与别名映射表

原文档给出的别名表是协议的核心数据,必须按"按序尝试、首个匹配生效"的语义使用:

语义目标 按序尝试的名称
In Progress "In Progress", "In Development", "Active", "Start Progress"
In Review "In Review", "In Code Review", "Code Review", "Review"
Open "Open", "Reopen", "Backlog", "To Do", "Reopened"
Won't Do "Won't Do", "Won't Fix", "Reject"
Done "Done", "Closed", "Resolved", "Close", "Resolve"

两条关键规则:

  1. 顺序即优先级:表中名称按尝试顺序排列,匹配到响应中第一个出现的别名即停止;
  2. 宁错不猜:若没有任何别名命中可用 transition,协议要求返回错误而非静默挑选一个无关状态(return an error rather than silently picking an unrelated state)。这是对 Agent 幻觉行为的直接防御——错误比把工单流转到错误状态的成本低得多。

从别名表覆盖的名称变体("Reopened"、"Start Progress" 这类非常规命名)可以推断,该协议是针对多个真实团队 JIRA 工作流归纳出的兼容层,而非单一环境的约定。

4. 指派人策略:工单所有权协议

transition-ticket 内置了一张指派策略表(assignee policy),把"状态转移"和"所有权变更"绑定成原子约定:

结果 / 目标状态 转移后的指派人
FIXED → In Review accountId(investigator,即修复者)
ABANDONED → Open 取消指派(null)——见 abandon-ticket.md
MISMATCH → Open 若设置了 original_assignee 则恢复之;否则取消指派

其中 FIXED 路径的规则值得展开。jira.md 的 assignee_rules 记录了这条规则背后的教训:修复完成后取消指派曾是一个实际发生过的错误行为——当 original_assignee == accountId(即工单本来就是当前用户领的)时取消指派,会导致"领取即修复"场景下工单在 In Review 阶段失去 owner。因此协议硬性规定:FIXED 工单移入 In Review 时,investigator 必须始终是 assignee,直到 PR 合并。SKILL.md 的 JIRA 参考部分也重复强调:"After a FIXED outcome, the ticket must stay assigned to the investigator ... Do not unassign on FIXED"。

ABANDONED 路径则由 abandon-ticket.md 展开为三步固定序列:① editJiraIssue 取消指派(assignee 置 null)→ ② 按 transition-ticket 以 target = "Open" 流转 → ③ 按 investigation-comment.md 模板写一条 Outcome 为 ABANDONED 的调查更新评论(What was investigated 填停止原因,其余段落填 N/A)。其硬约束是"绝不允许已领取的工单停留在 In Progress"(Never leave a claimed ticket in "In Progress"),覆盖用户取消、用户跳过、未找到可行修复、所有权冲突、会话提前结束等所有中途停止场景。

original_assignee 字段来源于 slim record(见 slim-record.md),在 claim 阶段由 getJiraIssue 读取 fields.assignee.accountId 保存(无指派人为 null)。MISMATCH 路径恢复它,是为了把"误领/跨仓库"的工单交还给原主;而 FIXED 路径刻意忽略它,因为此时所有权已经合法地转移到了 investigator 手中。

5. 在完整调度逻辑中的位置

jira.md 的 <logic> 段定义了工单操作的总调度:

  1. 用户给出具体 JIRA 工单 → 执行 claim-ticket(指派给自己并转移至 In Progress,其中就调用 transition-ticket 以 target = "In Progress" 流转);
  2. 用户要求处理 N 个符合条件工单 → 执行 fetch-flaky-tickets 的 JQL 搜索循环(labels = "flaky-test" AND status = "Open");
  3. 否则按测试名反查相关工单;
  4. 工作结束后,无论结果如何,对每个工单:a. 写调查更新评论;b. 按结果转移工单——abandoned → Open(经 abandon-ticket 取消指派)、mismatch → Open(恢复 original_assignee)、fixed → In Review(transition-ticket 步骤 4 指派给 accountId,绝不取消指派)。所有调用都必须透传 atlassianUserInfo 的 accountId;abandon / mismatch 时透传 slim record 中的 original_assignee。

所有数据交换遵循"slim record 单传"原则:<absolute_constraints> 规定永不返回原始 JIRA API 对象,调用方只能拿到 slim-record.md 定义的精简 JSON(含 jira_key、test_name(customfield_13007)、package(customfield_13009)、previous_attempts、original_assignee、skip_reason),以避免反复调用 Atlassian MCP 重复读取同一工单。

执行层面,SKILL.md 的子代理协议要求:与 JIRA 交互时派生 JiraManager 子代理,并按 jira-mananger-subagent.md 初始化——该子代理的系统提示中固化了同样的所有权规则("FIXED → In Review: always set assignee to accountId; never unassign"),其输入契约为 {operation, accountId, cloudId} 加操作特定字段(jira_key、target、original_assignee 等),缺失必填字段时快速失败返回 success: false;允许的工具白名单仅含 getTransitionsForJiraIssue、transitionJiraIssue、editJiraIssue、addCommentToJiraIssue 等只读/工单类 MCP 工具,Temperature 固定为 0.0。这解释了 transition-ticket 步骤中那些"看似啰嗦"的前置参数(cloudId、accountId)从何而来:它们是所有 JIRA 操作的公共前置条件,在 jira.md 的 operation_requirements 中集中声明。

6. 适用前提与实操约束

  • MCP 依赖:整套协议依赖 Atlassian MCP 可用且已认证,否则 jira.md 要求立即停止并提示用户安装/认证,不得继续;
  • 参数来源固定:cloudId 来自 getAccessibleAtlassianResources,accountId 来自 atlassianUserInfo,均不可手工伪造;
  • 与诊断循环的衔接:SKILL.md 规定修复后至少要以 Standard profile(--iterations 30,见其 <diagnose-iterations> 表)重跑 make test ARGS="diagnose ..." 验证通过才允许进入 FIXED → In Review 流转,即 transition-ticket 的 FIXED 入口是有"验证门槛"的;
  • 仓库范围:技能约束只在 core/、deployment/ 包内免确认跑测试,claim 阶段还会做仓库归属校验(customfield_13009 中的 {owner}/{repo} 与 git remote get-url origin 比对),不匹配则不领取并写入 skip_reason——这与 transition-ticket 的 MISMATCH → Open + 恢复原指派人的路径形成首尾呼应。

7. 小结

transition-ticket 是一个体量很小但设计密度很高的协议文件:它用一张别名表解决了跨团队 JIRA 工作流命名不一致问题,用"首个匹配 + 未匹配即报错"消除了 Agent 的状态猜测,用指派策略表把工单所有权随状态转移原子化,并针对 FIXED 交接明确了"不取消指派"的教训式约束。它与 claim-ticket、abandon-ticket、investigation-comment 一起,构成 Chainlink flaky test 修复技能中完整的工单生命周期管理闭环;其全部设计(slim record 单传、子代理白名单、temperature 0.0、fail-fast 契约)都服务于同一个目标:让 AI Agent 对共享 JIRA 空间的操作可预测、可审计、可回滚。

登录后查看全文
chainlink