The operator removed the "Fable can only be suggested after a failed Opus 5/xhigh session" policy on 2026-08-06 - it stood in the way too often. Until now that policy was implemented as code: --opus-xhigh-failed was the ONLY way route.sh's SELECTION chain could reach rows 5-6 (Fable), so the rubric still enforced a policy the operator had already dropped. Given a choice between (A) deleting the branches and the flag outright, so the calculator's output range closes at row 4, or (C) keeping a path that renders a Fable row on explicit instruction with rule=operator-override, the operator chose A (AskUserQuestion) - CLAUDE.md is explicit that the rubric stays the only deterministic lookup and a Fable choice is now always a deviation from it, recorded in STATE as an override rather than produced here. board.sh is untouched and still parses "Fable 5/high"/"Fable 5/xhigh" written by hand into a board line (route-selftest.sh section 6 now pins that half directly, since route.sh can no longer produce the strings itself). TDD: every affected selftest check was rewritten to fail against the unmodified route.sh first (72->68/69, confirmed red), then route.sh was edited to match. --opus-xhigh-failed is gone outright - passing it now exits 2 like any other unknown argument, not silently accepted as a no-op. route-selftest.sh: 73 -> 69 checks (three checks tested command shapes row_advisor() can no longer produce; the two row-5/row-6 reachability checks in section 1 collapsed into one "the flag is gone" check). coord 191, board 142 unaffected. Suite total 406 -> 402. Also folds in a standalone fix already pushed this session: section 14 was gating the wrong CLI fact (whether "claude --advisor fable" is rejected, which row_advisor() never depends on) instead of the one it actually rests on (whether opus/sonnet can advise a FABLE main model). Re-pointed and verified against the installed CC 2.1.226. Verified before committing: grepped every repo under ~/repos for a route line carrying --opus-xhigh-failed (none - one repo has it in prose only, not in its <!-- route: --> comment) and ran board.sh --plan/--brief over the real tree to confirm no repo's command line broke. Sent a follow-up coord-send to catalog superseding an earlier now-stale "406" stat-line correction with the current 402. skills/route/SKILL.md: usage block, "last-session record" framing, and the closing --opus-xhigh-failed paragraph rewritten to match. CLAUDE.md, README and CHANGELOG updated (checks 73->69, badge 406->402, new 0.21.0 entry). Version bumped 0.20.3 -> 0.21.0 across plugin.json, package.json and all three skill frontmatters (breaking CLI removal at 0.x -> minor, per v0.20.0 precedent). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ap1WKHCDPcSfpjo4ds2dQX
219 lines
11 KiB
Markdown
219 lines
11 KiB
Markdown
---
|
|
name: coord-send
|
|
description: >-
|
|
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. Also
|
|
covers retiring a broadcast that has become wrong or obsolete: "retract that
|
|
broadcast", "that announcement is outdated, pull it", "trekk tilbake kringkastingen",
|
|
"den broadcasten er utdatert".
|
|
version: "0.21.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
|
|
|
|
CSEND="${CLAUDE_PLUGIN_ROOT}/scripts/coord-send.sh"
|
|
|
|
There is no deployed copy anywhere else and no fallback path. A Bash tool call
|
|
never has `CLAUDE_PLUGIN_ROOT` set as a real shell variable — only this
|
|
skill's own rendering resolves the token — so a `${CLAUDE_PLUGIN_ROOT:-$HOME/.claude}`
|
|
fallback silently won every time the line above was actually executed,
|
|
routing through whatever happened to sit at `~/.claude/scripts/coord-send.sh`
|
|
instead of the plugin's own bundled script. That path was never a legitimate
|
|
parallel deployment — the operator invokes this skill only through its
|
|
natural-language front door, never a personal terminal alias — so the file
|
|
that sat there was a pure accident target with no owner keeping it current.
|
|
It has been deleted. If this fails to resolve, the fix is this expression —
|
|
never a restored fallback.
|
|
|
|
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
|
|
|
|
# retire one of THIS repo's own broadcasts (no subject, no body)
|
|
"$CSEND" --retract <broadcast-filename>
|
|
|
|
# a notice that needs no answer (any of the forms above)
|
|
"$CSEND" --to <repo> --subject "<subject>" --fyi <<'BODY'
|
|
<message body>
|
|
BODY
|
|
|
|
Sender identity (`--from`) defaults to the basename of the current git toplevel, so
|
|
you almost never set it. Outside a git repo there is no default — the send refuses
|
|
with exit 2 rather than naming itself after the working directory, so on a global
|
|
surface (`~/repos`, `$HOME`) pass `--from <repo>` and make the identity a choice.
|
|
Exit 0 = delivered; exit 2 = usage/IO error (read stderr and fix the arguments
|
|
rather than retrying blindly).
|
|
|
|
## Does it need an answer? (`--fyi`)
|
|
|
|
Every message declares whether its sender expects a reply. **Omitting `--fyi` is
|
|
the declaration that one is expected** — that is the default, and it is the safe
|
|
one: a forgotten flag over-counts what the recipient owes, which is visible,
|
|
while the opposite would create debt nobody ever sees.
|
|
|
|
Pass `--fyi` when the message is genuinely a notice: "shipped 0.9.0", "the spec
|
|
moved to docs/x.md", "your build is green again". Omit it when you are asking a
|
|
question, requesting a decision, or handing over work — anything where silence
|
|
would leave *you* blocked.
|
|
|
|
Two things this flag is not:
|
|
|
|
- **Not a way to lower the bar for the recipient.** Both terminal states stay
|
|
open on every message: a `--fyi` message must still be closed with
|
|
`coord-done`, and the recipient may still reply. The field says what *you*
|
|
expect, and the receiving session is told in as many words that it is a
|
|
declaration, not an instruction.
|
|
- **Not available on a broadcast.** `--broadcast` always writes
|
|
`reply-expected: no`, `--fyi` or not, because there is no reply path to a
|
|
broadcast at all (`--reply-to` resolves inside the recipient's own mailbox).
|
|
Passing it there is harmless and changes nothing.
|
|
|
|
A reply is not a special case either: `--reply-to` without `--fyi` expects one
|
|
back. When your reply closes the exchange — and it usually does — say so with
|
|
`--fyi` rather than leaving the other repo an open item.
|
|
|
|
## 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 broadcast that later turns out wrong can be retired with
|
|
`--retract` (see below), but only for repos that have not received it yet.
|
|
- **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.
|
|
|
|
## Retracting a broadcast
|
|
|
|
When a broadcast has become wrong or obsolete, retire it — don't send a correction
|
|
and leave the original standing, because every future first-time repo would receive
|
|
both. `--retract <broadcast-filename>` archives it out of the delivery queue.
|
|
|
|
Three things to be honest about when you report it:
|
|
|
|
- **It is un-send, not recall.** Repos that already received the broadcast keep it.
|
|
Retraction only stops delivery to repos that haven't seen it yet. If the old news
|
|
actively misleads someone who already got it, a correcting broadcast is still
|
|
needed — retraction alone does not reach them.
|
|
- **Only the sender may retract.** The `from:` field must match this repo's identity.
|
|
If the announcing repo has since been renamed, its old identity no longer resolves,
|
|
so pass `--from <old-sender>` explicitly (read the sender off the file). Say that
|
|
you did.
|
|
- **Nothing is deleted.** The message moves to `_broadcast/archive/`. Never remove a
|
|
mailbox file by hand — the script owns the filename grammar and delivery guarantees.
|
|
|
|
Find the filename in `~/.claude/coord/_broadcast/inbox/` (or from the injected
|
|
`--- broadcast: <filename> ---` line) rather than guessing it.
|
|
|
|
## 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.
|
|
|
|
**Example 6 — retract**
|
|
Input: "den gamle kringkastingen om plugin-navnet er utdatert, trekk den"
|
|
Action: locate the file in `_broadcast/inbox/`, then `--retract <that-filename>`
|
|
(adding `--from <old-sender>` if this repo has been renamed since). Report that
|
|
repos which already received it are unaffected.
|