repo-mailbox/skills/coord-send/SKILL.md
Kjell Tore Guttormsen 0a79e786fc feat(engine): harden mailbox CLIs for v0.2.0
- Atomic delivery: create the temp file inside the destination dir
  (dot-prefixed, invisible to the inbox glob) so the final rename never
  crosses filesystems and readers never see a half-written message.
- Reject . and .. explicitly in the --reply-to and coord-done name
  guards instead of relying on downstream failure.
- Add -h/--help to coord-inbox.sh (uniform across the three CLIs).
- Close selftest gaps: default mailbox path via HOME fallback, malformed
  frontmatter on the read path, read-only destination dir. 48 -> 64
  checks, all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fduZz8otpU3W3rhfoPD6t
2026-07-24 19:57:40 +02:00

6.4 KiB

name description version
coord-send Send an inter-repo coordination message through the local coord mailbox — a natural-language front door over the `coord-send` script. Use this whenever the user wants to notify, tell, message, ping, or coordinate with another repository (or all repositories) about work happening in the current repo: "send info to all repos", "tell repo-x the bug is fixed", "let repo Y and Z know", "broadcast that the spec changed", "reply to that coord message", or a deferred/conditional notice like "when the build is green, notify repo X". Also triggers on Norwegian phrasings: "send info til alle repo", "varsle repo X", "gi beskjed til Y og Z", "kringkast at …", "svar på coord-meldingen", "når Z er ferdig, varsle X". Trigger even when the user names a repo plus something to convey without saying "coord" explicitly — routing a message to another repo IS this skill. 0.2.0

coord-send — natural-language front door for inter-repo messages

This skill turns a plain request ("tell repo X that Y happened") into the right coord-send invocation. The script is the engine; this skill is only the mapping from intent to arguments. It never edits mailbox files by hand — always go through the script, because the script owns the filename grammar, frontmatter, and delivery guarantees.

Boundary — why messages are thin

The mailbox is transport, not state. A coord message is a notice, not the source of truth. The durable record of any decision lives in the owning repo's state files / docs and its git history. So when you compose a body, write a self-contained heads-up and point at where the real record lives (a commit hash, a doc path) — don't try to make the message itself the canonical artifact. Coordination content is also private: it never belongs on a public surface.

The engine

Resolve the script path portably — this one expression is correct both when the skill runs bundled inside the plugin and when the scripts are installed as personal scripts under ~/.claude/scripts/:

CSEND="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude}/scripts/coord-send.sh"

Interface (body comes from a quoted heredoc so nothing in it is shell-expanded):

# to one repo
"$CSEND" --to <repo> --subject "<subject>" <<'BODY'
<message body>
BODY

# to every repo (accumulates; first-time newcomers see it too)
"$CSEND" --broadcast --subject "<subject>" <<'BODY'
<message body>
BODY

# reply to a message this repo received (routes to the original sender and
# marks the original handled; target + "Re: …" subject are inferred)
"$CSEND" --reply-to <message-filename> <<'BODY'
<message body>
BODY

Sender identity (--from) defaults to the basename of the current git toplevel, so you almost never set it. Exit 0 = delivered; exit 2 = usage/IO error (read stderr and fix the arguments rather than retrying blindly).

Choosing the target

  • One named repo--to <repo>. The repo name is its directory basename; use the name the user gave. No registration exists — sending to a new name just creates that repo's inbox, so a typo silently creates a dead mailbox. If unsure a name is real, check ~/repos/ or existing ~/.claude/coord/<repo>/ before sending.

  • Several named repos ("X and Y") → loop --to once per repo with the same subject and body. There is no multi-target flag; the loop is the mechanism:

    for repo in repo-x repo-y; do
      "$CSEND" --to "$repo" --subject "<subject>" <<'BODY'
    <body>
    BODY
    done
    
  • All repos--broadcast. Prefer this only when the notice genuinely concerns everyone, because broadcasts accumulate and every future first-time repo sees the standing backlog. For a bounded, known set, loop --to instead so unrelated future repos don't inherit it.

  • A reply to something received--reply-to <filename>. Use the filename from the injected inbox/archive; it resolves the sender and closes the original in one step.

Composing the message

Derive a short, specific --subject from the intent if the user didn't give one ("ingest bug fixed", not "update"). In the body, state what happened, what the recipient should know or do, and a pointer to the durable record (commit hash, doc path). Keep it to the point — this is a notice, not a report. Match the repo's language convention for message content; keep it sober and factual.

Deferred and conditional sends

"When Z is done, notify X" means: do not send now. Hold the intent — the target, subject, and body — and continue the work. The moment condition Z is satisfied in this session, fire the send. This works because you carry the intent across turns within a session.

Be honest about the limit: a skill cannot persist intent across sessions. If the session is ending and the condition still hasn't been met, surface the pending message to the user so it isn't silently lost — don't imply it will fire later on its own.

After sending

Report what actually happened: the target(s), the subject, and the delivered filename(s) from the script's stdout. If a send failed (exit 2), say so with the stderr reason rather than claiming success. Delivery reaches the recipient at their next session start (the receive side is a hook), so tell the user it's queued, not that the other repo has seen it.

Examples

Example 1 — single repo, composed body Input: "tell repo-x the ingest bug is fixed, it was commit abc1234" Action: --to repo-x --subject "ingest bug fixed", body naming the fix and pointing at commit abc1234 and the owning doc.

Example 2 — broadcast Input: "let all the repos know the shared spec grew a new validation rule" Action: --broadcast --subject "spec: new validation rule", body summarizing the change and pointing at the spec's owning repo.

Example 3 — bounded multi-target Input: "gi beskjed til repo-x og repo-y om at gaten er re-pinnet" Action: loop --to repo-x then --to repo-y, same subject "shared gate re-pinned".

Example 4 — deferred Input: "når testene er grønne, varsle repo-y" Action: keep working; once the test run passes this session, send --to repo-y with the green result. If the session ends first, surface the pending notice to the user.

Example 5 — reply Input: "svar på coord-meldingen fra repo-x at vi tar det" Action: --reply-to <that-message-filename>, body acknowledging and stating the plan.