Message Send
Govern a message sent on behalf of a principal: a channel post or a reply in a bound conversation. The skill never receives a provider token, never lets approved content drift before delivery, and calls a message delivered only after the provider reads it back.
Composes
slack#read_thread
Runners
send(default): plans one channel post, requests approval only at the live post, posts once, and requires independent readback.plan: the provider-neutral authorization step. It binds the principal, audience, content digest, consent basis, preflight checks and approval gate into arunx/message-send.plan.v1packet, and never delivers.plan_post: validates a channel name, channel id, or exactslack://workspace/channeldestination, computes the message digest with Runx's nativedata.digest, and binds that digest, the named principal and the send intent throughplan. It does not post and needs no approval.plan_post_exact: for an operator that has already selected one exact destination, principal and text under its own policy. It reuses the same validation and digest and emits the same undeliveredrunx/message-send.notification_plan.v1plan without a second model judgment. Itssend_plan.originismessage-send.exact, not a claim thatplanran.post: accepts only an exactnotify_plan, the matching destination and text, and a stable UUID idempotency key. It recomputes the digest, compares the complete binding, stops unless exact native approval or a matching standing notification authority is present, runschannel.postthrough the configured binding, then reads the returned message locator back withchannel.post.readand seals only the bounded evidence fields.reconcile_post: reads back a post whose delivery outcome was ambiguous, without posting again.reply: delivers one admitted reply to a bound conversation. The operator supplies the exact source packet, thread, draft, provider and native turn identity. The skill holds unless the source proves complete coverage, then asks for exact approval, runsthread.replyon the chosen connector, and requiresthread.reply.readto resolve a stable message locator with the matching content digest. An accepted or queued request alone leaves the work held for reconciliation.
A channel post and a source-bound reply keep their own input checks: a post is bound to a destination and a plan, a reply to a source packet and a turn.
The binding may be local, self-hosted, third-party or Runx-hosted. The skill never receives a token, constructs a raw HTTP request, owns a tenant, or stores connector credentials in its inputs or receipts.
When to use this skill
- An agent needs to post one bounded message, or reply in one conversation, on behalf of a user, team, brand, account or service.
- An operator explicitly wants an authorization plan and selects
plan. - The workflow must prove the intended audience, content, consent basis and approval decision before delivery.
When not to use this skill
- To write copy only. Use a drafting or brand-voice skill unless delivery is in scope.
- To claim delivery from a plan; only provider readback is terminal evidence.
- To send without a named principal and audience.
- To bypass unsubscribe, consent, suppression, preflight, legal or human approval gates.
Procedure for plan
- Identify the principal being represented and the provider account or surface.
- Classify the send:
transactional,campaign,flow_step,support_reply,outreach,status, orinternal. - Bind channel and audience. Audience must be a named recipient, list, segment, support thread, channel, or scoped all-contacts decision; never an implicit broad default.
- Bind content by digest or stable draft reference. Do not approve mutable content by prose summary alone.
- Check consent, unsubscribe, suppression, compliance, preflight, and provider readiness. Missing evidence becomes a blocker.
- Decide the gate:
- drafts, previews, and test sends may proceed without live-delivery approval when provider policy permits them;
- customer, public, audience, or live sends require explicit approval;
- billing/account mutation is outside this skill and needs its own gate.
- Produce the smallest provider-neutral
send_planthat execution can consume without widening authority. - Return
needs_inputfor missing principal, audience, content digest, consent basis, or provider readiness; returnrefusedfor gate bypass.
Edge cases and stop conditions
- No principal: return
needs_input; the agent cannot speak as an unnamed actor. - No audience: return
needs_input; do not default to all contacts or a whole channel. - All contacts or broad audience: require explicit reconfirmation and a stricter preflight block.
- Mutable content: return
needs_inputuntil content is digest-bound. - Missing consent or unsubscribe path: block live delivery.
- Plan, destination, content-digest, principal or audience drift: stop.
- Approval denied or absent: do not deliver.
- Missing, ambiguous, wrong-provider or insufficient-scope binding: refuse rather than fall back to a raw token.
- Ambiguous mutation outcome: resume with the same idempotency key, or reconcile; never create a new key to escape uncertainty. Changed content never inherits the old approval.
- Raw credentials or contact dumps: redact; if redaction would remove the
evidence needed to decide, return
needs_input.
Outputs
send_planis therunx/message-send.plan.v1packet: the decision (ready,needs_input,deniedorrefused), the principal, provider, send class, channel, audience, digest-bound content, the gates the send needs, blockers, provider actions, evidence locators and the success checkpoint.notify_planis therunx/message-send.notification_plan.v1packet, the exact postpostandreconcile_postdeliver.- A reply ends in a
runx.work_outcome.v1packet for its host.
A post is delivered only when the sealed runx.provider.operation.v1 packet
identifies the operation and carries the provider-returned message locator,
conversation locator, content digest and occurrence time, and the follow-up
readback resolves that exact locator.
Worked example
An operator plans "release 2.4 is verified" for one internal channel. Approval
binds that exact text and destination. If the text changes to include another
claim, post refuses the digest mismatch. With a matching plan and one valid
Slack binding, the provider posts and returns the message id; the follow-up
readback resolves that id, and those provider observations, not the local plan,
prove delivery.
Inputs
send:channel_locator,text,principal,idempotency_key, and optionalprovider_context.plan:objectiveandprincipal, with optionalprovider_context,audience,content,consent_basis,operator_contextandconnector.post: thenotify_plan, the identical locator and text, and a stable idempotency key.reply:source_context,turn_id,expected_provider,thread_locatoranddraft_message.


