Mail inspection
Verified against lmctl 0.1.248.
lmctl mail inspects event-log messages, delivery evidence, and causal
lineage. Use it when lmctl status tells you a message is queued or delivered
but you need the message-level evidence.
Default lmctl chat is synchronous: a busy receiver returns a busy error and
does not create queued mail. Queued mail appears when the mailbox queue is
explicitly enabled (mailbox_queue_enabled=true in config or
LMCTL_MAILBOX_QUEUE_ENABLED=true) or when you are inspecting older queued
work.
For every subcommand, --json is the stable, versioned external contract.
Human-readable output is for people and is intentionally not a stable machine
interface.
Sent mail
lmctl mail sent --to "/abs/path/team.lmctl:Coder" --status queued --json
lmctl mail sent --to "/abs/path/team.lmctl:Coder" --status delivered --json
lmctl mail sent --since 3d --limit 50 --json
mail sent lists CREATE / RESPONSE_CREATE streams sent from SELF.
--status queued selects messages still present in message_pending;
--status delivered selects terminal messages. JSON includes
schema_version=1, schema=event-log-v1, identity, filters, messages, and
next_cursor.
When queueing is enabled, check mail sent --status queued before assuming a
delivery problem is a bug: most stuck mail is a genuinely busy receiver or a
receiver held by lmctl terminal. With default synchronous behavior, a busy
send returns a busy result instead of creating a queued row.
Mail identity filters are exact. Use the canonical absolute teamfile identity
shown by lmctl status or realpath ./team.lmctl; relative identities such as
./team.lmctl:Coder can return an empty result even when matching mail exists.
One message
lmctl mail history <message_id> --json
lmctl mail read <message_id> --json
lmctl mail seen <message_id> --json
mail history returns the ordered event stream for one message. Use it when
lmctl status is not enough detail.
mail read returns content and pending state without delivery, dispatch,
compare-and-swap, or any other state change.
mail seen combines event-log delivery evidence with historical provider
transcript evidence. Keep the terms separate: ack is not seen, seen is not
answered, and ack is not answered. seen answers the narrow question "was this
specific message_id observed in the transcript?"
Acknowledge handled mail
lmctl mail ack <message_id> --json
lmctl mail ack <message_id> --terminalize-pending --note "handled from terminal" --json
By default, ack is record-only: it appends an acknowledgement event that this
actor read or acted on the message. --terminalize-pending additionally
retires a still-queued, unclaimed message. If an in-flight delivery claim
exists, that claim wins.
Causal trees
lmctl mail tree --since 3d --json
lmctl mail tree --root "/abs/path/team.lmctl:Lead" --since 3d --limit 100 --json
mail tree returns causal roots, orphan policy, and an incomplete-capture note
so you can inspect how sends and responses relate without inventing lineage
from timestamps.
Causal handling and relay delivery
These are low-level composition primitives. They are useful when a terminal-held Lead or external relay reads mail directly and then sends follow-up work outside the normal delivered-message turn.
lmctl mail handle <message_id> --json
lmctl mail deliver <message_id> --json
mail handle declares that SELF is about to act because of this message. It
appends MESSAGE_HANDLING_STARTED and sets the caller's causal pointer, so
later outbound sends can be linked under the handled message in mail tree.
The pointer persists across multiple outbound sends for one hour; older pointers
are ignored so stale terminal work does not attach unrelated future sends. A
later handle replaces it, and a matching mail ack clears it. It requires
LMCTL_SELF_SESSIONID to resolve to one registered member. A repeated handle by
the same actor returns appended: false while refreshing the causal pointer.
mail deliver attempts one real provider delivery for exactly one queued
message. It is useful only for opt-in queued mail or older queued rows. Unlike
daemon lane batching, one deliver call sends only the named message; relay
code owns ordering, retry, and batching. Delivery-attempt JSON uses an
outcome field such as delivered, skipped_busy,
skipped_terminal, or error. Unknown, terminal, or already-claimed messages
return the standard error envelope; for example, a terminal message returns
errorKind: "invalid_state".
Relay and infra discovery
lmctl mail pending --limit 50 --json
lmctl mail pending --receiver "/abs/path/team.lmctl:Lead" --limit 50 --json
In lmctl 0.1.227, bare mail pending defaults to SELF in a member session.
From an operator shell with no resolved SELF, it remains fleet-scoped and
lists pending mail for every receiver in the local event-log projection. To
avoid ambiguity in scripts and relay tooling, pass the receiver explicitly:
lmctl mail pending --receiver "/abs/path/your-team.lmctl:Lead" --json
This is intentionally called out because older public-preview builds were
asymmetric: mail sent self-scoped while mail pending was fleet-scoped. Use
an explicit --receiver whenever the receiver identity matters.
mail pending is useful for relay or infrastructure discovery. It is not the
normal Lead command for delegation, and in the default synchronous mode it may
be empty because busy sends do not queue. The reference script in lmctl-src,
scripts/relay-loop.pl, composes mail pending, mail read, and
mail ack --terminalize-pending, and can be adapted to use mail deliver or
custom handling. It is a reference script, not a core lmctl command and not
part of this website repository.
For a complete no-cost fixture that exercises CREATE -> ENQUEUE -> MESSAGE_ACKNOWLEDGED, see
ClaudeMock mailbox drain fixture.