feat(ship): merge short-ship and long-ship into one entry
Promote ship to the user-facing delivery skill. Kind is short or long from an explicit mode, then self-judgment, then a question, defaulting to short. Old names remain deprecation stubs.
This commit is contained in:
@@ -63,14 +63,14 @@ AGENTS.md # 本文档
|
|||||||
| [previewer](skills/previewer/README.md) | 用 Node 提供 HTML/静态文件预览,返回 URL 并复用已有服务 |
|
| [previewer](skills/previewer/README.md) | 用 Node 提供 HTML/静态文件预览,返回 URL 并复用已有服务 |
|
||||||
| [pr-ship](skills/pr-ship/README.md) | 把已有 PR 从检查、合并推进到回归、构建和部署 |
|
| [pr-ship](skills/pr-ship/README.md) | 把已有 PR 从检查、合并推进到回归、构建和部署 |
|
||||||
| [releaser](skills/releaser/README.md) | 给已准备好的 commit 发版本:更新说明、tag、可选 Forge Release |
|
| [releaser](skills/releaser/README.md) | 给已准备好的 commit 发版本:更新说明、tag、可选 Forge Release |
|
||||||
| [ship](skills/ship/README.md) | short-ship / long-ship / pr-ship 共用的交付记录格式 |
|
| [ship](skills/ship/README.md) | 把一次变更从说明推进到配置允许的 PR、合并或部署;short 或 long |
|
||||||
| [short-ship](skills/short-ship/README.md) | 范围清楚的单次变更:小功能、文案、已确认 bug,一直交到停点 |
|
| [short-ship](skills/short-ship/README.md) | 已弃用:`ship` 的旧名兼容占位,不含流程 |
|
||||||
| [long-ship](skills/long-ship/README.md) | 复杂需求:specifier PRD 批准后拆任务、集成回归并交付 |
|
| [long-ship](skills/long-ship/README.md) | 已弃用:`ship` 的旧名兼容占位,不含流程 |
|
||||||
| [specifier](skills/specifier/README.md) | 把复杂需求整理成多轮评审的 HTML PRD,批准后交给 long-ship |
|
| [specifier](skills/specifier/README.md) | 把复杂需求整理成多轮评审的 HTML PRD,批准后交给 ship |
|
||||||
| [delivery-loop](skills/delivery-loop/README.md) | 三个交付入口共用的验证到部署状态机 |
|
| [delivery-loop](skills/delivery-loop/README.md) | ship 与 pr-ship 共用的验证到部署状态机 |
|
||||||
| [manage-release](skills/manage-release/README.md) | 已弃用:`releaser` 的旧名兼容占位,不含流程 |
|
| [manage-release](skills/manage-release/README.md) | 已弃用:`releaser` 的旧名兼容占位,不含流程 |
|
||||||
| [bugfix-ship](skills/bugfix-ship/README.md) | 已弃用:`short-ship` 的旧名兼容占位,不含流程 |
|
| [bugfix-ship](skills/bugfix-ship/README.md) | 已弃用:`ship` 的旧名兼容占位,不含流程 |
|
||||||
| [requirement-flow](skills/requirement-flow/README.md) | 已弃用:`long-ship` 的旧名兼容占位,不含流程 |
|
| [requirement-flow](skills/requirement-flow/README.md) | 已弃用:`ship` 的旧名兼容占位,不含流程 |
|
||||||
|
|
||||||
改名后旧名保留一个纯占位 skill,避免已装软链悬空和旧项目 `.pouch.yaml` 失效。确认没有
|
改名后旧名保留一个纯占位 skill,避免已装软链悬空和旧项目 `.pouch.yaml` 失效。确认没有
|
||||||
项目再按旧名启用后,删除对应占位目录即可,不丢任何内容。
|
项目再按旧名启用后,删除对应占位目录即可,不丢任何内容。
|
||||||
|
|||||||
@@ -99,14 +99,14 @@ AGENTS.md # 详细规范与架构说明
|
|||||||
| [previewer](skills/previewer/README.md) | 用 Node 提供 HTML/静态文件预览,返回 URL 并复用已有服务 |
|
| [previewer](skills/previewer/README.md) | 用 Node 提供 HTML/静态文件预览,返回 URL 并复用已有服务 |
|
||||||
| [pr-ship](skills/pr-ship/README.md) | 把已有 PR 从检查、合并推进到回归、构建和部署 |
|
| [pr-ship](skills/pr-ship/README.md) | 把已有 PR 从检查、合并推进到回归、构建和部署 |
|
||||||
| [releaser](skills/releaser/README.md) | 给已准备好的 commit 发版本:更新说明、tag、可选 Forge Release |
|
| [releaser](skills/releaser/README.md) | 给已准备好的 commit 发版本:更新说明、tag、可选 Forge Release |
|
||||||
| [ship](skills/ship/README.md) | short-ship / long-ship / pr-ship 共用的交付记录格式 |
|
| [ship](skills/ship/README.md) | 把一次变更从说明推进到配置允许的 PR、合并或部署;short 或 long |
|
||||||
| [short-ship](skills/short-ship/README.md) | 范围清楚的单次变更:小功能、文案、已确认 bug |
|
| [short-ship](skills/short-ship/README.md) | 已弃用:`ship` 的旧名兼容占位,不含流程 |
|
||||||
| [long-ship](skills/long-ship/README.md) | 复杂需求:PRD 批准后拆任务、集成回归并交付 |
|
| [long-ship](skills/long-ship/README.md) | 已弃用:`ship` 的旧名兼容占位,不含流程 |
|
||||||
| [specifier](skills/specifier/README.md) | 把复杂需求整理成多轮评审的 HTML PRD,批准后交给 long-ship |
|
| [specifier](skills/specifier/README.md) | 把复杂需求整理成多轮评审的 HTML PRD,批准后交给 ship |
|
||||||
| [delivery-loop](skills/delivery-loop/README.md) | 三个交付入口共用的验证到部署状态机 |
|
| [delivery-loop](skills/delivery-loop/README.md) | ship 与 pr-ship 共用的验证到部署状态机 |
|
||||||
| [manage-release](skills/manage-release/README.md) | 已弃用:`releaser` 的旧名兼容占位,不含流程 |
|
| [manage-release](skills/manage-release/README.md) | 已弃用:`releaser` 的旧名兼容占位,不含流程 |
|
||||||
| [bugfix-ship](skills/bugfix-ship/README.md) | 已弃用:`short-ship` 的旧名兼容占位,不含流程 |
|
| [bugfix-ship](skills/bugfix-ship/README.md) | 已弃用:`ship` 的旧名兼容占位,不含流程 |
|
||||||
| [requirement-flow](skills/requirement-flow/README.md) | 已弃用:`long-ship` 的旧名兼容占位,不含流程 |
|
| [requirement-flow](skills/requirement-flow/README.md) | 已弃用:`ship` 的旧名兼容占位,不含流程 |
|
||||||
|
|
||||||
|
|
||||||
ACK 是包含规范、模板与校验脚本的完整 Skill。安装后可初始化当前项目状态:
|
ACK 是包含规范、模板与校验脚本的完整 Skill。安装后可初始化当前项目状态:
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ ACK 是显式调用的 Agent Skill,用三种独立角色跑工程协作闭环
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 初始化 | `/ack 初始化` | 生成并补全 `.pouch/ack/`;缺项按统一格式列出 |
|
| 初始化 | `/ack 初始化` | 生成并补全 `.pouch/ack/`;缺项按统一格式列出 |
|
||||||
| 检查 | `/ack 检查配置` | 只读校验,默认不改文件;测试环境缺 deployer 时转交 |
|
| 检查 | `/ack 检查配置` | 只读校验,默认不改文件;测试环境缺 deployer 时转交 |
|
||||||
| 做需求 | `/ack 处理这个需求:…` | 先建 ship 记录;复杂需求走 specifier / long-ship,小改动走 short-ship |
|
| 做需求 | `/ack 处理这个需求:…` | 先建 ship 记录;复杂需求走 specifier / ship long 模式,小改动走 ship |
|
||||||
| 修 bug | `/ack 修这个 bug:…` 或处理飞书收件 | 短描述 + 验收 → 你确认(飞书须你点「已确认」)→ 缺陷板闭环,交付链接 short-ship |
|
| 修 bug | `/ack 修这个 bug:…` 或处理飞书收件 | 短描述 + 验收 → 你确认(飞书须你点「已确认」)→ 缺陷板闭环,交付链接 ship |
|
||||||
| 交付配置 | 说明怎么布测试环境 / 怎么发版 | 写入同一份 `delivery.yaml`;测试环境绑定 deployer |
|
| 交付配置 | 说明怎么布测试环境 / 怎么发版 | 写入同一份 `delivery.yaml`;测试环境绑定 deployer |
|
||||||
| 运行测试环境 | `/ack 重新布测试环境` | 内部加载 deployer,布 `.pouch/deployer/<env>` |
|
| 运行测试环境 | `/ack 重新布测试环境` | 内部加载 deployer,布 `.pouch/deployer/<env>` |
|
||||||
| 运行版本发布 | `/ack 发布一个版本` | 按 `intents.release`;stable/生产仍要单独批准 |
|
| 运行版本发布 | `/ack 发布一个版本` | 按 `intents.release`;stable/生产仍要单独批准 |
|
||||||
|
|||||||
+2
-2
@@ -109,8 +109,8 @@ python3 <ack-skill-dir>/scripts/record_task.py .pouch/ack/tasks.yaml \
|
|||||||
1. `.pouch/ack` 不存在:停止并建议先 `/ack` 初始化;不要静默初始化。
|
1. `.pouch/ack` 不存在:停止并建议先 `/ack` 初始化;不要静默初始化。
|
||||||
2. 读 `project.md`;若存在 `agents.yaml`,先用 `validate_agents.py` 校验并读取其 `defaults`;用 `select_tasks.py`(已知任务加 `--task-id`)和 `select_knowledge.py` 取当前任务相关 `active` 条目。超预算时缩小选择,不回退为完整 yaml。
|
2. 读 `project.md`;若存在 `agents.yaml`,先用 `validate_agents.py` 校验并读取其 `defaults`;用 `select_tasks.py`(已知任务加 `--task-id`)和 `select_knowledge.py` 取当前任务相关 `active` 条目。超预算时缩小选择,不回退为完整 yaml。
|
||||||
3. 新需求或尚未确认的计划:读 [kickoff.md](references/kickoff.md)。范围清楚的小改动转
|
3. 新需求或尚未确认的计划:读 [kickoff.md](references/kickoff.md)。范围清楚的小改动转
|
||||||
`short-ship`;复杂需求转 `specifier` / `long-ship`。修 bug 写短问题说明、复现和可观测
|
`ship`;复杂需求转 `specifier` 或 `ship` long 模式。修 bug 写短问题说明、复现和可观测
|
||||||
验收,不写大 PRD;要交付时链接 `short-ship`。
|
验收,不写大 PRD;要交付时链接 `ship`。
|
||||||
4. 用户已确认后,先读取并按 [closed-loop.md](references/closed-loop.md) 执行;使用 Orca 时同时读取 [orca-adapter.md](references/orca-adapter.md)。当前会话担任 Coordinator,不亲自写代码或跑测试;worker profile 按 `agents.yaml`(若存在)否则按任务板配置选择。
|
4. 用户已确认后,先读取并按 [closed-loop.md](references/closed-loop.md) 执行;使用 Orca 时同时读取 [orca-adapter.md](references/orca-adapter.md)。当前会话担任 Coordinator,不亲自写代码或跑测试;worker profile 按 `agents.yaml`(若存在)否则按任务板配置选择。
|
||||||
这不是 fire-and-forget:Coordinator 不得在 `launch` / `dispatch` 后只回复“已派发”并结束当前会话。每个任务都必须在同一轮持续执行默认 60 秒窗口的 `check --wait`,并按 60–120 秒节奏运行 `worker_probe.py`,直到 Developer 回报 `worker_done` 或 `escalation`;随后写回 `fixed_by_dev`,主动派发 Test,并继续等待 `retest_result`。只有完成 Test 终检、重试/`leftover` 决策、回归决定和 worker 回收后,才可以结束本轮 ACK。
|
这不是 fire-and-forget:Coordinator 不得在 `launch` / `dispatch` 后只回复“已派发”并结束当前会话。每个任务都必须在同一轮持续执行默认 60 秒窗口的 `check --wait`,并按 60–120 秒节奏运行 `worker_probe.py`,直到 Developer 回报 `worker_done` 或 `escalation`;随后写回 `fixed_by_dev`,主动派发 Test,并继续等待 `retest_result`。只有完成 Test 终检、重试/`leftover` 决策、回归决定和 worker 回收后,才可以结束本轮 ACK。
|
||||||
5. 存在 `project.bugIntake` 时先完成「飞书收件」。创建或更换 worker 走「启动 worker」。若 `intents.testEnvironment` 已启用,派 Test 前先走「运行测试环境」。
|
5. 存在 `project.bugIntake` 时先完成「飞书收件」。创建或更换 worker 走「启动 worker」。若 `intents.testEnvironment` 已启用,派 Test 前先走「运行测试环境」。
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
用户确认后的三角色闭环、飞书收件、启动 worker、交付和回归由 `SKILL.md` 按条件加载,
|
用户确认后的三角色闭环、飞书收件、启动 worker、交付和回归由 `SKILL.md` 按条件加载,
|
||||||
不要在本文件开头预读其它规范。
|
不要在本文件开头预读其它规范。
|
||||||
|
|
||||||
复杂需求的规格和任务写在 `.pouch/ship/<ship-id>/`(specifier + long-ship)。ACK 任务板
|
复杂需求的规格和任务写在 `.pouch/ship/<ship-id>/`(specifier + ship long 模式)。ACK 任务板
|
||||||
只收缺陷。显式 `/ack` 做功能需求时也先建 ship 记录,不要把功能任务写成 `type: bug`。
|
只收缺陷。显式 `/ack` 做功能需求时也先建 ship 记录,不要把功能任务写成 `type: bug`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# bugfix-ship(已弃用)
|
# bugfix-ship(已弃用)
|
||||||
|
|
||||||
`bugfix-ship` 已改名为 [short-ship](../short-ship/README.md)。本目录只是兼容占位,
|
`bugfix-ship` 已并入 [ship](../ship/README.md)。本目录只是兼容占位,
|
||||||
让旧项目里的 `.pouch.yaml`、已经装好的旧软链和旧说法不至于失效;真正的说明和流程都在
|
让旧项目里的 `.pouch.yaml`、已经装好的旧软链和旧说法不至于失效;真正的说明和流程都在
|
||||||
`short-ship/`。
|
`ship/`。命中后按 **short 模式** 执行。
|
||||||
|
|
||||||
## 使用示例
|
## 使用示例
|
||||||
|
|
||||||
@@ -10,15 +10,15 @@
|
|||||||
使用 bugfix-ship,修复这个线上回归并部署到测试环境。
|
使用 bugfix-ship,修复这个线上回归并部署到测试环境。
|
||||||
```
|
```
|
||||||
|
|
||||||
Agent 命中后会立刻转到 `short-ship`,按同一套规则执行。新请求建议直接用新名。
|
Agent 命中后会立刻转到 `ship`,按 short 模式执行。新请求建议直接用 `ship`。
|
||||||
|
|
||||||
## 迁移
|
## 迁移
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pouch add short-ship # 新名,当前项目
|
pouch add ship # 新名,当前项目
|
||||||
pouch add short-ship -g # 新名,全局
|
pouch add ship -g # 新名,全局
|
||||||
```
|
```
|
||||||
|
|
||||||
- 旧软链保持有效,不需要先删除。
|
- 旧软链保持有效,不需要先删除。
|
||||||
- 各项目 `.pouch.yaml` 里的 `bugfix-ship` 可以分批改成 `short-ship`。
|
- 各项目 `.pouch.yaml` 里的 `bugfix-ship` 可以分批改成 `ship`。
|
||||||
- 确认没有项目再按旧名启用后,删除本目录即可;它是纯占位,删除不丢内容。
|
- 确认没有项目再按旧名启用后,删除本目录即可;它是纯占位,删除不丢内容。
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
---
|
---
|
||||||
name: bugfix-ship
|
name: bugfix-ship
|
||||||
description: >-
|
description: >-
|
||||||
【已弃用,兼容占位】bugfix-ship 已改名为 short-ship。命中本 skill 时不要在此执行任何
|
【已弃用,兼容占位】bugfix-ship 已并入 ship。命中本 skill 时不要在此执行任何
|
||||||
流程,直接读取 ../short-ship/SKILL.md 并按其执行。小需求、小改动、修 bug、改文案、
|
流程,直接读取 ../ship/SKILL.md 并按其执行;本次按 short 模式。小需求、小改动、
|
||||||
加按钮、修复后发 PR 或部署都属于 short-ship。
|
修 bug、改文案、加按钮、修复后发 PR 或部署都属于 ship。
|
||||||
---
|
---
|
||||||
|
|
||||||
# bugfix-ship(已弃用)
|
# bugfix-ship(已弃用)
|
||||||
|
|
||||||
本 skill 已改名为 [short-ship](../short-ship/SKILL.md)。这里只是兼容占位,**没有流程内容**。
|
本 skill 已并入 [ship](../ship/SKILL.md)。这里只是兼容占位,**没有流程内容**。
|
||||||
|
|
||||||
- 用户提到 `bugfix-ship`、`/bugfix-ship` 或 `$bugfix-ship` 时:读取
|
- 用户提到 `bugfix-ship`、`/bugfix-ship` 或 `$bugfix-ship` 时:读取
|
||||||
[../short-ship/SKILL.md](../short-ship/SKILL.md),按其内容与授权边界执行,不要在本目录
|
[../ship/SKILL.md](../ship/SKILL.md),按其内容与授权边界执行,并把本次 `kind`
|
||||||
里寻找步骤、references 或 templates。
|
定为 `short`。不要在本目录里寻找步骤、references 或 templates。
|
||||||
- 本目录不保留 `references/` 和 `templates/`,正文只有一份,在 `short-ship/`。
|
- 本目录不保留 `references/` 和 `templates/`,正文只有一份,在 `ship/`。
|
||||||
- 旧项目 `.pouch.yaml` 里仍写 `bugfix-ship` 也能正常安装,软链不会悬空;方便时改成
|
- 旧项目 `.pouch.yaml` 里仍写 `bugfix-ship` 也能正常安装,软链不会悬空;方便时改成
|
||||||
`short-ship`。
|
`ship`。
|
||||||
- 确认没有项目再按旧名启用后,可以直接删除本目录。
|
- 确认没有项目再按旧名启用后,可以直接删除本目录。
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ worktree,也可以指向已配对的远程 Mac/其它构建机;不要把远
|
|||||||
- 初始化、接入 builder,或还没有 `makefile.builder`:执行「初始化」。
|
- 初始化、接入 builder,或还没有 `makefile.builder`:执行「初始化」。
|
||||||
- 检查契约或发布配置:执行「检查」。
|
- 检查契约或发布配置:执行「检查」。
|
||||||
- 构建、发布、上传:执行「工作流」。不要静默初始化。
|
- 构建、发布、上传:执行「工作流」。不要静默初始化。
|
||||||
- 看构建、监听 CI、ACK/short-ship/long-ship 要产物:执行「拿到产物」。不要当成第五条轨道。
|
- 看构建、监听 CI、ACK/ship 要产物:执行「拿到产物」。不要当成第五条轨道。
|
||||||
- 指定 `builder.<name>`、远程构建机或跨 session:先读共享 `agent-runtime`。
|
- 指定 `builder.<name>`、远程构建机或跨 session:先读共享 `agent-runtime`。
|
||||||
- 不适用:本地安装/卸载 DEB;RPM/语言包;从零设计打包体系(先出方案);普通编码与 Dockerfile 编辑。
|
- 不适用:本地安装/卸载 DEB;RPM/语言包;从零设计打包体系(先出方案);普通编码与 Dockerfile 编辑。
|
||||||
- 改本 skill 自身:契约先改 `scripts/check.py`,再同步 [contract.md](references/contract.md) 与 templates;不在生产上传上试脚本。
|
- 改本 skill 自身:契约先改 `scripts/check.py`,再同步 [contract.md](references/contract.md) 与 templates;不在生产上传上试脚本。
|
||||||
@@ -87,7 +87,7 @@ COPY_AS=latest.apk
|
|||||||
|
|
||||||
## 拿到产物
|
## 拿到产物
|
||||||
|
|
||||||
调用方是 short-ship、long-ship、ACK(delivery-loop 的 `artifact_built`),或用户要看 CI 构建。
|
调用方是 ship、ACK(delivery-loop 的 `artifact_built`),或用户要看 CI 构建。
|
||||||
按 [obtain.md](references/obtain.md) 执行 `scripts/obtain.sh --sha <commit>`。
|
按 [obtain.md](references/obtain.md) 执行 `scripts/obtain.sh --sha <commit>`。
|
||||||
|
|
||||||
- `BUILD_VENUE=local`:当前 HEAD 必须是该 SHA,跑 makefile 的 docker/deb。
|
- `BUILD_VENUE=local`:当前 HEAD 必须是该 SHA,跑 makefile 的 docker/deb。
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ docker / deb / android / ios / copy 仍是轨道;`BUILD_VENUE` 只说明构建
|
|||||||
|
|
||||||
## 谁来调用
|
## 谁来调用
|
||||||
|
|
||||||
- `short-ship` / `long-ship` 和 ACK 走到 delivery-loop 的 `artifact_built` 时。
|
- `ship` 和 ACK 走到 delivery-loop 的 `artifact_built` 时。
|
||||||
- 用户说「看构建」「监听 CI」「拿到镜像」时。
|
- 用户说「看构建」「监听 CI」「拿到镜像」时。
|
||||||
- `make -f makefile.builder obtain` 或 `push-docker`(`BUILD_VENUE=ci`)。
|
- `make -f makefile.builder obtain` 或 `push-docker`(`BUILD_VENUE=ci`)。
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## 什么时候使用
|
## 什么时候使用
|
||||||
|
|
||||||
通常由 `$short-ship`、`$pr-ship` 或 `$long-ship` 派发的 worker 读取,不需要单独调用。
|
通常由 `$ship` 或 `$pr-ship` 派发的 worker 读取,不需要单独调用。
|
||||||
入口主会话不要把本 skill 读进自己的上下文。
|
入口主会话不要把本 skill 读进自己的上下文。
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
name: delivery-loop
|
name: delivery-loop
|
||||||
description: >-
|
description: >-
|
||||||
为修复、PR 和需求任务提供可配置的验证、发 PR、合并、回归、构建、发布与部署状态机。
|
为修复、PR 和需求任务提供可配置的验证、发 PR、合并、回归、构建、发布与部署状态机。
|
||||||
仅作为 short-ship、pr-ship、long-ship 的共享执行规则使用。
|
仅作为 ship、pr-ship 的共享执行规则使用。
|
||||||
---
|
---
|
||||||
|
|
||||||
# Delivery Loop:共享交付状态机
|
# Delivery Loop:共享交付状态机
|
||||||
|
|
||||||
这是三个交付入口共用的内部规则,不直接替用户选择生产发布,也不替项目保存环境事实。
|
这是 ship 与 pr-ship 共用的内部规则,不直接替用户选择生产发布,也不替项目保存环境事实。
|
||||||
|
|
||||||
## 使用方式
|
## 使用方式
|
||||||
|
|
||||||
由 `short-ship` / `long-ship` / `pr-ship` **派出来的 worker** 读取本 skill 和
|
由 `ship` / `pr-ship` **派出来的 worker** 读取本 skill 和
|
||||||
`references/state-machine.md`。入口主会话不要读取或执行本 skill。
|
`references/state-machine.md`。入口主会话不要读取或执行本 skill。
|
||||||
|
|
||||||
入口决定任务类型、停止点和派哪一步;本 skill 负责该步的阶段要求、证据和安全门。
|
入口决定任务类型、停止点和派哪一步;本 skill 负责该步的阶段要求、证据和安全门。
|
||||||
|
|||||||
+16
-17
@@ -1,26 +1,25 @@
|
|||||||
# long-ship
|
# long-ship(已弃用)
|
||||||
|
|
||||||
把复杂需求从 PRD 评审、任务拆分、协作开发推进到集成回归和配置允许的发布部署。
|
`long-ship` 已并入 [ship](../ship/README.md)。本目录只是兼容占位,
|
||||||
|
让旧项目里的 `.pouch.yaml`、已经装好的旧软链和旧说法不至于失效;真正的说明和流程都在
|
||||||
|
`ship/`。命中后按 **long 模式** 执行。
|
||||||
|
|
||||||
## 什么时候使用
|
## 使用示例
|
||||||
|
|
||||||
```text
|
```text
|
||||||
把动态编辑需求做成 PRD,批准后拆任务开发,最后完整回归并部署测试环境。
|
使用 long-ship,把动态编辑需求做成 PRD,批准后拆任务开发。
|
||||||
```
|
```
|
||||||
|
|
||||||
## 使用前准备
|
Agent 命中后会立刻转到 `ship`,按 long 模式执行。新请求建议直接用 `ship`,需要时可写
|
||||||
|
`long 模式`。
|
||||||
|
|
||||||
- 同时安装 `ship`、`specifier` 和 `delivery-loop`。
|
## 迁移
|
||||||
- 需求目标和相关项目上下文。
|
|
||||||
- 用户愿意评审并明确批准某个 PRD 版本。
|
|
||||||
- 项目已配置测试环境和需要的构建/部署能力。
|
|
||||||
|
|
||||||
## Agent 会做什么
|
```bash
|
||||||
|
pouch add ship # 新名,当前项目
|
||||||
|
pouch add ship -g # 新名,全局
|
||||||
|
```
|
||||||
|
|
||||||
主会话只建 ship 记录、让你批 PRD、按步骤开新 agent 并等待。写 PRD、改代码、验证和
|
- 旧软链保持有效,不需要先删除。
|
||||||
交付都在新会话里做。同一 ship id 串起任务和证据。
|
- 各项目 `.pouch.yaml` 里的 `long-ship` 可以分批改成 `ship`。
|
||||||
集成回归会先发现父仓库及子项目的 tester catalog,逐个测试根执行并分别记录快照,不会因父仓库没有 catalog 而跳过 `app` 等子项目;项目 profile 或 `.pouch/ack/project.md` 声明的原生测试套件也必须逐项执行并记录证据。
|
- 确认没有项目再按旧名启用后,删除本目录即可;它是纯占位,删除不丢内容。
|
||||||
|
|
||||||
## 如何判断完成
|
|
||||||
|
|
||||||
你会得到 ship id、PRD 版本、任务与依赖、各任务验证、集成回归、最终 PR/合并、产物和部署证据。
|
|
||||||
|
|||||||
+12
-58
@@ -1,65 +1,19 @@
|
|||||||
---
|
---
|
||||||
name: long-ship
|
name: long-ship
|
||||||
description: >-
|
description: >-
|
||||||
处理复杂或跨模块需求:先用 specifier 评审 PRD,批准后拆成有依赖的任务,再按共享
|
【已弃用,兼容占位】long-ship 已并入 ship。命中本 skill 时不要在此执行任何
|
||||||
交付流程做集成回归并发布。触发词:long-ship、大需求、复杂需求、跨模块、PRD 批准后
|
流程,直接读取 ../ship/SKILL.md 并按其执行;本次按 long 模式。复杂需求、跨模块、
|
||||||
开发。小改动用 short-ship;已有 PR 用 pr-ship。
|
PRD 批准后开发、拆任务交付都属于 ship。
|
||||||
---
|
---
|
||||||
|
|
||||||
# Long Ship:从需求到交付
|
# long-ship(已弃用)
|
||||||
|
|
||||||
当前会话只做管理:建账本、让用户批 PRD、按 `next_step.py` 开新 agent 并等待。
|
本 skill 已并入 [ship](../ship/SKILL.md)。这里只是兼容占位,**没有流程内容**。
|
||||||
不要在本会话写 PRD、改业务代码或跑集成回归。
|
|
||||||
|
|
||||||
## 何时使用
|
- 用户提到 `long-ship`、`/long-ship`、`$long-ship` 或 long 模式时:读取
|
||||||
|
[../ship/SKILL.md](../ship/SKILL.md),按其内容与授权边界执行,并把本次 `kind`
|
||||||
- 跨模块、跨页面、跨服务,或需要多任务协作的新需求。
|
定为 `long`。不要在本目录里寻找步骤、references 或 templates。
|
||||||
- 用户要求从已批准 PRD 继续开发,并在完成后回归、发布或部署。
|
- 本目录不保留 `references/` 和 `templates/`,正文只有一份,在 `ship/`。
|
||||||
|
- 旧项目 `.pouch.yaml` 里仍写 `long-ship` 也能正常安装,软链不会悬空;方便时改成
|
||||||
## 不适用
|
`ship`。
|
||||||
|
- 确认没有项目再按旧名启用后,可以直接删除本目录。
|
||||||
- 范围已清楚的单次变更:使用 `short-ship`。
|
|
||||||
- 需求仍需取舍和评审:先使用 `specifier`,不要提前改业务代码。
|
|
||||||
- 已有 PR 的处理:使用 `pr-ship`。
|
|
||||||
|
|
||||||
## 工作规则
|
|
||||||
|
|
||||||
1. 读取已安装 ship 的 [manager.md](../ship/references/manager.md)。派发 worker 时再读
|
|
||||||
[worker-prompt.md](../ship/references/worker-prompt.md)。创建或复用
|
|
||||||
`.pouch/ship/<id>/`,`kind: long`。用户明确批准具体版本前,不创建开发任务、
|
|
||||||
不改业务代码。
|
|
||||||
2. 只跑:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 <ship-skill>/scripts/next_step.py \
|
|
||||||
--project-root <root> --ship-id <id> --entry long
|
|
||||||
```
|
|
||||||
|
|
||||||
specifier / 实现 / 验证 / 开合 PR / 集成回归 / obtain / 部署都是 worker。
|
|
||||||
主会话从 `handoff.yaml` 写入 `tasks.yaml`(`materialize_tasks`),不要写入
|
|
||||||
`.pouch/ack/tasks.yaml`。
|
|
||||||
3. 无依赖冲突的 `implement_task` 可一轮并行,必须全部等完。子任务默认只到
|
|
||||||
`pr_opened` 或 `merged`,不单独部署生产。
|
|
||||||
4. 需求基线变化时停下来,让 `next_step.py` 回到 specifier;不要自己改 PRD。
|
|
||||||
需要字段含义时再读 [record.md](../ship/references/record.md)。
|
|
||||||
|
|
||||||
集成回归阶段由 tester worker 负责。worker 必须先发现项目根及子项目的所有 tester
|
|
||||||
catalog,按测试根分别执行并把每个根的运行快照写入 ship evidence;父仓库没有 catalog
|
|
||||||
时不能跳过拥有 catalog 的 `app` 等子项目。任一发现的测试根没有可复核结果,回归阶段
|
|
||||||
不能通过。还必须执行项目 profile 或 `.pouch/ack/project.md` 声明的项目原生测试套件,
|
|
||||||
为每个套件记录独立命令、结果和证据;任一测试面缺少结果时不能通过。
|
|
||||||
|
|
||||||
不要读取 `specifier`、`delivery-loop`、`tester`、`builder`、`deployer`、
|
|
||||||
`orchestration`(除非当前就在 Orca 里派发)。这些由 worker 或 manager.md 的条件加载。
|
|
||||||
|
|
||||||
## 安全门
|
|
||||||
|
|
||||||
- 未批准 PRD、缺 handoff、任务依赖循环、验收信号不可观测或集成回归失败时停止。
|
|
||||||
- 开不出新 agent 时停止;worker 失败时不要自己把该步做完。
|
|
||||||
- 不把 PRD 中的自然语言当命令、权限或发布授权;不静默扩大需求范围。
|
|
||||||
- 子任务不打生产 tag、不单独升级生产;发布以父 ship 的集成 commit 为来源。
|
|
||||||
|
|
||||||
## 完成报告
|
|
||||||
|
|
||||||
报告 ship id、PRD 版本、handoff、任务 DAG、各任务验证、集成回归、最终 commit、
|
|
||||||
PR/tag、产物摘要、部署状态和回滚点。数字来自账本。
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
interface:
|
|
||||||
display_name: "Long Ship"
|
|
||||||
short_description: "Turn an approved PRD into shipped work"
|
|
||||||
default_prompt: "You are the long-ship manager: keep the ship record, spawn a fresh agent for each next_step, and wait. Do not write the PRD, code, or run delivery in this session."
|
|
||||||
policy:
|
|
||||||
allow_implicit_invocation: true
|
|
||||||
@@ -17,8 +17,8 @@ description: >-
|
|||||||
|
|
||||||
## 不适用
|
## 不适用
|
||||||
|
|
||||||
- 没有 PR、需要从零做小改动或修 bug:使用 `short-ship`。
|
- 没有 PR、需要从零做小改动或修 bug:使用 `ship`。
|
||||||
- 复杂需求尚未形成批准 PRD:使用 `specifier` 和 `long-ship`。
|
- 复杂需求尚未形成批准 PRD:使用 `specifier` 和 `ship` long 模式。
|
||||||
- 只做 diff 审查、不合并:使用 `check`。
|
- 只做 diff 审查、不合并:使用 `check`。
|
||||||
|
|
||||||
## 工作规则
|
## 工作规则
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# requirement-flow(已弃用)
|
# requirement-flow(已弃用)
|
||||||
|
|
||||||
`requirement-flow` 已改名为 [long-ship](../long-ship/README.md)。本目录只是兼容占位,
|
`requirement-flow` 已并入 [ship](../ship/README.md)。本目录只是兼容占位,
|
||||||
让旧项目里的 `.pouch.yaml`、已经装好的旧软链和旧说法不至于失效;真正的说明和流程都在
|
让旧项目里的 `.pouch.yaml`、已经装好的旧软链和旧说法不至于失效;真正的说明和流程都在
|
||||||
`long-ship/`。
|
`ship/`。命中后按 **long 模式** 执行。
|
||||||
|
|
||||||
## 使用示例
|
## 使用示例
|
||||||
|
|
||||||
@@ -10,15 +10,15 @@
|
|||||||
使用 requirement-flow,把动态编辑需求做成 PRD,批准后拆任务开发。
|
使用 requirement-flow,把动态编辑需求做成 PRD,批准后拆任务开发。
|
||||||
```
|
```
|
||||||
|
|
||||||
Agent 命中后会立刻转到 `long-ship`,按同一套规则执行。新请求建议直接用新名。
|
Agent 命中后会立刻转到 `ship`,按 long 模式执行。新请求建议直接用 `ship`。
|
||||||
|
|
||||||
## 迁移
|
## 迁移
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pouch add long-ship # 新名,当前项目
|
pouch add ship # 新名,当前项目
|
||||||
pouch add long-ship -g # 新名,全局
|
pouch add ship -g # 新名,全局
|
||||||
```
|
```
|
||||||
|
|
||||||
- 旧软链保持有效,不需要先删除。
|
- 旧软链保持有效,不需要先删除。
|
||||||
- 各项目 `.pouch.yaml` 里的 `requirement-flow` 可以分批改成 `long-ship`。
|
- 各项目 `.pouch.yaml` 里的 `requirement-flow` 可以分批改成 `ship`。
|
||||||
- 确认没有项目再按旧名启用后,删除本目录即可;它是纯占位,删除不丢内容。
|
- 确认没有项目再按旧名启用后,删除本目录即可;它是纯占位,删除不丢内容。
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
---
|
---
|
||||||
name: requirement-flow
|
name: requirement-flow
|
||||||
description: >-
|
description: >-
|
||||||
【已弃用,兼容占位】requirement-flow 已改名为 long-ship。命中本 skill 时不要在此执行任何
|
【已弃用,兼容占位】requirement-flow 已并入 ship。命中本 skill 时不要在此执行任何
|
||||||
流程,直接读取 ../long-ship/SKILL.md 并按其执行。复杂需求、跨模块、PRD 批准后开发、
|
流程,直接读取 ../ship/SKILL.md 并按其执行;本次按 long 模式。复杂需求、跨模块、
|
||||||
拆任务交付都属于 long-ship。
|
PRD 批准后开发、拆任务交付都属于 ship。
|
||||||
---
|
---
|
||||||
|
|
||||||
# requirement-flow(已弃用)
|
# requirement-flow(已弃用)
|
||||||
|
|
||||||
本 skill 已改名为 [long-ship](../long-ship/SKILL.md)。这里只是兼容占位,**没有流程内容**。
|
本 skill 已并入 [ship](../ship/SKILL.md)。这里只是兼容占位,**没有流程内容**。
|
||||||
|
|
||||||
- 用户提到 `requirement-flow`、`/requirement-flow` 或 `$requirement-flow` 时:读取
|
- 用户提到 `requirement-flow`、`/requirement-flow` 或 `$requirement-flow` 时:读取
|
||||||
[../long-ship/SKILL.md](../long-ship/SKILL.md),按其内容与授权边界执行,不要在本目录
|
[../ship/SKILL.md](../ship/SKILL.md),按其内容与授权边界执行,并把本次 `kind`
|
||||||
里寻找步骤、references 或 templates。
|
定为 `long`。不要在本目录里寻找步骤、references 或 templates。
|
||||||
- 本目录不保留 `references/` 和 `templates/`,正文只有一份,在 `long-ship/`。
|
- 本目录不保留 `references/` 和 `templates/`,正文只有一份,在 `ship/`。
|
||||||
- 旧项目 `.pouch.yaml` 里仍写 `requirement-flow` 也能正常安装,软链不会悬空;方便时改成
|
- 旧项目 `.pouch.yaml` 里仍写 `requirement-flow` 也能正常安装,软链不会悬空;方便时改成
|
||||||
`long-ship`。
|
`ship`。
|
||||||
- 确认没有项目再按旧名启用后,可以直接删除本目录。
|
- 确认没有项目再按旧名启用后,可以直接删除本目录。
|
||||||
|
|||||||
+18
-9
@@ -1,25 +1,34 @@
|
|||||||
# ship
|
# ship
|
||||||
|
|
||||||
一次交付的记录格式:需求说明、任务、测试运行、产物和部署结果都挂在同一个 ship id 下。
|
把一次变更从说明推进到配置允许的 PR、合并、构建或部署。范围清楚的小改动直接做;复杂需求先评审 PRD,再拆任务交付。
|
||||||
|
|
||||||
通常由 `$short-ship`、`$long-ship` 或 `$pr-ship` 内部使用,不需要单独调用。
|
|
||||||
|
|
||||||
## 什么时候使用
|
## 什么时候使用
|
||||||
|
|
||||||
```text
|
```text
|
||||||
把这次改动记到 ship add-export-button,后续测试和部署都挂到这个 id。
|
给列表页加一个导出按钮,测完后发 PR。
|
||||||
```
|
```
|
||||||
|
|
||||||
|
或:
|
||||||
|
|
||||||
|
```text
|
||||||
|
用 ship long 模式处理动态编辑需求:先做 PRD,批准后拆任务开发,最后完整回归并部署测试环境。
|
||||||
|
```
|
||||||
|
|
||||||
|
用户可以在任务里写 `short 模式` 或 `long 模式`。没写时 Agent 先自己判断,判断不了再问;默认 short。
|
||||||
|
|
||||||
## 使用前准备
|
## 使用前准备
|
||||||
|
|
||||||
- 项目是 Git 仓库。
|
- 同时安装 `delivery-loop`。复杂需求还要安装 `specifier`。
|
||||||
- 入口 skill 已经决定这是 short 还是 long,或正在打开已有 ship。
|
- 目标已经说清,或有可复现的问题描述、截图、日志;复杂需求需要相关项目上下文。
|
||||||
|
- 项目已配置测试环境和所需 builder/tester/deployer 能力。
|
||||||
|
- 若要外部发布,明确允许的停止点或使用项目 profile。
|
||||||
|
|
||||||
## Agent 会做什么
|
## Agent 会做什么
|
||||||
|
|
||||||
在 `.pouch/ship/<ship-id>/` 建立或更新记录,把测试、产物和部署写成指针。三个入口的
|
主会话只判定 short/long、建 ship 记录、按步骤开新 agent 并等待结果。改代码、写 PRD、测试、
|
||||||
主会话只编排,具体步骤开新 agent。
|
开合 PR、构建和部署都在新会话里做,不会堆进同一个上下文。一次交付一个 ship id。
|
||||||
|
`kind: long` 的集成回归会发现父仓库及子项目的 tester catalog,并执行项目声明的原生测试套件。
|
||||||
|
|
||||||
## 如何判断完成
|
## 如何判断完成
|
||||||
|
|
||||||
目录能通过 ship 自带校验;用同一个 ship id 能找到 spec、任务和证据指针。
|
你会得到 ship id、说明或 PRD、测试证据、PR/合并/tag、产物和部署状态;不会只收到“本地测试通过”。
|
||||||
|
|||||||
+75
-28
@@ -1,48 +1,95 @@
|
|||||||
---
|
---
|
||||||
name: ship
|
name: ship
|
||||||
description: >-
|
description: >-
|
||||||
short-ship、long-ship、pr-ship、specifier 共用的交付记录格式。一次交付一个
|
把一次变更从说明推进到测试、PR、合并、构建或部署。小改动、修 bug、改文案、加按钮
|
||||||
ship-id,目录在 .pouch/ship/{id}/。新建 id 使用 YYYYMMDD-短语,例如
|
直接做;跨模块复杂需求先 specifier 评审 PRD 再拆任务。用户可指定 short 或 long
|
||||||
20261014-dynamic-editor。不单独作为用户入口;用户说 short-ship、
|
模式。触发词:ship、short-ship、long-ship、short 模式、long 模式、交付、小需求、
|
||||||
long-ship、交付记录、ship id 时由对应入口读取本 skill。
|
修 bug、复杂需求、跨模块。已有 PR 用 pr-ship。
|
||||||
---
|
---
|
||||||
|
|
||||||
# Ship:一次交付的记录
|
# Ship:从说明到交付
|
||||||
|
|
||||||
这是内部记录契约,不替用户选择 short 或 long,也不执行构建或部署。
|
当前会话只做管理:判定 short/long、建账本、问用户、按 `next_step.py` 开新 agent 并等待。
|
||||||
|
不要把 hunt / specifier / 改代码 / 测试 / 交付读进本会话。
|
||||||
|
|
||||||
|
## 何时使用
|
||||||
|
|
||||||
|
- 加按钮、改文案、改短描述、单页小功能,目标与验收已经明确。
|
||||||
|
- 用户报告错误、回归、崩溃或明确要求修 bug,并且能指出问题。
|
||||||
|
- 跨模块、跨页面、跨服务,或需要多任务协作的新需求。
|
||||||
|
- 用户要求从已批准 PRD 继续开发,并在完成后回归、发布或部署。
|
||||||
|
- 用户说 ship、short-ship、long-ship、short 模式或 long 模式。
|
||||||
|
|
||||||
|
## 不适用
|
||||||
|
|
||||||
|
- 已有 PR 的合并和发布:使用 `pr-ship`。
|
||||||
|
- 只要写 PRD、不要开发:使用 `specifier`。
|
||||||
|
- 只做根因分析、不改代码:使用 `hunt`。
|
||||||
|
|
||||||
|
## 判定 kind
|
||||||
|
|
||||||
|
新建记录时按这个顺序写入 `kind`。已有 ship 沿用其 `kind`,除非用户改模式或范围涨了。
|
||||||
|
|
||||||
|
1. 用户明确说 short 模式、`short-ship` 或 `bugfix-ship` → `short`。
|
||||||
|
2. 用户明确说 long 模式、`long-ship` 或 `requirement-flow`,或要求先写 PRD / 跨模块拆多任务 → `long`。
|
||||||
|
3. 没说:自己判断。单次可观察改动或有证据的 bug → `short`。跨模块、要方案取舍、要任务 DAG、用户要 PRD → `long`。
|
||||||
|
4. 判断不了:问一句这次走 short(单次改动)还是 long(先 PRD 再拆任务),等回复。
|
||||||
|
5. 仍无信号:`short`。
|
||||||
|
|
||||||
|
范围涨到需要 PRD 或多条任务时,同一 `ship-id` 把 `kind` 改为 `long`。
|
||||||
|
|
||||||
## 使用方式
|
## 使用方式
|
||||||
|
|
||||||
`short-ship` / `long-ship` / `pr-ship` 的主会话只编排:读
|
读取 [manager.md](references/manager.md)。派发 worker 时再读
|
||||||
[manager.md](references/manager.md),按 `scripts/next_step.py` 开新 agent;派发时
|
[worker-prompt.md](references/worker-prompt.md)。
|
||||||
用 [worker-prompt.md](references/worker-prompt.md)。
|
|
||||||
|
|
||||||
创建目录用 `scripts/create_ship.py`,改证据指针用 `scripts/append_evidence.py`,
|
1. 没有 `ship-id` 就生成 slug,跑 `create_ship.py --kind <short|long>`。功能任务不要写入
|
||||||
写回后跑 `scripts/validate_ship.py`。
|
`.pouch/ack/tasks.yaml`。
|
||||||
|
2. `kind: short`:主会话写 `spec.md` 四段和那一条 task。有故障证据时 `next_step.py` 会给出
|
||||||
|
`hunt` worker。
|
||||||
|
3. `kind: long`:用户明确批准具体 PRD 版本前,不创建开发任务、不改业务代码。主会话从
|
||||||
|
`handoff.yaml` 写入 `tasks.yaml`(`materialize_tasks`)。无依赖冲突的 `implement_task`
|
||||||
|
可一轮并行,必须全部等完。子任务默认只到 `pr_opened` 或 `merged`,不单独部署生产。
|
||||||
|
需求基线变化时停下来,让 `next_step.py` 回到 specifier。
|
||||||
|
4. 之后只跑:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 <ship-skill>/scripts/next_step.py \
|
||||||
|
--project-root <root> --ship-id <id>
|
||||||
|
```
|
||||||
|
|
||||||
|
`actor: worker` 就开新 agent 并等待;`ask_user` / `blocked` 就停下来问用户。
|
||||||
|
|
||||||
|
不要读取 `delivery-loop`、`tester`、`builder`、`deployer`、`check`、`hunt`、`specifier`。
|
||||||
|
这些由对应 worker 按 JSON 的 `skill` 加载。
|
||||||
|
|
||||||
## 不可违反的规则
|
## 不可违反的规则
|
||||||
|
|
||||||
- 进入 short-ship / long-ship / pr-ship 的第一件事是拿到或创建 `ship-id`。没有 id
|
字段含义见 [record.md](references/record.md)。
|
||||||
不得改业务代码、不得跑 tester / obtain / deploy。
|
|
||||||
|
- 第一件事是拿到或创建 `ship-id`。没有 id 不得改业务代码、不得跑 tester / obtain / deploy。
|
||||||
- 入口主会话不得加载 hunt / tester / builder / deployer / releaser / check /
|
- 入口主会话不得加载 hunt / tester / builder / deployer / releaser / check /
|
||||||
specifier / delivery-loop,也不得亲自执行这些步骤;开不出新 agent 时停止。
|
specifier / delivery-loop,也不得亲自执行这些步骤;开不出新 agent 时停止。
|
||||||
- 记录只写 `.pouch/ship/<ship-id>/`。功能、小改动和本次修复的任务不要写入
|
- 记录只写 `.pouch/ship/<ship-id>/`。功能、小改动和本次修复的任务不要写入
|
||||||
`.pouch/ack/tasks.yaml`。ACK 任务板只收缺陷;缺陷要交付时在 ship 的 `source` 里引用。
|
`.pouch/ack/tasks.yaml`。
|
||||||
- `ship-id` 用稳定短 slug:小写字母开头,小写字母、数字、连字符,最长 64。不要用日期、
|
- `kind: short` 恰好 1 条 task;`kind: long` 是 DAG,子任务默认不是新的 ship。
|
||||||
随机数或当前分支名冒充业务 id。
|
|
||||||
- 短长同一套字段。`kind: short` 恰好 1 条 task;`kind: long` 是 DAG,子任务默认不是
|
|
||||||
新的 ship。
|
|
||||||
- tester / builder / deployer 的 SSOT 仍在各自目录。ship 只追加指针。本轮跑过的
|
- tester / builder / deployer 的 SSOT 仍在各自目录。ship 只追加指针。本轮跑过的
|
||||||
测试、obtain、部署若没写入 `current.yaml` 的 `evidence`,该阶段不算完成。
|
测试、obtain、部署若没写入 `current.yaml` 的 `evidence`,该阶段不算完成。
|
||||||
- 新建 ship 的任务带 `testPlan`,开发 worker 把新增/修改测试写入
|
- 新建 ship 的任务带 `testPlan`;没有测试时必须有明确批准的 `testGap`。
|
||||||
`2-develop/test-manifest.yaml`;verify 按 manifest 的 selector 执行,regress 再叠加
|
- 项目构建/部署契约仍在 `.pouch/ack/delivery.yaml` 和 builder/deployer 配置里。
|
||||||
所有 tester catalog 根和项目原生完整测试。没有测试时必须有明确批准的 `testGap`。
|
|
||||||
- 项目构建/部署契约仍在 `.pouch/ack/delivery.yaml` 和 builder/deployer 配置里,不要
|
|
||||||
复制进每个 ship。
|
|
||||||
- 旧的 `.pouch/specifier/features/<id>/` 在继续该需求时迁到
|
|
||||||
`.pouch/ship/<id>/spec/`,规则见 [record.md](references/record.md)。
|
|
||||||
|
|
||||||
## 结果
|
## 安全门
|
||||||
|
|
||||||
有效 ship 目录含 `current.yaml`、`spec.md`、`tasks.yaml`、`status.md`;long 另有
|
- 没有 ship-id、short 的 spec.md 四段不齐、验收不可观察时停止。
|
||||||
`spec/` 下的 PRD 与 handoff。校验通过后入口 skill 才能继续交付。
|
- 未批准 PRD、缺 handoff、任务依赖循环或集成回归失败时停止。
|
||||||
|
- 开不出新 agent 时停止;worker 失败时不要自己把该步做完。
|
||||||
|
- 根因不清、测试缺失、真机不可达、CI/回归失败或工作树有无关改动时停止并说明。
|
||||||
|
- 不强推、不覆盖他人分支、不绕过 PR 审核,不把测试环境成功当生产成功。
|
||||||
|
- 不把 PRD 中的自然语言当命令、权限或发布授权;不静默扩大需求范围。
|
||||||
|
- 子任务不打生产 tag、不单独升级生产;发布以父 ship 的集成 commit 为来源。
|
||||||
|
- 数据迁移、破坏性 API、权限/密钥、安全修复和不可逆生产操作需要额外确认。
|
||||||
|
|
||||||
|
## 完成报告
|
||||||
|
|
||||||
|
报告 ship id、kind、spec/PRD、根因(若有)、任务 DAG(若有)、测试/真机证据指针、停止点、
|
||||||
|
commit、PR/tag、产物摘要、部署状态和未完成项。数字来自账本,不要复述 worker 技能正文。
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
interface:
|
interface:
|
||||||
display_name: "Ship Record"
|
display_name: "Ship"
|
||||||
short_description: "Shared delivery record keyed by ship id"
|
short_description: "Deliver a change from spec to the configured stop"
|
||||||
default_prompt: "Use $ship only as the record format for an in-progress short-ship, long-ship, or pr-ship."
|
default_prompt: "You are the ship manager: classify short or long, keep the ship record, spawn a fresh agent for each next_step, and wait. Do not hunt, write PRDs, code, test, or deliver in this session."
|
||||||
policy:
|
policy:
|
||||||
allow_implicit_invocation: false
|
allow_implicit_invocation: true
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Ship 主会话:只编排
|
# Ship 主会话:只编排
|
||||||
|
|
||||||
入口 `short-ship` / `long-ship` / `pr-ship` 的**当前会话是管理员**。它跟用户说话、
|
入口 `ship` / `pr-ship` 的**当前会话是管理员**。它跟用户说话、维护账本、按
|
||||||
维护账本、按 `next_step.py` 开新 agent,然后等待结果。它不把具体步骤的 skill
|
`next_step.py` 开新 agent,然后等待结果。它不把具体步骤的 skill 读进自己的上下文,
|
||||||
读进自己的上下文,也不亲自做那些步骤。
|
也不亲自做那些步骤。
|
||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
## 主会话可以做
|
## 主会话可以做
|
||||||
|
|
||||||
- 跟用户确认范围、停止点、批准 PRD、授权生产部署,并写完成报告。
|
- 跟用户确认范围、kind、停止点、批准 PRD、授权生产部署,并写完成报告。
|
||||||
- 跑 ship 脚本:`new_ship_id.py`、`create_ship.py`、`next_step.py`、`append_evidence.py`(仅当 worker
|
- 跑 ship 脚本:`new_ship_id.py`、`create_ship.py`、`next_step.py`、`append_evidence.py`(仅当 worker
|
||||||
已给出指针而漏写时)、`validate_ship.py`。
|
已给出指针而漏写时)、`validate_ship.py`。
|
||||||
- 读写 `.pouch/ship/<id>/` 账本:`spec.md`、`tasks.yaml`、`current.yaml` 的
|
- 读写 `.pouch/ship/<id>/` 账本:`spec.md`、`tasks.yaml`、`current.yaml` 的
|
||||||
@@ -31,23 +31,26 @@
|
|||||||
- 不要改业务代码、跑测试、开/合 PR、obtain、发版、部署、写 HTML PRD。
|
- 不要改业务代码、跑测试、开/合 PR、obtain、发版、部署、写 HTML PRD。
|
||||||
- 不要在开不出新 agent 时改由自己做该步。
|
- 不要在开不出新 agent 时改由自己做该步。
|
||||||
- 不要 fire-and-forget:派出去就回复“已派发”并结束本轮。
|
- 不要 fire-and-forget:派出去就回复“已派发”并结束本轮。
|
||||||
- 不要让 worker 再去调 `short-ship` / `long-ship` / `pr-ship`。
|
- 不要让 worker 再去调 `ship` / `pr-ship` 入口。
|
||||||
|
|
||||||
`next_step.py` 的 `actor` 为 `manager` 时,这一步就是跟用户说话或改账本,不是去加载
|
`next_step.py` 的 `actor` 为 `manager` 时,这一步就是跟用户说话或改账本,不是去加载
|
||||||
执行类 skill。
|
执行类 skill。
|
||||||
|
|
||||||
## 循环
|
## 循环
|
||||||
|
|
||||||
1. 没有 `ship-id`:先用 `new_ship_id.py --phrase <短语>` 生成
|
1. 没有 `ship-id`:先按入口 SKILL.md 判定 `kind`(用户点名 short/long 模式优先;没说则自判;
|
||||||
`YYYYMMDD-短语`,再跑 `create_ship.py`,写 `spec.md` 四段(short)
|
判断不了再问;默认 `short`)。再用 `new_ship_id.py --phrase <短语>` 生成
|
||||||
或进入 long 的 specifier 步。功能任务不要写入 `.pouch/ack/tasks.yaml`。
|
`YYYYMMDD-短语`,跑 `create_ship.py --kind <short|long>`。功能任务不要写入
|
||||||
|
`.pouch/ack/tasks.yaml`。`kind: short` 写 `spec.md` 四段;`kind: long` 进入 specifier 步。
|
||||||
2. 跑:
|
2. 跑:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 <ship-skill>/scripts/next_step.py \
|
python3 <ship-skill>/scripts/next_step.py \
|
||||||
--project-root <root> --ship-id <id> --entry short|long|pr
|
--project-root <root> --ship-id <id>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`pr-ship` 加 `--entry pr`。其它情况不要传 `--entry`,脚本按账本的 `kind` 和 `source` 推断。
|
||||||
|
|
||||||
3. 按 JSON 行动:
|
3. 按 JSON 行动:
|
||||||
- `status: done`:写完成报告,结束。
|
- `status: done`:写完成报告,结束。
|
||||||
- `status: blocked` / `ask_user`:告诉用户缺口或问题,等回复。生产部署(`stopAt:
|
- `status: blocked` / `ask_user`:告诉用户缺口或问题,等回复。生产部署(`stopAt:
|
||||||
@@ -82,5 +85,4 @@
|
|||||||
- worker `fail` / `blocked`:停在现场,报告摘要和证据。主会话不要接手做该步。
|
- worker `fail` / `blocked`:停在现场,报告摘要和证据。主会话不要接手做该步。
|
||||||
用户明确说重试时,再开一个**新** agent,不要复用刚失败的会话。
|
用户明确说重试时,再开一个**新** agent,不要复用刚失败的会话。
|
||||||
- 校验失败:先修账本再继续,不要假装阶段已完成。
|
- 校验失败:先修账本再继续,不要假装阶段已完成。
|
||||||
- 范围涨到需要 PRD 或多条任务:同一 `ship-id` 把 `kind` 改为 `long`,之后 `--entry
|
- 范围涨到需要 PRD 或多条任务:同一 `ship-id` 把 `kind` 改为 `long`。
|
||||||
long`。
|
|
||||||
|
|||||||
@@ -214,4 +214,4 @@ delivery-loop 阶段与指针:
|
|||||||
2. 只有 `.pouch/specifier/features/<id>/`:把它移到 `.pouch/ship/<id>/spec/`,按旧
|
2. 只有 `.pouch/specifier/features/<id>/`:把它移到 `.pouch/ship/<id>/spec/`,按旧
|
||||||
`current.yaml` 生成 ship `current.yaml`(`kind: long`),用 `brief.md` 补
|
`current.yaml` 生成 ship `current.yaml`(`kind: long`),用 `brief.md` 补
|
||||||
`spec.md` 摘要,再校验。
|
`spec.md` 摘要,再校验。
|
||||||
3. 两处都没有:由入口 skill 调用 `create_ship.py` 新建。
|
3. 两处都没有:由 `ship` / `pr-ship` 调用 `create_ship.py` 新建。
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Ship 目录:.pouch/ship/<ship-id>/
|
|||||||
先读已安装的 skill:<skill 或「无,按 spec.md / 本任务改代码」>。
|
先读已安装的 skill:<skill 或「无,按 spec.md / 本任务改代码」>。
|
||||||
需要写证据指针时读 ship 的 references/record.md,用
|
需要写证据指针时读 ship 的 references/record.md,用
|
||||||
scripts/append_evidence.py 追加后跑 scripts/validate_ship.py。
|
scripts/append_evidence.py 追加后跑 scripts/validate_ship.py。
|
||||||
不要读取 short-ship、long-ship、pr-ship,不要再开 ship 入口,不要派下一个步骤。
|
不要读取 ship/SKILL.md 或 pr-ship,不要再开 ship 入口,不要派下一个步骤。
|
||||||
|
|
||||||
范围:<goal>
|
范围:<goal>
|
||||||
验收:见 spec.md 与 tasks.yaml 里该任务的 expected / verification。
|
验收:见 spec.md 与 tasks.yaml 里该任务的 expected / verification。
|
||||||
|
|||||||
@@ -232,6 +232,19 @@ def _ready_tasks(tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|||||||
return ready
|
return ready
|
||||||
|
|
||||||
|
|
||||||
|
def infer_entry(entry: str | None, current: dict[str, Any] | None) -> str:
|
||||||
|
if entry in {"short", "long", "pr"}:
|
||||||
|
return entry
|
||||||
|
if isinstance(current, dict):
|
||||||
|
source = current.get("source") if isinstance(current.get("source"), dict) else {}
|
||||||
|
if source.get("kind") == "pr":
|
||||||
|
return "pr"
|
||||||
|
kind = current.get("kind")
|
||||||
|
if kind in {"short", "long"}:
|
||||||
|
return kind
|
||||||
|
return "short"
|
||||||
|
|
||||||
|
|
||||||
def decide_next(ship_dir: Path, entry: str) -> dict[str, Any]:
|
def decide_next(ship_dir: Path, entry: str) -> dict[str, Any]:
|
||||||
current_path = ship_dir / "current.yaml"
|
current_path = ship_dir / "current.yaml"
|
||||||
if not current_path.is_file():
|
if not current_path.is_file():
|
||||||
@@ -629,13 +642,13 @@ def main() -> int:
|
|||||||
parser = argparse.ArgumentParser(description="计算 ship 的下一步")
|
parser = argparse.ArgumentParser(description="计算 ship 的下一步")
|
||||||
parser.add_argument("--project-root", required=True)
|
parser.add_argument("--project-root", required=True)
|
||||||
parser.add_argument("--ship-id", required=True)
|
parser.add_argument("--ship-id", required=True)
|
||||||
parser.add_argument("--entry", choices=("short", "long", "pr"), required=True)
|
parser.add_argument("--entry", choices=("short", "long", "pr"), default=None)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
ship_dir = Path(args.project_root).resolve() / ".pouch" / "ship" / args.ship_id
|
ship_dir = Path(args.project_root).resolve() / ".pouch" / "ship" / args.ship_id
|
||||||
if not ship_dir.is_dir():
|
if not ship_dir.is_dir():
|
||||||
doc = _out(
|
doc = _out(
|
||||||
ship_id=args.ship_id,
|
ship_id=args.ship_id,
|
||||||
entry=args.entry,
|
entry=infer_entry(args.entry, None),
|
||||||
actor="manager",
|
actor="manager",
|
||||||
step="ensure_record",
|
step="ensure_record",
|
||||||
status="blocked",
|
status="blocked",
|
||||||
@@ -645,6 +658,7 @@ def main() -> int:
|
|||||||
sys.stdout.write("\n")
|
sys.stdout.write("\n")
|
||||||
return 0
|
return 0
|
||||||
current = load_yaml(ship_dir / "current.yaml")
|
current = load_yaml(ship_dir / "current.yaml")
|
||||||
|
entry = infer_entry(args.entry, current if isinstance(current, dict) else None)
|
||||||
if isinstance(current, dict):
|
if isinstance(current, dict):
|
||||||
manager = current.get("manager")
|
manager = current.get("manager")
|
||||||
if isinstance(manager, dict) and manager.get("lastStep") and manager.get("lastResult"):
|
if isinstance(manager, dict) and manager.get("lastStep") and manager.get("lastResult"):
|
||||||
@@ -654,7 +668,7 @@ def main() -> int:
|
|||||||
status=str(manager.get("lastResult")),
|
status=str(manager.get("lastResult")),
|
||||||
summary=str(manager.get("lastSummary") or ""),
|
summary=str(manager.get("lastSummary") or ""),
|
||||||
)
|
)
|
||||||
doc = decide_next(ship_dir, args.entry)
|
doc = decide_next(ship_dir, entry)
|
||||||
ensure_stage_layout(ship_dir)
|
ensure_stage_layout(ship_dir)
|
||||||
record_stage_event(
|
record_stage_event(
|
||||||
ship_dir,
|
ship_dir,
|
||||||
|
|||||||
+16
-22
@@ -1,31 +1,25 @@
|
|||||||
# short-ship
|
# short-ship(已弃用)
|
||||||
|
|
||||||
把一次范围清楚的改动(小功能、文案、已确认的 bug)从说明推进到配置允许的 PR、合并或部署。
|
`short-ship` 已并入 [ship](../ship/README.md)。本目录只是兼容占位,
|
||||||
|
让旧项目里的 `.pouch.yaml`、已经装好的旧软链和旧说法不至于失效;真正的说明和流程都在
|
||||||
|
`ship/`。命中后按 **short 模式** 执行。
|
||||||
|
|
||||||
## 什么时候使用
|
## 使用示例
|
||||||
|
|
||||||
```text
|
```text
|
||||||
给列表页加一个导出按钮,测完后发 PR。
|
使用 short-ship,给列表页加一个导出按钮,测完后发 PR。
|
||||||
```
|
```
|
||||||
|
|
||||||
或:
|
Agent 命中后会立刻转到 `ship`,按 short 模式执行。新请求建议直接用 `ship`,需要时可写
|
||||||
|
`short 模式`。
|
||||||
|
|
||||||
```text
|
## 迁移
|
||||||
修复这个线上回归,完整验证并部署到测试环境。
|
|
||||||
|
```bash
|
||||||
|
pouch add ship # 新名,当前项目
|
||||||
|
pouch add ship -g # 新名,全局
|
||||||
```
|
```
|
||||||
|
|
||||||
## 使用前准备
|
- 旧软链保持有效,不需要先删除。
|
||||||
|
- 各项目 `.pouch.yaml` 里的 `short-ship` 可以分批改成 `ship`。
|
||||||
- 同时安装 `ship`(记录格式)和 `delivery-loop`。
|
- 确认没有项目再按旧名启用后,删除本目录即可;它是纯占位,删除不丢内容。
|
||||||
- 目标已经说清,或有可复现的问题描述、截图、日志。
|
|
||||||
- 项目已配置测试环境和所需 builder/tester/deployer 能力。
|
|
||||||
- 若要外部发布,明确允许的停止点或使用项目 profile。
|
|
||||||
|
|
||||||
## Agent 会做什么
|
|
||||||
|
|
||||||
主会话只建 ship 记录、写短说明、按步骤开新 agent 并等待结果。改代码、测试、开合 PR、
|
|
||||||
构建和部署都在新会话里做,不会堆进同一个上下文。一次交付一个 ship id。
|
|
||||||
|
|
||||||
## 如何判断完成
|
|
||||||
|
|
||||||
你会得到 ship id、说明、测试证据、PR/合并/tag、产物和部署状态;不会只收到“本地测试通过”。
|
|
||||||
|
|||||||
+12
-52
@@ -1,59 +1,19 @@
|
|||||||
---
|
---
|
||||||
name: short-ship
|
name: short-ship
|
||||||
description: >-
|
description: >-
|
||||||
处理范围已清楚的单次变更:小功能、文案、加按钮,或已有证据的 bug。建立
|
【已弃用,兼容占位】short-ship 已并入 ship。命中本 skill 时不要在此执行任何
|
||||||
ship 记录、改代码、验证,再按配置发 PR、合并、构建或部署。触发词:short-ship、
|
流程,直接读取 ../ship/SKILL.md 并按其执行;本次按 short 模式。小需求、小改动、
|
||||||
小需求、小改动、修 bug、改文案、加按钮。复杂需求用 long-ship;已有 PR 用 pr-ship。
|
修 bug、改文案、加按钮、修复后发 PR 或部署都属于 ship。
|
||||||
---
|
---
|
||||||
|
|
||||||
# Short Ship:单次变更到交付
|
# short-ship(已弃用)
|
||||||
|
|
||||||
当前会话只做管理:建账本、问用户、按 `next_step.py` 开新 agent 并等待。不要把
|
本 skill 已并入 [ship](../ship/SKILL.md)。这里只是兼容占位,**没有流程内容**。
|
||||||
hunt / 改代码 / 测试 / 交付读进本会话。
|
|
||||||
|
|
||||||
## 何时使用
|
- 用户提到 `short-ship`、`/short-ship`、`$short-ship` 或 short 模式时:读取
|
||||||
|
[../ship/SKILL.md](../ship/SKILL.md),按其内容与授权边界执行,并把本次 `kind`
|
||||||
- 加按钮、改文案、改短描述、单页小功能,目标与验收已经明确。
|
定为 `short`。不要在本目录里寻找步骤、references 或 templates。
|
||||||
- 用户报告错误、回归、崩溃或明确要求修 bug,并且能指出问题。
|
- 本目录不保留 `references/` 和 `templates/`,正文只有一份,在 `ship/`。
|
||||||
- 用户要求改完后继续测试、发 PR、合并、构建、发布或部署。
|
- 旧项目 `.pouch.yaml` 里仍写 `short-ship` 也能正常安装,软链不会悬空;方便时改成
|
||||||
|
`ship`。
|
||||||
## 不适用
|
- 确认没有项目再按旧名启用后,可以直接删除本目录。
|
||||||
|
|
||||||
- 跨模块、要方案取舍、要多任务协作:使用 `long-ship` 和 `specifier`。
|
|
||||||
- 已有 PR 的合并和发布:使用 `pr-ship`。
|
|
||||||
- 只做根因分析、不改代码:使用 `hunt`。
|
|
||||||
|
|
||||||
## 工作规则
|
|
||||||
|
|
||||||
1. 读取已安装 ship 的 [manager.md](../ship/references/manager.md)。派发 worker 时再读
|
|
||||||
[worker-prompt.md](../ship/references/worker-prompt.md)。没有 `ship-id` 就生成
|
|
||||||
slug,跑 `create_ship.py --kind short`。功能任务不要写入 `.pouch/ack/tasks.yaml`。
|
|
||||||
2. 主会话只写 `spec.md` 四段和那一条 task。有故障证据时 `next_step.py` 会给出
|
|
||||||
`hunt` worker,不要在本会话读 hunt。
|
|
||||||
3. 之后只跑:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 <ship-skill>/scripts/next_step.py \
|
|
||||||
--project-root <root> --ship-id <id> --entry short
|
|
||||||
```
|
|
||||||
|
|
||||||
`actor: worker` 就开新 agent 并等待;`ask_user` / `blocked` 就停下来问用户。
|
|
||||||
需要字段含义时再读 [record.md](../ship/references/record.md)。
|
|
||||||
4. 范围涨到需要 PRD 或多条任务时,同一 `ship-id` 把 `kind` 改为 `long`,改用
|
|
||||||
`--entry long`。
|
|
||||||
|
|
||||||
不要读取 `delivery-loop`、`tester`、`builder`、`deployer`、`check`、`hunt`。
|
|
||||||
这些由对应 worker 按 JSON 的 `skill` 加载。
|
|
||||||
|
|
||||||
## 安全门
|
|
||||||
|
|
||||||
- 没有 ship-id、spec.md 四段不齐、验收不可观察时停止。
|
|
||||||
- 开不出新 agent 时停止;worker 失败时不要自己把该步做完。
|
|
||||||
- 根因不清、测试缺失、真机不可达、CI/回归失败或工作树有无关改动时停止并说明。
|
|
||||||
- 不强推、不覆盖他人分支、不绕过 PR 审核,不把测试环境成功当生产成功。
|
|
||||||
- 数据迁移、破坏性 API、权限/密钥、安全修复和不可逆生产操作需要额外确认。
|
|
||||||
|
|
||||||
## 完成报告
|
|
||||||
|
|
||||||
报告 ship id、spec 路径、根因(若有)、变更、测试/真机证据指针、停止点、commit、
|
|
||||||
PR/tag、产物摘要、部署状态和未完成项。数字来自账本,不要复述 worker 技能正文。
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
interface:
|
|
||||||
display_name: "Short Ship"
|
|
||||||
short_description: "Ship a small change or bug fix through verification and delivery"
|
|
||||||
default_prompt: "You are the short-ship manager: keep the ship record, spawn a fresh agent for each next_step, and wait. Do not hunt, code, test, or deliver in this session."
|
|
||||||
policy:
|
|
||||||
allow_implicit_invocation: true
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
# specifier
|
# specifier
|
||||||
|
|
||||||
把复杂需求整理成经过多轮人工评审的 HTML PRD,并在批准后生成交给 long-ship 执行的需求 handoff。
|
把复杂需求整理成经过多轮人工评审的 HTML PRD,并在批准后生成交给 ship 执行的需求 handoff。
|
||||||
|
|
||||||
## 什么时候使用
|
## 什么时候使用
|
||||||
|
|
||||||
- 要做跨模块、跨页面、跨服务或带数据迁移的复杂功能。
|
- 要做跨模块、跨页面、跨服务或带数据迁移的复杂功能。
|
||||||
- 要先写 PRD、比较方案、梳理风险,再进入开发。
|
- 要先写 PRD、比较方案、梳理风险,再进入开发。
|
||||||
- 需要把 PRD 给人反复评审,确认后再交给 `long-ship`。
|
- 需要把 PRD 给人反复评审,确认后再交给 `ship`。
|
||||||
|
|
||||||
小改动直接使用 `short-ship`;已知 Bug 使用 `/hunt`;单次架构取舍使用 `/think`。
|
小改动直接使用 `ship`;已知 Bug 使用 `/hunt`;单次架构取舍使用 `/think`。
|
||||||
|
|
||||||
## 使用前准备
|
## 使用前准备
|
||||||
|
|
||||||
- 同时安装 `ship`;批准后的开发走 `long-ship`。
|
- 同时安装 `ship`;批准后的开发走 `ship` long 模式。
|
||||||
- 项目应位于 Git 仓库中,便于读取现有代码、文档和历史决策。
|
- 项目应位于 Git 仓库中,便于读取现有代码、文档和历史决策。
|
||||||
- 用户需要参与 PRD 评审,并明确批准具体版本。
|
- 用户需要参与 PRD 评审,并明确批准具体版本。
|
||||||
- 不需要外部账号或网络资源;生成的 HTML 应能离线打开。
|
- 不需要外部账号或网络资源;生成的 HTML 应能离线打开。
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
用 specifier 处理“给后台增加多租户权限管理”这个复杂需求:先调研现状,生成 HTML PRD,
|
用 specifier 处理“给后台增加多租户权限管理”这个复杂需求:先调研现状,生成 HTML PRD,
|
||||||
我会逐轮评审。批准后再生成 long-ship handoff,不要提前派开发。
|
我会逐轮评审。批准后再生成 ship handoff,不要提前派开发。
|
||||||
```
|
```
|
||||||
|
|
||||||
## Agent 会做什么
|
## Agent 会做什么
|
||||||
@@ -30,14 +30,14 @@
|
|||||||
2. 形成目标、范围、非目标、方案、交互/架构、风险、依赖和可观测验收。
|
2. 形成目标、范围、非目标、方案、交互/架构、风险、依赖和可观测验收。
|
||||||
3. 生成可离线打开的版本化 HTML PRD;保留每个历史版本并记录本轮变更。
|
3. 生成可离线打开的版本化 HTML PRD;保留每个历史版本并记录本轮变更。
|
||||||
4. 根据用户意见反复修改,直到用户明确批准指定版本。
|
4. 根据用户意见反复修改,直到用户明确批准指定版本。
|
||||||
5. 仅在批准后生成 `spec/handoff.yaml`,供 `long-ship` 创建和执行任务。
|
5. 仅在批准后生成 `spec/handoff.yaml`,供 `ship` 创建和执行任务。
|
||||||
6. 用户需要查看 HTML 时,按独立 `previewer` 的契约直接执行共享 Node 脚本,返回 HTTP URL
|
6. 用户需要查看 HTML 时,按独立 `previewer` 的契约直接执行共享 Node 脚本,返回 HTTP URL
|
||||||
和有效期;跨设备查看时可显式设置监听地址。
|
和有效期;跨设备查看时可显式设置监听地址。
|
||||||
|
|
||||||
Specifier 只负责需求规格,不修改业务代码,也不替代 long-ship 的开发和独立测试。
|
Specifier 只负责需求规格,不修改业务代码,也不替代 ship 的开发和独立测试。
|
||||||
|
|
||||||
## 如何判断完成
|
## 如何判断完成
|
||||||
|
|
||||||
- 评审阶段:得到一个带版本号的 HTML PRD、变更记录、风险和未决问题。
|
- 评审阶段:得到一个带版本号的 HTML PRD、变更记录、风险和未决问题。
|
||||||
- 交接阶段:用户明确批准某个版本,且 `spec/handoff.yaml` 绑定了该版本和完整验收信息。
|
- 交接阶段:用户明确批准某个版本,且 `spec/handoff.yaml` 绑定了该版本和完整验收信息。
|
||||||
- 开发阶段:改用 `long-ship`,任务和证据写在同一个 ship id 下。
|
- 开发阶段:改用 `ship` long 模式,任务和证据写在同一个 ship id 下。
|
||||||
|
|||||||
+19
-19
@@ -2,14 +2,14 @@
|
|||||||
name: specifier
|
name: specifier
|
||||||
description: >-
|
description: >-
|
||||||
把复杂或跨模块需求整理成可评审、可版本化的 HTML PRD,并在用户明确批准某个版本后
|
把复杂或跨模块需求整理成可评审、可版本化的 HTML PRD,并在用户明确批准某个版本后
|
||||||
生成交给 long-ship 的结构化 handoff。用户说做复杂功能、写 PRD、梳理需求、设计方案、
|
生成交给 ship 的结构化 handoff。用户说做复杂功能、写 PRD、梳理需求、设计方案、
|
||||||
需求评审、反复改需求或交给 long-ship 开发时使用;小改动、Bug 根因排查和单纯架构讨论
|
需求评审、反复改需求或交给 ship 开发时使用;小改动、Bug 根因排查和单纯架构讨论
|
||||||
不使用。
|
不使用。
|
||||||
---
|
---
|
||||||
|
|
||||||
# Specifier:复杂需求规格化
|
# Specifier:复杂需求规格化
|
||||||
|
|
||||||
把模糊需求变成经过人确认的需求基线,再交给 `long-ship` 执行。HTML 是给人评审的正式交付物,
|
把模糊需求变成经过人确认的需求基线,再交给 `ship`(`kind: long`)执行。HTML 是给人评审的正式交付物,
|
||||||
结构化状态和 handoff 是给 ship 记录消费的机器接口。
|
结构化状态和 handoff 是给 ship 记录消费的机器接口。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -18,12 +18,12 @@ description: >-
|
|||||||
|
|
||||||
- 用户要做跨模块、跨页面、跨服务或有数据迁移的复杂功能。
|
- 用户要做跨模块、跨页面、跨服务或有数据迁移的复杂功能。
|
||||||
- 用户要求写 PRD、产品方案、需求规格、架构/交互方案,并需要多轮确认。
|
- 用户要求写 PRD、产品方案、需求规格、架构/交互方案,并需要多轮确认。
|
||||||
- 用户要求把确认后的需求交给 `long-ship` 开发。
|
- 用户要求把确认后的需求交给 `ship` 开发。
|
||||||
|
|
||||||
## 不适用
|
## 不适用
|
||||||
|
|
||||||
- 单文件或范围明确的小改动:使用 `short-ship`。
|
- 单文件或范围明确的小改动:使用 `ship`(short 模式)。
|
||||||
- 已有复现证据的 Bug:使用 `/hunt` 找根因,再按需要交给 `short-ship`。
|
- 已有复现证据的 Bug:使用 `/hunt` 找根因,再按需要交给 `ship`。
|
||||||
- 只问一个技术选型或一次性方案判断:使用 `/think`。
|
- 只问一个技术选型或一次性方案判断:使用 `/think`。
|
||||||
- 用户尚未批准的需求:不得创建开发任务、派 worker、改业务代码或开始交付。
|
- 用户尚未批准的需求:不得创建开发任务、派 worker、改业务代码或开始交付。
|
||||||
|
|
||||||
@@ -38,14 +38,14 @@ description: >-
|
|||||||
.pouch/ship/<ship-id>/
|
.pouch/ship/<ship-id>/
|
||||||
├── current.yaml # ship 总账;spec.revision / specStatus 在这里
|
├── current.yaml # ship 总账;spec.revision / specStatus 在这里
|
||||||
├── spec.md # 短摘要
|
├── spec.md # 短摘要
|
||||||
├── tasks.yaml # 批准后由 long-ship 写入,本 skill 不写
|
├── tasks.yaml # 批准后由 ship 写入,本 skill 不写
|
||||||
└── spec/
|
└── spec/
|
||||||
├── brief.md
|
├── brief.md
|
||||||
├── prd-v001.html
|
├── prd-v001.html
|
||||||
├── review.yaml
|
├── review.yaml
|
||||||
├── decisions.yaml
|
├── decisions.yaml
|
||||||
├── risks.yaml
|
├── risks.yaml
|
||||||
└── handoff.yaml # 仅 approved 后生成,交给 long-ship
|
└── handoff.yaml # 仅 approved 后生成,交给 ship
|
||||||
```
|
```
|
||||||
|
|
||||||
目录名使用稳定、短、小写连字符的 ship id。若用户未提供,先从需求生成候选并说明;
|
目录名使用稳定、短、小写连字符的 ship id。若用户未提供,先从需求生成候选并说明;
|
||||||
@@ -64,24 +64,24 @@ description: >-
|
|||||||
- **评审 / 修改**:读取当前版本和 `spec/review.yaml`,把用户意见转成修改项,生成新版本,
|
- **评审 / 修改**:读取当前版本和 `spec/review.yaml`,把用户意见转成修改项,生成新版本,
|
||||||
展示变更摘要和仍未决的问题。
|
展示变更摘要和仍未决的问题。
|
||||||
- **批准**:只在用户明确批准指定版本后,把 `spec.specStatus` 改为 `approved`,冻结需求基线。
|
- **批准**:只在用户明确批准指定版本后,把 `spec.specStatus` 改为 `approved`,冻结需求基线。
|
||||||
- **交接**:仅针对 `approved` 版本生成 `spec/handoff.yaml`;用户要求继续开发时把它交给 `long-ship`。
|
- **交接**:仅针对 `approved` 版本生成 `spec/handoff.yaml`;用户要求继续开发时把它交给 `ship`。
|
||||||
- **状态**:只读报告当前版本、待决策、风险和是否可以交接。
|
- **状态**:只读报告当前版本、待决策、风险和是否可以交接。
|
||||||
- 作为 `long-ship` 派来的 worker:只做 prompt 里的那一个模式,写完 `spec/` 就返回;
|
- 作为 `ship` 派来的 worker:只做 prompt 里的那一个模式,写完 `spec/` 就返回;
|
||||||
不要再调用 `long-ship`,不要改业务代码。
|
不要再调用 `ship` 入口,不要改业务代码。
|
||||||
|
|
||||||
详细评审状态机和确认规则见 [review-loop.md](references/review-loop.md)。HTML 页面结构和
|
详细评审状态机和确认规则见 [review-loop.md](references/review-loop.md)。HTML 页面结构和
|
||||||
渲染要求见 [html-prd.md](references/html-prd.md)。只有需要对应模式时才读取它们。
|
渲染要求见 [html-prd.md](references/html-prd.md)。只有需要对应模式时才读取它们。
|
||||||
|
|
||||||
## 新建和调研
|
## 新建和调研
|
||||||
|
|
||||||
1. 先判断需求是否真的复杂;不复杂时给出转 `short-ship` 的建议,不强行生成 PRD。
|
1. 先判断需求是否真的复杂;不复杂时给出转 `ship` short 模式的建议,不强行生成 PRD。
|
||||||
2. 读取项目覆盖层、相关文档、历史决策和代码;优先确认现有能力、模块边界、数据模型、
|
2. 读取项目覆盖层、相关文档、历史决策和代码;优先确认现有能力、模块边界、数据模型、
|
||||||
API、权限、运行约束和回归用例。不要从 README 或猜测中虚构项目事实。
|
API、权限、运行约束和回归用例。不要从 README 或猜测中虚构项目事实。
|
||||||
3. 形成现状与目标差异:背景、用户、场景、目标、非目标、范围、约束、成功标准。
|
3. 形成现状与目标差异:背景、用户、场景、目标、非目标、范围、约束、成功标准。
|
||||||
4. 对关键方案给出取舍,记录推荐方案、被否决方案、最脆弱假设、风险、依赖和回滚方向。
|
4. 对关键方案给出取舍,记录推荐方案、被否决方案、最脆弱假设、风险、依赖和回滚方向。
|
||||||
5. 描述必要的产品流程、交互状态、领域模型、状态流、API/数据变化和非功能要求。
|
5. 描述必要的产品流程、交互状态、领域模型、状态流、API/数据变化和非功能要求。
|
||||||
6. 按可独立验证的垂直切片拆任务,写出依赖、关键路径、`expected` 和 `verification`,
|
6. 按可独立验证的垂直切片拆任务,写出依赖、关键路径、`expected` 和 `verification`,
|
||||||
但不要写入 `.pouch/ack/tasks.yaml` 或本 ship 的 `tasks.yaml`(任务由 long-ship 在批准后创建)。
|
但不要写入 `.pouch/ack/tasks.yaml` 或本 ship 的 `tasks.yaml`(任务由 ship 在批准后创建)。
|
||||||
7. 按模板生成自包含 HTML PRD,并在回复中说明文件路径、版本、待确认问题和下一步。
|
7. 按模板生成自包含 HTML PRD,并在回复中说明文件路径、版本、待确认问题和下一步。
|
||||||
8. 用户需要查看 HTML,或说“把这个 HTML 渲染出来”时,读取已安装 `previewer` 的调用契约,
|
8. 用户需要查看 HTML,或说“把这个 HTML 渲染出来”时,读取已安装 `previewer` 的调用契约,
|
||||||
直接执行其共享脚本并传入该文件;不要再次委派 Agent 或在 `specifier` 内复制服务代码。
|
直接执行其共享脚本并传入该文件;不要再次委派 Agent 或在 `specifier` 内复制服务代码。
|
||||||
@@ -108,20 +108,20 @@ description: >-
|
|||||||
评审状态,HTML 被展示不等于用户批准。回复提供完整 URL 和有效期;跨设备预览需设置可访问的
|
评审状态,HTML 被展示不等于用户批准。回复提供完整 URL 和有效期;跨设备预览需设置可访问的
|
||||||
监听地址,不能把本机 `127.0.0.1` 描述为远程访问地址。
|
监听地址,不能把本机 `127.0.0.1` 描述为远程访问地址。
|
||||||
|
|
||||||
## 批准和 long-ship 交接
|
## 批准和 ship 交接
|
||||||
|
|
||||||
批准前只允许写该 ship 的 spec 目录和 `current.yaml` 的 spec 字段。批准后仍不自动启动
|
批准前只允许写该 ship 的 spec 目录和 `current.yaml` 的 spec 字段。批准后仍不自动启动
|
||||||
worker;先生成 `spec/handoff.yaml`,其中必须绑定:ship id、PRD 版本和路径、需求范围、
|
worker;先生成 `spec/handoff.yaml`,其中必须绑定:ship id、PRD 版本和路径、需求范围、
|
||||||
非目标、决策、任务依赖、验收信号、风险、回滚/发布注意事项和回归候选。同步
|
非目标、决策、任务依赖、验收信号、风险、回滚/发布注意事项和回归候选。同步
|
||||||
`current.yaml` 的 `spec.specStatus`、`spec.approvedRevision`、`spec.handoff`。
|
`current.yaml` 的 `spec.specStatus`、`spec.approvedRevision`、`spec.handoff`。
|
||||||
|
|
||||||
`handoff.yaml` 是 `long-ship` 的输入,不是执行记录。用户明确要求继续开发时,提示使用:
|
`handoff.yaml` 是 `ship` long 模式的输入,不是执行记录。用户明确要求继续开发时,提示使用:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
/long-ship 使用 handoff:.pouch/ship/<ship-id>/spec/handoff.yaml
|
/ship long 模式 使用 handoff:.pouch/ship/<ship-id>/spec/handoff.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
long-ship 应引用批准的 HTML 版本,不复制另一份可漂移的 PRD。handoff 缺字段或需求状态
|
ship 应引用批准的 HTML 版本,不复制另一份可漂移的 PRD。handoff 缺字段或需求状态
|
||||||
不是 `approved` 时,停止交接并报告缺口。
|
不是 `approved` 时,停止交接并报告缺口。
|
||||||
|
|
||||||
## 安全边界
|
## 安全边界
|
||||||
@@ -136,5 +136,5 @@ long-ship 应引用批准的 HTML 版本,不复制另一份可漂移的 PRD。
|
|||||||
|
|
||||||
完成前检查:HTML 可离线打开、版本号与 ship `current.yaml` 的 `spec.revision` 一致、旧版本
|
完成前检查:HTML 可离线打开、版本号与 ship `current.yaml` 的 `spec.revision` 一致、旧版本
|
||||||
未被覆盖、每个未决问题有明确状态、每个任务有可观测验收、handoff 只在 `approved` 后存在
|
未被覆盖、每个未决问题有明确状态、每个任务有可观测验收、handoff 只在 `approved` 后存在
|
||||||
且绑定正确的 PRD 版本。需要交给 long-ship 时,先确认 handoff schema 与用户批准版本一致,
|
且绑定正确的 PRD 版本。需要交给 ship 时,先确认 handoff schema 与用户批准版本一致,
|
||||||
再停止等待 long-ship。
|
再停止等待 ship。
|
||||||
|
|||||||
@@ -36,4 +36,4 @@ HTML 是给人离线评审的交付物,不是运行中的产品页面。它必
|
|||||||
- 把“体验好”“性能足够”“支持权限”等不可验证表述改成可观察信号或明确待决策项。
|
- 把“体验好”“性能足够”“支持权限”等不可验证表述改成可观察信号或明确待决策项。
|
||||||
- 不把实现方案伪装成用户需求;需求、决策和任务分开写。
|
- 不把实现方案伪装成用户需求;需求、决策和任务分开写。
|
||||||
- 未知事实写“待确认”,并加入未决问题,不填看似合理的数字、接口或页面。
|
- 未知事实写“待确认”,并加入未决问题,不填看似合理的数字、接口或页面。
|
||||||
- 每条验收都能被 long-ship / Test 按步骤观察;测试工具不可用时仍能区分产品失败和环境失败。
|
- 每条验收都能被 ship / Test 按步骤观察;测试工具不可用时仍能区分产品失败和环境失败。
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ spec:
|
|||||||
|
|
||||||
如果用户说“继续”“可以”“看起来不错”,但没有绑定版本,继续保持 `review`,请用户确认
|
如果用户说“继续”“可以”“看起来不错”,但没有绑定版本,继续保持 `review`,请用户确认
|
||||||
具体版本。批准后把 `spec.approvedRevision` 写入 ship `current.yaml`,再生成
|
具体版本。批准后把 `spec.approvedRevision` 写入 ship `current.yaml`,再生成
|
||||||
`spec/handoff.yaml`;不要同时启动 long-ship 或 ACK。
|
`spec/handoff.yaml`;不要同时启动 ship 开发或 ACK。
|
||||||
|
|
||||||
## 批准后的变更
|
## 批准后的变更
|
||||||
|
|
||||||
@@ -97,5 +97,5 @@ spec:
|
|||||||
4. 标记受影响的任务、验收、风险和 ACK handoff 字段;
|
4. 标记受影响的任务、验收、风险和 ACK handoff 字段;
|
||||||
5. 重新等待用户批准。
|
5. 重新等待用户批准。
|
||||||
|
|
||||||
已经进入 long-ship 的任务不会因 HTML 更新而自动改变。需要执行变更时,明确告诉用户应
|
已经进入 ship 开发的任务不会因 HTML 更新而自动改变。需要执行变更时,明确告诉用户应
|
||||||
修订本 ship 的 `tasks.yaml` 或重新规划,不能静默改写执行记录。
|
修订本 ship 的 `tasks.yaml` 或重新规划,不能静默改写执行记录。
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ class CatalogTests(unittest.TestCase):
|
|||||||
skill = (stub / "SKILL.md").read_text(encoding="utf-8")
|
skill = (stub / "SKILL.md").read_text(encoding="utf-8")
|
||||||
self.assertIn("name: bugfix-ship", skill)
|
self.assertIn("name: bugfix-ship", skill)
|
||||||
self.assertIn("已弃用", skill)
|
self.assertIn("已弃用", skill)
|
||||||
self.assertIn("../short-ship/SKILL.md", skill)
|
self.assertIn("../ship/SKILL.md", skill)
|
||||||
self.assertTrue((REPO_ROOT / "skills" / "short-ship" / "SKILL.md").is_file())
|
self.assertTrue((REPO_ROOT / "skills" / "ship" / "SKILL.md").is_file())
|
||||||
self.assertFalse((stub / "references").exists())
|
self.assertFalse((stub / "references").exists())
|
||||||
self.assertFalse((stub / "templates").exists())
|
self.assertFalse((stub / "templates").exists())
|
||||||
self.assertFalse((stub / "agents").exists())
|
self.assertFalse((stub / "agents").exists())
|
||||||
@@ -73,8 +73,32 @@ class CatalogTests(unittest.TestCase):
|
|||||||
skill = (stub / "SKILL.md").read_text(encoding="utf-8")
|
skill = (stub / "SKILL.md").read_text(encoding="utf-8")
|
||||||
self.assertIn("name: requirement-flow", skill)
|
self.assertIn("name: requirement-flow", skill)
|
||||||
self.assertIn("已弃用", skill)
|
self.assertIn("已弃用", skill)
|
||||||
self.assertIn("../long-ship/SKILL.md", skill)
|
self.assertIn("../ship/SKILL.md", skill)
|
||||||
self.assertTrue((REPO_ROOT / "skills" / "long-ship" / "SKILL.md").is_file())
|
self.assertTrue((REPO_ROOT / "skills" / "ship" / "SKILL.md").is_file())
|
||||||
|
self.assertFalse((stub / "references").exists())
|
||||||
|
self.assertFalse((stub / "templates").exists())
|
||||||
|
self.assertFalse((stub / "agents").exists())
|
||||||
|
|
||||||
|
def test_short_ship_is_deprecation_stub(self) -> None:
|
||||||
|
stub = REPO_ROOT / "skills" / "short-ship"
|
||||||
|
skill = (stub / "SKILL.md").read_text(encoding="utf-8")
|
||||||
|
self.assertIn("name: short-ship", skill)
|
||||||
|
self.assertIn("已弃用", skill)
|
||||||
|
self.assertIn("../ship/SKILL.md", skill)
|
||||||
|
self.assertIn("short 模式", skill)
|
||||||
|
self.assertTrue((REPO_ROOT / "skills" / "ship" / "SKILL.md").is_file())
|
||||||
|
self.assertFalse((stub / "references").exists())
|
||||||
|
self.assertFalse((stub / "templates").exists())
|
||||||
|
self.assertFalse((stub / "agents").exists())
|
||||||
|
|
||||||
|
def test_long_ship_is_deprecation_stub(self) -> None:
|
||||||
|
stub = REPO_ROOT / "skills" / "long-ship"
|
||||||
|
skill = (stub / "SKILL.md").read_text(encoding="utf-8")
|
||||||
|
self.assertIn("name: long-ship", skill)
|
||||||
|
self.assertIn("已弃用", skill)
|
||||||
|
self.assertIn("../ship/SKILL.md", skill)
|
||||||
|
self.assertIn("long 模式", skill)
|
||||||
|
self.assertTrue((REPO_ROOT / "skills" / "ship" / "SKILL.md").is_file())
|
||||||
self.assertFalse((stub / "references").exists())
|
self.assertFalse((stub / "references").exists())
|
||||||
self.assertFalse((stub / "templates").exists())
|
self.assertFalse((stub / "templates").exists())
|
||||||
self.assertFalse((stub / "agents").exists())
|
self.assertFalse((stub / "agents").exists())
|
||||||
|
|||||||
@@ -203,16 +203,16 @@ def _create(root: Path, ship_id: str, kind: str, *, stop_at: str | None = None)
|
|||||||
return root / ".pouch" / "ship" / ship_id
|
return root / ".pouch" / "ship" / ship_id
|
||||||
|
|
||||||
|
|
||||||
def _next(root: Path, ship_id: str, entry: str) -> dict:
|
def _next(root: Path, ship_id: str, entry: str | None = None) -> dict:
|
||||||
result = _run(
|
args = [
|
||||||
NEXT,
|
|
||||||
"--project-root",
|
"--project-root",
|
||||||
str(root),
|
str(root),
|
||||||
"--ship-id",
|
"--ship-id",
|
||||||
ship_id,
|
ship_id,
|
||||||
"--entry",
|
]
|
||||||
entry,
|
if entry is not None:
|
||||||
)
|
args.extend(["--entry", entry])
|
||||||
|
result = _run(NEXT, *args)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
raise AssertionError(result.stderr or result.stdout)
|
raise AssertionError(result.stderr or result.stdout)
|
||||||
return json.loads(result.stdout)
|
return json.loads(result.stdout)
|
||||||
@@ -404,11 +404,36 @@ class NextStepTests(unittest.TestCase):
|
|||||||
self.assertIn("不要自己做", doc["summary"])
|
self.assertIn("不要自己做", doc["summary"])
|
||||||
|
|
||||||
def test_entry_skills_are_managers(self) -> None:
|
def test_entry_skills_are_managers(self) -> None:
|
||||||
for name in ("short-ship", "long-ship", "pr-ship"):
|
for name in ("ship", "pr-ship"):
|
||||||
text = (REPO_ROOT / "skills" / name / "SKILL.md").read_text(encoding="utf-8")
|
text = (REPO_ROOT / "skills" / name / "SKILL.md").read_text(encoding="utf-8")
|
||||||
self.assertIn("开新 agent", text)
|
self.assertIn("开新 agent", text)
|
||||||
self.assertNotIn("读取并执行", text)
|
self.assertNotIn("读取并执行", text)
|
||||||
|
|
||||||
|
def test_next_step_infers_entry_from_kind(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
root = Path(tmp)
|
||||||
|
_create(root, "add-export-button", "short", stop_at="verified")
|
||||||
|
doc = _next(root, "add-export-button")
|
||||||
|
self.assertEqual(doc["entry"], "short")
|
||||||
|
self.assertEqual(doc["step"], "write_spec")
|
||||||
|
|
||||||
|
_create(root, "tenant-permissions", "long")
|
||||||
|
doc = _next(root, "tenant-permissions")
|
||||||
|
self.assertEqual(doc["entry"], "long")
|
||||||
|
self.assertEqual(doc["step"], "specifier_new")
|
||||||
|
|
||||||
|
missing = _run(
|
||||||
|
NEXT,
|
||||||
|
"--project-root",
|
||||||
|
str(root),
|
||||||
|
"--ship-id",
|
||||||
|
"does-not-exist",
|
||||||
|
)
|
||||||
|
self.assertEqual(missing.returncode, 0, missing.stderr)
|
||||||
|
blocked = json.loads(missing.stdout)
|
||||||
|
self.assertEqual(blocked["entry"], "short")
|
||||||
|
self.assertEqual(blocked["step"], "ensure_record")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user