fix(dispatch): background report signs with --from; coord-send refuses a worktree-derived sender

In a linked worktree basename(git toplevel) is the worktree's name, so the
background child's report was signed `from: dispatch-bg-form` (measured by
the PM on 741ada8) - a mailbox no session holds.

- skills/dispatch: the starter reports by full path with
  `coord-send.sh --from <repo> --to <sender>`; board-selftest §19b gains the
  check (red on 741ada8) plus a known-negative control that runs the same
  predicate against the old "via coord-send" wording.
- coord-send.sh: a DERIVED sender inside a linked worktree exits 2, naming
  --from and the repo the worktree belongs to. Chose refusal over a warning
  because stderr is where a session least looks and the mail would already be
  delivered, and over a silent redirect for the reason the retired
  ktg-plugin-marketplace address is rejected rather than redirected.
  Linked-ness is git's own test (git-dir != git-common-dir), not ".git is a
  file", because a submodule has that too. coord-selftest section 39: 9
  checks, 3 red before the fix; controls for submodule, main checkout,
  explicit --from and a worktree named like its repo.
- Six other scripts derive identity the same way; named as a bounded gap in
  CLAUDE.md, not changed (not ordered).

Suites under /bin/bash 3.2: coord 266, board 497, route 73, orders 116,
guard 54 = 1006 (was 995). npm test 12/12.

Order 20260923T082933Z-466405184-from-.claude.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-23 11:20:39 +02:00
commit 7b09114cd6
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
7 changed files with 136 additions and 8 deletions

View file

@ -1966,6 +1966,22 @@ printf '%s\n' "$dsk_bg" | grep -q 'coord-order-done\.sh --repo [^ ]* '
check "background starter closes with --repo (a worktree's name is not the repo's)" $?
if printf '%s\n' "$dsk_bg" | grep -qE '^[[:space:]]*coord-order-(claim|done) '; then rc=1; else rc=0; fi
check "background section has no bare coord-order-claim/-done command line" "$rc"
# ...and the REPORT is signed with --from, by full path. coord-send derives its
# sender from the same toplevel basename, so in the worktree the report was
# signed `from: dispatch-bg-form` (measured by the PM on 741ada8, order
# 20260923T082933Z-466405184). The predicate reads the starter BLOCK only and
# is a function, so the known-negative control runs the same code: every
# mention of coord-send in the starter must be the full-path call with --from.
dsk_starter="$(printf '%s\n' "$dsk_bg" | awk '/^```text/{f=1; next} f && /^```/{exit} f')"
dsk_unsigned_report() {
n_all="$(printf '%s\n' "$1" | grep -o 'coord-send' | wc -l | tr -d ' ')"
n_ok="$(printf '%s\n' "$1" | grep -oE 'bash [^ ]*/scripts/coord-send\.sh --from [^ ]+ ' | wc -l | tr -d ' ')"
[ "$n_all" -eq 0 ] || [ "$n_ok" -ne "$n_all" ]
}
[ -n "$dsk_starter" ] && ! dsk_unsigned_report "$dsk_starter"
check "background starter reports by FULL path to coord-send.sh with --from (a worktree's name is not the repo's)" $?
dsk_unsigned_report 'ORDRE x ... (4) Lukk ... Rapport som coord til .claude via coord-send.'
check "control: the report predicate catches a starter that says 'via coord-send' without --from" $?
# The description is what makes a skill trigger; a form it does not mention is
# a form nobody reaches through the front door.
dsk_fm="$(awk 'NR==1 && /^---$/{f=1; next} f && /^---$/{exit} f' "$DSKILL")"

View file

@ -1388,6 +1388,46 @@ check "launchd control: a program path INSIDE the plugin cache is caught" $?
/bin/rm -f "$BADPL" "$CACHEPL" 2>/dev/null
# 39. A LINKED WORKTREE's directory name is not a repo name, so a sender derived
# from it is invented, and section 22's rule applies: refuse, never guess.
# Order 20260923T082933Z-466405184 (.claude): a background child runs in
# <repo>/.claude/worktrees/<name>, and basename(git toplevel) there is <name>.
# Measured by the PM on 741ada8: the child's report arrived signed
# `from: dispatch-bg-form`, a mailbox no session holds, so any reply to it
# would have gone nowhere. Linked-ness is git's own test (git-dir differs from
# git-common-dir), not a path pattern and not ".git is a file": a SUBMODULE also
# has .git as a file, and its basename IS its real name - the control below.
WT="$(cd "$(mktemp -d)" && pwd -P)"
git -C "$WT" init -q wtmain >/dev/null 2>&1
git -C "$WT/wtmain" -c user.name=t -c user.email=t@t -c commit.gpgSign=false commit -q --allow-empty -m init >/dev/null 2>&1
git -C "$WT/wtmain" worktree add -q "$WT/wt-child" >/dev/null 2>&1
[ -f "$WT/wt-child/.git" ]; check "worktree fixture: wt-child really is a linked worktree (ground truth)" $?
wto="$( (cd "$WT/wt-child" && "$SEND" --to wttarget --subject s --message "WT-BODY" </dev/null) 2>&1 )"; rc=$?
[ "$rc" -eq 2 ]; check "worktree: a sender derived in a linked worktree is refused (exit 2)" $?
[ -z "$(ls "$CLAUDE_COORD_DIR/wttarget/inbox" 2>/dev/null)" ]; check "worktree: nothing is delivered under the worktree's name" $?
printf '%s' "$wto" | grep -q -- '--from wtmain'; check "worktree: the refusal names --from and the repo the worktree belongs to" $?
wtok="$( (cd "$WT/wt-child" && "$SEND" --to wttarget --from wtmain --subject s --message "WT-OK" </dev/null) 2>&1 )"; rc=$?
[ "$rc" -eq 0 ] && grep -q '^from: wtmain$' "$CLAUDE_COORD_DIR"/wttarget/inbox/*.md 2>/dev/null
check "worktree control: an explicit --from still sends from inside the worktree" $?
wtm="$( (cd "$WT/wtmain" && "$SEND" --to wtmaintarget --subject s --message "WT-MAIN" </dev/null) 2>&1 )"; rc=$?
[ "$rc" -eq 0 ] && grep -q '^from: wtmain$' "$CLAUDE_COORD_DIR"/wtmaintarget/inbox/*.md 2>/dev/null
check "worktree control: the MAIN checkout of the same repo still derives its sender" $?
# A worktree whose directory happens to carry the repo's own name derives the
# right identity, so refusing it would be a false positive.
mkdir -p "$WT/elsewhere"
git -C "$WT/wtmain" worktree add -q "$WT/elsewhere/wtmain" >/dev/null 2>&1
( cd "$WT/elsewhere/wtmain" && "$SEND" --to wtsame --subject s --message "WT-SAME" </dev/null >/dev/null 2>&1 ); rc=$?
[ "$rc" -eq 0 ]; check "worktree control: a worktree named exactly like its repo is not refused" $?
# Submodule control: .git is a file there too, and git-dir == git-common-dir.
git -C "$WT" init -q wtsub >/dev/null 2>&1
git -C "$WT/wtsub" -c user.name=t -c user.email=t@t -c commit.gpgSign=false commit -q --allow-empty -m init >/dev/null 2>&1
git -C "$WT/wtmain" -c protocol.file.allow=always submodule add -q "$WT/wtsub" wtsub >/dev/null 2>&1
[ -f "$WT/wtmain/wtsub/.git" ]; check "submodule fixture: .git really is a file in the submodule (ground truth)" $?
( cd "$WT/wtmain/wtsub" && "$SEND" --to wtsubtarget --subject s --message "WT-SUB" </dev/null >/dev/null 2>&1 ); rc=$?
[ "$rc" -eq 0 ] && grep -q '^from: wtsub$' "$CLAUDE_COORD_DIR"/wtsubtarget/inbox/*.md 2>/dev/null
check "worktree control: a SUBMODULE (.git is a file) still derives its own name" $?
/bin/rm -rf "$WT" 2>/dev/null
echo "----"
echo "PASS=$PASS FAIL=$FAIL"
[ "$FAIL" -eq 0 ]

View file

@ -22,6 +22,8 @@
# the broadcast queue so no future repo receives it. This is un-send, not
# recall - repos that already received it are unaffected.
# --from overrides the sender/self identity (default: basename of git toplevel/cwd).
# Inside a linked git worktree the default is refused (exit 2): the toplevel's
# basename there is the worktree's name, not the repo's - pass --from.
#
# Exit: 0 delivered, 1 delivered but --reply-to's original could NOT be closed
# (the reply is sent; do not re-send it, close the original by hand),
@ -62,13 +64,33 @@ done
# a real message was delivered under exactly that name. An invented identity is
# worse than none: it signs mail as a repo that does not exist and, on the read
# side, opens a mailbox that may belong to someone else. Refuse and say how.
DERIVED_FROM=0
if [ -z "$FROM" ]; then
FROM="$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null)"
DERIVED_FROM=1
fi
if [ -z "$FROM" ]; then
echo "coord-send: cannot resolve sender identity (not inside a git repo); pass --from <repo> to choose one explicitly" >&2
exit 2
fi
# The same rule for a LINKED WORKTREE: its toplevel basename is the worktree's
# directory name (<repo>/.claude/worktrees/<name> for a background session), so
# the derived sender is invented. git's own test decides linked-ness - git-dir
# differs from git-common-dir - because ".git is a file" is also true of a
# submodule, whose basename IS its name. A worktree directory that happens to
# carry the repo's own name derives correctly and passes. Fails OPEN when git
# cannot answer: the refusal needs a positive measurement.
if [ "$DERIVED_FROM" -eq 1 ]; then
WT_GD="$(git rev-parse --path-format=absolute --git-dir 2>/dev/null)"
WT_CD="$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null)"
if [ -n "$WT_GD" ] && [ -n "$WT_CD" ] && [ "$WT_GD" != "$WT_CD" ]; then
WT_REPO="$(basename "$(dirname "$WT_CD")")"
if [ "$FROM" != "$WT_REPO" ]; then
echo "coord-send: refusing to sign as '$FROM': that is a linked git worktree's directory name, not a repo name; pass --from explicitly (this worktree belongs to: --from $WT_REPO)" >&2
exit 2
fi
fi
fi
# A leading _ is reserved for engine internals (_broadcast today; the rule
# reserves the namespace so a later _seen or _config cannot reopen the hole).
case "$FROM" in