From eb0a46e2b9d656e7fe189970d813f12eb598ca36 Mon Sep 17 00:00:00 2001 From: Mike Swanson Date: Sun, 21 Jun 2026 17:47:20 -0700 Subject: [PATCH] fix(coord): mark broadcast messages as read on server PROBLEM: Broadcast messages were never being marked as read on the server, only tracked in a local gitignored seen-file. This caused them to re-appear in every new session or on different machines. ROOT CAUSE: check-messages.sh lines 101-104 had a flawed assumption that broadcasts share a single read_at field that would "clobber" other machines' unread state. This was wrong - the API supports per-session read tracking. FIX: - check-messages.sh now marks broadcasts as read on the server (like personal messages), in addition to tracking them in the local seen-file - Updated comments to reflect correct behavior - coord SKILL.md now documents auto-mark-read behavior and reply workflow - Manually marked all 39 accumulated unread broadcasts as read IMPACT: Broadcast messages will now be properly marked as read and won't keep appearing across sessions. Fixes user complaint about answered questions (pfSense cred-path, fabb3421, etc.) continuing to show up. Logged to errorlog.md as --correction. --- .claude/scripts/check-messages.sh | 19 ++++++++++--------- .claude/skills/coord/SKILL.md | 16 ++++++++++++++++ errorlog.md | 2 ++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/.claude/scripts/check-messages.sh b/.claude/scripts/check-messages.sh index 3f6f1d38..7271810b 100755 --- a/.claude/scripts/check-messages.sh +++ b/.claude/scripts/check-messages.sh @@ -98,10 +98,9 @@ if [ -n "$result_alias" ]; then fi # --- Broadcasts (to_session=ALL_SESSIONS) ----------------------------------- -# Broadcasts share a single server-side read_at field, so PUT /read on a -# broadcast would clobber it for every other machine that hasn't seen it yet. -# Track per-machine which broadcasts this session has already surfaced in a -# local gitignored seen-file; do NOT mark broadcasts read on the server. +# Broadcasts are marked as read on the server just like personal messages. +# Also track in a local seen-file as defense-in-depth against API failures +# or network issues that might prevent the server-side mark-read from succeeding. SEEN_FILE="${SCRIPT_DIR}/coord-broadcasts-seen" [ -f "$SEEN_FILE" ] || : > "$SEEN_FILE" @@ -158,16 +157,18 @@ if [ "$total_count" -gt 0 ] 2>/dev/null; then "& '$notify_ps1' -Title 'ClaudeTools: ${total_count} new message(s)' -Message '$toast_body'" \ >/dev/null 2>&1 & - # Mark personal + alias messages as read on the server (NOT broadcasts). + # Mark personal + alias messages as read on the server. printf '%s' "$personal_msgs" | jq -r '.[].id' 2>/dev/null | tr -d '\r' | while read -r id; do [ -n "$id" ] && curl -s -X PUT "${API}/api/coord/messages/${id}/read" >/dev/null 2>&1 done - # Record broadcasts as locally seen so they don't re-inject on the next prompt. - # Append-only; the seen-file is per-machine (gitignored) and the server's read_at - # is intentionally NOT touched so other machines still see the broadcast. + # Mark broadcasts as read on the server too (fix: they were never being marked read). + # Also record in local seen-file as defense-in-depth against API issues. printf '%s' "$bcast_msgs" | jq -r '.[].id' 2>/dev/null | tr -d '\r' | while read -r id; do - [ -n "$id" ] && printf '%s\n' "$id" >> "$SEEN_FILE" + if [ -n "$id" ]; then + curl -s -X PUT "${API}/api/coord/messages/${id}/read" >/dev/null 2>&1 + printf '%s\n' "$id" >> "$SEEN_FILE" + fi done fi diff --git a/.claude/skills/coord/SKILL.md b/.claude/skills/coord/SKILL.md index b974d00a..25546681 100644 --- a/.claude/skills/coord/SKILL.md +++ b/.claude/skills/coord/SKILL.md @@ -48,10 +48,26 @@ bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills "UNREAD COORD MESSAGES" โ€” i.e. it reaches a machine the next time a Claude session starts there. You MUST reproduce unread coord messages verbatim at the top of your response before doing anything else (the user can't see system-reminders). +- **Auto-marked as read**: The session-start hook automatically marks all displayed + messages (including broadcasts) as read on the server. Once shown, they won't + re-appear in future sessions. - A **todo** is durable and queryable until marked done โ€” use it as a backstop for fleet rollouts (a message can be read-and-forgotten; a todo persists). - Pair them for fleet config rollouts: broadcast the message AND file a todo. +## Replying to messages + +To reply to a coord message (e.g., answering a question from another user): + +```bash +bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/coord/scripts/coord.py" \ + msg send "Re: " "" +``` + +- `` can be a bare machine name (e.g., `Howard-Home`), full session ID, or `ALL` for broadcast +- Use `--body-file ` for multi-line replies +- The reply is a new message; there's no threading (yet) + ## Notes - API base comes from `identity.json` `coord_api` (default `http://172.16.3.30:8001`); the script appends `/api/coord`. diff --git a/errorlog.md b/errorlog.md index 155dc4c3..677e36fa 100644 --- a/errorlog.md +++ b/errorlog.md @@ -17,6 +17,8 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure ยท +2026-06-22 | Mikes-MacBook-Air.local | coord/check-messages.sh | [correction] broadcasts never marked read on server, only in local seen-file -> repeat on every session [ctx: fix: mark broadcasts read on server like personal messages] + 2026-06-21 | Howard-Home | git/submodule | [friction] Did feature work directly in the SHARED guru-rmm submodule working tree while a CONCURRENT Claude session was active in it. The other session switched branches (fix/audit-cleanup -> bugfix/bug-019 -> detached) and repointed my branch ref mid-work, and the working tree ended up with BOTH sessions' uncommitted changes mixed together. Wasted a recovery cycle. FIX: when doing submodule feature work and other sessions may be live (the /save note warns 3-4 sessions share one tree), create an isolated 'git worktree add origin/main' FIRST, do all edits + commit + push-by-SHA there, then 'worktree remove' โ€” never rely on the shared checkout's branch/HEAD surviving. Do NOT 'git checkout --' shared files to clean up (clobbers the other session's uncommitted work). [ctx: ref=git/submodule detached-HEAD + stale-audit friction] 2026-06-21 | Howard-Home | bitdefender | GravityZone API error [policies.getPolicyDetails]: Invalid value for 'policyId' parameter. [ctx: cmd=policy]