#!/bin/bash # coord-order-send.sh - deliver a WORK ORDER into a repo's order queue # (~/.claude/coord//orders/). Model-invoked; no network, no service. # # Usage: # coord-order-send.sh --to --subject "" [--from ] # [--message "" | --prompt-file ] # Body comes from --message, from --prompt-file, or from stdin (heredoc) when # neither is given. The body IS the whole prompt the dispatched session runs on. # # Prints `order-id=` on stdout; the order file is .md in the queue. # # WHY A SECOND CHANNEL, and not just another inbox message: the two have # OPPOSITE authorization classes. Inbox content is untrusted cross-repo data # that may never instruct a session (Rule 6); a dispatch order is # operator-authorized work by construction - dispatch IS the operator's # authorization. Mixing the classes in one channel would mean either mail that # can instruct, or orders that cannot - both wrong. So the infrastructure is # reused and the channel is not. # # That authority rests on dispatch being this queue's ONLY writer BY # CONVENTION. The engine does not enforce it and cannot: --from redefines # identity here exactly as it does in coord-send.sh, so any session can write # an order into any repo's queue. The read side says so in the words it injects # rather than claiming a guarantee that does not exist. # # Exit: 0 delivered, 2 usage/IO error and nothing written. # ASCII only, bash 3.2 safe. set -u export LC_ALL=C COORD="${CLAUDE_COORD_DIR:-$HOME/.claude/coord}" TO=""; SUBJECT=""; FROM=""; MESSAGE=""; HAVE_MESSAGE=0; PROMPT_FILE="" require_value() { # bash 3.2: `shift 2` past the end of $# is a no-op, so a trailing value-flag # without its value would loop forever. Every two-arg flag must check first. if [ "$2" -lt 2 ]; then echo "coord-order-send: $1 requires a value" >&2; exit 2; fi } while [ $# -gt 0 ]; do case "$1" in --to) require_value --to $#; TO="$2"; shift 2 ;; --subject) require_value --subject $#; SUBJECT="$2"; shift 2 ;; --from) require_value --from $#; FROM="$2"; shift 2 ;; --message) require_value --message $#; MESSAGE="$2"; HAVE_MESSAGE=1; shift 2 ;; --prompt-file) require_value --prompt-file $#; PROMPT_FILE="$2"; shift 2 ;; -h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "coord-order-send: unknown argument: $1" >&2; exit 2 ;; esac done # --- Resolve sender identity (same rule as coord-send.sh) ------------------ # git toplevel or an explicit --from, never basename(pwd): an invented identity # signs an order as a repo that does not exist. if [ -z "$FROM" ]; then FROM="$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null)" fi if [ -z "$FROM" ]; then echo "coord-order-send: cannot resolve sender identity (not inside a git repo); pass --from " >&2 exit 2 fi case "$FROM" in _*) echo "coord-order-send: invalid sender identity: $FROM (names starting with _ are reserved for the engine)" >&2; exit 2 ;; esac # Frontmatter is line-oriented: a CR/LF inside a field would inject extra # frontmatter lines or a premature '---' terminator. sanitize_field() { printf '%s' "$1" | tr '\r\n' ' ' | tr -d '\000-\037'; } FROM="$(sanitize_field "$FROM")" SUBJECT="$(sanitize_field "$SUBJECT")" # --- Validate target ------------------------------------------------------- [ -n "$TO" ] || { echo "coord-order-send: missing --to " >&2; exit 2; } case "$TO" in */*|.|..|_*) echo "coord-order-send: invalid target repo name: $TO" >&2; exit 2 ;; esac # Retired address, same rule and same reason as coord-send.sh: a polyrepo # DIRECTORY is not a git repo, so no session can ever hold that identity and # read what lands there. Reject at the sender, never redirect. case "$TO" in ktg-plugin-marketplace) echo "coord-order-send: ktg-plugin-marketplace is a retired coord address (it is a polyrepo directory, not a git repo - no session can ever hold that identity); send to --to catalog instead" >&2 exit 2 ;; esac [ -n "$SUBJECT" ] || { echo "coord-order-send: missing --subject" >&2; exit 2; } # --- Body ------------------------------------------------------------------ # --prompt-file is first-class because that is the shape dispatch already has: # the skill writes the order to a file, and making the caller cat it would put # the body through one more shell than it needs to pass. if [ -n "$PROMPT_FILE" ]; then [ "$HAVE_MESSAGE" -eq 1 ] && { echo "coord-order-send: use either --message or --prompt-file, not both" >&2; exit 2; } [ -f "$PROMPT_FILE" ] || { echo "coord-order-send: no prompt file at $PROMPT_FILE" >&2; exit 2; } # test -s, not test -e: an empty order is a session started and told nothing, # which from the far end is indistinguishable from one waiting for a Go. [ -s "$PROMPT_FILE" ] || { echo "coord-order-send: the prompt file is empty: $PROMPT_FILE (the order would tell the session nothing)" >&2; exit 2; } BODY="$(cat "$PROMPT_FILE")" elif [ "$HAVE_MESSAGE" -eq 1 ]; then BODY="$MESSAGE" else BODY="$(cat)" fi if [ -z "$BODY" ]; then echo "coord-order-send: empty order body (pass --message, --prompt-file, or pipe the prompt on stdin)" >&2 exit 2 fi # --- Write ----------------------------------------------------------------- # The order id round-trips through argv, through the injection's claim hints and # into the startup command board.sh --dispatch emits, so it is shell-clean BY # CONSTRUCTION: the sender name is sanitized into the id, never carried raw. DEST_DIR="$COORD/$TO/orders" mkdir -p "$DEST_DIR" 2>/dev/null || { echo "coord-order-send: cannot create $DEST_DIR" >&2; exit 2; } TS="$(date -u +%Y%m%dT%H%M%SZ)" SAFE_FROM="$(printf '%s' "$FROM" | tr -c 'A-Za-z0-9._-' '-')" ORDER_ID="${TS}-$$${RANDOM}-from-${SAFE_FROM}" DEST="$DEST_DIR/$ORDER_ID.md" DATE_ISO="$(date -u +%Y-%m-%dT%H:%M:%SZ)" # Temp file inside the destination dir (dot-prefixed so the *.md glob never # sees it): the final mv is a same-filesystem rename, so a reader - or a # concurrent claimer - never observes a half-written order. TMP="$(mktemp "$DEST_DIR/.coord-order.XXXXXX" 2>/dev/null)" [ -n "$TMP" ] || { echo "coord-order-send: cannot create temp file in $DEST_DIR" >&2; exit 2; } { echo "---" echo "from: $FROM" echo "to: $TO" echo "order-id: $ORDER_ID" echo "subject: $SUBJECT" echo "date: $DATE_ISO" echo "---" printf '%s\n' "$BODY" } > "$TMP" if ! mv "$TMP" "$DEST" 2>/dev/null; then /bin/rm -f "$TMP" 2>/dev/null; echo "coord-order-send: write failed" >&2; exit 2 fi echo "coord-order-send: order delivered to $TO ($ORDER_ID.md)" echo "order-id=$ORDER_ID" exit 0