[!danger] 依旧是长文警告。。。
最近这几天,新的模型又开始一个接一个发布了,真都体验不过来了。佬话重提下,模型的能力越来越强了,写代码的门槛基本上快没了。现在就算不是技术出身,我们也能借助 ai很快做出一个能跑的项目来。
但项目真正放到生产环境里,问题就会变成ai生成的代码能不能长期维护下去。比如需求有没有先说清楚,方案有没有经过设计,开发过程有没有遵守项目规范,做完之后有没有测试和验证,这些工程化问题并不会因为模型变强就自动消失的。
所以我现在用 ai 开发,关注重点已经放到了整个开发过程上。怎么让ai去按照相对稳定的流程推进,怎么让生成的代码符合项目规范,怎么减少遗漏、返工和人工反复 review,这些事情对我来说还是比较重要的。特别是涉及团队用ai开发的时候,频繁的review确实很让人头大。
因为我自己算是热衷于sdd这类开发方式的。在上一篇文章里,我分享了trellis在实际开发中的定位和用法,也聊了它怎么通过spec、任务拆分和上下文管理,把 ai 开发这种流程搞得稳定一些。
而这次我准备再继续聊另一个工程化工作流框架comet。从我这段时间体验下来,不得不说comet 是一个非常强的 agent skill harness。接下来我就简单以我个人的体验来分享下comet的强悍能力。
最早接触comet
我最早知道 comet,还是从站内大佬发的开源推广帖开始的:
本帖使用社区开源推广,符合推广要求。我申明并遵循社区要求的以下内容:
我的帖子已经打上 开源推广 标签: 是
我的开源项目完整开源,无未开源部分: 是
我的开源项目已链接认可 LINUX DO 社区: 是
我帖子内的项目介绍,AI生成、润色内容部分已截图发出: 是
以上选择我承诺是永久有效的,接受社区和佬友监督: 是
以下为项目介绍正文内容,AI生成、润色内容已使用截图方式发出
佬友们,我最…
我发现它也是为了解决我比较关心的 ai 开发编排问题。openspec 和 superpowers 我之前都用过,还把它们放在一起使用过,所以看到大佬专门做了一层编排,还是感觉很厉害的。
主要是我在其他社区也经常刷到分享的 确实很厉害 嘿嘿
用过openspec 和 superpowers的佬们,应该都比较清楚它们各自在做什么吧。
openspec主要是负责change、spec和归档这套需求生命周期的。比如我们可以先把需求整理成 spec,再根据变更去开发,最后把已经完成的内容合并回正式规范里。
而superpowers更偏向具体的工程方法。从需求澄清、设计、计划,到测试、调试和代码审查,它会给ai一套相对完整的执行流程。
它们一个负责把需求和规范管起来,一个负责把开发流程管起来。我之前是结合openspec和 superpowers组合起来用的,但是实际体验却没有预想中那么协调。
两套skill都有自己的阶段和上下文,真正串起来之后,什么时候该从 openspec 切到 superpowers,做完之后怎么再切回来,中间被打断了又该从哪里继续,这些都需要去额外处理。
特别是任务跑得比较长的时候,只要偶尔阶段状态没有接好,后面修复上下文和重新对齐进度就感觉很麻烦。
有时候代码已经搞了一半,重新开一个会话,又要花时间告诉 ai 前面做了什么、现在走到哪一步、接下来该读取哪个文件。就是感觉不太协调的那种,维护起来比较麻烦。
而我觉得comet当时主要解决的就是这个问题的吧。那时候的comet还是以classic工作流为主的,它保留了openspec和superpowers作为外部依赖,再通过状态、阶段编排和guard脚本把两套能力串起来。
openspec继续管理change和spec,superpowers继续负责设计、计划、测试等这些工程方法,而comet则在外面管理当前走到哪个阶段、下一步是否可以继续、任务中断后该怎么恢复等等。
[!warning]不过因为后面 fable 5、gpt5.6 这种强模型开始出现后,模型本身已经能完成相当多的探索,计划、实现和测试工作了,再让它严格走一遍完整的方法链的话, 反而会和模型自己的判断有冲突了。整个过程就会变慢,上下文和 token 消耗也会跟着增加。
不过也就是在这个阶段,comet又推出了native工作流模式。
本帖使用社区开源推广,符合推广要求。我申明并遵循社区要求的以下内容:
我的帖子已经打上 开源推广 标签: 是
我的开源项目完整开源,无未开源部分: 是
我的开源项目已链接认可 LINUX DO 社区: 是
我帖子内的项目介绍,AI生成、润色内容部分已截图发出: 是
以上选择我承诺是永久有效的,接受社区和佬友监督: 是
以下为项目介绍正文内容,AI生成、润色内容已使用截图方式发出
之前我的开…
native模式保留了需求澄清、spec、验收标准、状态管理和中断恢复这些外置能力,具体怎么探索、怎么实现、要不要使用 tdd、要不要调用子代理,则交给强模型自己判断就好了。
这个变化也是我真正开始爽用comet的原因。好了,接下来我们就进入正文理解吧。
comet
因为我现在主要使用的就是gpt5.6这一档强模型的,所以native这种轻约束的工作方式,更符合我现在的开发习惯。
当然classic 依然有自己的价值。有些团队希望大家按照同一套工程方法开发,也可以继续选择 classic。
所以 native 和 classic 的选择,不能只看模型强不强。模型自身的执行能力是一方面,项目和团队到底需要多强的流程约束,也是一个很重要的判断标准。
另外我看了一圈,社区里关于 comet 的实际使用分享还不算多,所以这篇我会多保留一些说明过程。算是半篇教程,再加半篇我自己的使用体验吧。
如果只看前面的classic,我们很容易继续把它理解成openspec加superpowers,再在外面套一层状态机。
但是现在版本的 comet 已经不止这一部分了。
它有native 和 classic 两套独立的需求工作流,也提供skill的创建、评估、发布和分发能力。需求怎么从想法走到归档,任务中断后怎么继续,结果怎么验收,团队自己的工作流怎么整理成 skill,这些都已经被放进了同一套工具链里。
官方把 comet 定义成一套 agent skill harness。这个词听起来有点抽象,我自己更愿意把它理解成包在agent外面的一层工程化框架。
模型继续负责思考和写代码,comet负责管理一次 change 怎么开始、当前走到哪里、中断后怎么恢复,以及满足哪些条件才算真正完成。
如果我们把现在的 comet 拆开来看,大概可以分成三块。
第一块是需求交付工作流,也就是 native 和 classic。两套工作流都可以把一次产品需求或者代码变更,从最开始的想法一路推进到实现、验证和归档,只是具体的执行方式不一样。
第二块则是状态恢复和验收。comet会记录当前 change 走到了哪里,还缺什么产物,验证有没有通过等等。即使中间被打断后还可以继续,就算验证失败了也能回到实现阶段修复,不需要每次都重新猜项目现在是什么状态了。
第三块就是skill工程化了。comet 可以把我们自己的工作流整理成可复用的 skill,然后再对它进行评估、审核、发布和分发等等。这一块其实已经超出了单次需求开发了,开始往团队能力沉淀上走了。
所以我们平时真正使用的时候,主要会接触下面这些入口:
[!example]
- /comet负责启动一次 change,并根据项目配置进入native或classic。
- /comet-any负责创建、优化、组合或整理可复用的 skill。
- comet eval负责评估 skill 的实际效果。
- comet publish负责审核和发布/comet-any生成的产物。
- comet status和comet doctor负责查看当前状态、下一步动作和恢复建议。
这几个入口放在一起,基本就是现在 comet的全貌了。它既可以管理一次需求怎么交付,也可以管理skill是怎么创建验证和发布的。
那我们继续往下看。
comet 安装与初始化
comet支持的平台很多。截至8月份发布的0.4.0-beta.18,comet 已适配34个ai编码平台了。
它会根据不同平台的目录约定,把比如skill、rules和hooks这些安装到对应位置上。这样一来同一套工作流可以放进多个ai编码工具里,就不需要我们再每个平台重新维护一份了。
当然,不同平台最终能发挥出来的能力也是不完全一样的。
就比如有的平台支持hooks和独立subagent execution,而有的平台就只能使用基础 skill。所以安装成功只能说明comet 可以被平台识别了,但是具体能用到什么程度,主要还得看平台本身提供了哪些能力。
安装还是全局安装npm包就可以了, 不过我这篇的截图和内容主要是基于0.4.0-beta.17版本的。发文时官方最新版本为 0.4.0-beta.18了都,后续版本的界面和部分能力可能会有点差异。
npm install -g @rpamis/[email protected]
检查命令是否可用:
comet --version

我已经安装过了,然后接下来我们就可以在项目中使用了,进入项目后我们只需要执行:
comet init
就可以安装了,大概是这样的

真正重要的是工作流选择。comet会给出 native、classic 和两者三个选项。
native 面向强模型,保留澄清、状态、检查和自动推进这些外置能力。
classic 会提供更完整的spec、设计、计划和 tdd 阶段,并继续使用 openspec 和 superpowers。
两者也可以同时安装,只是它们会保留各自独立的入口和工作区,/comet 默认进入 native,需要时也可以显式进入 classic。
[!info]如果我们用的是强模型 建议就用native模式就行了。
选择 classic 或者两者时,还需要选择 skill 安装模式。copy给每个平台保存独立副本,而symlink是让多个平台共用.comet/skills/中的一份文件。考虑到项目还需要团队协作,我选择了更直观的copy了。
不过这里还有一个容易忽略的区别。只有classic还会安装或者配置 openspec、superpowers,而native自己就能完成整套工作流,不需要这些外部 skill了。
[!info] 这一步也能很直观地看出两套工作流的结构差异。native 本身就是一套自包含工作流,classic 还需要把 openspec 和 superpowers 接入同一个开发流程,所以初始化时会多出外部依赖的检测和安装。
安装完成后,终端会列出各个平台的 skill、rule 和 hook 安装结果。如果我们选择 both,并使用默认 docs 布局的话,项目里会出现三组工作区:docs/comet/ 属于 native,docs/openspec/ 保存 classic 的 change 和 spec,docs/superpowers/ 保存设计、计划和验证报告。
我们安装完成后提示这个:

一般我安装完成后我都习惯先用这两个命令看看:
comet doctor
comet status
comet doctor 用来检查安装和配置是否完整,comet status 会显示项目默认入口,以及彼此分开的 native、classic 和未托管 openspec changes。
安装部分我们大概了解这些就够了。等真正开始做项目开发的时候,我们平时使用最多的入口还是 /comet,所以我们不用把 classic 的每一个阶段命令都背下来的。
接下来我们还是先回到 classic模式,先看看之前comet 是怎么把 openspec 和 superpowers 串成一条完整工作流的。
其实等我们理解了这个模式以后,后面我们再去看native为什么会出现,应该就会更容易看出两套模式的差别了。
这里我们可以先简单看下安装成功后的config.yaml配置内容:

经典五阶段强约束 classic模式机制
我们先看下经典的classic模式是如何实现路由任务、推进阶段,并在中断后恢复的。
一、/comet确定流程选择
对于我们项目中遇到新增的功能或者需要跨模块改造或接口调整和数据结构变化这类相对复杂需求的时候,classic就会走一套完整的流程,并把需求设计开发验证和归档分开去处理。
比如我只需要在ai编程工具里输入/comet命令就行。这些/comet-*都是skill入口,不是终端命令。等进入classic 后,完整功能会走下面五个阶段了:
open → design → build → verify → archive

这条完整路径叫做full。此外classic还准备了hotfix和tweak两条轻量路径,这俩都会跳过深度设计,直接走这个阶段:
open → build → verify → archive
我们正常使用/comet命令时不需要去提前手动选择。等进入classic 后,/comet-classic会结合我们具体的需求描述、仓库状态和风险信号自动路由的。
比如我们是修复已有bug,并且没有新增能力、接口和架构变化时就会优先进入hotfix模式。
如果可以收敛到单个openspec change而又不需要完整设计的调整会进入tweak模式。
如果是新功能相对复杂或跨模块修改则会进入full模式了。
而且如果证据不足的话或者需求描述和风险信号互相冲突的情况下,comet就会先停下来让我们确认,不会自己猜一条路径。已经明确想走轻量流程时,也可以直接使用/comet-hotfix或 /comet-tweak命令手动选择。
如果auto_transition配置开启后,没有歧义的阶段会就在guard通过后自动衔接。而需求确认、执行方式、验证偏差和最终归档仍然会停下来等我们选择的。
二、full 按五阶段从需求走到归档
当确定走 full 以后,comet 会按 open、design、build、verify、archive 依次推进。每一步调用谁、做什么、留下什么文件,可以先看这张表:

open 会先调查项目并确认需求边界,然后生成 proposal.md、design.md、tasks.md、delta spec 和 .comet.yaml。其中 delta spec是需求和验收标准的权威来源,后面的设计和实现都要围绕它继续。
进入 design 后,comet 会把这些文件整理成 handoff,再交给 superpowers 生成 design doc。交接包会保存来源和 hash,前面的需求文件发生 变化时,guard 会要求重新生成。上下文压缩也发生在这里,beta 会完整保留 delta spec,把其他文件换成 hash 引用,大约能节省 25% 到 30% token,不过默认保持 off。
build 会继续生成实现计划,并让我们选择 branch 或 worktree、执行方式、tdd 和 review mode。任务完成后进入 verify,light 负责构建、测试、安全和轻量审查,full 还会检查两份设计、验收场景和 spec 漂移。验证失败会回到 build,验证报告和 guard 都通过以后才能进入 archive。
archive 前还会做最后确认。确认以后,delta spec 会合并进主 spec,change 会移到 docs/openspec/changes/archive/,design doc 和 plan 也会写入归档标记,最后记录 archived: true。
三、hotfix 和 tweak 保留轻量路径
hotfix 和 tweak 都会跳过完整的 design,继续保留 openspec change、验证和归档。正常从 /comet 进入时会自动路由,已经明确目标时也可以直接调用对应 skill。
hotfix:open → build(包含根因消除检查) → verify → archive
tweak: open → build(通过 openspec apply) → verify → archive

两条路径都会保留 proposal、tasks 和状态文件,也都要经过最终归档。过程中出现跨模块、schema、新 public api 或深层架构变化时,comet会暂停确认是否升级 full,已经完成的代码和 openspec 产物不会丢失的。
四、状态文件让任务可以随时继续
不过我们实际开发中往往很难在一个上下文窗口里从需求一直跑到归档的。假如我们做到design 或 build 时关掉窗口了,换个新的窗口会话,我们就可以直接重新输入神秘命令:
/comet 继续
接下来 comet会自动去完成这四件事的:
[!example]
- 读取项目配置,从而确定这次进入的classic。
- 查找正在进行的change,如果只有一个就直接恢复,存在多个则先让我们选择。
- 对照.comet.yaml文件和磁盘产物,去判断当前停在open、design、build、verify 还是 archive阶段。
- 复用原来的 branch 或 worktree,然后再调用当前阶段对应的skill。
之所以它能这样恢复,其实是因为 classic 会把进度持续写进项目文件的:

但是如果状态和实际文件对不上时,classic就会根据磁盘产物重新校准的。
当change绑定的branch 或 worktree 不一致时classic就会暂停确认,跨设备恢复的时候需要
要先同步代码、.comet/config.yaml、当前的openspec change的产物、.comet.yaml 以及相关的 superpowers 文档。然后我们通过git提交和推送是比较常见的做法。
我们平时可以直接使用/comet继续其实就已经够了,只有路由或安装异常时我们才需要使用comet status和comet doctor命令查看和矫正。
这套恢复机制对个人长任务和团队协作其实都很实用的。openspec管理需求与spec,superpowers来负责设计、计划和开发,comet则把阶段状态、交接、验证和恢复串联起来。
我们只需要从 /comet 进入,后续流程就会根据当前change和磁盘产物继续推进。

不过对我现在常用的强模型来说,这套完整方法链有时又显得偏重,这也正是我后来转向native 的原因吧。
两种工作流对应两种约束方式
我们前面看完classic模式后会发现,其实它对整个开发过程管得非常细。
从需求、设计、计划、tdd、子 agent 开发,到代码审查、验证和归档,每一步都有对应的 skill 和阶段约束。对于能力相对弱一些的模型来说,这套方式其实非常有价值,可以减少项目开发中的遗漏,也能避免模型做到一半开始偏离需求。
不过随着模型不断的变强,情况也开始发生变化。
[!warning]相信很多人在使用模型开发中都有感觉,即使我们不使用superpowers,模型自己也会调查项目、拆解问题、形成计划、补充测试和检查实现。这个时候再叠加一套相似的通用方法,模型自身的思考过程和 skill 规定的执行方式就可能互相影响,最后反而多了一层token 和时间消耗。
comet 作者在分享native模式的时候也做了说明,我是非常认同的。
所以 native模式的发布开始把具体怎么执行交还给模型。只保留需求澄清、spec、验收标准、状态推进、验证、归档和跨设备恢复等等这些需要长期保存的事实。模型主要负责怎么实现,而 runtime负责判断是否完成。项目文件负责在会话中断后怎么恢复这些。
总的来说,classic负责把工程方法交代得足够清楚,而native更愿意相信强模型自己的判断,同时把需求、验收和状态这些不能丢的部分继续保持。
[!info]但 native 并不是 classic 的替代品。
模型能力只是会影响我们最终需要多少执行指导,而涉及到项目风险,团队协作和交付治理则是另一条维度了。
以后更可能出现的情况是native覆盖更多常规开发任务,而 classic就留在高风险、强协作和强审计的交付场景里去执行。
两套工作流模式我们可以做一个比较直观的使用场景对比来看:

所以我现在的理解是native去负责让强模型更自由地完成任务,而classic去负责让团队更稳定地交付任务。
那我们接下来再看看 native 具体又是怎么工作的,以及它为什么只用一套更轻的runtime 就能把整个需求稳定的推进下去。
强模型工作流 native模式机制
native模式会继续保留需求、验收和状态这些外置能力的,不过具体怎么实现则交给模型来判断。我们可以先记住这三层分工:
[!todo]
模型主要负责怎么去实现
runtime主要负责是否完成
项目文件主要负责怎么恢复的
native模式的四个阶段是这样流转的:

下面我直接按照一次实际需求场景change 的执行顺序来看看这套机制是怎么跑起来的。
一、从 /comet创建一个 native change
依旧开始执行/comet 命令,比如我这次给项目增加的是邮箱和密码登录接口,直接从统一入口创建任务:
/comet 给当前项目增加邮箱和密码登录接口
然后/comet就会先读取.comet/config.yaml,根据default_workflow进入 native模式。

这里不会根据任务大小或者当前模型临时猜测工作流的, 项目配置写的是native,就会确定性进入 /comet-native。
创建change时,comet还会根据当前仓库状态让我们选择任务放在哪里执行,这里一般是三个工作区选择:

如果我们准备在当前目录里串行完成任务,选择current就够了。如果是团队同时推进多个 change,或者准备开多个 agent 并行开发,worktree会更合适,每个 change 都有自己的分支和工作目录,恢复时也不会跑到另一个任务里。
等我们选择完成后,项目里会出现两类内容:
docs/comet/changes/add-login-api/
└── brief、spec、状态和最终验证报告
.comet/runtime/native/changes/add-login-api/
└── 本机运行状态、日志、锁和事务
其中前一部分会跟着项目保存,后面shape、verify和archive产生的正式文件都会放在这里。后一部分只服务当前设备上的执行过程,平时不用我们手动修改。
同时,.comet/current-change.json也会记录接下来的写入属于哪个change。它只是确定当前任务归属,一个项目仍然可以保留多个正在进行的 native change。
到这里,comet只是为需求建立了一个可以持续推进和恢复的任务现场。但是具体要做成什么样、哪些行为需要我们决定,这些会在接下来的shape阶段让我们继续确认的。
二、shape阶段完成需求澄清与验收定义
等change 创建以后,native是不会马上开始写代码的,而是先进入shape阶段。
native 虽然没有单独的 brainstorming 阶段,但是shape其实本身就承担了需求探索和高强度澄清。这个阶段会先调查项目里已经存在的接口、数据结构和开发规范,把能从仓库确认的事实查清楚,再把真正会影响产品行为的问题交给我们决定。

比如我们以登录接口需求为例,哪些问题需要确认、哪些我们就可以交给模型,分界其实很清楚的:

其中左边会改变最终使用效果,也会直接影响验收标准的。而右边属于实现方法,只要最终结果符合 spec,native 会让模型结合当前代码自己决定的。
shape提供了两种需求澄清模式,控制的是问题怎么分轮提出的:

我这里在.comet/config.yaml中是这样配置的:

我这里配置的是batch模式,所以 comet 会先分析问题之间的依赖,再把当前已经具备前置条件的问题放在一轮里确认。
它也不会把所有未知项一次性扔出来,仍然依赖其他决定的问题会留到下一轮。两种模式只影响提问节奏,进入 build前都需要我们确认完整的共享理解。
在我们确认过程中,change 目录会逐步生成三类正式产物:
docs/comet/changes/add-login-api/
├── brief.md
├── specs/<capability>/spec.md
└── comet-state.yaml

其中这里的spec记录的是完整目标状态。等我们后面进入verify时,runtime就会从这些正式产物里解析A1到An,然后再让verifier去逐项检查,所以shape阶段漏掉的边界,到了最后也很难凭空验收出来。
等所有问题都处理完以后,comet还会把目标、范围、关键决定、非目标和验收标准重新去汇总一次。
只有我们明确确认这份共享理解,runtime才会允许change从shape阶段进入build阶段。最开始提交一句功能需求,并不等于已经完成这次确认了。
到这个阶段,需求其实已经从一句增加登录接口,变成了一组可以实现、可以逐项检查、也可以跨会话恢复的项目文件。接下来就轮到强模型自己决定怎么把它做出来了。
三、build阶段由模型自主完成实现与交接
共享理解确认以后,runtime会返回下一步continuation,同一个/comet-native继续进入 build。native没有再切换到openspec、superpowers 或其他阶段 skill了,每次续行都会重新读取 brief、spec、comet-state.yaml和当前仓库状态。

在这个阶段,模型拥有的执行空间会明显大很多了:

这也是native和classic在执行阶段最明显的差别。
classic会明确组织设计、计划、tdd 和 review等。
而native只给出目标、验收边界和当前状态,具体路径交给强模型根据项目风险判断。
等代码完成以后,builder会提交一次handoff,说明这轮实现改了什么、当前候选对应哪些验收项,以及接下来需要重点检查的内容。
builder handoff
├── 本轮完成的实现
├── 对应的 brief 和 spec
├── 需要验收的 A1…An
└── 建议 runtime 关注的检查
不过这里的handoff只代表builder已经把实现交出来了,但是还不能作为最终通过结果。模型即使认为代码已经完成,也没有权限直接把 change 推进到 archive,下一步仍然要由runtime 和新的只读verifier重新验收的。
如果build过程中发现spec里缺少一个会改变产品行为的决定,change会退回到shape阶段重新确认。
如果只是实现没有达到现有验收标准,则继续留在build修复。阶段怎么走由runtime返回的 continuation来决定,agent不需要依赖聊天上下文猜下一步了。
这样一来,模型在实现时就可以充分发挥自身的能力了,交付边界也不会跟着执行方式一起放开。代码写完以后,native真正比较硬的一层才开始出现,也就是runtime检查和独立verifier。
四、runtime checks与只读verifier完成独立验收
当builder提交handoff以后,native不会直接接受它给出的完成结论。
runtime会先登记本次verify attempt,从brief和spec中读取完整验收列表,再执行当前任务真正需要的检查。

runtime checks是负责确认命令有没有真实执行的。测试构建或lint出现非零退出和超时,都不能被一段已经通过的文字覆盖。
完整的stdout和stderr会留在本机日志里,comet-state.yaml只保存退出码、耗时和检查摘要,避免长日志一直占用上下文。
其实同一个代码候选已经完成的检查可以直接复用,不需要在每个阶段重复跑一遍了。
如果verifier发现还需要一项额外检查,也要通过request-checks交给runtime来执行,不能自己生成一份检查结果。
命令检查完成后,runtime就会启动一个新的只读verifier。它和前面的builder是分开的,只根据代码、brief、spec和检查证据,对全部验收项重新判断的:

所以native里的verifier并不负责继续改代码。它只读地找问题,最终能不能通过仍由 runtime 根据完整验收结果判断。
builder负责实现,verifier负责检查,两边不会在同一个执行上下文里自己写完再给自己通过。
验证过程中我们看到的临时verifier response,其实也属于.comet/runtime/native/里的本机中间产物。
runtime会先校验这份响应有没有漏项、重复项或非法结果,再把稳定摘要写进comet-state.yaml。最终给我们阅读的是verification.md,临时响应不用手工修改,也不需要跟着项目同步。

不过有些平台不能提供独立的verifier execution,这时 comet会明确记录语义验收已经降级,并在archive前等待一次人工确认,这样不会把同一段模型上下文伪装成独立验收。
等全部A1到An和必要检查都通过后,change才能继续归档。只要其中一项失败,runtime就会把具体缺口写进下一步,再把任务送回 build了。
五、repair loop对验证失败进行有界修复
比如我这次的登录接口第一次verify的时候就没有直接通过。verifier发现邮箱大小写没有统一处理,同一个邮箱使用不同大小写时,登录结果可能不一致。
runtime并没有让 verifier顺手改代码,而是把这个缺口写进continuation,再把 change 退回build。

回到build以后,模型拿到的是明确的失败验收项和检查结果,不需要重新猜问题出在哪里。修复完成后会产生新的 builder handoff,同时开启下一次verify attempt。
不过这里有一个很重要的规则:
[!info]新的 verifier仍然要重新检查完整A1到An,不能只看刚刚失败的邮箱大小写。因为一次修复可能影响原来已经通过的行为,只验证失败项,很容易修好一个问题又带出另一个问题。
为了避免agent在build和verify之间一直空转,runtime 会在comet-state.yaml中保存几组计数:

未解决验收项减少、失败检查变成通过,或者缺失信息被补齐,才会被认为有真实进展。重复运行同一条失败命令、只改一遍说明,或者让失败项来回变化,都不会重置停滞计数。

native默认允许的同一实现需求允许的实现失败轮次上限是由max_verify_failures来控制的,新项目默认是5。 如果没有进展和runtime 执行错误的话则由其他计数和预算控制,达到条件后流程会停下来的,然后把失败原因和 resolution_action 交给用户处理了。
等这一轮修复结束后,邮箱大小写问题通过了新的verifier验收,其他登录行为也重新检查了一遍。到这里,代码层面的实现才算真正收敛,接下来即使换会话或换设备,整个结果也需要能够继续恢复了。
六、portable state支撑跨会话与跨设备恢复
其实native把可以长期同步和恢复的状态称为portable state。
我们简单理解来说,就是新会话只要拿到这些项目文件,即使没有上一轮聊天记录,也能知道 change做到了哪里、哪些验收已经通过,以及接下来应该执行什么。
comet-state.yaml文件就是这套恢复机制的核心:
comet-state.yaml
├── 当前 phase 和 status
├── iteration 与 attempt
├── 完整验收项和检查摘要
├── builder handoff 与 verifier 结论
├── blockers 和有限 history
└── 下一步 continuation
comet 会把可以同步的项目状态和只在当前设备使用的执行状态分开来放:

图中左边是恢复一个change需要的稳定边界,右边都放在.comet/runtime/native/下面了。如果我们的本地电脑出现runtime丢失或版本落后或换了一台设备的时候,我们就可以根据comet-state.yaml进行重建,再也不需要再把日志、锁和临时的verifier response一起同步过来了。
比如我们会话中断以后,再次重新进入项目只需要输入:
/comet 继续
接下来就会按照这条路径来进行恢复了:

正常情况下只有一个明确change时可以直接续上流程的,如果存在多个候选的时候就会先让我们选择。
我们在开启ambient_resume后,普通的继续上次的任务这种请求也会先经过只读恢复探测的,目标如果不明确的时候仍然会停下来确认的,是不会随便就绑定到某个change的。
这里恢复的是之前已经写进项目文件的事实,不会把旧设备的执行结果直接当成通过证据的。
如果verify运行到一半被中断了,runtime就会把这次执行标记为interrupted状态,然后再重新完成必要检查并启动新的verifier。
处于archive-ready时如果我们更换了设备,也会先重新确认已有验证状态的。
其中代码和状态也必须处在同一个任务现场的。
如果代码没有同步、产物根发生变化,或者当前的branch/worktree与change的绑定不一致时,runtime就会返回await-user或blocked,然后等我们处理完再继续,不会擅自在错误目录里写代码的。
[!info]这套分层对个人开发其实很实用的,这样会话开得再长也不用一直背着完整聊天记录了。如果放到团队开发中,brief、spec和portable state就会跟着仓库一起同步,即使我们换成员、换agent或换设备以后,接手的仍然是同一个change状态。
七、archive 完成验证报告、spec 更新与任务归档
等全部验收项和必要检查通过以后,change就会进入archive-ready了。这时实现和验证已经结束,archive只负责把最终结果写进项目的长期文档,再把本次 change 收起来。

正常情况下archive不会重新执行verify。前面已经通过的runtime checks和verifier结果会直接用于生成最终报告,避免同一份代码在归档时又重复跑一遍检查了。
其中verification.md是我们最终能直接阅读的验收报告,内容会从comet-state.yaml生成:

它和前面我们看到的临时verifier response用途不太一样。临时响应只服务当前attempt,而verification.md才是归档后跟着 change 长期保存的用户报告。报告丢失或者落后时,我们也可以根据 portable state 重新生成。
接下来,runtime还会把change里的target spec应用到项目的canonical spec。target spec 描述的是这个能力完成后的完整行为,归档时再根据声明执行 create、modify 或 remove,让 docs/comet/specs/ 保持项目当前真正生效的规格。
归档前
docs/comet/changes/add-login-api/
归档后
docs/comet/specs/<capability>/spec.md
docs/comet/archive/YYYY-MM-DD-add-login-api/
如果有两个change同时修改同一个capability,archive就会先停下来要求确认顺序。
先归档的结果会成为新的canonical spec,后面的change需要重新读取,如果 runtime 返回重新对齐动作,就可以按照continuation 处理后重新build和verify,不能直接覆盖前一个change 的规格。
至于归档是否需要停下来确认,我们可以通过archive_confirmation来配置。automatic会在 verify 通过后继续,required会在移动目录前等待我们明确确认。没有独立verifier execution 的降级场景,即使配置了自动归档,也会先等待确认。
archive写 spec、生成报告和移动目录时会使用可恢复事务。
过程中断以后,重新执行同一个archive动作会从事务状态继续,不需要手工搬目录或修改yaml的。
成功归档后,.comet/runtime/native/中这个change的本机执行目录会被清理,brief、spec、comet-state.yaml和verification.md则会跟着归档结果长期保留下来。
到这里,一次native change就基本上完整走完了:

八、native与classic对小任务采用不同的轻量路径
前面我们完整走了一遍native,可是会有一个疑问:
[!info]改一处配置、修一个小 bug,也要把四个阶段全部跑完吗?
这里native和classic采用了两种不同的轻量方式。
classic会根据任务意图缩短流程,而native则保留完整生命周期,把阶段内部的执行压缩得更轻。

我们来简单对比下这两种模式区别:

native的轻,其实主要体现在执行方法上。
一个边界很清楚的小修改,shape可能没有额外产品问题,只需要确认简短的共享理解,build可以直接完成实现,verify只运行必要检查,通过后继续 archive。brief、spec和状态产物依然保留,里面的内容和执行步骤会明显短一些。
不过这里还要区分一件事,comet不会根据任务大小在native和classic之间自动切换。/comet命令只读取.comet/config.yaml中的default_workflow配置项。
当前项目默认进入native,小任务也会继续走native;进入classic以后,才会在classic内部识别full、hotfix、tweak 或resume。
当然,我们其实也没有必要把每一次很小的编辑都创建成change。比如我们改一个错别字或者临时调整本机调试参数,或者做完就可以丢弃的实验,如果不需要spec、验收记录、恢复和归档,我一般会直接让普通 agent 处理就可以了。
只要改动会影响项目行为,需要团队追踪,或者可能跨会话继续,即使代码量很小,放进 native 仍然有价值。是否进入 comet,看的更多是这次改动需不需要长期管理,和实际修改了多少行代码关系不大。
comet 对个人与团队开发的实际价值
前面我们把classic和native的执行过程都讲了一遍,如果我们单独看状态、handoff、verifier和归档,其实会感觉comet很复杂增加了不少东西。不过如果我们把这些机制放回实际开发里,那它们解决的是一个很现实的问题:
[!info]模型越来越能写代码以后,我们应该怎么把一次对话变成可以稳定交付的项目任务。
一、个人开发从盯过程转向确认结果
当我们个人使用agent来写一个长期功能时,最累的部分其实经常不在写代码部分。比如我需要不断补充上下文、确认模型有没有漏需求、检查测试是否真的执行,换个窗口以后还要重新解释上一次做到哪里。
所以comet就是把这些容易反复消耗精力的部分放进了 change:

对我个人开发来说,最大的变化是不用一直去规定模型下一步该怎么做了。我的精力就可以放在登录行为、异常结果、数据风险和验收标准上,具体怎么调查实现和测试交给强模型判断。
我个人觉得这种分工对一个人维护多个项目其实很实用。比如我们的任务今天没有做完,等隔了几天换了一个agent继续任务,那么需求状态和失败证据仍然会跟着项目保留的,也不需要我仔充当模型的长期记忆了。
二、团队开发拥有统一的任务事实
因为我目前还是个人项目在推进使用的,后续我会考虑放在我们项目团队上面推行 comet。
按照我体验的结果来看,如果放到团队项目开发中,问题就会从我上次做到哪里,变成我们现在到底以哪一份结果为准了。
不同的开发成员、不同的agent和不同的会话如果只依赖聊天记录的话,就很容易各自理解一套需求,也很难确认交接过来的代码到底都经历了哪些检查。
而comet留下的几类项目文件,刚好就可以组成了同一个任务现场:

当团队成员接手时,就可以先去看change里的事实,然后再决定怎么继续,不需要完全相信上一段对话总结了。
如果多个任务并行时,change、branch 和worktree也会把各自的代码与状态进行分开,修改同一个capability的时候,归档顺序和规格冲突就会被明确拦下来。
其实我感觉这对多agent协作还是比较重要吧。agent可以更换,执行上下文也可以重新创建,但是团队确认过的需求边界、验收的结果和当前的状态是不会跟着某个会话一起消失掉的。
三、哪些任务值得进入comet
comet其实也不需要覆盖项目里的每一次修改。因为建立change、确认spec和完成验收都是有成本的,任务是否值得进入工作流,我们是可以先看它到底需不需要被长期管理的。

在我看来代码改动少不代表风险低。
一个认证判断、数据迁移或者公共api的小改动,可能只有几十行代码,但它需要明确边界、独立验收和后续追踪,这类任务放进 comet 的价值会很高。
反过来说,一次性实验随时可以丢弃的调试代码,以及不影响项目行为的简单编辑,没有必要为了流程完整专门创建 change。
所以我现在判断是否使用comet,主要还是看三个方面吧:
[!info] 这次修改会不会改变长期行为,失败以后影响大不大,以及任务是否需要被其他成员或下一次会话继续。只要其中一项比较重要,comet 增加的流程成本通常都能从后面的少返工、少遗漏和稳定交接里补回来。
最后
本来这篇我是想简单写一下的,结果写着写着就会发现要补充的地方越来越多,不知道你们看累了没有,反正我是写累了。。。
关于 comet,最开始我好奇的是它怎么把openspec和superpowers稳定地串联的,后面又使用 native一段时间以后,我觉得确实很强,特别是针对强模型开发来说,可以充分让模型发挥能力,再把结果交给runtime验收。
这里也非常感谢comet作者 @benym 把它开源出来,还一直在补中文文档、评估报告和不同平台的适配。 再次感谢大佬。
不过不管是comet 还是 trellis,两套工程化框架其实各有优点和使用场景。我个人觉得它们不是简单的功能对比,而是两条工程路线的区别。至于我们在项目中怎么选型 因为本篇太长了我就不再写了,累了都。后续我再出一篇针对mattpocock/skills comet trellis 的使用场景区分吧。
[!warning] 以内容基于我当前版本的实际使用和个人理解,不同版本或场景下可能存在差异,欢迎补充和指正。
最后我还是想说一下:
[!info] 个人开发不要过度工程化。有些时候工程化框架对于一个人开发来说经常是额外负担,容易降低开发乐趣和速度。还是要谨慎选择适合自己的开发方式。
关于 trellis 的分享可以看: 从 vibe coding 到 spec coding:我用 Trellis 的实践总结
官方参考/学习文档:Comet - Comet Docs