docs: add inbox markdown test plans
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
# Inbox Workflow Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document tracks cross-command scenarios where the main value is the interaction between multiple `inbox` subcommands.
|
||||
|
||||
All examples assume:
|
||||
|
||||
- isolated temp database
|
||||
- `inbox --db TMPDIR/coord.db --json init` already executed
|
||||
- assertions follow the shared rules in [../_shared/README.md](../_shared/README.md)
|
||||
|
||||
## case: thread-lifecycle-happy-path
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `send -> fetch -> claim -> update(in_progress) -> update(blocked) -> reply -> done -> show` 的主干链路可用,且线程与消息历史一致。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 空数据库已完成 `init`
|
||||
- 发送方为 `leader`
|
||||
- 执行方为 `worker-a`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json send --from leader --to worker-a --subject "Implement feature X" --summary "Add retry policy" --body "Implement retry handling for the HTTP client." --run run_blog_001 --task T1
|
||||
inbox --db TMPDIR/coord.db --json fetch --agent worker-a --status pending
|
||||
inbox --db TMPDIR/coord.db --json claim --agent worker-a --thread THREAD_ID --lease-seconds 300
|
||||
inbox --db TMPDIR/coord.db --json update --agent worker-a --thread THREAD_ID --status in_progress --summary "Implementation started" --body "Scanning current HTTP client usage."
|
||||
inbox --db TMPDIR/coord.db --json update --agent worker-a --thread THREAD_ID --status blocked --summary "Need timeout decision" --payload-json '{"question":"Should retries apply to read timeouts?"}'
|
||||
inbox --db TMPDIR/coord.db --json reply --from leader --to worker-a --thread THREAD_ID --summary "Retry read timeouts" --body "Yes, include read timeouts in the retry policy."
|
||||
inbox --db TMPDIR/coord.db --json done --agent worker-a --thread THREAD_ID --summary "Retry policy implemented" --body "The HTTP client now retries the selected transient failures."
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `send` 返回新建线程,线程状态为 `pending`
|
||||
- `fetch` 返回唯一匹配线程
|
||||
- `claim` 后线程状态为 `claimed`
|
||||
- 第一次 `update` 后线程状态为 `in_progress`
|
||||
- 第二次 `update` 后线程状态为 `blocked`
|
||||
- `reply` 返回一条 `kind=answer` 的消息
|
||||
- `done` 后线程状态为 `done`
|
||||
- `show` 返回线程状态 `done`,并包含完整消息历史
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 全链路所有命令退出码为 `0`
|
||||
- `show.data.thread.status == "done"`
|
||||
- `show.data.messages` 长度为 `6`
|
||||
- 历史中的状态推进顺序与执行顺序一致,不出现丢消息或状态回退
|
||||
|
||||
## case: blocked-question-reply-resume-to-done
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证被阻塞线程在收到答复后可以继续推进,并最终进入完成态。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在由 `leader` 发给 `worker-c` 的线程
|
||||
- `worker-c` 已经成功 `claim`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json update --agent worker-c --thread THREAD_ID --status blocked --summary "Need policy decision" --body "Should guest users be redirected to login or shown a 403 page?"
|
||||
inbox --db TMPDIR/coord.db --agent worker-c --json wait-reply --thread THREAD_ID --after-message BLOCKED_MESSAGE_ID --timeout-seconds 2
|
||||
inbox --db TMPDIR/coord.db --json reply --from leader --to worker-c --thread THREAD_ID --summary "Redirect to login" --body "Redirect guests to login for the MVP."
|
||||
inbox --db TMPDIR/coord.db --json done --agent worker-c --thread THREAD_ID --summary "Policy applied" --body "The flow now redirects guests to login."
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `update` 将线程推进到 `blocked`
|
||||
- `wait-reply` 在答复出现后唤醒
|
||||
- 唤醒结果包含答复消息
|
||||
- `done` 成功将线程推进到 `done`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `wait-reply.data.woke == true`
|
||||
- `wait-reply.data.message.kind == "answer"`
|
||||
- 最终 `done.data.thread.status == "done"`
|
||||
- 该用例强调“阻塞后可恢复”,不是单纯验证 reply 本身
|
||||
|
||||
## case: fail-lifecycle-from-claim-to-terminal
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证线程在被领取后可以直接进入失败终态,并且 `show` 对终态读取一致。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 空数据库已完成 `init`
|
||||
- `leader` 已向 `worker-b` 发送任务
|
||||
- `worker-b` 已 `claim` 该线程
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json fail --agent worker-b --thread THREAD_ID --summary "Migration failed" --body "The migration cannot proceed because the prior schema is inconsistent."
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `fail` 返回线程状态 `failed`
|
||||
- `show` 返回相同终态
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `fail.data.thread.status == "failed"`
|
||||
- `show.data.thread.status == "failed"`
|
||||
- 失败消息保留在线程历史中,可被后续排障读取
|
||||
|
||||
## case: cancel-lifecycle-after-worker-claim
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证线程在执行者已领取后,发起方仍可以取消任务,并进入 `cancelled` 终态。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `leader` 已向 `worker-c` 发送任务
|
||||
- `worker-c` 已成功 `claim`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json cancel --agent leader --thread THREAD_ID --reason "Task superseded by a larger refactor"
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `cancel` 成功
|
||||
- 返回线程状态 `cancelled`
|
||||
- 返回的消息记录取消原因
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `cancel.data.thread.status == "cancelled"`
|
||||
- 取消属于终态转换,不要求执行者先主动释放 lease
|
||||
- 原因字段可被后续 `show` 或审计场景消费
|
||||
|
||||
## case: watch-wakes-then-fetch-sees-new-thread
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `watch` 的等待语义与 `fetch --unread` 的可见性一致,确保新线程到达时执行者既会被唤醒,也能随后拉到未读任务。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-d` 尚无匹配 `pending` 线程
|
||||
- `watch` 先于 `send` 启动
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json watch --agent worker-d --status pending --timeout-seconds 2
|
||||
inbox --db TMPDIR/coord.db --json send --from leader --to worker-d --subject "Build admin editor" --summary "Create the first editor screen" --body-file TMPDIR/task.md --artifact TMPDIR/task.md --artifact-kind brief --artifact-metadata-json '{"label":"task-brief"}' --run run_blog_004 --task T4
|
||||
inbox --db TMPDIR/coord.db --json fetch --agent worker-d --status pending --unread
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `watch` 因新线程到达而唤醒
|
||||
- 唤醒结果中的 `thread_id` 与 `send` 返回值一致
|
||||
- 随后 `fetch --unread` 仍能看到该 `pending` 线程
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `watch.data.woke == true`
|
||||
- `watch.data.thread.thread_id == send.data.thread.thread_id`
|
||||
- `fetch.data.threads` 长度为 `1`
|
||||
- `watch` 唤醒不应提前消费掉线程的未读可见性
|
||||
|
||||
## case: artifact-visible-through-send-and-show
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `send` 写入的 body-file 与 artifact 信息能被后续 `show` 完整读回。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `TMPDIR/task.md` 已存在,内容为测试任务正文
|
||||
- 空数据库已完成 `init`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json send --from leader --to worker-d --subject "Build admin editor" --summary "Create the first editor screen" --body-file TMPDIR/task.md --artifact TMPDIR/task.md --artifact-kind brief --artifact-metadata-json '{"label":"task-brief"}' --run run_blog_004 --task T4
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `send` 成功创建线程并附带一条 artifact
|
||||
- `show` 的首条消息包含从文件读取的正文与 artifact 列表
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 首条消息 `body` 等于 `TMPDIR/task.md` 的文件内容
|
||||
- 首条消息 `artifacts` 长度为 `1`
|
||||
- 首个 artifact 的 `path` 等于 `TMPDIR/task.md`
|
||||
- 首个 artifact 的 `kind` 等于 `brief`
|
||||
|
||||
## case: unread-clears-after-mark-read-and-reappears-on-new-message
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 read cursor 的最关键用户感知行为:未读任务可被显式清空,并会在同线程新消息到达后重新出现。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `leader` 已向 `worker-e` 发送一个 `pending` 线程
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json fetch --agent worker-e --status pending --unread
|
||||
inbox --db TMPDIR/coord.db --agent worker-e --json show --thread THREAD_ID --mark-read
|
||||
inbox --db TMPDIR/coord.db --json fetch --agent worker-e --status pending --unread
|
||||
inbox --db TMPDIR/coord.db --json send --from leader --to worker-e --thread THREAD_ID --summary "Use sentence case" --body "Keep the nav labels in sentence case."
|
||||
inbox --db TMPDIR/coord.db --json fetch --agent worker-e --status pending --unread
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 第一次 `fetch --unread` 返回该线程
|
||||
- `show --mark-read` 成功推进 `worker-e` 的 read cursor
|
||||
- 第二次 `fetch --unread` 无匹配结果
|
||||
- 新消息追加后,第三次 `fetch --unread` 再次返回该线程
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 第一次 `fetch` 返回 1 条线程
|
||||
- 第二次 `fetch` 退出码为 `10`,错误码为 `no_matching_work`
|
||||
- 追加消息后第三次 `fetch` 再次返回 1 条线程
|
||||
- 未读状态是按 agent 视角计算,而不是线程级布尔值
|
||||
|
||||
## case: wait-reply-clears-blocked-unread-for-agent
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证等待答复的消费者在收到答复后,其阻塞线程未读状态会被消费,避免“已经处理过回复但列表仍显示未读”的错觉。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-c` 已拥有一个 `blocked` 线程
|
||||
- 该线程阻塞消息对应的 `message_id` 已知
|
||||
- `worker-c` 使用 `wait-reply` 等待答复
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --agent worker-c --json wait-reply --thread THREAD_ID --after-message BLOCKED_MESSAGE_ID --timeout-seconds 2
|
||||
inbox --db TMPDIR/coord.db --json reply --from leader --to worker-c --thread THREAD_ID --summary "Redirect to login" --body "Redirect guests to login for the MVP."
|
||||
inbox --db TMPDIR/coord.db --agent worker-c --json fetch --status blocked --unread
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `wait-reply` 在答复后唤醒
|
||||
- 唤醒结果携带 `answer` 消息
|
||||
- 随后的 `fetch --status blocked --unread` 不再返回该线程
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `wait-reply.data.woke == true`
|
||||
- `wait-reply.data.message.kind == "answer"`
|
||||
- 后续 `fetch` 退出码为 `10`
|
||||
- 对等待中的 agent 来说,答复消费与未读清理是同一条用户契约链路
|
||||
Reference in New Issue
Block a user