feat(engine): let a message say it needs no answer, and count debt without losing sight of the rest
Rule 7 (0.5.0) shipped an obligation on a format with four fields, none of which could tell a question from a notice. Two consequences fell out of that gap: the injection had to name both terminal states and prefer neither, and coord-count.sh had to treat every unarchived file as a reply owed. The fifth field closes both. coord-send.sh --fyi writes reply-expected: no; omitting it writes yes. Absent means expected, because every message already on disk lacks the field - so a forgotten flag over-counts debt, which is visible, rather than creating debt nobody sees. A reply is not a special case. A broadcast is always no: --reply-to resolves inside the recipient's own mailbox and a broadcast never lands there, so there is no reply path to promise. coord-count.sh now prints TWO integers per mailbox, not one. Replacing pending with debt was the obvious reading of "count debt rather than unarchived messages" and it is wrong here: board.sh counts the same inbox files itself, so a debt-only count would put two different numbers under one name with nothing to reconcile them, and a mailbox holding only notices would read as empty while its messages keep being re-injected. The field is frontmatter and only frontmatter - a body line claiming "reply-expected: no" at column 0 cannot silence a real debt, and a file without valid frontmatter counts as owing a reply. Section 20's wording changed because its stated reason expired, but its second half matters more now, not less: the marking is a DECLARATION, not an instruction. Without that clause one word in an untrusted message becomes a lever that mints obligations in another repo. coord-selftest 136 -> 151. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016iJoZVmU2guTEZcMghk88z
This commit is contained in:
parent
261a75bd7b
commit
c0ccb1d611
8 changed files with 314 additions and 29 deletions
|
|
@ -8,6 +8,14 @@
|
|||
# coord-send.sh --reply-to <file> [--subject "Re: ..."] [--from <repo>] [--message "<text>"]
|
||||
# coord-send.sh --retract <file> [--from <repo>]
|
||||
# Body comes from --message, or from stdin (heredoc) when --message is omitted.
|
||||
# --fyi marks the message as expecting no reply (frontmatter reply-expected: no).
|
||||
# Absent, a reply IS expected: every message written before the field existed
|
||||
# lacks it, so absence has to keep meaning what it always meant, and a
|
||||
# forgotten flag then over-counts debt instead of hiding it. The receiver is
|
||||
# still free to close either kind with coord-done - the field declares what the
|
||||
# sender expects, it does not oblige anyone. A broadcast is always
|
||||
# reply-expected: no, because --reply-to resolves inside the recipient own
|
||||
# mailbox and a broadcast never lands there: there is no reply path to promise.
|
||||
# --reply-to <basename> replies to a message in THIS repo's inbox/archive: it
|
||||
# routes to the original sender and marks the original handled (coord-done).
|
||||
# --retract <basename> retires one of YOUR OWN broadcasts: it is archived out of
|
||||
|
|
@ -22,7 +30,7 @@ export LC_ALL=C
|
|||
COORD="${CLAUDE_COORD_DIR:-$HOME/.claude/coord}"
|
||||
|
||||
TO=""; BROADCAST=0; SUBJECT=""; FROM=""; MESSAGE=""; HAVE_MESSAGE=0; REPLYTO=""; REPLY_ORIG=""
|
||||
RETRACT=""
|
||||
RETRACT=""; FYI=0
|
||||
|
||||
# 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.
|
||||
|
|
@ -34,6 +42,7 @@ while [ $# -gt 0 ]; do
|
|||
case "$1" in
|
||||
--to) require_value --to $#; TO="$2"; shift 2 ;;
|
||||
--broadcast) BROADCAST=1; shift ;;
|
||||
--fyi) FYI=1; shift ;;
|
||||
--reply-to) require_value --reply-to $#; REPLYTO="$2"; shift 2 ;;
|
||||
--retract) require_value --retract $#; RETRACT="$2"; shift 2 ;;
|
||||
--subject) require_value --subject $#; SUBJECT="$2"; shift 2 ;;
|
||||
|
|
@ -175,6 +184,10 @@ DATE_ISO="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|||
# Temp file lives INSIDE the destination dir (dot-prefixed so the inbox
|
||||
# *.md glob never sees it): the final mv is then a same-filesystem rename,
|
||||
# so readers never observe a half-written message.
|
||||
REPLY_EXPECTED=yes
|
||||
[ "$FYI" -eq 1 ] && REPLY_EXPECTED=no
|
||||
[ "$BROADCAST" -eq 1 ] && REPLY_EXPECTED=no
|
||||
|
||||
TMP="$(mktemp "$DEST_DIR/.coord-send.XXXXXX" 2>/dev/null)"
|
||||
[ -n "$TMP" ] || { echo "coord-send: cannot create temp file in $DEST_DIR" >&2; exit 2; }
|
||||
{
|
||||
|
|
@ -183,6 +196,12 @@ TMP="$(mktemp "$DEST_DIR/.coord-send.XXXXXX" 2>/dev/null)"
|
|||
echo "to: $TARGET_LABEL"
|
||||
echo "subject: $SUBJECT"
|
||||
echo "date: $DATE_ISO"
|
||||
# Fifth field, appended after the historic four so an older reader that stops
|
||||
# at the ones it knows is unaffected. A broadcast is pinned to "no" whether or
|
||||
# not --fyi was passed: that is not a defaulted value but the absence of a
|
||||
# reply path (see the header), and a message that claimed otherwise would be
|
||||
# asking for something the engine cannot deliver.
|
||||
echo "reply-expected: $REPLY_EXPECTED"
|
||||
echo "---"
|
||||
printf '%s\n' "$BODY"
|
||||
} > "$TMP"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue