# Getting Started and Command Reference

> A `remits-cli` skill reference. **Load this when** you need the exact command surface: authentication, host versus data mode, the tool execution lifecycle, hierarchy-scoped reads, and every flag.
>
> The table of contents below carries **real line numbers** (`- L84  Some Heading`), resolved when
> this file is installed, so they are never stale. Read the head, pick your sections, and offset-read
> only those. The entry text is the heading verbatim, so it also greps.

## Table of Contents

- [Getting Started](#getting-started)
  - [Authentication](#authentication)
  - [Host vs Data Mode](#host-vs-data-mode)
  - [Tool Execution Lifecycle](#tool-execution-lifecycle)
  - [Hierarchy-scoped tool reads](#hierarchy-scoped-tool-reads)
  - [Data Mode](#data-mode)
- [Command Reference](#command-reference)
  - [Reading a failing run](#reading-a-failing-run)
  - [A pass count only means something within one data lane](#a-pass-count-only-means-something-within-one-data-lane)
  - [Verification envelopes](#verification-envelopes)
  - [Staging modes: workset vs full snapshot](#staging-modes-workset-vs-full-snapshot)
  - [Prod banners and retryable failures](#prod-banners-and-retryable-failures)

## Getting Started

### Authentication

Authenticate once per session. Opens the user's browser for OAuth login:

```bash
remits-cli auth --account-id <ACCOUNT_ID>
remits-cli auth --account-id <ACCOUNT_ID> --base-url http://localhost:8080
```

- `--account-id` is auto-resolved from `account-info.json` when you're inside an account repo, so you can usually just run `remits-cli auth`. Pass `--account-id <ID>` explicitly to target a different account (e.g., authenticating into a child account from a parent-account repo) — the explicit flag always wins over the repo's `account-info.json` and any prior session.
- `--base-url` targets a specific platform instance (e.g., localhost for development). Sessions are stored per account + base URL + data mode, so authenticating against localhost does not overwrite a production session for the same account.
- If any command returns a 401 error, re-run `remits-cli auth` (with the same `--base-url` if you were targeting a non-default instance).

### Host vs Data Mode

Treat host selection and data mode as two separate decisions:

- `--base-url` chooses the Remits host: localhost vs a deployed environment.
- `--data-mode` chooses the data segment on that host: `test` vs `prod`.

Examples:

```bash
# Deployed prod host, but test data segment
remits-cli tools --base-url https://your-prod-host --data-mode test

# Localhost host, but prod data segment on that localhost instance
remits-cli tool --base-url http://localhost:8080 --name mcp_account_view --data-mode prod
```

Do not assume `--data-mode prod` implies the deployed prod host, or that `--data-mode test` implies localhost. If host matters, read `~/.remits-cli/sessions.json` first and pass `--base-url` explicitly.

### Tool Execution Lifecycle

`remits-cli tool` supports both synchronous and asynchronous execution. Use normal synchronous execution
for quick investigation tools:

```bash
remits-cli tool --account-id 21 --target-account 37 --name "mcp_account_view" --input '{"accountId": 37}' --data-mode prod
```

**There are two independent async mechanisms — do not confuse or stack them:**

1. **The tool's own async mode** (`mcp_run_action` / `mcp_run_agent`, via `executionMode:"async"` in the
   tool input). The tool spawns the long work server-side and **returns immediately in the same HTTP
   response** with its own run identifiers — `actionRunId` (or `agentRunId`) and, for agents, a stable
   `sessionId`. You poll it with the tool's **own** status protocol (`controlAction:"status"`). This is the
   preferred path for long Actions/Agents, because the run ids come back on the very first call.

2. **The CLI transport async** (`--async true`). This wraps *any* tool call in a background server task and
   returns a CLI-level `callId` immediately, which you poll with `remits-cli tool status --call-id`. Use it
   for long tools that do **not** have their own async mode. Its start response carries `callId`,
   `status:"running"`, `threadGroupingId`, `accountId`, `branchName`, and `dataMode` — but **not** any
   tool-specific ids, because the tool has not run yet; those arrive inside the `result` of the polled
   completed status. The start, status and completed responses all carry a `world` block naming the
   repo/scope account, execution account, target account when any, data lane, branch/workspace, component
   branch, staging lane and component source/signature when known.

For `mcp_run_action` / `mcp_run_agent`, prefer mechanism (1) alone — it already makes the call non-blocking
**and** returns the run ids up front. Pass your own `actionRunId`/`agentRunId` so you can poll it
deterministically:

```bash
remits-cli tool --account-id 21 --as-account 49 --target-account 49 --name "mcp_run_action" --input '{"actionId":200,"executionMode":"async","actionRunId":"my-stable-run-id","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
```

Every tool response is saved in full to
`./.remits-cli/actors/<local-agent>/tool-responses/<callId>.json`. Large responses may print an early
size/hash line and a selective-read hint before any preview text, but the saved CLI file is not truncated
or offloaded. Legacy flat `./.remits-cli/tool-responses/<callId>.json` files remain readable as fallback;
`remits-cli doctor local-state` shows which state belongs to the active actor. This is separate from the
platform's OpenRouter model loop, where the AI client may offload large tool results before feeding
context back to the model.

For cross-account workflows, keep the roles explicit:

- `--account-id` is the repo/scope account that bounds discovery and selects the local verification context.
- `--as-account` changes the execution account, so subscriber/component-branch resolution behaves as that
  account.
- `--target-account` / `--target-account-id` names the tool's data target without changing component
  resolution. It is sent as `input.accountId` **and** `input.targetAccountId` when `--input` omits them,
  because that is the key the platform's account-targeting tools read (`mcp_run_action` resolves
  `input.accountId` and otherwise falls back to the run scope). A value already in `--input` wins.
  Legacy `input.accountId` on its own is still accepted as a target, but the CLI warns — on stderr and as a
  `warnings[]` entry in the response, so `--json` callers see it too — when it differs from the scope account
  and neither explicit flag was supplied.

**A target is not an execution account.** `--target-account` says which records the tool touches;
`--as-account` says which account's components, branch subscription and staging lane resolve. When the work
should run as the other account — the usual case for a subscriber or a client account — pass `--as-account`
too. The response `world` block is the check: `repoAccountId` / `executionAccountId` / `targetAccountId` are
three separate fields, and `world.accountId` is the EXECUTION account, matching what a verification manifest
declares.

### Hierarchy-scoped tool reads

For read/discovery tools, the account resolved from the checkout/session is the **scope root**, not proof
that the business record is owned by that account. This closes the common support loop where you know a
precise document, object, event, or indexed source id but do not yet know which child account owns it.

Use the tool flags rather than editing JSON by hand:

```bash
remits-cli tool --name mcp_firestore_search \
  --input '{"collection":"statements","documentId":"1234"}' \
  --scope children --data-mode prod
```

Available flags:

| Flag | Meaning |
|---|---|
| `--scope self|children|hierarchy` | Expand from the repo/session account for read/discovery. Exact-id lookups usually default to `children`; broad searches default to `self` unless widened. |
| `--as-account ID` | Execute as this reachable account, so that account's subscription edge and component branch apply. |
| `--target-account ID` / `--target-account-id ID` | Exact owner/data target when already known; sent as `input.accountId` + `input.targetAccountId` unless `--input` already sets them. Required by mutating tools that operate on one account; does not by itself change component resolution — add `--as-account` for that. |
| `--account-ids 1,2,3` | Explicit bounded owner list. The platform verifies every id against the scope root. |
| `--anchor-account-id ID` | Path-disambiguation anchor for multi-parent account relationships. |

The CLI merges these into `--input`; a value already present in `--input` wins. Tool responses echo
`scopeRootAccountId`, `scope`, `accountIds`, and, when an exact owner is discovered,
`resolvedTargetAccountId`. Feed that returned owner to `mcp_firestore_patch`, action/test runs, and browser
tokens. Mutating tools do not infer or fan out writes.

The implicit account ceiling is intentionally different by shape: broad searches stay capped at 100 accounts
unless the tool says otherwise, while exact-id discovery may span up to 1000 accounts by default. If a broad
tool returns `scopeTooBroad`, narrow with `--target-account-id`, `--account-ids`, or a smaller `--scope`.

`mcp_firestore_search` handles exact Firestore document ids. `mcp_index_search` handles fuzzy/semantic
lookup through Vertex. `mcp_bigquery_query` handles warehouse lookup/query with server-resolved
`{table_current}`/`{table}` placeholders and the scoped `{account_filter}` predicate. BigQuery is a prod
analytics surface, so use `--data-mode prod` when you intend to query it.

Poll a CLI-transport async call (mechanism 2) by call id:

```bash
remits-cli tool status --call-id <callId> --data-mode prod
```

Pass `--wait true` to have the CLI process poll locally until the transport call completes (short polling
requests instead of one long HTTP connection):

```bash
remits-cli tool --name "some_long_tool_without_its_own_async" --async true --wait true --input '{...}' --data-mode prod
```

Stacking both (`--async true` **and** `executionMode:"async"`) works but is redundant: the tool's
`actionRunId`/`agentRunId`/`sessionId` then appear only in the polled completed `result`, not in the CLI
start response — which is why the start response looks "incomplete." Pick one mechanism.

`--timeout-ms <ms>` controls the per-request HTTP timeout. Prefer async execution over a large timeout for
multi-minute work so the server task is not tied to one HTTP connection.

### Data Mode

Controls whether you work with test data or production data. **Default is `test`.**

```bash
remits-cli data-mode                  # Show current mode
remits-cli data-mode set prod         # Switch to prod for investigations
remits-cli data-mode set test         # Switch back to test for development
```

## Command Reference

```
remits-cli auth [--base-url URL] [--account-id ID] [--data-mode test|prod]
remits-cli sessions [list|remove] [--account-id ID]
remits-cli config [set] [--agent claude|codex|gemini]
remits-cli agent serve [--worker-agent claude|codex|gemini] [--max-concurrent N] [--mode edit|investigate] [--serves a,b] [--label NAME] [--data-mode test|prod]
remits-cli agent workers [--json]
remits-cli agent register [--label NAME] [--data-mode test|prod] [--account-id ID] [--max-concurrent N]
remits-cli agent work [--wait SECONDS] [--json]
remits-cli agent status [--state idle|working|paused] [--ticket ID] [--activity "..."] [--step "..."]
remits-cli agent list [--account-id ID] [--json]
remits-cli agent map [--account-ids 1,4] [--json]       # who is EDITING which repository, from which checkout/branch/staging lane
remits-cli agent inspect [--account-id ID] [--scope self|children] [--user-id ID|--user-email EMAIL] [--limit N] [--json]
remits-cli activity inspect [--account-id ID] [--scope self|children|related] [--workspace NAME] [--branch NAME] [--workstream ID] [--user-id ID|--user-email EMAIL] [--limit N] [--json]
remits-cli workstream status [--workstream ID|--workspace NAME] [--json]
remits-cli agent release
remits-cli ticket read|accept|status|progress|complete|release|reopen|assign|planning --ticket ID [--status S] [--resolution "..."] [--summary "..."] [--category C] [--assignee EMAIL] [--workstream VALUE] [--planned-in VALUE] [--board-stage VALUE] [--rank N] [--size VALUE] [--blocked-by VALUE] [--notes "..."]
remits-cli ticket lease|unlease|force-unlease --ticket ID [--reason "..."]   # force-unlease breaks SOMEBODY ELSE'S lease; operator only
remits-cli ticket where --ticket ID                    # WHERE this work is: repo account, checkout, branch, staging lane, live lease + holder's location, claim, your phase
remits-cli ticket ask --ticket ID --question "..." [--context "..."] [--to WHO]   # park on a human decision
remits-cli ticket answer --ticket ID --message "..." [--route false]             # answer it (alias: reply); re-routes by default
remits-cli ticket message --ticket ID --message "..." [--to a@x,b@y] [--subject "..."] [--channel email]  # PUBLIC — the requester reads it
remits-cli ticket note --ticket ID --message "..."     # INTERNAL — the next worker and a reviewer read it
remits-cli ticket deliver --ticket ID --channel email --to a@x [--template T] [--subject "..."] [--idempotency-key K]   # ask for it to be SENT
remits-cli ticket deliveries --ticket ID [--channel email]   # what is queued to go out, and what already went
remits-cli ticket delivered --ticket ID --delivery-id ID | ticket delivery-failed --ticket ID --delivery-id ID --reason "..."
remits-cli ticket tag --ticket ID --tags a,b           # make a cluster of near-identical tickets visible as one
remits-cli ticket artifact --ticket ID --type TYPE --label "..." [--url U | --content "..."]
remits-cli ticket participant --ticket ID --email E [--role watcher|requester|agent]
remits-cli ticket field --ticket ID --key K --value V  # the ORGANIZATION's own field, outside the planning slots
remits-cli ticket reclaim [--ticket ID | --account-id ID] [--stale-hours 24] [--apply]   # OPERATOR: take back an agent's abandoned ticket
remits-cli ticket queue --account-id ID [--status ...] [--unrouted] [--unassigned] [--awaiting-response] [--min-runs N] [--workstream W] [--board-stage S] [--planned-in P] [--search "..."] [--sort-by ...] [--json]
remits-cli ticket create --account-id ID --subject "..." --type defect|question|task|incident|enhancement [--priority P] [--description "..."] [--tags a,b] [--workstream W] [--affected-component C] [--implementation-account-id ID] [--reference-id KEY]
remits-cli start [--foreground true] [--port 8787]
remits-cli stop
remits-cli status [--base-url URL] [--account-id ID] [--data-mode test|prod] [--json]
remits-cli whoami [--base-url URL] [--account-id ID] [--data-mode test|prod] [--json]
remits-cli doctor local-state [--json]                  # active local actor, workspace source, legacy state, tracked .remits-cli files
remits-cli listen [stop|status] [--foreground true]   # compatibility alias
remits-cli data-mode [set test|prod]
remits-cli components stage [--workset | --changed-only] [--branch <name>] [--workspace <name>] [--empty-workset clear] [--data-mode test|prod] [--json|--verbose]   # default = FULL SNAPSHOT of the repo; --workset = only what git says changed, lane reconciled to it
remits-cli workspace [show | use <name> | use --auto | clear]
remits-cli components status [--branch <name>] [--component-type <type>] [--component-id <id>] [--json|--verbose]   # also: has anyone landed since this lane was staged (freshness)
remits-cli components lanes [--json]                         # every indexed staging lane on the account
remits-cli components entries --lane-id <id> [--json|--verbose]  # authoritative staged files for one lane
remits-cli components clear [--branch <name>] [--component-type <type>] [--component-id <id>] [--all] [--json|--verbose]   # id alone scopes to one component when unambiguous (ids are type-local; add --component-type if the same id is staged in multiple families); no filter clears the whole branch scope; --all forces the full wipe
remits-cli components sync [--safe [--yes]] [--branch <name>] [--data-mode test|prod] [--force-tombstones] [--dry-run] [--create-variant-branch] [--summary] [--changed-only [--changed-since <ref>]]   # gated; on trunk = full repo->DB reconcile, on a variant branch = ComponentVariant overlays only
remits-cli components commit [--yes] [--safe] [--allow-shared-branch] [--create-variant-branch] [--message|-m "msg"] [--data-mode test|prod] [--force-tombstones] [--json [--summary]]   # phase 1 merge-stages + compile-validates changed source (never reconciles the lane); on TRUNK refuses before any git write unless --yes; --safe gates variant sync writes
remits-cli components branches [--json]                                # branches carrying committed variants, with counts + drift
remits-cli components branch <name> [--json]                           # one branch: owner account, overridden / added / removed, drift flags, subscribers
remits-cli components branch <name> --diff <componentId> --component-type <kind> [--json]
remits-cli components branch <name> --subscribers [--json]
remits-cli components branch <name> --copy-to <newBranch> [--dry-run] [--force] [--json] # seed a new variant branch with this branch's stored overlays; force if target has overlays/subscribers
remits-cli components branch <name> --subscribe <accountId> [--parent-account <id>] [--domain <host>] [--dry-run] [--confirm-primary-edge]   # make an account resolve this branch
remits-cli components branch <name> --unsubscribe <accountId>                          # return that account to trunk
remits-cli components branch <name> --retire [--force]                                 # delete the branch's overlays
remits-cli test run --test <id|name> [--branch <stagingScope>] [--names "a|b"] [--watch true|false] [--wait true|false] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>] [--json]
remits-cli test status --task-id <taskId> [--branch <stagingScope>] [--data-mode test|prod] [--json]   # falls back to the DURABLE run record once the live status expires
remits-cli test runs [--test <id|name>] [--limit 20] [--compare] [--all-lanes] [--as-account <ID>] [--json]  # durable run history: pass counts, live AI cost, lane, content hash
remits-cli test compare --base <taskId> --head <taskId> [--json]                                            # per-case improved / regressed / changed, cost and world deltas
remits-cli corpus import --manifest corpus-manifest.json [--corpus <name>] [--as-account <ID>] [--data-mode test|prod --confirm-prod] [--json]   # seed an evaluation corpus: cases + immutable artifacts; idempotent by caseKey
remits-cli corpus cases --corpus <name> [--split S] [--tag T|--tags T,U] [--key K|--keys K,L] [--include-values] [--include-retired] [--limit N] [--json]
remits-cli corpus runs --corpus <name> [--limit 20] [--json]                                   # runs that measured the corpus: outcomes, AI mode (live|MOCKED|none), cost, world
remits-cli corpus results --corpus <name> [--case <caseKey>] [--task-id <taskId>] [--json]
remits-cli corpus compare --corpus <name> --base <taskId> --head <taskId> [--json]              # per-case direction, metric deltas, expectedChanged
remits-cli corpus consistency --corpus <name> --case <caseKey> [--json]                        # did the case produce the same metrics every run?
remits-cli corpus retire --corpus <name> --case <caseKey> [--case ...] [--restore] [--json]    # drop a case from future runs; past measurements stay readable
```

`test run` starts a server-side task immediately. By default the CLI process polls until the task is
terminal; `--watch false` disables websocket progress streaming but still waits. Pass `--wait false` to
return after launch with the task id. A single run with `--names "a|b|c"` selects cases into one suite task,
and those cases execute sequentially in declaration order. To overlap independent slow cases, launch
separate `remits-cli test run --names "<case>" --wait false` commands from the same staged lane and keep the
printed task ids; complete each proof with `test status --task-id <id>`. Avoid parallel runs for cases that
share mutable fixtures, suite-level side effects, or undeclared live AI/provider calls.

### Reading a failing run

`test run` and `test status` print a **verdict**, not the run. The whole run object used to be dumped as
pretty JSON in human mode - 220 KB for one 92-case suite, most of it identifiers repeated once per case - so
the command an agent runs most often was the one that spent its remaining room to think. Every byte is still
there behind `--json`, and behind `test status --task-id <id> --json` once the live status expires.

What you get instead, and what to do with it:

```text
Cases: 59 passed, 33 failed, 92 total

Failure roots (33 failed case(s), 21 distinct root(s)):
  10x  assert statement.data.processingStatus == 'Analyzed'
        e.g. bundled UK statements  (+9 more)
   2x  assert statement.data.feeBreakdownChecked == true
        e.g. 7851 Flat Rate  (+1 more)
Largest root first: remits-cli test run --test "Statement Reader Calculations" --names "bundled UK statements"
```

**Thirty-three failures are not thirty-three problems.** Cases are grouped by their assertion root - the
failure with the particulars (ids, numbers, Groovy's `Expression:`/`Values:` decoration) removed - largest
group first, and one pivot block is printed per root rather than per case. Fix the largest root against one
named case, then re-run the whole suite. Re-running the suite before you have a root is how a session spends
an afternoon at the same pass count.

### A pass count only means something within one data lane

`test runs` is scoped to the data lane of the command by default, and says so:

```text
Data lane: test   (6 more run(s) in the other lane; --all-lanes to include)
```

A run's `dataMode` **is** the lane it was written in, so `64/92` in the prod lane and `59/92` in the test lane
are two different facts, not a regression. `--compare` (and `test runs --compare`) picks the newest two runs
of the same **shape** - same data lane, branch, workspace and case count - and says how many newer runs it
skipped; it refuses rather than comparing across worlds. `test status --data-mode prod` on a run recorded in
the test lane returns the run's real lane and now says that is what happened.

A run labelled **`AI MOCKED`** replayed every AI turn from `aiMock`: it produces the same outcomes, scores and
metrics a measured run would, so read that label before treating green as evidence. `compare` warns when base and
head used different AI modes; `consistency` warns before calling a case unstable when its runs mixed modes.

```bash
remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>]
remits-cli token inspect --token <token|tokenKey|URL>                  # inspect token metadata, safety/dataMode evidence, and full context
remits-cli tools [--account-id <repoId>] [--as-account <executionId>] [--branch <name>] [--data-mode test|prod] [--variant-branch <name|none>]
remits-cli tool --name <toolName> [--account-id <repoId>] [--as-account <executionId>] [--target-account <targetId>] [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--variant-branch <name|none>] [--timeout-ms 60000] [--async true --wait true]
remits-cli tool status --call-id <callId> [--data-mode test|prod]
remits-cli verify start --summary "..." [--manifest file.json] [--ticket ID] [--data-mode test|prod]   # default: test
remits-cli verify list [--max 50] [--json]
remits-cli verify use <envelopeId>
remits-cli verify current
remits-cli verify clear [--all]
remits-cli verify show [--envelope ID] [--json]
remits-cli verify manifest --file file.json [--envelope ID]
remits-cli verify manifest --print [--envelope ID]
remits-cli verify attach --artifact path --label "..." [--envelope ID]
remits-cli verify attach --screenshot path --label "..." [--envelope ID]
remits-cli verify attach --note "..." [--envelope ID]
remits-cli verify browser-start --url URL [--label "..."]
remits-cli verify browser-step [--action click|fill|upload|reload|observe|...] [--target "..."] [--value "..."] [--url URL] [--label "..."]
remits-cli verify browser-snapshot [--url URL] [--label "..."]
remits-cli verify stage --workset
remits-cli verify test --test <id|name> [--names "case"]
remits-cli verify token --path <embeddablePathOrId>
remits-cli verify sync --safe
remits-cli verify tool --name <toolName> [--input "{...}"]
remits-cli verify status [--envelope ID]
remits-cli verify report [--envelope ID]
remits-cli verify abandon --envelope ID --reason "..."
remits-cli verify supersede --envelope ID --superseded-by <envelopeId> [--reason "..."]
```

### Activity inspection

`remits-cli activity inspect` is a read-only operator view for understanding an agent or account
workstream without touching a checkout or staging lane. It joins the current staging-lane registry,
verification envelopes, durable Test runs, corpus measurements, and live CLI agent presence, then adds
heuristic smell signals such as shared lanes, broad overlays, retained staged entries, unfinished
verification, current evidence failures, repeated test failures, corpus failures, and tests that were run
outside a verification envelope.

Use it from the platform repo when a remote agent fleet is looping or a human reports that work is going
around in circles:

```bash
remits-cli activity inspect --account-id 4 --scope children --data-mode test
remits-cli activity inspect --account-id 21 --scope related --workspace remits-fee-test-t-001r-172690b-r9 --data-mode prod
remits-cli activity inspect --account-id 4 --scope children --user-email jason@example.com --json
remits-cli workstream status --workspace remits-fee-test-t-001r-172690b-r9
remits-cli agent inspect --account-id 1742 --limit 50
```

`--scope children` expands from the selected account to its hierarchy children, which is usually the right
shape for platform-level review. `--scope related` follows component owners, staging lanes and verification
packet worlds out of the hierarchy — use it for split-world work where the repo account, component owner and
execution/data target differ.

**`--workspace`, `--branch` and `--workstream` NARROW `--scope related`.** With one of them the account set
becomes this account, its component owners, and only the accounts carrying matching evidence — not the whole
subtree — and the lanes, envelopes, test runs and corpus results are filtered to that workstream too. Without
one, `related` returns the subtree plus owners, which on a platform account is hundreds of accounts and an
arbitrary limit-bounded slice of their evidence. The response echoes what was applied as `filters`.

A related account is included only when the caller can already act on it, it is a descendant of the selected
account, or it is a component owner on that account's resolution path.
`--user-id` / `--user-email` filters the stream to one CLI user when the platform can identify them from
staged lanes, envelopes, tests, or live agent registration. `--json`
returns the full structured payload for deeper analysis; the text view is intentionally compact and
human-readable.

`remits-cli workstream status` is the local half. It keeps actor directories isolated but groups recent tool
response files, verification packets, call ids, envelope ids and world roles under one logical workstream id.
The id defaults to the current workspace; override it with `--workstream <id>` or `REMITS_WORKSTREAM_ID`.

The same id is stamped onto every verification packet world, so it joins the two halves:

```bash
remits-cli workstream status --workstream fee-immediate
remits-cli activity inspect --scope related --workstream fee-immediate
```

A campaign that spans several worlds is reported as several worlds, never merged into one line: the world
shown is the latest run's, with the distinct worlds listed beneath it.

> This is not a support ticket's `--workstream`, which is an account's own routing label (matched by
> `agent serve --serves`). Same flag name, unrelated namespaces.

### Verification envelopes

**First, the thing you probably want instead.** `remits-cli evidence` prints what you have actually run
and in which world, grouped by world, with no envelope and nothing to start:

```bash
remits-cli evidence            # this actor's trail
remits-cli evidence --json     # structured, for your final report
```

Every stage, test, token, tool and sync appends to it automatically. Use it for "what have I already
run?", "did that execute in the prod lane?" and "what do I put in my final message?".

**Verification envelopes are optional, and exist for a verdict someone ELSE will rely on** — a support
ticket, a human who asked you to prove specific things, a handoff another agent will act on. They are not
a routine step before editing. An envelope you started for your own benefit is nearly always
`remits-cli evidence` in disguise, and it costs you turns that prove nothing.

When a verdict is genuinely wanted, start it after you know the target
account/branch/workspace/data-mode tuple:

```bash
remits-cli workspace use --auto
remits-cli components status
remits-cli verify start --summary "Hosted upload updates an existing profile" --manifest acceptance.json
```

**Name what must be true, or there is no verdict to reach.** An envelope with no `claims` and no
`requiredEvidence` reports `evidence_only` — a world-stamped log. That is a complete, final state, not a
partial one, and nothing about it is outstanding. When you DO want a verdict, a manifest file is one way to
declare the contract; `verify claim` is the one-command way, and it works on a live envelope:

```bash
remits-cli verify claim market-filter --text "US market scoring excludes AU and GB statements"
remits-cli verify claim pilot-green --text "the pilot suite passes" --test "Acquirer Pilot"
```

Prove a claim by naming it on the command that already proves it. `--claim <id>` is stamped onto the
evidence packet every command sends, so it works on `verify test`, `token`, `tool`, `stage`, `sync` and
`attach` alike, and takes a comma-separated list:

```bash
remits-cli verify test --test "Acquirer Pilot" --claim pilot-green
remits-cli verify tool --name mcp_run_action --as-account 36 --claim market-filter
remits-cli verify attach --claim market-filter --note "AU statements excluded and counted"
```

A claim with no shape is satisfied by any successful packet tagged with its id. A claim that names a
`--test` suite (or `--packet-type`) must be proven by that shape. Re-declaring an id replaces it, so
tightening a claim is also one command.

**When an envelope with a contract will not close, read the report — do not start another one.** Several
open envelopes for the same goal, or workspaces named `...-r7`/`-r8`/`-r9`, is the loop signature, and
`activity inspect` reports both as smells. Close what you are not finishing: `verify supersede --envelope
<old> --superseded-by <new>` or `verify abandon --envelope <old> --reason "false start"`.

An active envelope is stored per command world (`baseUrl + accountId + git branch + workspace`)
inside the active local actor's mirror; **data mode is deliberately not part of the active pointer key**:
`./.remits-cli/actors/<local-agent>/verification/active-contexts.json`. The legacy flat
`./.remits-cli/verification/active` pointer is still understood for compatibility, but normal
auto-attachment uses the actor-scoped context map, so one actor's localhost envelope does not silently
capture another actor's production command. Stage/test/token/tool/sync commands run a server preflight
against the envelope's manifest/start world before attaching, and that is where test/prod mismatches are
refused or detached. The explicit wrappers do the same thing and make the intent visible in terminal
history:

```bash
remits-cli verify stage --workset
remits-cli verify test --test "Adyen Import Recovery" --names "browser upload recovery" --claim recovery
remits-cli verify token --path /page/pricing-config --as-account 21 --data-mode test
remits-cli verify sync --safe
remits-cli verify report
```

Existing commands also accept `--verify-envelope <id>` to attach to a specific envelope and
`--no-verify-envelope` to suppress automatic attachment for one command.

Failed `remits-cli test run` output includes compact pivots per failed case: duration, trace id,
bounded `report(...)` diagnostics, live HTTP signals, and resolved component provenance when the
platform returns it. Re-read an existing run with `remits-cli test status --task-id <taskId>` before
rerunning a long suite. If the same suite/case flips pass/fail while the resolved component provenance
signature is unchanged, the CLI prints a nondeterministic signal and records `nondeterministic:true` on
the `test_run` packet. Treat that as an AI/live-data/flaky-fixture investigation cue, not as proof that
source changed.

Test-run requirements must name the suite/cases they mean:

```json
{"id":"api_flow", "packetType":"test_run", "suite":"Statement API Flow", "allCases":true}
{"id":"one_case", "packetType":"test_run", "suite":"Statement API Flow", "cases":["rejects duplicated fee evidence"]}
```

A suite-only Test requirement is treated as `allCases:true`. A category-only Test requirement matches only
a run carrying that exact evidence category, or the case whose name the category names; unrelated Test
runs cannot replace it. `verify start` and `verify manifest` warn when a requirement has a `packetType` but
no discriminator. The evaluator treats pending async tool packets as pending, not passing; a later
`tool status` packet is the proof. Tool packets store the response file path, byte size and hash rather
than copying the whole tool payload into the envelope.

Use `remits-cli verify list` to review open envelopes for the account without SQL. The list shows stale
counts, requirements-changed markers, and last packet time. `verify start` warns when another open envelope
with the same summary/account/world exists; use `verify use <id>` to continue that contract or
`verify supersede` when a newer envelope replaces it. If a duplicate or abandoned attempt should no longer
read as live work, use `verify abandon`. Both keep the evidence history. Starting a prod-data envelope
without required evidence prints a loud no-contract warning; add a manifest unless the envelope is
intentionally evidence-only.

The manifest is the proof contract. It should name the world being exercised: repo account, host,
git/component branch, workspace, data mode, source layer, user journeys, artifacts and hashes, and the
evidence categories required before the final response may claim the work is done. If the manifest
cannot be written because the workflow is ambiguous, ask before implementation. For machine-gated report
requirements, prefer structured entries like `{"id":"actual_upload","packetType":"browser_step",
"category":"browser_session.actual_upload"}`. Plain strings work for evidence categories, but do not use
plain `"test_run"` as acceptance: it cannot say which Test or case proved the behavior.

Common packet meanings:

| Packet | It proves | It does not prove by itself |
|---|---|---|
| `stage` | Which components were uploaded to the staging lane, including workset vs overlay | The requested behavior works |
| `component_status` | Which source world and lane are currently staged or clean | Any behavior was exercised |
| `test_run` | A Test component passed in the recorded account/branch/workspace/data lane | A hosted browser journey unless that Test actually drove it |
| `token_inspect` | A token was minted/inspected for a specific account, source layer, and lane | A user clicked through the page |
| `browser_session` / `browser_step` / `browser_snapshot` | The browser journey was opened or observed with named actions | Server-side persistence unless paired with assertions or follow-up state checks |
| `tool_call` | A platform tool ran in the recorded world and returned the recorded result | User-visible behavior unless the tool is the requested entry point |
| `sync_dry_run` | The planned durable writes and safety gates | Anything was written |
| `sync_mutation` | Durable source moved to trunk or variant and returned a sync SHA | The committed source behaves after staging is cleared |
| `artifact` | A file/screenshot/note exists with path, size, and sha256 | The workflow consumed it |

Packet meanings do not widen just because they are attached to the same envelope. In summaries, keep the
proof noun: `passed` for a Test case, `measured` for corpus comparisons, `synced` for source movement,
`complete` for ticket lifecycle, and `verified` only for claims listed under `Verified` by
`remits-cli verify report`.

Final claims should come from `remits-cli verify report`. Treat `Verified` as the acceptance boundary.
`Additional evidence` is useful handoff context, but it does not satisfy a missing required packet unless
the report lists it under `Verified`. If the report says `evidence_only`, no verdict was requested; if it
says `partially_verified`, stale, or missing evidence, say that plainly instead of widening the claim. In
particular, staged proof is not committed variant/trunk proof, a token is not browser proof, and a direct
DOM or Alpine state mutation is not the same as a user action.

Evidence from the wrong world is excluded before it can satisfy a requirement. When the manifest declares
fields such as `repoAccountId`, `gitBranch`, `componentBranch`, `workspace`, or `dataMode`, the report names
the mismatch (`expected X got Y`) under missing evidence. Later stage/sync/source facts can stale earlier
packets; explicit invalidations stale only packets collected before the invalidation, so a fresh rerun can
verify the same requirement again.

For tests specifically:
- If `--data-mode` is omitted, `remits-cli test run` uses `test` and sends `dataModeSource:"cliDefault"`.
  An explicit `--data-mode prod` sends `dataModeSource:"explicitFlag"` so production test runs are
  auditable from the server status payload even when the original terminal history is gone.
- `--names` is `|`-delimited and may be repeated. A selector containing commas matches a case with that
  exact name first, else its comma-separated parts; an unmatched selector fails the run instead of
  reporting zero cases as success.
- `--as-account <ID>` runs AS a descendant subscriber so its edge selects the component branch
  (*"what does customer X get?"*); `--variant-branch <name>` probes a branch from any checkout
  (*"what does branch Y look like?"*), and `--variant-branch none` forces production/subscription semantics.
  Omit both and the working tree decides — see `branch-variants.md`.
- `--branch <stagingScope>` on `test run` selects the Redis staging namespace only. It is useful with
  `--variant-branch none` when an existing staged cache on the real git branch would shadow committed trunk
  or variant rows. On `components sync` / `commit`, `--branch` is different: it names the GitHub branch to
  reconcile.
- `--json` on `test run` prints exactly one JSON document to stdout: the final status/result. Safety
  banners, start/progress lines, and websocket case progress go to stderr so shell callers can parse
  stdout directly. The exit code is non-zero when the run fails, does not complete, or a requested case
  selector matches nothing.
- `--force-tombstones` is only for non-trunk variant syncs, when missing trunk component files are known,
  intentional tombstone overrides. It is rejected on trunk.
- `--dry-run` is only for `components sync` on non-trunk variant branches. It reports the variant write
  plan without writing rows, caching the sync SHA, or clearing staging.
- `--summary` on `components sync` prints compact counts plus named updated (with the fields that
  changed), renamed, skipped, removal/tombstone, error, and gate details instead of the full payload. A
  trunk sync reports an untouched component under `unchanged`, not `updated`.
- `--json` on `components stage`, `components sync`, `components commit`, and `tool` prints one
  parseable JSON document to stdout. Safety banners and prose go to stderr, and sync safety-gate failures
  are reported inside `gateViolations` with a non-zero exit code instead of appending prose after the JSON.
- `components commit --json` prints the COMMIT outcome, not the sync's: `phase` (`stage` → `git` → `sync`
  → `pull` → `complete`), `gitCommitted`, `pushed`, `pushedSha`, `synced`, `postSyncSha`, `pulled`, the
  nested `sync` (summary with `--summary`), and on failure `error` + `nextStep`. It stops at the first
  failed phase. `phase: "sync"` with `pushed: true` means the remote holds work the platform has not
  reconciled — follow `nextStep`; do not re-run `components commit`.
- The `stage` phase of `components commit` is changed-source compile validation. It merge-stages the
  changed runtime source and intentionally leaves unrelated staged entries in the lane, so it is not a
  substitute for `components stage --workset` when a clean workset lane is part of the proof.
- `components commit` on trunk refuses before staging, committing, or pushing unless `--yes` is present —
  with or without `--safe`. Trunk has no dry-run plan, so nothing can preview the full repo-to-DB reconcile.
- `components commit` refuses before any git write while its branch is checked out in another worktree
  (`sharedBranchWorktrees` in `--json`). Worktrees of one branch share its ref, so a stale one would commit
  over landed work. Use one clone per agent; `--allow-shared-branch` overrides after `git status` is clean
  apart from your own changes.
- `components commit` refuses before any git write, and `components sync` refuses, on a **feature branch** — one
  `components status` reports as `[subscription-fallback]` (no committed variants, no subscribers). Runs from
  it resolve the subscribed branch; landing it would write overlays nobody subscribes to and flip every sibling
  lane on that branch to them (`refusal: "feature_branch_landing"` in `--json`). Merge it into the branch it
  resolves and land from there. `--create-variant-branch` overrides, for deliberately creating a NEW variant
  branch (its first sync looks identical). `--dry-run` previews are never refused.
- `components commit` refuses before any git write, and `components sync --safe` refuses, when this checkout's
  `origin` is not the repository the platform syncs for the resolved account (`branchContext.repository` in
  `components status`). A plain sync warns and reports `repositoryCheck`, including under `--summary`.
- **Fail-closed sync gates.** On non-trunk variant branches these flags now force a server dry-run first,
  evaluate that plan before any overlay row is written, and only then run the mutating sync when the plan
  passes. On trunk, there is no safe dry-run plan, so do not treat these as scoped commit controls:
  - `--changed-only` — fail unless every planned write is a component **this checkout actually changed**
    (matched by type + id, type + name, or the repo file it came from — so a root `README.md` edit counts).
    This is the strongest guard against a sync that quietly rewrites components you never touched. It
    also fails when the checkout is not a git working tree, because "git could not answer" must never
    be read as "nothing changed".
    > **Pair it with `--changed-since <ref>` after you have committed.** On its own `--changed-only`
    > reads UNCOMMITTED edits, and the documented flow commits and pushes *before* syncing (sync reads
    > the pushed remote, so it cannot see uncommitted work at all) — so the changed set is empty at
    > exactly the moment the gate runs, and it refuses the whole plan. `--changed-since origin/main`
    > (or the commit you branched from) makes the changed set the components your commits touched.
    > The refusal message says this when it detects the empty-set case.
  - `--fail-on-removed` — fail if the plan removes or tombstones anything.
  - `--expected-removed <type:id>` — whitelist the removals you intend (repeatable, or comma-delimited,
    e.g. `--expected-removed action:5,reader:9`). It **implies** `--fail-on-removed`, so any removal you
    did not name fails the sync.
  - `--fail-on-errors` — fail if the server reported any per-component sync error.
  - `--names-only` — dry-run and print only `BUCKET type:id name` lines for the planned writes, then stop
    without writing overlays.

  - `--safe` — the NAME for that combination, and the recommended agent path on a variant branch. It
    expands to `--summary --changed-only --fail-on-errors --fail-on-removed`, resolves `--changed-since`
    from this branch's merge base with trunk when you did not name one (local refs only — it never runs
    an implicit `git fetch`, and refuses with the fetch command when nothing local can answer), and
    prints the planned writes before mutating unless `--yes` is passed. On TRUNK there is no plan to
    gate, so it states what a trunk reconcile does (every row rewritten from the repo; any live component
    missing from the repo DELETED) and requires `--yes`. It does not override a narrower
    `--expected-removed`.

  A good default for an unattended promotion is:
  `remits-cli components sync --safe`

  When `--changed-only` refuses a plan far larger than your changed set, the usual cause is a branch that
  is BEHIND trunk: it still physically carries old copies of files nobody on it touched, and a variant
  sync turns each of those into an unrelated override. The refusal says so. Merge trunk in, push, re-run.

### Staging modes: workset vs full snapshot

`components stage` reports three numbers, and they answer three different questions:

| Number | Question |
|---|---|
| **workset** | how many components git reports this working tree changed |
| **submitted** | how many this command uploaded |
| **overlay** | how many staged entries the lane now holds — **what a run resolves** |

| Mode | Uploads | Lane afterwards |
|---|---|---|
| `components stage` (default) | the whole repository manifest | reconciled to the whole repo — a FULL SNAPSHOT |
| `components stage --workset` | only the git-changed components | reconciled to exactly those |
| `components stage --changed-only` | only the git-changed components | merged; earlier entries are left in place |

`--workset` is the iteration mode. `--changed-only` keeps its long-standing merge semantics, so it cannot
shrink a lane inherited from an earlier full stage; the command warns when it retains entries that way.
`--changed-only --replace-lane` is the explicit spelling of `--workset`.

`components commit` uses merge semantics only for its internal compile-validation stage. That packet says
the changed source compiled in the current overlay; it does not say the lane was reconciled or that
retained entries stopped shadowing committed source.

An empty workset never clears a lane: `--workset` on a clean tree stages nothing and leaves the lane as
it is. `--empty-workset clear` opts into the clear; `components clear --all` is the direct way.

A deleted component file cannot be represented in Redis staging — clearing a staged entry falls back to
the committed row, so the component still resolves. The command reports those changes as NOT
REPRESENTABLE. On a non-trunk variant branch, prove a deletion through
`components sync --dry-run --summary --fail-on-errors`; on trunk there is no dry-run plan, so use the
full pre-sync safety check before any mutating reconcile.

The repo-root `README.md` is staged as the account's README-purpose Prompt. It is part of the git workset
for `components stage --workset`, even though it does not live under `components/`. It behaves like any
other component on trunk AND on a variant branch: a variant sync stores a README change as an overlay of
the README prompt, and an agent's `.md` in `components/agents/` travels with that agent's overlay.

### Prod banners and retryable failures

- Every command that can touch production (`tool`, `test run`, `components sync`) prints a `PROD DATA`
  banner naming the operation, the resolved account, and the host — and distinguishes a live **WRITE**
  from a live **READ** and from a **DRY RUN**. `remits-cli tool` also prints an explicit **TEST DATA WRITE**
  banner for mutating tool calls in the test lane, including multi-action tools such as
  `mcp_account_user_admin` where the write is signaled by `input.action` (`account_create`, `user_update`,
  `edge_update`, etc.). If you see a WRITE banner you did not intend, stop.
- A tool call that fails **in the platform runtime** rather than in the tool (Groovy reflective dispatch
  of a runtime-compiled component, an empty connection pool, a Redis reconnect, a lock-wait timeout)
  now comes back as HTTP `503` with `failureClass: "transient_infrastructure"` and `retryable: true`,
  and the CLI prints `TRANSIENT INFRASTRUCTURE FAILURE (retryable)`. Retry that **once**; prefer an
  idempotent input if the tool has side effects. A genuine tool error stays HTTP `500` with
  `failureClass: "tool_error"` — do **not** retry it, fix the input or the component.
