# 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.
