218 lines
7.5 KiB
Markdown
218 lines
7.5 KiB
Markdown
# Ship 记录契约
|
||
|
||
## 目录
|
||
|
||
- [布局](#布局)
|
||
- [current.yaml](#currentyaml)
|
||
- [short 与 long](#short-与-long)
|
||
- [任务](#任务)
|
||
- [证据指针](#证据指针)
|
||
- [旧 specifier 目录](#旧-specifier-目录)
|
||
|
||
## 布局
|
||
|
||
```text
|
||
.pouch/ship/<ship-id>/
|
||
current.yaml # 总账:kind、状态、stopAt、指针
|
||
spec.md # 短需求说明(short 和 long 都有)
|
||
tasks.yaml # 本 ship 的任务 DAG
|
||
2-develop/test-manifest.yaml # 开发者新增/修改测试的机器清单
|
||
status.md # 给人看的一页摘要
|
||
1-spec/ # 规格、评审、根因和 handoff
|
||
2-develop/ # 开发任务和变更
|
||
3-test/ # 任务验证与集成回归
|
||
4-review/ # PR、审核与合并
|
||
5-build/ # 合并后的构建/obtain
|
||
6-publish/ # 发布产物
|
||
7-deploy/ # 部署与健康检查
|
||
stage.yaml # 阶段状态和包含的步骤
|
||
activity.md # 该阶段的调度、结果和证据摘要
|
||
spec/ # 仅 long:PRD 版本、评审、handoff
|
||
brief.md
|
||
prd-v001.html
|
||
review.yaml
|
||
decisions.yaml
|
||
risks.yaml
|
||
handoff.yaml # 仅 approved 后
|
||
```
|
||
|
||
`status.md` 给人看;Agent 以 `current.yaml` 为准。两份不一致时改 `status.md`。
|
||
阶段目录由 `create_ship.py` 初始化,由 `next_step.py` 在派发和收到结果时追加活动记录;
|
||
大日志留在原测试、构建或部署位置,阶段目录只保存摘要和路径。
|
||
|
||
新建 ship 使用可排序的 `YYYYMMDD-短语`,例如 `20261014-dynamic-editor`。旧的无日期
|
||
记录仍可读取和校验,不要为了改名移动既有 ship 目录。
|
||
|
||
## current.yaml
|
||
|
||
必填:`shipId`、`kind`、`status`、`title`、`spec`、`tasks`、`evidence`。
|
||
`shipId` 必须等于目录名。
|
||
|
||
```yaml
|
||
shipId: add-export-button
|
||
kind: short
|
||
title: 列表页增加导出按钮
|
||
status: in_progress
|
||
stopAt: review_ready
|
||
source:
|
||
kind: user
|
||
ref: null
|
||
parentShipId: null
|
||
childShipIds: []
|
||
spec:
|
||
path: spec.md
|
||
revision: null
|
||
specStatus: null
|
||
prd: null
|
||
approvedRevision: null
|
||
reviews: null
|
||
handoff: null
|
||
tasks:
|
||
path: tasks.yaml
|
||
delivery:
|
||
stopAt: review_ready
|
||
state: change_ready
|
||
manager:
|
||
lastStep: null
|
||
lastResult: null
|
||
inFlight: null
|
||
evidence:
|
||
testRuns: []
|
||
artifacts: []
|
||
deploys: []
|
||
prs: []
|
||
commits: []
|
||
```
|
||
|
||
`kind`:`short` | `long`。
|
||
|
||
`status`:`draft`、`spec_review`、`approved`、`in_progress`、`blocked`、`done`、`parked`、`superseded`。
|
||
|
||
`delivery.state` 使用 delivery-loop 内部里程碑:`change_ready`、`verified`、
|
||
`pr_opened`、`merged`、`regression_passed`、`artifact_built`、`published`、`deployed`。
|
||
|
||
用户可见的 canonical `stopAt` 与内部里程碑映射如下:
|
||
|
||
| stopAt | 停止含义 | 内部里程碑 |
|
||
| --- | --- | --- |
|
||
| `develop_verified` | 开发验证完成:单元测试、新增功能集成测试和静态检查通过;不等于完整回归 | `verified` |
|
||
| `review_ready` | PR/MR 已创建,等待审核 | `pr_opened` |
|
||
| `release_ready` | PR 已合并且完整回归通过;尚未表示发布或部署 | `regression_passed` |
|
||
| `released` | 产物已构建并发布 | `published` |
|
||
| `deployed` | 目标环境实际部署完成并通过健康检查 | `deployed` |
|
||
|
||
为兼容旧记录,`verified` 是 `develop_verified` 的别名,`validation_ready` 是
|
||
`release_ready` 的别名;新记录应使用 canonical 名称。`delivery.state` 内部里程碑
|
||
名称保持不变。
|
||
|
||
`source.kind`:`user` | `hunt` | `ack` | `pr` | `feishu`。修 ACK 缺陷时
|
||
`source.kind: ack` 且 `ref` 为缺陷任务 id。
|
||
|
||
`manager` 由入口主会话维护,可选。`lastStep` / `lastResult` / `lastSummary` 记录刚结束的那一步;
|
||
`inFlight` 在 worker 未返回时写成步骤名,返回后清掉。worker 不要写这些字段。
|
||
`lastResult`:`pass` | `fail` | `blocked` | `ask`。
|
||
|
||
## short 与 long
|
||
|
||
同一套字段,厚度不同。
|
||
|
||
| 字段 | short | long |
|
||
| --- | --- | --- |
|
||
| `spec.md` | 四段:要做什么、不做什么、可观察验收、怎么验。有故障证据时加复现和根因 | 同一四段作摘要 |
|
||
| `spec/` | 不要创建 | specifier 写入 PRD、评审、决策、风险、handoff |
|
||
| `spec.revision` 等 | 保持 `null` | 与 PRD 版本同步 |
|
||
| `tasks.yaml` | 恰好 1 条 | DAG;`dependsOn` 指向本文件内 id |
|
||
| 子项 | 无 | 默认是 task。只有该切片有独立 `stopAt` 并单独发布时才晋升为子 ship,写入 `childShipIds` |
|
||
|
||
范围变大时保持同一 `shipId`,把 `kind` 改成 `long`,补 `spec/` 并拆任务。
|
||
|
||
`spec.md` 不要写成 HTML PRD。long 的评审稿是 `spec/prd-vNNN.html`。
|
||
|
||
## 任务
|
||
|
||
`tasks.yaml` 只描述本 ship 的工作项,不是 ACK 缺陷板。
|
||
|
||
新建 ship 的任务默认带 `testPlan`。开发 worker 在 manifest 中记录测试的稳定 id、
|
||
runner、target、selector、覆盖的验收项和 `added|changed` 状态。`verify` 必须逐条执行
|
||
manifest;`regress` 在完整 tester catalog 和项目原生测试之外再次确认这些条目通过。
|
||
仅文档或明确无行为变化的任务可使用 `policy: not_applicable`,但必须填写被主会话接受的
|
||
`testGap.reason` 与 `acceptedBy`。
|
||
|
||
```yaml
|
||
shipId: add-export-button
|
||
tasks:
|
||
- id: T-01
|
||
title: 在列表页增加导出按钮
|
||
dependsOn: []
|
||
expected:
|
||
- 登录用户在列表页看到「导出」
|
||
verification:
|
||
- 浏览器打开列表页,按钮可见且可点
|
||
testPlan:
|
||
policy: required
|
||
manifest: 2-develop/test-manifest.yaml
|
||
status: open
|
||
stopAt: review_ready
|
||
```
|
||
|
||
允许的任务 `status`:`open`、`in_progress`、`blocked`、`verified`、`merged`、`done`。
|
||
|
||
long 的子任务默认 `stopAt` 为 `pr_opened` 或 `merged`,不要单独打生产 tag 或升级生产。
|
||
父 ship 在依赖任务完成后做集成回归,再按父记录的 `delivery.stopAt` 继续。
|
||
|
||
从 `spec/handoff.yaml` 建任务时复制 `id`、`title`、`dependsOn`、`expected`、
|
||
`verification`,不要写进 `.pouch/ack/tasks.yaml`。
|
||
|
||
## 证据指针
|
||
|
||
不要把 tester 快照、镜像层或 compose 日志复制进 ship 目录。追加指针:
|
||
|
||
```yaml
|
||
evidence:
|
||
testRuns:
|
||
- path: .pouch/tester/runs/20261013T031000Z-smoke-ab12.yaml
|
||
result: pass
|
||
suite: smoke
|
||
artifacts:
|
||
- sha: 0123456789abcdef0123456789abcdef01234567
|
||
digest: sha256:abc
|
||
ref: harbor.example.com/app:1.2.3
|
||
venue: ci
|
||
deploys:
|
||
- env: test
|
||
revision: 0123456789abcdef0123456789abcdef01234567
|
||
health: pass
|
||
prs:
|
||
- url: https://git.example.com/app/-/merge_requests/12
|
||
status: opened
|
||
commits:
|
||
- sha: 0123456789abcdef0123456789abcdef01234567
|
||
role: baseline
|
||
```
|
||
|
||
delivery-loop 阶段与指针:
|
||
|
||
| 阶段 | 至少追加 |
|
||
| --- | --- |
|
||
| `change_ready` | `commits` `role: baseline` |
|
||
| `verified` | `testRuns` |
|
||
| `pr_opened` | `prs` |
|
||
| `merged` | `commits` `role: merged` |
|
||
| `regression_passed` | `testRuns`(回归) |
|
||
| `artifact_built` / `published` | `artifacts` |
|
||
| `deployed` | `deploys` |
|
||
|
||
`path` 相对项目根。文件不存在则校验失败。用 `scripts/append_evidence.py` 追加后立刻
|
||
`validate_ship.py`。
|
||
|
||
## 旧 specifier 目录
|
||
|
||
打开某个 id 时按这个顺序:
|
||
|
||
1. `.pouch/ship/<id>/` 已存在:用它。若同时还留着
|
||
`.pouch/specifier/features/<id>/`,报告旧路径为遗留,不要两处同时写。
|
||
2. 只有 `.pouch/specifier/features/<id>/`:把它移到 `.pouch/ship/<id>/spec/`,按旧
|
||
`current.yaml` 生成 ship `current.yaml`(`kind: long`),用 `brief.md` 补
|
||
`spec.md` 摘要,再校验。
|
||
3. 两处都没有:由入口 skill 调用 `create_ship.py` 新建。
|