docs: add inbox markdown test plans
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# Inbox Markdown Test Plan
|
||||
|
||||
## Purpose
|
||||
|
||||
This directory contains the human-readable Markdown test plan for the `inbox` CLI.
|
||||
|
||||
It complements automated Go tests. The goal is not to restate implementation details, but to preserve the user-visible CLI contract in a form that can be reviewed, extended, and executed manually when needed.
|
||||
|
||||
## Directory Rules
|
||||
|
||||
- one folder per command or shared area
|
||||
- one `README.md` per folder
|
||||
- no one-file-per-case sprawl
|
||||
- no numeric test IDs
|
||||
- each case is identified by `path + case slug`
|
||||
|
||||
Recommended case heading pattern:
|
||||
|
||||
```md
|
||||
## case: send-rejects-invalid-payload-json
|
||||
```
|
||||
|
||||
## Authoring Principles
|
||||
|
||||
- focus on externally visible behavior of the CLI
|
||||
- prefer stable command examples that a new agent can replay against a temp database
|
||||
- describe both success shape and failure contract
|
||||
- when a case already exists in automated Go tests, reuse its scenario rather than inventing a new one
|
||||
- keep terminology consistent with command flags and JSON fields exposed by the CLI
|
||||
|
||||
## Common Execution Model
|
||||
|
||||
Most cases in this directory assume the same baseline:
|
||||
|
||||
1. create an isolated temporary directory
|
||||
2. choose a database path such as `TMPDIR/coord.db`
|
||||
3. run `inbox --db TMPDIR/coord.db --json init`
|
||||
4. run the target command sequence against that database
|
||||
|
||||
Unless a case says otherwise:
|
||||
|
||||
- commands should use `--json`
|
||||
- assertions should check both exit code and JSON payload
|
||||
- examples may use explicit `--agent`, or rely on the root `--agent` flag when that is the behavior under test
|
||||
|
||||
## Folder Map
|
||||
|
||||
- `README.md`: global conventions and glossary
|
||||
- `_shared/README.md`: reusable fixtures, JSON assertions, exit codes, payload rules
|
||||
- `workflows/README.md`: cross-command end-to-end scenarios
|
||||
- per-command folders: command-specific cases and edge conditions
|
||||
|
||||
## Glossary
|
||||
|
||||
- `thread`: the durable coordination unit tracked by `thread_id`
|
||||
- `message`: an event-bearing entry appended to a thread
|
||||
- `artifact`: a file attachment associated with a message
|
||||
- `read cursor`: the per-agent marker used by unread flows
|
||||
- `lease`: the temporary ownership granted by `claim` and extended by `renew`
|
||||
- `terminal state`: a thread state such as `done`, `failed`, or `cancelled`
|
||||
|
||||
## Relationship To Automated Tests
|
||||
|
||||
The current best executable reference is [internal/cli/inbox/integration_test.go](../../../internal/cli/inbox/integration_test.go).
|
||||
|
||||
When this Markdown plan is expanded:
|
||||
|
||||
- prefer matching an existing automated scenario first
|
||||
- record any additional manual-only contract coverage explicitly in the relevant command document
|
||||
- keep `docs/tests/inbox/ROADMAP.md` synchronized with authored files and case slugs
|
||||
+93
-135
@@ -25,14 +25,14 @@ Current state:
|
||||
- `inbox` CLI is implemented end-to-end
|
||||
- automated Go integration tests already exist for the main lifecycle, wait flows, unread behavior, artifacts, and JSON error contracts
|
||||
- this roadmap now exists under `docs/tests/inbox/ROADMAP.md`
|
||||
- the command, workflow, and shared Markdown test-plan documents have not been authored yet
|
||||
- all planned global, shared, workflow, and command-level Markdown test-plan documents have been authored
|
||||
|
||||
Progress summary for planned test-plan documents, excluding `ROADMAP.md`:
|
||||
|
||||
- planned document files: `16`
|
||||
- authored document files: `0`
|
||||
- planned document files: `17`
|
||||
- authored document files: `17`
|
||||
- planned case slugs in this roadmap: `61`
|
||||
- authored case slugs in this roadmap: `0`
|
||||
- authored case slugs in this roadmap: `61`
|
||||
|
||||
## Scope
|
||||
|
||||
@@ -105,12 +105,12 @@ Allowed status values in this roadmap:
|
||||
|
||||
The Markdown test-plan set starts at zero, but these automated tests already exist and should be used as source material when writing the docs:
|
||||
|
||||
- [integration_test.go](/home/kurihada/project/ai-workflow-skill/internal/cli/inbox/integration_test.go#L12) `TestInboxLifecycle`
|
||||
- [integration_test.go](/home/kurihada/project/ai-workflow-skill/internal/cli/inbox/integration_test.go#L176) `TestInboxFailLifecycle`
|
||||
- [integration_test.go](/home/kurihada/project/ai-workflow-skill/internal/cli/inbox/integration_test.go#L243) `TestInboxRenewWaitReplyAndCancel`
|
||||
- [integration_test.go](/home/kurihada/project/ai-workflow-skill/internal/cli/inbox/integration_test.go#L392) `TestInboxWatchListUnreadAndAppend`
|
||||
- [integration_test.go](/home/kurihada/project/ai-workflow-skill/internal/cli/inbox/integration_test.go#L549) `TestInboxUnreadReadCursor`
|
||||
- [integration_test.go](/home/kurihada/project/ai-workflow-skill/internal/cli/inbox/integration_test.go#L639) `TestInboxJSONErrorsAndExitCodes`
|
||||
- [integration_test.go](../../../internal/cli/inbox/integration_test.go#L12) `TestInboxLifecycle`
|
||||
- [integration_test.go](../../../internal/cli/inbox/integration_test.go#L176) `TestInboxFailLifecycle`
|
||||
- [integration_test.go](../../../internal/cli/inbox/integration_test.go#L243) `TestInboxRenewWaitReplyAndCancel`
|
||||
- [integration_test.go](../../../internal/cli/inbox/integration_test.go#L392) `TestInboxWatchListUnreadAndAppend`
|
||||
- [integration_test.go](../../../internal/cli/inbox/integration_test.go#L549) `TestInboxUnreadReadCursor`
|
||||
- [integration_test.go](../../../internal/cli/inbox/integration_test.go#L639) `TestInboxJSONErrorsAndExitCodes`
|
||||
|
||||
These tests do not remove the need for the Markdown plan. They only reduce discovery work.
|
||||
|
||||
@@ -158,23 +158,23 @@ docs/tests/inbox/
|
||||
|
||||
| Path | Purpose | Planned Cases | Authored Cases | Status |
|
||||
| --- | --- | ---: | ---: | --- |
|
||||
| `docs/tests/inbox/README.md` | Global testing conventions and glossary | 0 | 0 | pending |
|
||||
| `docs/tests/inbox/_shared/README.md` | Shared fixtures, JSON assertions, exit-code rules | 0 | 0 | pending |
|
||||
| `docs/tests/inbox/workflows/README.md` | Cross-command scenarios | 8 | 0 | pending |
|
||||
| `docs/tests/inbox/init/README.md` | `init` command cases | 2 | 0 | pending |
|
||||
| `docs/tests/inbox/send/README.md` | `send` command cases | 6 | 0 | pending |
|
||||
| `docs/tests/inbox/fetch/README.md` | `fetch` command cases | 4 | 0 | pending |
|
||||
| `docs/tests/inbox/claim/README.md` | `claim` command cases | 4 | 0 | pending |
|
||||
| `docs/tests/inbox/renew/README.md` | `renew` command cases | 3 | 0 | pending |
|
||||
| `docs/tests/inbox/update/README.md` | `update` command cases | 5 | 0 | pending |
|
||||
| `docs/tests/inbox/reply/README.md` | `reply` command cases | 4 | 0 | pending |
|
||||
| `docs/tests/inbox/done/README.md` | `done` command cases | 4 | 0 | pending |
|
||||
| `docs/tests/inbox/fail/README.md` | `fail` command cases | 4 | 0 | pending |
|
||||
| `docs/tests/inbox/cancel/README.md` | `cancel` command cases | 3 | 0 | pending |
|
||||
| `docs/tests/inbox/list/README.md` | `list` command cases | 4 | 0 | pending |
|
||||
| `docs/tests/inbox/show/README.md` | `show` command cases | 4 | 0 | pending |
|
||||
| `docs/tests/inbox/watch/README.md` | `watch` command cases | 3 | 0 | pending |
|
||||
| `docs/tests/inbox/wait-reply/README.md` | `wait-reply` command cases | 3 | 0 | pending |
|
||||
| `docs/tests/inbox/README.md` | Global testing conventions and glossary | 0 | 0 | done |
|
||||
| `docs/tests/inbox/_shared/README.md` | Shared fixtures, JSON assertions, exit-code rules | 0 | 0 | done |
|
||||
| `docs/tests/inbox/workflows/README.md` | Cross-command scenarios | 8 | 8 | done |
|
||||
| `docs/tests/inbox/init/README.md` | `init` command cases | 2 | 2 | done |
|
||||
| `docs/tests/inbox/send/README.md` | `send` command cases | 6 | 6 | done |
|
||||
| `docs/tests/inbox/fetch/README.md` | `fetch` command cases | 4 | 4 | done |
|
||||
| `docs/tests/inbox/claim/README.md` | `claim` command cases | 4 | 4 | done |
|
||||
| `docs/tests/inbox/renew/README.md` | `renew` command cases | 3 | 3 | done |
|
||||
| `docs/tests/inbox/update/README.md` | `update` command cases | 5 | 5 | done |
|
||||
| `docs/tests/inbox/reply/README.md` | `reply` command cases | 4 | 4 | done |
|
||||
| `docs/tests/inbox/done/README.md` | `done` command cases | 4 | 4 | done |
|
||||
| `docs/tests/inbox/fail/README.md` | `fail` command cases | 4 | 4 | done |
|
||||
| `docs/tests/inbox/cancel/README.md` | `cancel` command cases | 3 | 3 | done |
|
||||
| `docs/tests/inbox/list/README.md` | `list` command cases | 4 | 4 | done |
|
||||
| `docs/tests/inbox/show/README.md` | `show` command cases | 4 | 4 | done |
|
||||
| `docs/tests/inbox/watch/README.md` | `watch` command cases | 3 | 3 | done |
|
||||
| `docs/tests/inbox/wait-reply/README.md` | `wait-reply` command cases | 3 | 3 | done |
|
||||
|
||||
## Authoring Order
|
||||
|
||||
@@ -198,121 +198,79 @@ Reason:
|
||||
|
||||
## Authored Case Register
|
||||
|
||||
No Markdown test cases have been authored yet.
|
||||
|
||||
When the first case is written, add rows in this format:
|
||||
|
||||
| Path | Case Slug | Coverage Note | Status |
|
||||
| --- | --- | --- | --- |
|
||||
| `docs/tests/inbox/send/README.md` | `send-creates-new-thread` | minimal happy path for new thread creation | done |
|
||||
| `docs/tests/inbox/workflows/README.md` | `thread-lifecycle-happy-path` | end-to-end happy path from send to show after done | done |
|
||||
| `docs/tests/inbox/workflows/README.md` | `blocked-question-reply-resume-to-done` | blocked thread receives answer and resumes to done | done |
|
||||
| `docs/tests/inbox/workflows/README.md` | `fail-lifecycle-from-claim-to-terminal` | claimed thread transitions to failed terminal state | done |
|
||||
| `docs/tests/inbox/workflows/README.md` | `cancel-lifecycle-after-worker-claim` | claimed thread can be cancelled by initiator | done |
|
||||
| `docs/tests/inbox/workflows/README.md` | `watch-wakes-then-fetch-sees-new-thread` | watch wake-up remains consistent with unread fetch visibility | done |
|
||||
| `docs/tests/inbox/workflows/README.md` | `artifact-visible-through-send-and-show` | body-file and artifact data survive send and show | done |
|
||||
| `docs/tests/inbox/workflows/README.md` | `unread-clears-after-mark-read-and-reappears-on-new-message` | read cursor clears unread and new message restores it | done |
|
||||
| `docs/tests/inbox/workflows/README.md` | `wait-reply-clears-blocked-unread-for-agent` | wait-reply consumes reply and clears blocked unread view | done |
|
||||
| `docs/tests/inbox/init/README.md` | `init-creates-schema-on-empty-db` | initializes an empty database path and returns initialized status | done |
|
||||
| `docs/tests/inbox/init/README.md` | `init-is-idempotent-on-existing-db` | repeated init succeeds on the same database path | done |
|
||||
| `docs/tests/inbox/send/README.md` | `send-creates-new-thread` | creates a pending thread with an initial task message | done |
|
||||
| `docs/tests/inbox/send/README.md` | `send-appends-message-to-existing-thread` | appends a message to an existing non-terminal thread | done |
|
||||
| `docs/tests/inbox/send/README.md` | `send-reads-body-from-body-file` | reads message body from a file path | done |
|
||||
| `docs/tests/inbox/send/README.md` | `send-attaches-artifact-with-metadata` | persists artifact path, kind, and metadata on send | done |
|
||||
| `docs/tests/inbox/send/README.md` | `send-rejects-invalid-payload-json` | rejects malformed payload JSON with invalid_input | done |
|
||||
| `docs/tests/inbox/send/README.md` | `send-rejects-invalid-artifact-metadata-json` | rejects malformed artifact metadata JSON | done |
|
||||
| `docs/tests/inbox/fetch/README.md` | `fetch-returns-pending-thread-for-target-agent` | returns pending candidate work for the target agent | done |
|
||||
| `docs/tests/inbox/fetch/README.md` | `fetch-respects-status-and-limit-filters` | enforces status filtering and max row count | done |
|
||||
| `docs/tests/inbox/fetch/README.md` | `fetch-unread-uses-read-cursor` | unread filtering depends on per-agent read cursor state | done |
|
||||
| `docs/tests/inbox/fetch/README.md` | `fetch-returns-no-matching-work-when-empty` | empty fetch result returns no_matching_work | done |
|
||||
| `docs/tests/inbox/claim/README.md` | `claim-acquires-thread-lease` | claims a pending thread and records a claim event message | done |
|
||||
| `docs/tests/inbox/claim/README.md` | `claim-rejects-when-thread-missing` | missing thread returns not_found | done |
|
||||
| `docs/tests/inbox/claim/README.md` | `claim-rejects-when-thread-already-claimed` | active lease conflict returns lease_conflict | done |
|
||||
| `docs/tests/inbox/claim/README.md` | `claim-records-requested-lease-duration` | claim event payload records requested lease duration | done |
|
||||
| `docs/tests/inbox/renew/README.md` | `renew-extends-active-lease` | owner renews an active lease and gets a renewal event | done |
|
||||
| `docs/tests/inbox/renew/README.md` | `renew-rejects-non-owner` | non-owner renew attempt returns lease_conflict | done |
|
||||
| `docs/tests/inbox/renew/README.md` | `renew-rejects-without-active-lease` | missing active lease returns invalid_state | done |
|
||||
| `docs/tests/inbox/update/README.md` | `update-moves-thread-to-in-progress` | owner moves a thread into in_progress with a progress message | done |
|
||||
| `docs/tests/inbox/update/README.md` | `update-moves-thread-to-blocked-with-payload` | blocked update writes a question message and payload JSON | done |
|
||||
| `docs/tests/inbox/update/README.md` | `update-accepts-body-file-and-artifact` | update supports body-file input and artifact attachments | done |
|
||||
| `docs/tests/inbox/update/README.md` | `update-rejects-invalid-payload-json` | malformed update payload returns invalid_input | done |
|
||||
| `docs/tests/inbox/update/README.md` | `update-rejects-non-owner` | non-owner update attempt returns lease_conflict | done |
|
||||
| `docs/tests/inbox/reply/README.md` | `reply-adds-answer-message` | default reply kind is answer and thread stays non-terminal | done |
|
||||
| `docs/tests/inbox/reply/README.md` | `reply-supports-control-kind` | reply can explicitly send control messages | done |
|
||||
| `docs/tests/inbox/reply/README.md` | `reply-attaches-artifact` | reply persists artifact data on the message | done |
|
||||
| `docs/tests/inbox/reply/README.md` | `reply-rejects-invalid-payload-json` | malformed reply payload returns invalid_input | done |
|
||||
| `docs/tests/inbox/done/README.md` | `done-marks-thread-terminal` | owner completes a thread into done terminal state | done |
|
||||
| `docs/tests/inbox/done/README.md` | `done-persists-result-body-and-artifact` | result body and artifacts remain visible after done | done |
|
||||
| `docs/tests/inbox/done/README.md` | `done-rejects-non-owner` | non-owner done attempt returns lease_conflict | done |
|
||||
| `docs/tests/inbox/done/README.md` | `done-rejects-on-terminal-thread` | terminal threads reject repeated done calls | done |
|
||||
| `docs/tests/inbox/fail/README.md` | `fail-marks-thread-failed` | owner completes a thread into failed terminal state | done |
|
||||
| `docs/tests/inbox/fail/README.md` | `fail-persists-failure-body-and-artifact` | failure body and artifacts remain visible after fail | done |
|
||||
| `docs/tests/inbox/fail/README.md` | `fail-rejects-non-owner` | non-owner fail attempt returns lease_conflict | done |
|
||||
| `docs/tests/inbox/fail/README.md` | `fail-rejects-on-terminal-thread` | terminal threads reject repeated fail calls | done |
|
||||
| `docs/tests/inbox/cancel/README.md` | `cancel-marks-thread-cancelled` | cancel moves a non-terminal thread to cancelled | done |
|
||||
| `docs/tests/inbox/cancel/README.md` | `cancel-persists-reason-and-artifact` | cancel records its reason text and attachments | done |
|
||||
| `docs/tests/inbox/cancel/README.md` | `cancel-rejects-when-thread-missing` | missing thread returns not_found on cancel | done |
|
||||
| `docs/tests/inbox/list/README.md` | `list-filters-by-status` | status filter limits listed threads | done |
|
||||
| `docs/tests/inbox/list/README.md` | `list-filters-by-created-by` | created-by filter limits listed threads | done |
|
||||
| `docs/tests/inbox/list/README.md` | `list-filters-by-assigned-to` | assigned-to filter limits listed threads | done |
|
||||
| `docs/tests/inbox/list/README.md` | `list-respects-limit` | list returns at most the requested number of rows | done |
|
||||
| `docs/tests/inbox/show/README.md` | `show-returns-thread-and-message-history` | show returns thread metadata and ordered history | done |
|
||||
| `docs/tests/inbox/show/README.md` | `show-includes-artifacts-per-message` | show expands message artifacts in detail view | done |
|
||||
| `docs/tests/inbox/show/README.md` | `show-mark-read-advances-read-cursor` | mark-read changes subsequent unread visibility | done |
|
||||
| `docs/tests/inbox/show/README.md` | `show-rejects-when-thread-missing` | missing thread returns not_found on show | done |
|
||||
| `docs/tests/inbox/watch/README.md` | `watch-wakes-on-matching-thread` | watch wakes on a new matching thread event | done |
|
||||
| `docs/tests/inbox/watch/README.md` | `watch-respects-status-filter` | watch only wakes on events whose resulting thread status matches | done |
|
||||
| `docs/tests/inbox/watch/README.md` | `watch-times-out-with-no-activity` | watch timeout returns no_matching_work | done |
|
||||
| `docs/tests/inbox/wait-reply/README.md` | `wait-reply-wakes-on-answer-after-message` | wait-reply resumes from a known message boundary | done |
|
||||
| `docs/tests/inbox/wait-reply/README.md` | `wait-reply-can-start-from-after-event` | wait-reply resumes from a known event cursor | done |
|
||||
| `docs/tests/inbox/wait-reply/README.md` | `wait-reply-times-out-when-no-reply` | wait-reply timeout returns no_matching_work | done |
|
||||
|
||||
## Pending Case Backlog
|
||||
|
||||
### `docs/tests/inbox/workflows/README.md`
|
||||
No pending case slugs remain in the current plan.
|
||||
|
||||
- `pending` `thread-lifecycle-happy-path`
|
||||
- `pending` `blocked-question-reply-resume-to-done`
|
||||
- `pending` `fail-lifecycle-from-claim-to-terminal`
|
||||
- `pending` `cancel-lifecycle-after-worker-claim`
|
||||
- `pending` `watch-wakes-then-fetch-sees-new-thread`
|
||||
- `pending` `artifact-visible-through-send-and-show`
|
||||
- `pending` `unread-clears-after-mark-read-and-reappears-on-new-message`
|
||||
- `pending` `wait-reply-clears-blocked-unread-for-agent`
|
||||
When a new CLI contract or workflow needs coverage:
|
||||
|
||||
### `docs/tests/inbox/init/README.md`
|
||||
|
||||
- `pending` `init-creates-schema-on-empty-db`
|
||||
- `pending` `init-is-idempotent-on-existing-db`
|
||||
|
||||
### `docs/tests/inbox/send/README.md`
|
||||
|
||||
- `pending` `send-creates-new-thread`
|
||||
- `pending` `send-appends-message-to-existing-thread`
|
||||
- `pending` `send-reads-body-from-body-file`
|
||||
- `pending` `send-attaches-artifact-with-metadata`
|
||||
- `pending` `send-rejects-invalid-payload-json`
|
||||
- `pending` `send-rejects-invalid-artifact-metadata-json`
|
||||
|
||||
### `docs/tests/inbox/fetch/README.md`
|
||||
|
||||
- `pending` `fetch-returns-pending-thread-for-target-agent`
|
||||
- `pending` `fetch-respects-status-and-limit-filters`
|
||||
- `pending` `fetch-unread-uses-read-cursor`
|
||||
- `pending` `fetch-returns-no-matching-work-when-empty`
|
||||
|
||||
### `docs/tests/inbox/claim/README.md`
|
||||
|
||||
- `pending` `claim-acquires-thread-lease`
|
||||
- `pending` `claim-rejects-when-thread-missing`
|
||||
- `pending` `claim-rejects-when-thread-already-claimed`
|
||||
- `pending` `claim-records-requested-lease-duration`
|
||||
|
||||
### `docs/tests/inbox/renew/README.md`
|
||||
|
||||
- `pending` `renew-extends-active-lease`
|
||||
- `pending` `renew-rejects-non-owner`
|
||||
- `pending` `renew-rejects-without-active-lease`
|
||||
|
||||
### `docs/tests/inbox/update/README.md`
|
||||
|
||||
- `pending` `update-moves-thread-to-in-progress`
|
||||
- `pending` `update-moves-thread-to-blocked-with-payload`
|
||||
- `pending` `update-accepts-body-file-and-artifact`
|
||||
- `pending` `update-rejects-invalid-payload-json`
|
||||
- `pending` `update-rejects-non-owner`
|
||||
|
||||
### `docs/tests/inbox/reply/README.md`
|
||||
|
||||
- `pending` `reply-adds-answer-message`
|
||||
- `pending` `reply-supports-control-kind`
|
||||
- `pending` `reply-attaches-artifact`
|
||||
- `pending` `reply-rejects-invalid-payload-json`
|
||||
|
||||
### `docs/tests/inbox/done/README.md`
|
||||
|
||||
- `pending` `done-marks-thread-terminal`
|
||||
- `pending` `done-persists-result-body-and-artifact`
|
||||
- `pending` `done-rejects-non-owner`
|
||||
- `pending` `done-rejects-on-terminal-thread`
|
||||
|
||||
### `docs/tests/inbox/fail/README.md`
|
||||
|
||||
- `pending` `fail-marks-thread-failed`
|
||||
- `pending` `fail-persists-failure-body-and-artifact`
|
||||
- `pending` `fail-rejects-non-owner`
|
||||
- `pending` `fail-rejects-on-terminal-thread`
|
||||
|
||||
### `docs/tests/inbox/cancel/README.md`
|
||||
|
||||
- `pending` `cancel-marks-thread-cancelled`
|
||||
- `pending` `cancel-persists-reason-and-artifact`
|
||||
- `pending` `cancel-rejects-when-thread-missing`
|
||||
|
||||
### `docs/tests/inbox/list/README.md`
|
||||
|
||||
- `pending` `list-filters-by-status`
|
||||
- `pending` `list-filters-by-created-by`
|
||||
- `pending` `list-filters-by-assigned-to`
|
||||
- `pending` `list-respects-limit`
|
||||
|
||||
### `docs/tests/inbox/show/README.md`
|
||||
|
||||
- `pending` `show-returns-thread-and-message-history`
|
||||
- `pending` `show-includes-artifacts-per-message`
|
||||
- `pending` `show-mark-read-advances-read-cursor`
|
||||
- `pending` `show-rejects-when-thread-missing`
|
||||
|
||||
### `docs/tests/inbox/watch/README.md`
|
||||
|
||||
- `pending` `watch-wakes-on-matching-thread`
|
||||
- `pending` `watch-respects-status-filter`
|
||||
- `pending` `watch-times-out-with-no-activity`
|
||||
|
||||
### `docs/tests/inbox/wait-reply/README.md`
|
||||
|
||||
- `pending` `wait-reply-wakes-on-answer-after-message`
|
||||
- `pending` `wait-reply-can-start-from-after-event`
|
||||
- `pending` `wait-reply-times-out-when-no-reply`
|
||||
1. add the new case to the relevant `README.md`
|
||||
2. add the new slug to `Authored Case Register`
|
||||
3. update `Current Snapshot` and `Document Progress`
|
||||
|
||||
## Definition Of Done
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# Inbox Shared Test Conventions
|
||||
|
||||
## Purpose
|
||||
|
||||
This document captures shared assumptions used by multiple `inbox` test-plan documents so command and workflow files can stay focused on behavior rather than repeating setup boilerplate.
|
||||
|
||||
## Recommended Fixture Shape
|
||||
|
||||
Use an isolated temp workspace per case:
|
||||
|
||||
- database path: `TMPDIR/coord.db`
|
||||
- optional body file: `TMPDIR/body.md`
|
||||
- optional artifact file: `TMPDIR/artifact.txt`
|
||||
|
||||
Recommended bootstrap command:
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json init
|
||||
```
|
||||
|
||||
## Global Flags
|
||||
|
||||
Root-level flags apply to every subcommand:
|
||||
|
||||
- `--db`: SQLite database path, default `.agents/coord.db`
|
||||
- `--json`: emit machine-readable JSON
|
||||
- `--agent`: acting agent identity shortcut used by commands that accept agent context
|
||||
|
||||
When a command-specific `--agent` or `--from` flag is omitted, the root `--agent` value may be used instead. Cases that verify fallback behavior should state that explicitly.
|
||||
|
||||
## Success JSON Contract
|
||||
|
||||
Successful JSON output uses this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"command": "send",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
Shared assertion points:
|
||||
|
||||
- `ok` is `true`
|
||||
- `command` matches the invoked subcommand
|
||||
- `data` contains the command-specific payload
|
||||
|
||||
## Error JSON Contract
|
||||
|
||||
Failure JSON output uses this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"error": {
|
||||
"code": "invalid_input",
|
||||
"message": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Shared assertion points:
|
||||
|
||||
- `ok` is `false`
|
||||
- `error.code` matches the stable contract
|
||||
- `error.message` is present and human-readable
|
||||
|
||||
## Exit Code Contract
|
||||
|
||||
The current CLI contract uses these exit codes:
|
||||
|
||||
| Exit Code | Meaning | Typical Error Code |
|
||||
| --- | --- | --- |
|
||||
| `0` | success | none |
|
||||
| `10` | no matching work / timeout without match | `no_matching_work` |
|
||||
| `20` | lease conflict | `lease_conflict` |
|
||||
| `30` | invalid input, invalid state, usage-style error | `invalid_input` or `invalid_state` |
|
||||
| `40` | referenced thread or message missing | `not_found` |
|
||||
| `50` | unexpected internal failure | `internal_error` |
|
||||
|
||||
When a case expects no result, assert both the exit code and the JSON error code.
|
||||
|
||||
## Body Input Rules
|
||||
|
||||
Commands that support `--body` and `--body-file` follow these rules:
|
||||
|
||||
- `--body` and `--body-file` are mutually exclusive
|
||||
- `--body-file` content is read verbatim into the message body
|
||||
- unreadable `--body-file` should be treated as `invalid_input`
|
||||
|
||||
Relevant commands:
|
||||
|
||||
- `send`
|
||||
- `update`
|
||||
- `reply`
|
||||
- `done`
|
||||
- `fail`
|
||||
|
||||
## Artifact Rules
|
||||
|
||||
Commands with artifact support use these shared rules:
|
||||
|
||||
- `--artifact` may be repeated
|
||||
- `--artifact-kind` may be specified once for all artifacts, or once per artifact
|
||||
- `--artifact-metadata-json` may be specified once for all artifacts, or once per artifact
|
||||
- `--artifact-kind` and `--artifact-metadata-json` are invalid without at least one `--artifact`
|
||||
- an empty artifact path is invalid input
|
||||
|
||||
When artifact behavior is under test, assert at least:
|
||||
|
||||
- artifact count
|
||||
- artifact `path`
|
||||
- artifact `kind`
|
||||
- metadata presence when supplied
|
||||
|
||||
## Read And Unread Assertions
|
||||
|
||||
Unread-related cases should verify behavior from the agent's point of view, not only raw message existence.
|
||||
|
||||
Recommended checks:
|
||||
|
||||
- `fetch --unread` returns a thread before read acknowledgement
|
||||
- `show --mark-read` clears unread state for that agent
|
||||
- a new message to the same thread makes the thread unread again
|
||||
- `wait-reply` may clear blocked unread state for the waiting agent when the reply is consumed
|
||||
|
||||
## Workflow Authoring Rule
|
||||
|
||||
If a case spans multiple commands, place the end-to-end narrative in `workflows/README.md` first, then add narrower command-level cases only when they introduce behavior that is easier to reason about in isolation.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Inbox `cancel` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers thread cancellation via `inbox cancel`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: cancel-marks-thread-cancelled
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `cancel` 可以把非终态线程推进到 `cancelled` 终态,并生成控制消息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个非终态线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json cancel --agent leader --thread THREAD_ID --reason "Task superseded by a larger refactor"
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `thread.status == "cancelled"`
|
||||
- `message.kind == "control"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `cancel` 是线程级终态转换
|
||||
- 取消时会释放活跃 lease
|
||||
|
||||
## case: cancel-persists-reason-and-artifact
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `cancel` 的原因文本与附件会被完整持久化。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个非终态线程 `THREAD_ID`
|
||||
- `TMPDIR/cancel.md` 已存在
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json cancel --agent leader --thread THREAD_ID --reason "Task superseded by a larger refactor" --artifact TMPDIR/cancel.md --artifact-kind brief
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `cancel` 成功
|
||||
- 取消消息 `summary` 与 `body` 都保留取消原因
|
||||
- 取消消息包含 1 个 artifact
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `cancel` 既保留人类可读原因,也支持附带上下文材料
|
||||
|
||||
## case: cancel-rejects-when-thread-missing
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `cancel` 对不存在线程返回稳定的 not-found 错误契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 空数据库已完成 `init`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json cancel --agent leader --thread thr_missing
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `40`
|
||||
- JSON 错误码为 `not_found`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `cancel` 不会为缺失线程隐式创建控制消息
|
||||
@@ -0,0 +1,112 @@
|
||||
# Inbox `claim` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers lease acquisition via `inbox claim`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: claim-acquires-thread-lease
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `claim` 可以把 `pending` 线程切换到 `claimed`,并生成租约事件消息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个 `pending` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json claim --agent worker-a --thread THREAD_ID --lease-seconds 300
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `thread.status == "claimed"`
|
||||
- `thread.assigned_to == "worker-a"`
|
||||
- `message.kind == "event"`
|
||||
- `message.summary == "thread claimed"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `claim` 同时更新线程状态与活跃租约
|
||||
- 成功领取会附带一条事件消息,而不是静默改状态
|
||||
|
||||
## case: claim-rejects-when-thread-missing
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `claim` 对不存在的线程返回稳定的 not-found 错误契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 空数据库已完成 `init`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json claim --agent worker-z --thread thr_missing
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `40`
|
||||
- JSON 错误码为 `not_found`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 缺失线程会被明确区分为引用错误,而不是 lease 冲突
|
||||
|
||||
## case: claim-rejects-when-thread-already-claimed
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证同一线程在已有活跃租约时,其他执行者无法重复领取。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-z` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json claim --agent worker-y --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `20`
|
||||
- JSON 错误码为 `lease_conflict`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 活跃 lease 是 `claim` 的排他条件
|
||||
|
||||
## case: claim-records-requested-lease-duration
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `claim --lease-seconds` 的请求值会进入事件消息 payload,便于后续审计。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个 `pending` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json claim --agent worker-a --thread THREAD_ID --lease-seconds 300
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `message.payload_json.lease_seconds == 300`
|
||||
- `message.payload_json.lease_token` 存在
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 请求的租约时长不是仅用于内部计算,也会被持久化到事件消息中
|
||||
@@ -0,0 +1,112 @@
|
||||
# Inbox `done` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers successful terminal completion via `inbox done`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: done-marks-thread-terminal
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证租约拥有者可以将线程推进到 `done` 终态,并生成结果消息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-a` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
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."
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `thread.status == "done"`
|
||||
- `message.kind == "result"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `done` 会把线程推进到成功终态
|
||||
- 完成时会释放活跃 lease
|
||||
|
||||
## case: done-persists-result-body-and-artifact
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `done` 能持久化结果正文与附件,并被后续 `show` 读取。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-a` 已成功 `claim` 线程 `THREAD_ID`
|
||||
- `TMPDIR/result.md` 已存在
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json done --agent worker-a --thread THREAD_ID --summary "Retry policy implemented" --body-file TMPDIR/result.md --artifact TMPDIR/result.md --artifact-kind report
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `done` 成功
|
||||
- 最终结果消息 `body` 等于文件内容
|
||||
- 结果消息包含 1 个 `report` artifact
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `done` 是结果交付命令,不只是状态切换命令
|
||||
|
||||
## case: done-rejects-non-owner
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证非租约拥有者不能代替执行者完成线程。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-a` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json done --agent worker-b --thread THREAD_ID --summary "Retry policy implemented"
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `20`
|
||||
- JSON 错误码为 `lease_conflict`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `done` 受活跃 lease 所属者约束
|
||||
|
||||
## case: done-rejects-on-terminal-thread
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证已进入终态的线程不能再次执行 `done`。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 线程 `THREAD_ID` 已经是 `done`、`failed` 或 `cancelled`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json done --agent worker-a --thread THREAD_ID --summary "Retry policy implemented"
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `30`
|
||||
- JSON 错误码为 `invalid_state`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `done` 对终态线程是幂等失败,而不是重复成功
|
||||
@@ -0,0 +1,111 @@
|
||||
# Inbox `fail` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers failure terminal completion via `inbox fail`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: fail-marks-thread-failed
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证租约拥有者可以把线程推进到 `failed` 终态,并生成失败结果消息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-b` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```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."
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `thread.status == "failed"`
|
||||
- `message.kind == "result"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `fail` 与 `done` 共享结果消息模型,但进入的是失败终态
|
||||
|
||||
## case: fail-persists-failure-body-and-artifact
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `fail` 能持久化失败说明与附件。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-b` 已成功 `claim` 线程 `THREAD_ID`
|
||||
- `TMPDIR/failure.md` 已存在
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json fail --agent worker-b --thread THREAD_ID --summary "Migration failed" --body-file TMPDIR/failure.md --artifact TMPDIR/failure.md --artifact-kind report
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `fail` 成功
|
||||
- 最终结果消息 `body` 等于文件内容
|
||||
- 结果消息包含 1 个 `report` artifact
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 失败终态同样要能完整交付排障材料
|
||||
|
||||
## case: fail-rejects-non-owner
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证非租约拥有者不能把线程标记为失败。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-b` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json fail --agent worker-x --thread THREAD_ID --summary "Migration failed"
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `20`
|
||||
- JSON 错误码为 `lease_conflict`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `fail` 与 `done` 一样受 lease owner 约束
|
||||
|
||||
## case: fail-rejects-on-terminal-thread
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证已进入终态的线程不能再次执行 `fail`。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 线程 `THREAD_ID` 已经是 `done`、`failed` 或 `cancelled`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json fail --agent worker-b --thread THREAD_ID --summary "Migration failed"
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `30`
|
||||
- JSON 错误码为 `invalid_state`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `fail` 对终态线程不会重复成功
|
||||
@@ -0,0 +1,117 @@
|
||||
# Inbox `fetch` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers agent-scoped candidate thread retrieval via `inbox fetch`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: fetch-returns-pending-thread-for-target-agent
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `fetch` 能按目标执行者拉取待处理线程。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `leader` 已向 `worker-a` 发送至少一个 `pending` 线程
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json fetch --agent worker-a --status pending
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 返回 `data.threads`
|
||||
- 至少包含一个 `assigned_to == "worker-a"` 且 `status == "pending"` 的线程
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `fetch` 默认是执行者视角的候选工作列表,不是全局线程扫描
|
||||
|
||||
## case: fetch-respects-status-and-limit-filters
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `fetch` 同时遵守状态过滤与返回上限。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-a` 拥有多个不同状态的线程
|
||||
- 其中至少两个线程满足目标状态过滤条件
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json fetch --agent worker-a --status pending,blocked --limit 1
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 返回线程数不超过 `1`
|
||||
- 返回的每条线程都满足 `status in ["pending","blocked"]`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `fetch` 的 `status` 与 `limit` 会同时生效
|
||||
- 返回顺序按 `updated_at` 倒序,优先暴露最新线程
|
||||
|
||||
## case: fetch-unread-uses-read-cursor
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `fetch --unread` 基于 agent 的 read cursor 计算未读,而不是仅按线程是否存在新消息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `leader` 已向 `worker-e` 发送一个 `pending` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```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` 后,第二次 `fetch --unread` 无匹配结果
|
||||
- 新消息追加后,第三次 `fetch --unread` 再次返回该线程
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 未读判断依赖 `thread_reads.last_read_message_id`
|
||||
- 新消息到达会让同线程重新进入未读结果集
|
||||
|
||||
## case: fetch-returns-no-matching-work-when-empty
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `fetch` 在没有匹配线程时返回稳定的“无工作”错误契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 空数据库已完成 `init`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json fetch --agent worker-z --status pending
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `10`
|
||||
- JSON 错误码为 `no_matching_work`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 空结果不是成功空数组,而是显式的“无匹配工作”信号
|
||||
@@ -0,0 +1,64 @@
|
||||
# Inbox `init` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers the externally visible behavior of `inbox init`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: init-creates-schema-on-empty-db
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证在空数据库路径上执行 `init` 会创建可用的 inbox schema,并返回稳定的初始化响应。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 选择一个尚不存在的数据库路径 `TMPDIR/coord.db`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json init
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 返回 `ok=true`
|
||||
- `command` 为 `init`
|
||||
- `data.db_path` 等于传入路径
|
||||
- `data.status` 为 `initialized`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `init` 在空路径上可以直接完成 schema 初始化
|
||||
- 初始化结果足以让后续 `send`、`fetch` 等命令继续使用同一数据库
|
||||
|
||||
## case: init-is-idempotent-on-existing-db
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `init` 可以对已初始化过的数据库重复执行,而不会报错或破坏已有 schema。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `TMPDIR/coord.db` 已经执行过一次 `inbox --db TMPDIR/coord.db --json init`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json init
|
||||
inbox --db TMPDIR/coord.db --json init
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 两次命令都退出码为 `0`
|
||||
- 两次响应都返回 `data.status == "initialized"`
|
||||
- 两次响应都返回相同的 `data.db_path`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `init` 是幂等操作
|
||||
- 对已存在 schema 的重复初始化不应引入额外迁移失败或状态漂移
|
||||
@@ -0,0 +1,107 @@
|
||||
# Inbox `list` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers thread listing behavior via `inbox list`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: list-filters-by-status
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `list --status` 只返回指定状态集合内的线程。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 数据库中存在多个不同状态的线程
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json list --status pending,blocked
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 返回的每条线程都满足 `status in ["pending","blocked"]`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `list` 是全局筛选视角,状态过滤不会被忽略
|
||||
|
||||
## case: list-filters-by-created-by
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `list --created-by` 能按线程创建者筛选结果。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 至少有两位不同创建者产生的线程
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json list --created-by leader
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 返回的每条线程都满足 `created_by == "leader"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `created-by` 过滤条件直接作用在线程元数据上
|
||||
|
||||
## case: list-filters-by-assigned-to
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `list --assigned-to` 能按当前指派执行者筛选线程。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 数据库中存在多个不同 `assigned_to` 的线程
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json list --assigned-to worker-d --status pending
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 返回的每条线程都满足 `assigned_to == "worker-d"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `list` 可用于管理侧查看某位执行者当前承担的线程集合
|
||||
|
||||
## case: list-respects-limit
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `list --limit` 会约束返回条数,并按更新时间倒序返回最新线程。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 存在多个满足过滤条件的线程
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json list --assigned-to worker-d --limit 1
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 返回线程数不超过 `1`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `list` 的 limit 是硬上限,不会返回超量结果
|
||||
@@ -0,0 +1,87 @@
|
||||
# Inbox `renew` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers lease renewal behavior via `inbox renew`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: renew-extends-active-lease
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证租约拥有者可以对活跃 lease 执行续租,并生成续租事件消息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-c` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json renew --agent worker-c --thread THREAD_ID --lease-seconds 600
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `thread.status` 保持原状态
|
||||
- `message.kind == "event"`
|
||||
- `message.summary == "lease renewed"`
|
||||
- `message.payload_json.lease_seconds == 600`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `renew` 是在原线程上追加续租事件,而不是重新 claim
|
||||
|
||||
## case: renew-rejects-non-owner
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证非租约拥有者不能续租别人的活跃 lease。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-c` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json renew --agent worker-x --thread THREAD_ID --lease-seconds 600
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `20`
|
||||
- JSON 错误码为 `lease_conflict`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `renew` 与 `claim` 一样受 lease owner 约束
|
||||
|
||||
## case: renew-rejects-without-active-lease
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证线程没有活跃租约时,`renew` 会明确失败。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在线程 `THREAD_ID`
|
||||
- 该线程当前没有活跃 lease
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json renew --agent worker-c --thread THREAD_ID --lease-seconds 600
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `30`
|
||||
- JSON 错误码为 `invalid_state`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `renew` 依赖已有活跃租约
|
||||
- 没有 lease 属于状态错误,不是 not-found
|
||||
@@ -0,0 +1,111 @@
|
||||
# Inbox `reply` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers reply behavior within an existing thread via `inbox reply`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: reply-adds-answer-message
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `reply` 默认会向现有线程追加一条 `answer` 消息,并保持线程状态不变。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个非终态线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
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."
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `message.kind == "answer"`
|
||||
- `thread.thread_id == THREAD_ID`
|
||||
- 线程状态保持原值
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `reply` 是线程内追加消息,而不是状态转换命令
|
||||
|
||||
## case: reply-supports-control-kind
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `reply --kind control` 可以发送控制类消息,而不局限于默认 `answer`。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个非终态线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json reply --from leader --to worker-a --thread THREAD_ID --kind control --summary "Pause rollout" --body "Pause rollout until QA confirms the fix."
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `message.kind == "control"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `reply` 的消息种类可由调用方显式指定
|
||||
|
||||
## case: reply-attaches-artifact
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `reply` 支持追加带附件的答复消息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个非终态线程 `THREAD_ID`
|
||||
- `TMPDIR/decision.md` 已存在
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json reply --from leader --to worker-a --thread THREAD_ID --summary "Retry read timeouts" --artifact TMPDIR/decision.md --artifact-kind brief --artifact-metadata-json '{"label":"decision"}'
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `message.artifacts` 长度为 `1`
|
||||
- artifact 路径、kind、metadata 都可读
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `reply` 与 `send/update/done/fail` 共享附件写入契约
|
||||
|
||||
## case: reply-rejects-invalid-payload-json
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `reply` 对非法 `--payload-json` 输入返回稳定错误契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个非终态线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json reply --from leader --to worker-a --thread THREAD_ID --summary "Retry read timeouts" --payload-json not-json
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `30`
|
||||
- JSON 错误码为 `invalid_input`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `reply` 的 payload 与其他消息写入命令一样需要通过 JSON 校验
|
||||
@@ -0,0 +1,176 @@
|
||||
# Inbox `send` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers thread creation and message append behavior exposed by `inbox send`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: send-creates-new-thread
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `send` 在未指定既有线程时会创建新线程,并写入首条任务消息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 空数据库已完成 `init`
|
||||
|
||||
### 输入
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 返回 `thread.thread_id`
|
||||
- `thread.status == "pending"`
|
||||
- `thread.created_by == "leader"`
|
||||
- `thread.assigned_to == "worker-a"`
|
||||
- `message.kind == "task"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `send` 会新建线程而不是只插入孤立消息
|
||||
- 新线程的默认初始状态是 `pending`
|
||||
|
||||
## case: send-appends-message-to-existing-thread
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `send` 在指定既有 `--thread` 时会向原线程追加消息,而不是重建线程。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个由 `leader` 发给 `worker-d` 的线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json send --from leader --to worker-d --thread THREAD_ID --summary "Use a markdown editor" --body "Prefer a textarea-based markdown editor for v1."
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `send` 成功,返回的 `thread.thread_id` 仍为 `THREAD_ID`
|
||||
- 线程状态保持原值,不被强制改写为新状态
|
||||
- `show` 可见消息数增加
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 追加消息不会重置线程生命周期
|
||||
- 线程历史按时间顺序保留旧消息与新消息
|
||||
|
||||
## case: send-reads-body-from-body-file
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `send --body-file` 会把文件内容写入消息正文。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `TMPDIR/task.md` 已存在,内容为测试正文
|
||||
|
||||
### 输入
|
||||
|
||||
```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
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `send` 成功
|
||||
- `show` 首条消息的 `body` 与文件内容一致
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `body-file` 内容会被原样读取
|
||||
- 该行为与直接传 `--body` 的最终存储结果等价
|
||||
|
||||
## case: send-attaches-artifact-with-metadata
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `send` 支持附带 artifact、kind 和 metadata,并可在返回值或后续 `show` 中读取。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `TMPDIR/task.md` 已存在
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json send --from leader --to worker-d --subject "Build admin editor" --summary "Create the first editor screen" --artifact TMPDIR/task.md --artifact-kind brief --artifact-metadata-json '{"label":"task-brief"}'
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `message.artifacts` 长度为 `1`
|
||||
- artifact `path == "TMPDIR/task.md"`
|
||||
- artifact `kind == "brief"`
|
||||
- artifact `metadata_json.label == "task-brief"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `send` 可以在创建消息时持久化附件及其结构化元数据
|
||||
|
||||
## case: send-rejects-invalid-payload-json
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `send` 对非法 `--payload-json` 输入给出稳定错误契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 空数据库已完成 `init`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json send --from leader --to worker-z --subject "Invalid payload json" --payload-json not-json
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `30`
|
||||
- JSON 错误码为 `invalid_input`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 非法 payload 在写库前就会被拒绝
|
||||
- 错误归类为输入问题,而不是内部错误
|
||||
|
||||
## case: send-rejects-invalid-artifact-metadata-json
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `send` 对非法 artifact metadata JSON 给出稳定错误契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 空数据库已完成 `init`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json send --from leader --to worker-z --subject "Invalid artifact json" --artifact TMPDIR/report.md --artifact-metadata-json not-json
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `30`
|
||||
- JSON 错误码为 `invalid_input`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- artifact metadata 会在写入前校验 JSON 合法性
|
||||
|
||||
## Notes
|
||||
|
||||
- 新建线程时未显式传 `--summary`,会回退到 `--subject`
|
||||
- `--body` 与 `--body-file` 互斥;该约束由 shared 文档统一说明
|
||||
@@ -0,0 +1,115 @@
|
||||
# Inbox `show` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers per-thread detail retrieval via `inbox show`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: show-returns-thread-and-message-history
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `show` 会返回线程详情和完整消息历史。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已存在一个含多条消息的线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 返回 `data.thread`
|
||||
- 返回 `data.messages`
|
||||
- 消息按创建时间升序排列
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `show` 是线程详情与时间序历史的读取入口
|
||||
|
||||
## case: show-includes-artifacts-per-message
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `show` 返回的每条消息都包含其关联 artifact 列表。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 线程 `THREAD_ID` 中至少一条消息附带 artifact
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- 相关消息节点包含 `artifacts`
|
||||
- artifact 的 `path`、`kind`、`metadata_json` 可读
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `show` 需要把附件一并展开,而不是只返回 message 基本字段
|
||||
|
||||
## case: show-mark-read-advances-read-cursor
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `show --mark-read` 会推进调用 agent 的 read cursor,并影响后续 unread 查询。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-e` 有一个未读线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `show` 成功
|
||||
- 随后的 `fetch --unread` 对该线程不再返回结果
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `mark-read` 的副作用是推进该 agent 的 `last_read_message_id`
|
||||
|
||||
## case: show-rejects-when-thread-missing
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `show` 对不存在线程返回稳定的 not-found 错误契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 空数据库已完成 `init`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json show --thread thr_missing
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `40`
|
||||
- JSON 错误码为 `not_found`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `show` 不会对缺失线程返回空对象
|
||||
|
||||
## Notes
|
||||
|
||||
- 使用 `--mark-read` 时必须提供 agent 身份,可通过根级 `--agent` 或命令参数传入
|
||||
@@ -0,0 +1,139 @@
|
||||
# Inbox `update` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers progress and blocked transitions via `inbox update`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: update-moves-thread-to-in-progress
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证租约拥有者可以把线程推进到 `in_progress`,并生成进度消息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-a` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
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."
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `thread.status == "in_progress"`
|
||||
- `message.kind == "progress"`
|
||||
- `message.to_agent` 指向线程创建者
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `update` 会把状态推进和消息追加合并为同一次事务
|
||||
|
||||
## case: update-moves-thread-to-blocked-with-payload
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `update --status blocked` 会写入阻塞问题消息,并保留结构化 payload。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-a` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
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?"}'
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 命令退出码为 `0`
|
||||
- `thread.status == "blocked"`
|
||||
- `message.kind == "question"`
|
||||
- `message.payload_json.question` 保存提问内容
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `blocked` 更新会生成面向创建者的问题消息
|
||||
|
||||
## case: update-accepts-body-file-and-artifact
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `update` 支持通过 `body-file` 与 artifact 发送结构化进度材料。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-a` 已成功 `claim` 线程 `THREAD_ID`
|
||||
- `TMPDIR/progress.md` 已存在
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json update --agent worker-a --thread THREAD_ID --status in_progress --summary "Implementation started" --body-file TMPDIR/progress.md --artifact TMPDIR/progress.md --artifact-kind note
|
||||
inbox --db TMPDIR/coord.db --json show --thread THREAD_ID
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `update` 成功
|
||||
- 对应消息 `body` 等于文件内容
|
||||
- 对应消息包含 1 个 artifact,kind 为 `note`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `update` 的正文与 artifact 支持与 `send/reply/done/fail` 保持一致
|
||||
|
||||
## case: update-rejects-invalid-payload-json
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `update` 对非法 `--payload-json` 输入返回稳定错误契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-a` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json update --agent worker-a --thread THREAD_ID --status blocked --summary "Need timeout decision" --payload-json not-json
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `30`
|
||||
- JSON 错误码为 `invalid_input`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- 阻塞问题的 payload 需要满足合法 JSON 约束
|
||||
|
||||
## case: update-rejects-non-owner
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证非租约拥有者不能更新线程状态。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-a` 已成功 `claim` 线程 `THREAD_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json update --agent worker-b --thread THREAD_ID --status in_progress --summary "Implementation started"
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `20`
|
||||
- JSON 错误码为 `lease_conflict`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `update` 明确依赖活跃 lease 所属者
|
||||
@@ -0,0 +1,93 @@
|
||||
# Inbox `wait-reply` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers blocking reply wait behavior within one thread via `inbox wait-reply`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: wait-reply-wakes-on-answer-after-message
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `wait-reply` 可以从某条已知消息之后开始等待,并在答复到达后唤醒。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `worker-c` 已拥有一个 `blocked` 线程 `THREAD_ID`
|
||||
- 阻塞消息的 `message_id` 为 `BLOCKED_MESSAGE_ID`
|
||||
|
||||
### 输入
|
||||
|
||||
```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."
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `wait-reply` 退出码为 `0`
|
||||
- `wait-reply.data.woke == true`
|
||||
- 返回的 `message.kind == "answer"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `wait-reply` 可以可靠地从既知消息边界之后等待后续答复
|
||||
|
||||
## case: wait-reply-can-start-from-after-event
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `wait-reply --after-event` 支持从既知事件游标之后恢复等待。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 已通过先前的 `watch` 或 `wait-reply` 结果拿到某个 `NEXT_EVENT_ID`
|
||||
- 线程 `THREAD_ID` 后续还会收到新的回复类消息
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --agent worker-c --json wait-reply --thread THREAD_ID --after-event NEXT_EVENT_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."
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `wait-reply` 在事件游标之后的新回复出现时被唤醒
|
||||
- 返回新的 `next_event_id`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `after-event` 允许等待逻辑在断点之后继续,而不会重复消费旧回复
|
||||
|
||||
## case: wait-reply-times-out-when-no-reply
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证在超时时间内没有匹配回复出现时,`wait-reply` 返回稳定超时契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 存在一个线程 `THREAD_ID`
|
||||
- 不会有新的 `answer/control/result` 消息到达
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --agent worker-c --json wait-reply --thread THREAD_ID --timeout-seconds 1
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `10`
|
||||
- JSON 错误码为 `no_matching_work`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `wait-reply` 超时被视为“没有等到匹配回复”
|
||||
|
||||
## Notes
|
||||
|
||||
- 默认唤醒 kinds 为 `answer,control,result`
|
||||
- 当返回消息是发给等待 agent 的外来消息时,`wait-reply` 会顺带推进该 agent 的 read cursor
|
||||
@@ -0,0 +1,91 @@
|
||||
# Inbox `watch` Test Plan
|
||||
|
||||
## Scope
|
||||
|
||||
This document covers blocking thread-activity wait behavior via `inbox watch`.
|
||||
|
||||
Shared conventions live in [../_shared/README.md](../_shared/README.md).
|
||||
|
||||
## case: watch-wakes-on-matching-thread
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `watch` 在新匹配线程到达时会被唤醒,并返回线程、消息与事件信息。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- `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"
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `watch` 退出码为 `0`
|
||||
- `watch.data.woke == true`
|
||||
- 返回 `thread`、`message`、`event`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `watch` 唤醒结果不仅说明“醒了”,还提供触发该唤醒的具体事件上下文
|
||||
|
||||
## case: watch-respects-status-filter
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证 `watch --status` 只会对匹配状态的后续事件唤醒。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 存在一个会被推进到 `blocked` 的线程 `THREAD_ID`
|
||||
- `watch` 以 `--status blocked` 先启动
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json watch --agent worker-c --status blocked --timeout-seconds 2
|
||||
inbox --db TMPDIR/coord.db --json update --agent worker-c --thread THREAD_ID --status blocked --summary "Need policy decision"
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- `watch` 只在线程进入 `blocked` 后返回
|
||||
- 返回的 `thread.status == "blocked"`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `watch` 的状态过滤作用在“事件发生后的线程状态”上
|
||||
|
||||
## case: watch-times-out-with-no-activity
|
||||
|
||||
### 用例意义
|
||||
|
||||
验证在超时时间内没有匹配活动时,`watch` 返回稳定超时契约。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 没有新匹配事件会发生
|
||||
|
||||
### 输入
|
||||
|
||||
```bash
|
||||
inbox --db TMPDIR/coord.db --json watch --agent worker-d --status pending --timeout-seconds 1
|
||||
```
|
||||
|
||||
### 预期输出
|
||||
|
||||
- 退出码为 `10`
|
||||
- JSON 错误码为 `no_matching_work`
|
||||
|
||||
### 断言结论
|
||||
|
||||
- `watch` 超时被归类为“无匹配工作”,而不是内部错误
|
||||
|
||||
## Notes
|
||||
|
||||
- 未传 `--after-event` 时,`watch` 默认从“当前时刻之后”开始等待,不会回放既有事件
|
||||
@@ -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