send_message
Deliver a role-addressed channel message to one recipient, several roles, or all others, with per-recipient addenda and tracked obligations that stay open until resolved.
Instructions
Send a message on the channel. 'to' is one role name, a LIST of role names, or '' for every other role. Sending to yourself is rejected. A multi-recipient message is ONE message with one body and one id: one thread, one set of acknowledgements, and — the reason it exists — one text that is byte-identical for everyone, so four roles cannot end up voting on four slightly different drafts. Read state is tracked per recipient. Only kind='proc' and kind='status' may go to several roles, and action_required=true is REFUSED for them: a debt needs exactly one owner, or 'closed' stops being a definite state and the resolve→confirm loop has nothing to hang on. Need work from three roles — send three messages, one debt each. 'addenda' ({role: text}) carries a personal tail per recipient inside the same message: the shared body stays identical while the part that legitimately differs travels with it instead of in four follow-up letters. Each recipient sees theirs as 'addendum'. Optional 'kind' tags the namespace (bug/feat/proc/status/question/answer — use 'answer' with reply_to for replies to a question); optional 'work_status' tags progress (proposed/in_progress/done_local/needs_you/done/blocked). These fields REPLACE the old text conventions — do not duplicate them as 'bug:' topic prefixes or '[status]' tags in the body. As the work progresses, move the status on the SAME message with set_work_status instead of sending new messages. Choosing the mechanism: need a formal decision from the other side → kind='proc' (surfaces in awaiting_ack); need work/action done → action_required=true (surfaces in open_obligations, starts with status='open', closed via resolve_message). A REPLY to a multi-recipient message may keep the same recipients whatever its kind: answering four roles is not a broadcast you chose, it is the audience the question already had — sending four separate letters instead is exactly the copy-paste this channel asks you to avoid. action_required stays single-recipient. kind='proc' REQUIRES TWO EXPLICIT ANSWERS, and omitting either is refused at send time rather than discovered later: (1) 'pin_key' — the pin this proposal changes, or null if it changes none. With a key the link is structural rather than inferred from wording, so pin_set(approved_by=this) cannot fail afterwards for not naming the key, the proposal is findable with list_messages(pin_key=...), and a successful pin_set retires the drafts it settles. A proposal that names a pin must be addressed to EVERY other role — everyone READS a pin round — and must also declare 'voters': the roles whose 'agree' it needs. voters='' is every role (the classic rule); voters=['x','y'] scopes the decision to the roles it is actually between, and the rest still receive it, may still vote, and simply do not block. What you declare is stored on the message and is the exact rule pin_set will check, so a round can no longer collect a full quorum and then be refused. Omitting 'voters' is refused: in a channel with a part-time member an unscoped round cannot close, and silence must not be mistaken for a veto. (2) 'about_message_id' — the message this one is about, or null if it stands on its own. A nudge ('still need your vote on #1403') asks for a decision about ANOTHER message, so it does not become a decision of its own: it stays out of awaiting_ack and retires when its target is voted on, superseded or deleted. 'decision_requested=false' opens a proposal for READING rather than voting ('ten points, tell me what is wrong, I am not collecting votes') — it stays out of awaiting_ack while acknowledge still works for anyone who wants to weigh in. 'topic' is at most 80 characters. The body has no server limit, but there is a limit on what you can TYPE in one call: if the text is a document rather than a message, put it in with upload_content + seal_content and pass body_ref= instead of 'body' — that also gives the document its own sha256, separate from the digest of the letter carrying it. Returns the new message id and created_at timestamp.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | ||
| body | No | ||
| kind | No | ||
| topic | Yes | ||
| voters | No | ||
| addenda | No | ||
| pin_key | No | ||
| body_ref | No | ||
| reply_to | No | ||
| work_status | No | ||
| action_required | No | ||
| about_message_id | No | ||
| decision_requested | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||