Issue 不是更长的 Prompt

发布边界:本文给出工程合同模型和示例,不冒充 GitHub 的正式 Schema;具体 Agent 能力、权限与自动化边界以产品文档和仓库配置为准。

摘要

Issue 不是更长的 Prompt,而是一次可判定的变更授权。本文把任务对象拆成合同、运行与交付三层,定义 identity tuple、状态门槛、steer/retry/cancel 事件和 stale-result fencing,并说明 merge、reject、revert 三种退出路径。

关键词

Coding Agent、GitHub Issue、任务合同、状态机、Stale Result

目录

  • 一、合同不是更长的提示词,而是一次可判定的变更授权
  • 二、一个任务对象必须拆成合同、运行与交付三层
  • 三、状态机的重点不是画箭头,而是规定每条边的门槛
  • 四、steer、retry、cancel 必须是事件,不能是对历史的改写
  • 五、stale result 是并发控制问题,不是模型质量问题
  • 六、rationale 与 confidence 只能优化复核队列
  • 七、一个不冒充 GitHub 正式 Schema 的 Issue Contract 示例
  • 八、回退不是一个按钮,而是三个不同阶段的退出路径
  • 参考来源

Coding Agent 留在聊天框里时,一次任务通常只是“提示词—回答—继续追问”。用户可以凭上下文判断它做到哪一步,也可以随时放弃这段对话。可当 Agent 被分配到 Issue,在临时开发环境中独立工作,并以 Draft PR 交付代码时,任务就不再是一段对话,而是进入了仓库的变更控制链路:它会占用执行预算、创建分支、产生提交、触发或等待 CI、接受人工复核,最后影响可合并代码。

GitHub 在 2026 年 7 月发布的 Linear 集成说明已经展示了这种转变:把一个 Linear Issue 分配给 Copilot cloud agent 后,Agent 会读取 Issue 内容,在 GitHub Actions 提供的临时环境中工作,打开 Draft PR,并把进度返回到 Issue;用户还可以按任务选择模型、自定义 Agent、目标分支、工作分支,并在执行中追加 steer 指令。与此同时,GitHub Issues 的自动化控制开始为部分 Issue 元数据动作记录 rationale、confidence 和 approval suggestion。两项更新放在一起看,真正重要的不是“Issue 也能启动 Agent”,而是 Issue 正在成为人、Agent、CI 与审核者共同读取的控制面。

但普通 Issue 仍然不等于工程合同。它可能只有一句目标,没有非目标;可能写了“修复登录问题”,却没有指定仓库、基线、允许改动的模块和验收测试;也可能在 Agent 运行期间被改写,导致输出对应的其实是旧需求。要让这条链路可执行、可回退、可审计,任务对象至少要把三类东西分开:合同快照、执行记录和交付证据。三者不能被一个不断变化的 Issue 正文混成一团。

任务对象拆成三层

一、合同不是更长的提示词,而是一次可判定的变更授权

一个合格的 Issue Contract 首先要回答“什么结果才算完成”,其次回答“Agent 被允许怎样完成”,最后回答“在什么情况下必须停止”。因此,合同快照至少应包含以下信息。

第一组是任务身份与责任边界:任务标识、合同版本、发起人、责任人、人工审核者、目标仓库、基准分支、基准提交,以及本次运行使用的工作分支。分支名本身不够,因为同一个 main 会持续前进;只有把 base_branch 与启动时的 base_sha 一起记录,后续才能判断 Agent 当时面对的代码世界是什么。

第二组是意图边界:目标、背景、范围和非目标。目标描述要可观察,例如“对批量导入接口增加幂等键校验,并在重复请求时返回既有任务编号”,而不是“优化导入稳定性”。范围列出允许触碰的模块、接口或文档;非目标明确本次不做数据库迁移、不改公共 API、不重写历史数据。非目标不是写给 Agent 的礼貌提示,而是防止它把局部修复扩张成架构改造的判定条件。

第三组是执行约束:允许使用的工具、允许访问的资源、禁止产生的副作用、模型或自定义 Agent、运行时限、AI/Actions 预算、最大尝试次数、最大改动规模,以及终止条件。GitHub 官方文档说明 cloud agent 每次任务只能改一个指定仓库、一次只在一个分支上工作、每个任务只能打开一个 PR,单个会话最长 59 分钟;这些产品限制应当进入任务规划,而不能等超时后才被发现。团队还可以设置更短的任务预算,例如 40 分钟、最多一次自动 retry、最多改 12 个文件。这里的预算不是精确预测,而是把“继续探索是否仍值得”变成显式决策。

第四组是验收合同:必须通过的测试、静态检查、构建任务、行为断言、兼容性约束、文档更新和人工验证步骤。验收条件要尽量绑定到可重复执行的证据。例如,“新增重复请求测试并通过 ./gradlew test”比“确保幂等性正确”更可执行;“公共 OpenAPI diff 为空”比“不要破坏兼容性”更可验证。对于无法自动化的部分,也要写明由谁、在什么环境、按什么步骤确认。

这些字段不必全部成为 GitHub 的原生字段。GitHub Issue 原生支持标题、正文、assignee、label、type、milestone 等元数据,组织还可以配置 issue fields,Issue Form 也能用必填输入收集信息并转换为标准 Markdown 正文。但是 GoalNon-goalsAllowed toolsBudgetTermination 并不是 GitHub 面向所有仓库提供的统一正式字段。最稳妥的实现,是把少量需要筛选和自动化的值放在原生元数据或团队自定义 issue fields 中,把完整合同写进版本化的 Issue 正文模板;控制面再在每次启动时对正文和字段做快照。

联合身份挡住旧结果

二、一个任务对象必须拆成合同、运行与交付三层

只保留 Issue 当前状态,会丢失“Agent 当时读到什么”;只保留 PR,会丢失“为什么允许做这次修改”;只保留会话日志,又无法证明哪一组检查和审批对应最终代码。因此,一个可审计的任务对象应有三层记录。

合同层保存启动时的不可变快照。最低限度包括 contract_version、Issue URL、Issue revision 或内容摘要、目标仓库、base_branchbase_sha、工作分支、目标、范围、非目标、工具约束、验收条件、预算和终止条件。Issue 后续仍可编辑,但编辑只会产生新合同版本,不能反向改写已经启动的运行。

运行层run_idattempt 为主键。它记录本次使用的模型、自定义 Agent、开始与结束时间、运行状态、输入合同版本、实际消耗、会话日志、工具调用、生成的提交、steer 事件、停止原因和错误摘要。GitHub 的 Agent session 已经能够展示进度、token 使用、会话长度、工具与验证活动,并把 cloud agent 提交链接回会话日志;这些平台证据很有价值,但团队控制面仍应保存指向它们的稳定引用,并明确它们属于哪次 attempt。

交付层描述“什么代码正在请求进入目标分支”。它至少包括 PR URL、PR 状态、head_sha、对应的 base_sha、CI run/check 标识、检查结论、人工审核决定、决定人、决定时间、拒绝原因、merge commit 或后续 revert PR。运行成功不等于交付成功:Agent 可以顺利结束,但 PR 可能没有满足验收;CI 可以全绿,但检查的是旧的 head;审核者可以批准,但批准后又有新提交。只有交付证据绑定到同一份代码身份,状态才能继续前进。

实践中,可以把一次可合并候选定义为四元组:

candidate = contract_version + base_sha + head_sha + attempt

这不是 GitHub 的官方对象或字段,而是建议由团队控制面维护的验证身份。任何一个值变化,都意味着先前的 CI、review 或授权是否仍有效需要重新判定。它把“看起来还是同一个 Issue 和 PR”转换成“是不是同一份合同、同一条基线、同一份输出、同一次执行”的严格问题。

状态边必须带门槛

三、状态机的重点不是画箭头,而是规定每条边的门槛

一条最小闭环可以表达为:

Issue → Agent Run → Draft PR → CI → Human Review → Merge / Reject

为了可操作,建议把它细化为团队控制面的状态,而不是假装这些名字都是 GitHub 原生状态:

ISSUE_DRAFT
  └─> ISSUE_READY
        └─> RUN_QUEUED
              └─> RUNNING
                    ├─> RUN_FAILED / RUN_TIMED_OUT / RUN_CANCELLED
                    └─> DRAFT_PR_OPEN
                          ├─> CI_AWAITING_APPROVAL
                          ├─> CI_RUNNING
                          ├─> CI_FAILED
                          └─> CI_PASSED
                                └─> HUMAN_REVIEW
                                      ├─> CHANGES_REQUESTED → 新 attempt 或继续 steer
                                      ├─> REJECTED
                                      └─> APPROVED → MERGED

ISSUE_DRAFT → ISSUE_READY 的门槛,是合同必填项完整、依赖已满足、责任人与审核者明确,并且任务规模适合单次 Agent 运行。这里的“适合”要尊重实际产品约束:跨仓库改动、需要多 PR 协调或明显超过会话时限的任务,应先拆分,而不是把失败交给 Agent 自己发现。

ISSUE_READY → RUN_QUEUED 时必须冻结合同快照并记录 base_sha。若启动入口会直接创建 PR,例如 GitHub 的 Linear 集成,就可以很快出现 Draft PR,但这不表示实现已经完成。Draft PR 的工程价值恰恰是把工作中输出放进不可直接合并的隔离区。GitHub 的通用 PR 规则也明确:Draft PR 不能合并,转为 ready for review 后才进入正式审核阶段。

DRAFT_PR_OPEN → CI 需要先确认被检查的 head_sha。GitHub 对 Copilot PR 的默认行为还包含一个重要人工门槛:Agent 推送后,Actions workflow 默认不会自动运行,维护者通常需要检查改动并点击允许执行;仓库可以另行配置自动运行。无论采用哪种策略,控制面都不能只记录“CI passed”,而要记录哪一个 check、由哪个可信来源、针对哪个 SHA 通过。受保护分支的 required status checks、required reviews 和 stale approval 设置,正是把这一要求落实到服务器端合并门槛的机制。

CI_PASSED → HUMAN_REVIEW 也不是把日志转交给人浏览,而是要求审核者同时复核三件事:实现是否满足目标,改动是否越过范围与非目标,证据是否覆盖验收条件。GitHub 明确要求 Copilot 产生的 PR 与其他贡献一样接受完整审核;在要求 PR approval 的仓库里,任务发起人对 Copilot PR 的批准甚至不计入所需审批数,还需要另一位审核者。这个设计提示我们:发起、执行和独立验证不应被压缩成一次“看起来合理”的确认。

APPROVED → MERGED 的门槛,应再次计算候选四元组。若审批后 head_sha 改变、基线更新触发严格同步要求、required check 失效,或合同版本被修改,结果应回到相应阶段,而不是沿用旧绿灯。REJECTED 则应是有原因、有责任人的终态:关闭 PR、保留分支和日志、记录不合格项。需要重新做时,创建新的 attempt,而不是清空旧记录后假装第一次运行从未发生。

Steer、Retry、Cancel 都是事件

四、steer、retry、cancel 必须是事件,不能是对历史的改写

Agent 工作过程中最容易破坏审计性的操作,就是把新指令直接追加到聊天或 Issue,然后继续把所有输出称为“同一个任务”。更稳妥的做法,是把 steer、retry 和 cancel 都建模为带时间、发起人和作用域的事件。

Steer 用于在当前合同内纠偏,例如“复用已有的 ErrorHandler,不要新增异常层级”。GitHub cloud agent 支持在会话中追加指令,Agent 会在当前工具调用结束后处理新输入。控制面应把每条 steer 追加到事件流,而不是修改最初提示词。还要区分两类 steer:如果它只是实现偏好,不改变目标、范围或验收,可以继续当前 attempt;如果它新增接口、扩大改动范围、修改非目标或验收条件,就已经是合同变更,必须提升 contract_version。旧 attempt 随即成为 SUPERSEDEDSTALE_RESULT,由人决定停止、保留参考,还是在新合同下重新启动。

Retry 不是把失败按钮再按一次,而是创建新的 run_id 与递增的 attempt。它应明确继承哪份合同、是否复用旧分支或提交、为什么上次失败、这次改变了什么。网络抖动导致的基础设施 retry,可以复用同一合同;验收失败后的 retry 往往需要新的 steer 或代码基线。无论哪种情况,旧 attempt 的日志、消耗和结果都不可覆盖,否则团队无法区分“模型第二次做对了”与“第一次结果被悄悄改掉”。

Cancel 是撤销本次运行继续执行的资格,而不是删除历史。GitHub 的 Stop session 会终止对应 Actions run,同时保留已经推送的提交;cloud agent 会话可以归档但不能删除。控制面因此应把取消后的提交视为证据或候选素材,而不是自动可合并结果。若团队后来决定采用这些提交,应在新的 attempt 中显式导入、重新绑定合同版本,并重新执行 CI 与审核。

五、stale result 是并发控制问题,不是模型质量问题

当 Issue、分支和审核都可以并发变化时,一个结果即使代码本身正确,也可能已经失效。建议至少在以下情况下标记 STALE_RESULT:Agent 启动后合同版本变化;目标分支推进到超出允许窗口的新基线;同一任务出现更新的有效 attempt;PR 的 head_sha 在 CI 或人工批准后改变;CI 结果对应的不是当前 head;审核基于旧 diff;任务被取消或正式拒绝后,旧分支又被推送;依赖 Issue 或外部接口版本已经变化。

stale 不等于删除,也不等于失败。它表示“这份结果不能凭现有证据继续晋级”。处理方式可以是重新基于新 base_sha 执行、把旧提交 cherry-pick 到新 attempt、重新跑检查、重新请求审核,或者直接拒绝。关键是 stale 判定要由可计算的版本和 SHA 触发,而不是依赖某个人记得“这个 PR 好像有点旧”。GitHub 的 branch protection 可以在新提交后撤销 stale approvals,也可以要求分支在合并前与 base 保持同步;任务控制面应在此基础上再补上合同版本和 attempt 维度。

六、rationale 与 confidence 只能优化复核队列

GitHub Issues 新增的 rationale、confidence 和 approvals 很容易被误读为一套通用 Agent 授权框架。官方边界其实很清楚:这组能力目前是 public preview,适用于自动化对 label、field、issue type、close 和 assignee 等受支持 Issue 属性的修改,不覆盖推代码或开 PR;approval suggestion 还是工作流便利,不是服务器端安全边界,有权限的 Agent 可以直接应用变更。

因此,rationale 和 confidence 最适合解决“人先看哪一条”的问题。例如高置信度的标签建议自动应用,中低置信度建议进入复核;某次运行的理由摘要也可以帮助审核者定位风险。但它们不能回答另外两个问题:Agent 是否被授权修改这个仓库,以及当前代码是否已经被验证。前者需要权限与合同状态,后者需要绑定到具体 SHA 的 CI 和人工审核。

可以把三类判断明确分开:confidence 负责路由,authorization 负责准入,verification 负责放行。模型说自己有 95% 置信度,不会让越界改动变得被授权;解释得再完整,也不会让未运行的测试变成通过;人工同意一条 Issue 标签建议,更不等于批准其后所有代码副作用。理由应该被保存,置信度可以影响队列,但合并资格只能来自可执行约束和可复核证据。

七、一个不冒充 GitHub 正式 Schema 的 Issue Contract 示例

下面的示例把 GitHub 原生元数据与团队自定义正文分开。标题、正文、assignee、label、type、milestone 是 GitHub Issue 能承载的常见元数据;组织 issue fields 或 Project fields 是否使用,由团队配置决定。代码块中的 Contract versionGoalAllowed tools 等标题是团队约定的 Markdown 合同段落,不是 GitHub 为所有仓库预置的正式字段。

GitHub 原生/可配置元数据:
- Title: [Agent Task] 为批量导入接口增加幂等处理
- Type: Task
- Assignee: Copilot
- Labels: agent-ready, backend, bounded-change
- Milestone: 2026.08 Reliability
- 可选组织 issue fields: Priority=High, Effort=Medium

## Contract identity
- Contract version: v3
- Owner: @alice
- Required human reviewer: @bob
- Repository: acme/import-service
- Base branch: main
- Base SHA: 8f31c2a
- Working branch: agent/issue-1842-import-idempotency

## Goal
当客户端以相同 idempotency key 重复提交同一批量导入请求时,返回首次创建的
任务编号,不再创建第二个任务。

## Scope
- `src/import/api/**`
- `src/import/service/ImportJobService.java`
- 对应单元测试与接口文档

## Non-goals
- 不迁移数据库结构
- 不修改公共请求/响应字段
- 不重写历史任务
- 不调整其他导入通道

## Allowed tools and constraints
- 允许读取和修改本仓库、运行 shell、Gradle、单元测试和 lint
- 允许读取现有 Issue/PR 作为上下文
- 不得修改 `.github/workflows/**`
- 不得向外部服务写入数据
- 若需要跨仓库改动,立即停止并报告

## Acceptance criteria
- [ ] 新增“首次请求创建任务”的测试
- [ ] 新增“相同 key 与相同 payload 返回既有任务”的测试
- [ ] 新增“相同 key 与不同 payload 返回冲突”的测试
- [ ] `./gradlew test` 通过
- [ ] OpenAPI breaking-change check 通过
- [ ] 现有导入接口响应结构不变
- [ ] PR 正文列出改动、验证结果和剩余风险

## Budget
- Session timeout: 40 minutes
- Max attempts without human re-authorization: 1
- Max changed files: 12
- Max AI/Actions budget: 使用团队为该任务配置的上限

## Termination conditions
遇到以下任一情况停止,不自行扩大范围:
- 需要数据库迁移或修改公共 API
- 需要访问未授权外部系统或凭证
- 无法在 40 分钟内建立可运行测试
- 预计改动超过 12 个文件
- 当前 main 与 Base SHA 的差异使原方案失效

## Change control
- 实现偏好可通过 steer 追加,并写入运行事件流
- 改变 Goal、Scope、Non-goals 或 Acceptance criteria 时,Contract version 必须递增
- retry 必须创建新的 run_id/attempt,不覆盖旧运行
- cancel 后现有提交不得直接进入合并流程
- CI 与人工批准必须绑定当前 contract_version、base_sha、head_sha、attempt

这个模板的价值不在字段数量,而在于每一项都能参与状态转换。若目标不可观察,验收就无法判定;若没有非目标,steer 就无法判断是否越界;若不记录基线与 head,CI 和审批就可能验证错对象;若没有预算和终止条件,Agent 只能以超时或失控扩张结束;若 retry 覆盖旧运行,审计记录就失去意义。

退出路径要分三个阶段

八、回退不是一个按钮,而是三个不同阶段的退出路径

“可回退”至少包含三种动作。运行尚未完成时,用 cancel 停止会话并冻结已有提交;PR 已产生但不应进入主干时,用 reject 关闭 PR、保留原因和证据;已经合并后发现问题时,以 merge commit 为锚点创建 revert commit 或 revert PR,并重新走 CI 与审核。三者的对象不同,不能都写成模糊的“rollback”。

真正可审计的系统还要回答:谁在什么合同版本下启动了哪次运行;Agent 使用了什么输入并做过哪些 steer;哪些提交来自哪次 session;CI 检查了哪个 SHA;谁基于哪个 diff 做出批准或拒绝;最终合并了什么;若回退,又撤销了哪个 merge commit。GitHub 已经提供 Issue、session log、commit、PR、Checks 和 Review 等证据载体,控制面的任务不是重新发明它们,而是用稳定标识把它们串成一条不能被事后改写的链。

当这条链建立起来,Issue 才从“给 Agent 的需求描述”升级为工程合同。Agent 可以自主探索,但只能在冻结的目标、范围和预算内探索;人可以中途 steer,但重大变更必须生成新合同版本;失败可以 retry,但每次尝试都有独立身份;结果可以保留,但 stale 输出不能沿用旧验收;PR 可以看起来很合理,但只有当前 SHA 上的 CI 与人工复核都成立,才具备合并资格。

因此,Issue 驱动 Coding Agent 的核心控制面不是一个更复杂的聊天窗口,而是一台以版本、状态和证据运行的变更机器。它不要求模型永远正确,而是要求每次错误都有明确停止点,每次重试都有历史,每次批准都有对象,每次合并都能还原理由与证据。这才是从“Agent 帮我写代码”走向“Agent 可以进入生产工程流程”的真正分界线。

参考来源

引用编号与逐项用途见 source-map.md。本文主要依据 GitHub 2026 年 7 月 23 日发布的两篇 Changelog,以及 GitHub Docs 中关于 Copilot cloud agent、Linear 集成、Agent sessions、PR 审核、Issue 元数据、Issue Forms、Draft PR 与受保护分支的官方说明。所有自定义状态名、候选四元组和 Issue Contract 段落均为本文提出的控制面设计,不宣称为 GitHub 官方 Schema。

FAQ

Issue 里有验收标准还不够吗?

不够。还需要授权范围、工具约束、预算、终止条件、运行身份、状态门槛和退出路径。

retry 为什么不能直接重跑?

重跑会产生新的 attempt;必须保留旧代记录并阻止迟到结果覆盖当前版本。

confidence 能不能决定自动合并?

不能单独决定。它只能优化复核队列,合并仍应依赖可验证检查与授权门槛。