DM
Use $dm for a short, synchronous conversation with another dispatch-managed
lane. The long form is "dispatch message": the workflow is backed by
dispatch send, not by a separate dispatch message CLI op in v0.
Use a goal/delegation workflow instead when the target lane should own background work, source-control follow-through, monitoring, heartbeats, or a long-running task.
Preconditions
Use dispatch first:
uv run dispatch doctor --no-app-server
uv run dispatch list
uv run dispatch daemon status
If the environment is new, run uv run dispatch doctor once before messaging.
Fix PATH, Codex auth, stale daemon files, registry, or plugin asset warnings
before assuming a DM failure is about the target lane.
For an owned Hermes lane, dispatch daemon status --json must show the binding
ready with send support. Durable Hermes messages require both
prompt_submit_if_idle_v1 and prompt_turn_correlation_v1; stock Hermes
939e45c/0.21.2 lacks the correlation capability, so Dispatch refuses the
send. The supported runtime uses a local unpublished patch. Send plain text with
an idempotency key, and inspect the returned local receipt:
uv run dispatch send <hermes-ref> '<message>' \
--idempotency-key dm:<stable-event-id> --json
uv run dispatch delivery get <receipt-id> --json
An exact key and request replay the same local receipt. A changed request returns
delivery_conflict; an unknown outcome stays held and must not be resent.
Hermes DMs do not support images, steer, queue, interject, context injection, or
native history recovery in this slice.
The target should be an owned dispatch lane selected by dispatch ref when
possible. Attached lanes are turn-write locked by default. Dispatch permits
DM/send verbs only when local config explicitly enables
[policy] allow_attached_writes = true; check the lane's writable,
capabilities, and write_locked_reason fields before sending.
If the user wants to message an existing desktop Codex thread, attach/sync it only for observation unless they explicitly enable attached writes after reading ADR-0005 and ADR-0018.
Handles And URIs
Use bare @Name handles for conversational identity. When the thread id is
known, make the first human-facing mention a Markdown Codex URI. Compose a link
whose label is the handle and whose destination is the Codex URI:
label: @Target
destination: codex://threads/<target-thread-id>
Use the dispatch ref or full thread id for dispatch send. Use the URI link in
the message body so the recipient and the human can open the thread directly.
Message Shape
Send one small request with dispatch send --intro so Dispatch appends the
standard visible attribution footer and includes the reply hint:
<freeform message>
Contract: read-only, brief answer, reply in this lane unless asked otherwise.
dispatch (dm): [@Sender](codex://threads/<thread-id>) `<ref>`
↳ reply `dispatch send <ref> "..."`
Then deliver it:
uv run dispatch send <target-ref> '<message>' --intro
When a bounded question needs visual evidence, attach it to the same send instead of creating a separate workflow:
uv run dispatch send <target-ref> 'Does this match the expected state?' --image ./screen.png --intro
Repeat --image or --image-url as needed for Codex lanes; local images must be PNG, JPEG, GIF, or WebP and at most 20 MiB, while remote images must use HTTPS and resolve publicly. Use --image-detail auto|low|high|original only when detail matters. Images work for normal Codex DM sends, steering, queueing, and interjection, but not silent context injection. A queued DM stores references and validation metadata, never image bytes, and revalidates files and remote content before delivery.
Keep DMs conversational and bounded. Prefer one ask. Include only the context
needed for the target lane to answer without reading the sender's full
transcript. Add Codex thread links inside the freeform message when the recipient
or human needs a direct desktop link. If the current Codex thread is not managed
by dispatch, omit --intro and include the sender link manually. Do not use
$dm to smuggle in broad implementation work.
Harvesting
dispatch send starts the target turn. Use dispatch get and dispatch daemon log
to confirm thread state and accepted actions:
uv run dispatch get <target-ref>
uv run dispatch daemon log --limit 10
Important v0 limitation: dispatch get is thread metadata unless the CLI
surface requests transcript output. For Hermes, use dispatch get <target-ref> --include-transcript; the result is bounded, partial live_observed evidence.
For Codex, use dispatch tail <target-ref> --limit 50 for persisted history when
the lane is non-ephemeral, or open the Codex thread link. tail and
selector-scoped history read App Server transcript history and backfill
Dispatch's local history index for that thread; bare history is only an
indexed overview. Do not pretend a DM harvested text you did not actually read.
Optional Contract Lines
Use one line only when it reduces ambiguity:
Contract: answer only; no repo/tracker/source-control mutation.
Contract: 3-6 lines; include evidence paths if you check live files.
Contract: sanity-check the wording; no implementation.
Contract: reply back to <sender Markdown thread link> when done.
Guardrails
- Do not use
$dmfor background execution or task ownership. - Do not create new lanes unless the user asks.
- Do not pin, archive, rename, or stop lanes as part of ordinary DM flow.
- Do not send secrets, private data, long logs, or unrelated transcript dumps.
- If the exchange becomes work ownership, stop and switch to a goal/delegation workflow.
Example
From <@Docs Markdown thread link> to <@Dispatch Markdown thread link> via $dm:
Quick terminology check: should the usage docs say "lane" everywhere, or is
"thread" acceptable in the operator guide when referring to raw Codex ids?
Contract: read-only; 3-5 lines; include evidence paths if you check repo docs.