用 PROGRESS.md 锁定切片验收完成态:OpenRig 的 Progress↔Review 完成一致性机制解析

原创2026-10-02 11:08:451,333 阅读
文章标签:人工智能AI Agent多智能体Agent 编排代码智能体CLI

用 PROGRESS.md 锁定切片验收完成态:OpenRig 的 Progress↔Review 完成一致性机制解析

本文以 OpenRig 仓库中 release-0.4.7 切片 03-shared-by-label(Shared by label)的完整实录为线索,系统讲解切片的**进度文件(PROGRESS.md)**如何作为持久化验收状态、如何在 Progress 与 Review 两个视图之间保持"完成"判定一致(即仓库测试套件所称的 VM-006 progress-review-done-coherence 联合机制),并给出从意图、契约、构建、QA 证据落盘到验收锁定的完整命令流与源码级原理。读完你既能照搬这份切片工作流,也能理解 OpenRig 中 rig proof add、rig scope slice approve、rig scope audit 三个命令如何共同构成"可验证、可追溯、不撒谎"的交付闭环。

一、PROGRESS.md 在切片生命周期中的定位

在 OpenRig 的默认 SDLC(文档见 docs/reference/sdlc-conventions.md 的 PART A)中,每个切片(slice)目录由四类文件构成其"运行面"(operating surface),mission-slice-sop 技能 与约定 SSOT 对它们有明确的 WHO/WHEN/HOW 分工:

文件 责任主体 何时更新 内容
SPEC.md / 旧式 README.md 规划(design) 创建时、范围变化时 前档 intent:,正文按序包含 ## Intent、## Mini-requirements、## Proof contract
PROGRESS.md 编排者维护 §1,每个 agent 记录自己的产出 交付状态变化时、验收时 一行一个产出(checkbox),细节链接下钻,保持前档 stage/verified 诚实
PROOF.md + proof/ 负责该切片的 impl/QA 组合 宣告切片完成之前 按效果(by effect)说明验证了什么、怎么验证的,媒体证据放 proof/
NOTES.md(任务级) 任意 agent 上下文重大变化时 压缩(compaction)前后归档状态

关联文档正是上述第二行:切片 03-shared-by-label 的 PROGRESS.md(路径 packages/daemon/test/fixtures/progress-review-done-coherence/03-shared-by-label/PROGRESS.md)。它是从冻结的 dogfood 工作区快照 release-0.4.7-workspace 原样复制而来的"已完成切片"实录,文件头的引用块明确写了它的使用契约:

WHO/WHEN: every agent working this slice logs its own outcomes here after every slice-done AND every commit. One line per outcome, link down for detail.

也就是说:每次切片阶段完成、每次提交,都必须在这里留下一行产出记录。模板侧的基线见 slice-progress.md 模板(初始只含"Implementation complete / Tests passing / Review approved"三项)与 mission-progress.md 模板(任务级四项:Scope complete / Implementation in progress / QA review pass / Merge ship)。

二、逐段解读这份"已完成"切片的 PROGRESS.md

2.1 TL;DR:一句话钉住交付结论

关联文档的 TL;DR 写道:

Final candidate 5afea9d is committed, independently verified, and delivery-locked. QA PASS maps all six proof-contract items to curated visual media; the final scope audit is clean.

这四句话是整份文件的"结论摘要",分别对应四个可核验事实:最终候选提交存在(SHA 5afea9d)、经过独立复核、完成 delivery 锁定、6 项 proof-contract 全部有 QA PASS 证据、最终 rig scope audit 零发现。它符合 sdlc-conventions.md PART A §A3 的"诚实护栏"——结论必须能在文件与证据中逐条回查。

2.2 Acceptance:七项验收清单(全勾选)

## Acceptance 是切片的持久化验收状态,本例为 7 项全勾选:

  1. Implementation complete — 实现完成,等待 post-edit QA;
  2. Tests passing — actions/__tests__/albums.test.ts 10/10(按 QA 要求补充了空串/纯空白回退用例);
  3. Review approved — QA POST-EDIT APPROVED(工作树 gate:10/10、diff 干净、视觉对比 PASS、设计一致性 PASS);
  4. Committed — 最终候选 SHA 5afea9d,分支 stripe-pro-tier,3 个文件 +243/-6;是 9360fb1 的"仅元数据重写"(作者/提交者改为仓库约定的 Mike Schwarz <esoteric.run@gmail.com>),tree-SHA 与 90a3cac 完全一致、零内容漂移;
  5. Proof drop — 对最终 5afea9d 的规范 QA PASS,映射到契约第 1-6 项,附真实 Tailwind 渲染图;
  6. Scope audit — ok: true,mission 与 slice 均零发现;
  7. Delivery-lock — 由 dev1-qa@product-team 于 2026-07-11T01:49:25.159Z 批准。

注意第 4 项刻意写明了"重写身份但不改内容"这一细节——这正是仓库里"诚实记录"的体现:SHA 变了,但字节内容没有变,读者不会被误导。测试套件 progress-review-done-coherence.test.ts 的 V3 用例独立验证了这一点:它用投影器自己的 checkbox 语法重新解析这份 PROGRESS.md,断言恰好解析出 7 行(text/source.line/done 与作者书写逐字节一致)。

2.3 Notes:十四行时间线 = 一份完整的切片档案

## Notes 是倒序或正序累计的事件日志,本例共 14 条,构成一条从"立项"到"交付锁定"的完整轨迹:

  • 07-10 立项与溯源:Scaffold 为 release feature OPR.0.4.7.3;R2 源追溯以 qitem-20260710214705-90964874 派发,返回 confirmed-open(98% 置信度),最小改动面为"批量创作者名推导 + 一行 AlbumTile 元数据行 + 聚焦测试";规划轨迹以 qitem-20260710215012-2561769c 交予 design(曾被 /login 阻塞);
  • 07-10 产品决策:Founder 批准展示名方案,随后批准泛化兜底 Album owner,明确禁止 email 兜底;
  • 07-11 设计与契约:dev1.design 认证后读取窄化后的产品面(page.tsx 的 AlbumTile + actions/albums.ts),编写 mini-requirements 与 6 项 proof contract;渲染 mockups/shared-by-label.html → PNG,覆盖 shared/fallback/owner/long-name 四态;确立设计契约:sharedByName 在服务端解析,UI 永不收到 email/id;并捕获了一个 min-w-0 截断陷阱;
  • 07-11 实现:dev1.impl 构建锁定集合,恰好改动 linkpix-app 的 3 个文件(actions/albums.ts、app/(authenticated)/app/page.tsx、新建 actions/__tests__/albums.test.ts),TDD 红→绿 8 用例,全量 143 通过 / 2 个预先存在的 members 失败;QA 首次打回(in-spec):缺少空串/纯空白回退测试,补充后 10/10;
  • 07-11 复核与提交:rev1.r2 独立工作树复核 CLEAR(零发现,99% 置信度);创建候选提交 9360fb1(+243/-6),git diff --check 干净,提交前 10/10 绿;明确排除预先存在的脏文件;
  • 07-11 证据受阻与替代:live 认证 /app 视觉对比被阻塞(Clerk pk/sk 实例不匹配导致无限重定向 + 无种子数据库),dev1.impl 用真实 Tailwind v4 + globals.css token 编译出忠实交付渲染(Playwright 截图),实证了 328px 处截断、502px 内容带省略号;
  • 07-11 收尾:dev1.design 设计一致性 PASS(四态逐一对照);QA 独立复验最终候选 5afea9d(身份、分支、父提交 7e72c81、三文件范围、tree-SHA 与 9360fb1 一致、聚焦 10/10、全量基线 145/147),投递规范 QA PASS,rig scope audit --mission release-0.4.7 零发现。

这 14 条日志的价值在于:任何后来者(含被压缩后恢复的 agent)都能仅凭这份文件重建切片的完整因果链——这正是 PROGRESS.md "survive compaction" 的设计目标。

三、核心机制:Progress↔Review 的"完成"联合(VM-006)

3.1 问题:两个视图对同一义务的判定会打架

rig proof add 落盘证据时不会去勾选 PROGRESS.md 或契约文件里的 checkbox(见 proof.ts 的落盘逻辑:只写 proof/ 下的新 .md 文件)。因此同一个 proof-contract 义务,在 Progress 视图(读 checkbox 勾选态)与 Review 视图(读 QA 通过的 verdict)会得出不一致的"是否完成"。这正是 VM-006(progress-review-done-coherence)要消灭的联合问题。

3.2 解法:done = checkboxTicked OR qaVerified

核心实现在 slice-detail-projector.ts 的 buildAcceptance()(L481 起)。其流程分四步:

  1. 扫描候选文件(L490-535):只扫被选中的节点文件(SPEC.md 优先,旧式 README.md 兜底)+ IMPLEMENTATION-PRD.md + PROGRESS.md + IMPLEMENTATION.md,用同一个 logical-checkbox 语法解析 [ ]/[x] 行,记录 {text, done, source:{file,line}};
  2. 按规范文本去重(L540-547):casefold + trim 后首次出现者胜出,扫描顺序决定引用归属(README > PRD > PROGRESS > IMPLEMENTATION);
  3. pristine 三连跳过(L555-561):仅当 PROGRESS.md 只有模板自带的三个未勾选占位行时,才整体跳过(严格判 pristine,宁多勿漏);
  4. QA-verdict 提升(L563-636):调用 extractProofContractSelected 取出契约项,用 textKey(trim+casefold)做1:1 门控关联:某 key 若同时命中 >1 个契约义务或 >1 个接受行,则一个都不提升(fail-closed);仅当契约项在 Review 侧被 composeDelivered 标为 verified 时,才置 item.done = true; item.doneVia = "qa-verdict"(L629-631)。最后把剩余已勾选但无 verdict 的行标为 doneVia = "checkbox"(L634)。

提升过程永不反向置 false(作者记录不被推翻,FR-3),且提升数量被约束为"绝不超过 Review 侧的 verified 计数"(pm FR-2 不变量 2)。

3.3 关键设计:join key 必须等于去重 key

PromisedItem.rawText(compose.ts L399-423)保留作者书写的原始字节(含行内图片引用),提升 join 与接受行去重都在这份 raw text 上做。为什么重要?测试注释给出了历史教训:前代实现用"剥掉图片后的文本"做 join,而接受侧去重用的是"raw trim+casefold",前者是严格更粗的关系,会把两条本应区分的行折叠成一个义务——这就是 B1 碰撞(false-lift 了非契约行)。差分测试 RC1/RC2/AMB(progress-review-done-coherence.test.ts)正是三个在冻结前代 0ec6411c 上必红的回归用例:图片剥离导致误提升、未验证序数被提升、字节相同歧义行被硬币式提升。修复后 raw-on-both 使"join 关系 == 去重关系",碰撞退化为区分。

3.4 一图看懂 VM-006 的完成联合

PROGRESS.md 勾选态 (checkbox) ─┐
                              ├─► buildAcceptance(): done = ticked OR qaVerified
契约行 rawText (README/PRD) ──┤      1:1 门控提升(fail-closed)
                              │      doneVia ∈ {"checkbox","qa-verdict"}
proof/ 的 C1 QA PASS ─────────┘
      (composeDelivered 判定 verified)

测试 V1 验证了本切片的"六契约行全部由 QA verdict 提升":13/13 完成、0 ACTIVE、6 行 doneVia === "qa-verdict",且提升行全部引用 README.md(README-first 去重的引用归属),与 Review 侧 6 个 verified 精确一致。

四、从源到结果:完整的切片工作流(B0 流程)

sdlc-conventions.md 将 SDLC 分两部分:PART A(简单流程,永远适用) 与 PART B(严格覆盖层,仅当被指派时适用)。本切片是 PART B 的完整实例,其 B0 流程为:

intent → mini-requirements + proof contract → (UI 切片: mockups)
      → plan-lock (rig scope slice approve --scope spec)
      → build the LOCKED set
      → QA: mockup ↔ delivered VISUAL compare
      → proof drops (rig proof add <slice> …)
      → proof-lock (rig scope slice approve --scope delivery)

4.1 两道锁:plan-lock 与 proof-lock

均由同一个命令 rig scope slice approve 写入(一个 daemon 侧写路径:前档时间戳 + 追加式审计行同时落盘):

锁 命令 语义
Plan-lock rig scope slice approve <slice> --scope spec "规格符合我的意图;这一套制品将被构建",冻结 SPEC 与 mockups
Proof-lock rig scope slice approve <slice> --scope delivery(默认) 终态"完成"签收,触发冻结

审批是冻结/签收,绝不等于 proven-green——proven-green 必须由 C1 证据(§五)证明;--on-behalf-of 如实记录代理关系。scope.ts 命令 还支持 plan-lock 专用选项 --locked-artifacts <paths>:用逗号分隔的切片相对路径替换默认推导的锁定集合,用于显式声明"就构建这些"。

4.2 证据落盘:rig proof add 的完整参数面

proof.ts L182 起的 add 子命令是证据的唯一正规入口。完整调用形态:

rig proof add <slice-path> \
  --artifact-type qa \
  --verdict PASS \
  --candidate-sha <the-proven-tip> \
  --money-evidence "one line of money evidence" \
  [--file <artifact.md> | --body <text>] \
  [--name <filename>] \
  [--evidences "1,3"] \
  [--self-check "I looked at the captures; they show the claim"] \
  [--media "walk.webm,panel.png"] \
  [--json]

关键行为(均有源码佐证):

  • C1 五必填在落盘瞬间强校验(L312-324):slice、candidate_sha、artifact_type、verdict、money_evidence 缺失或超出封闭集即拒绝写入——"证据在手上时校验"就是为防事后补造;
  • 两个批准的封闭集(扩展属于约定变更):artifact_type ∈ {guard | qa | rev1-r1 | rev1-r2 | adjudication};verdict ∈ {CLEAR | BLOCKING | CONCERNING | PASS | NOT-CLEAR};
  • --media 必须是切片 proof/ 相对路径(L264-291):绝对路径、逃逸出切片目录的引用都会被拒绝;视频扩展名渲染为 <video>,其余渲染为 ref markdown 引用,追加到正文 ## Media 段——这就是 C1 头与媒体证据的关联机制;
  • --name 只允许裸文件名(L435-447):拒绝路径分隔符、..、绝对路径,落盘只能落在 proof/ 内(FR-8 约束),且必须 .md 结尾;
  • 证据索引按"选定契约源"解析(L343-404):用与 Review 合成器字节相等的同一选择规则从 SPEC/PRD/README 选契约,--evidences 按 1-based 索引或原文文本匹配;未知引用只警告、不拒绝;
  • D2 覆盖 advisory:当契约存在而本次落盘未覆盖任何项、或缺少 self_check 时,输出 ADVISORY("Packet-2 将把这些项显示为 MISSING/▲ 证据不足信号")——advisory 永远不改退出码(BR-7),可见但从不阻塞。

4.3 审计兜底:rig scope audit

rig scope audit(slice-close 时运行)检查约定项:章节标题齐全、proof contract 良构、proof/ 制品携带合法 C1 头、UI 切片引用 mockup。按 sdlc-conventions.md §A6,它是 advisory / fail-open:每条发现只记录、只建议,永不阻塞写入路径——"未知报告为未知,而不是失败"。本切片第 6 项验收"scope audit ok: true、零发现"正是该命令的输出。

五、以本切片为实例:C1 证据与设计合同

5.1 一份真实的 C1 证据

proof/qa-PASS-shared-by-label.md(证据文件)是"drop 动词"的产出,其 YAML 前档正是 C1 头:

slice: OPR.0.4.7.3
candidate_sha: 5afea9da6bbd248191f78ef2613d1fd8706a38ae
artifact_type: qa
verdict: PASS
money_evidence: Exact three-file candidate; focused 10/10; visual and design
  coherence PASS; long-name 328px clip versus 502px content with ellipsis.
evidences: ["1","2","3","4","5","6"]
self_check: Inspected the committed diff, reran focused and full unit
  verification, opened the delivered render beside the locked mockup, confirmed
  all six observable outcomes, and verified the curated PNG hash matches the
  reviewed artifact.

注意几个"诚实"细节:candidate_sha 是 join key(C2 约定:该制品评判的是哪个被证明的 tip);self_check 是 agent 对"我真的看了证据"的书面断言;正文还主动声明残差——Clerk 密钥不匹配 + 无法运行 Supabase,故功能管线由聚焦测试与源码追溯证明、视觉保真由真实 Tailwind 浏览器渲染与设计 PASS 证明;全量基线 145 通过 / 2 个未改动的 members.test.ts 失败,且未引入任何目标文件诊断问题。把"没验证什么"写清楚,正是 §A3 诚实护栏要求的行为。

5.2 设计合同:隐私不变量如何在架构上成立

该切片的业务核心是把"Shared by 谁"做成结构性安全而非"视觉上没漏"。设计合同(README.md 设计合同节)规定:

  • 读模型 getAlbumsForUser() 对每个共享专辑返回一个已解析完成的 sharedByName: string(可用显示名,或字面量 "Album owner");对自有专辑返回 null;
  • UI 不做任何兜底逻辑,也永不收到 owner 的 email 或 id;
  • "可用显示名" = 非空、trim 后的人类名字;若唯一标识是 email 或不透明 id,一律视为无名字 → "Album owner"。

这样 proof-contract 第 3 项(无标识符泄漏)就是结构性成立的,而非靠 UI 层碰巧没渲染。实现侧(IMPLEMENTATION-PRD.md)要求:共享集用单次批量查询(按 createdByUserId 一次 findMany 投影 {id, displayName},遵循既有的 Promise.all + Map 批处理模式,禁止每卡片 N+1)在服务端完成解析。

UI 侧(app/(authenticated)/app/page.tsx 的 AlbumTile)渲染为封面与标题之间的"溯源 eyebrow":

{album.sharedByName && (
  <p className="text-xs font-medium text-muted-foreground truncate">
    Shared by {album.sharedByName}
  </p>
)}

放置决策(在 README 中被标记为"不得重新争议")为:text-xs、text-muted-foreground、单行、hover 不变色;并明确否掉了"并入元数据行"(Shared by Jane | 12 photos | …)的两个备选方案。

截断陷阱是本文档最值得复用的工程教训:truncate(overflow:hidden; text-overflow:ellipsis; white-space:nowrap)要真正生效,tile 必须能缩到内容宽度以下;而 grid item 默认 min-width:auto,会阻止收缩、把长名字撑爆整列。修复是给作为 grid item 的 <Link> 加 min-w-0。mockup 渲染实证:tile 稳定在 328px,502px 的 eyebrow 内容被裁切并显示省略号——proof-contract 第 6 项(长名单行省略)据此通过。

六、可验证性设计:FS-1 IO 守卫与测试矩阵

buildAcceptance 还内嵌了一个 IO 守卫(slice-detail-projector.ts L608-617):只有当存在"未勾选且属于契约"的接受行时,才去读 proof/ 目录做联合提升。因为联合只会"增加"完成态,所以全勾选或零契约的切片做零次额外 IO。测试 FS-1a/b/c 用 vi.mock("node:fs") 包装 readdirSync 精确计数:零契约零读、全勾选零读、有未勾选契约恰好每投影读一次。

整套差分测试矩阵(progress-review-done-coherence.test.ts)覆盖:

  • V1-V4:六契约行提升 13/13、手勾无证据行 doneVia=checkbox、PROGRESS 七行逐字节保持、契约子集与 DELIVERED 承诺子集相等;
  • V6′:composeDelivered 产物在冻结 fixture 上钉死(delegation canary);
  • V7a/V7b:含真实行内图片引用的契约行仍能 join 提升,而仅含图片的非契约行不会被误提升;
  • V8a-V8d/V8f:缺失降级安全——PRD 缺失/无契约节时退到 README 契约(one-home 定律),三源皆无时退化为纯勾选态,任何缺省都不抛异常;
  • RC1/RC2/AMB/INV:B1 碰撞回归 + 提升数恒 ≤ Review verified 计数;
  • B desync guard:逻辑 checkbox 的续行(continuation)被 join 且仍以 qa-verdict 提升,防止两个解析器悄悄失同步。

七、实战速查:把这份切片流程移植到你的任务

  1. 写契约:切片正文必须有 ## Intent / ## Mini-requirements / ## Proof contract(UI 切片契约项写 plannedRef 指向 mockup 小节);
  2. plan-lock:rig scope slice approve <slice> --scope spec;
  3. 只构建锁定集合,自己跑测试、用眼睛验证(rig capture 时忽略灰色 ghost 自动补全文本——那是预览不是真实内容);
  4. 证据 drop(绝不手放文件):rig proof add <slice> --artifact-type qa --verdict PASS --candidate-sha <tip> --money-evidence "<一句>" --evidences "1,2,…" --media "截图.png";
  5. proof-lock:rig scope slice approve <slice> --scope delivery;
  6. 审计兜底:rig scope audit --mission <mission>(advisory,零发现只是加分项);
  7. PROGRESS.md 收尾:每阶段完成即追加一行,最后用 TL;DR + Acceptance + Notes 钉死结论、SHA、证据映射与残差。

结语

一份 PROGRESS.md 看起来只是勾选清单与日志,但在 OpenRig 的架构里它是"完成"判定的持久化事实源:勾选态与 QA 证据通过 VM-006 的 raw-text 1:1 联合被统一,C1 头让证据自带出处,fail-open 的审计让约定只建议不阻塞。从 slice-detail-projector.ts、compose.ts 到 proof.ts,再到这份冻结的 03-shared-by-label 实录与它的差分测试,整条链路的每一环都可以在仓库里逐行验证——这本身就是"可验证交付"最好的注脚。

登录后查看全文
openrig