Files
blackroad/operator/memory/memory-watchdog.sh
Alexa Amundson 6777292f6e
Some checks failed
Lint & Format / detect (push) Has been cancelled
Lint & Format / js-lint (push) Has been cancelled
Lint & Format / py-lint (push) Has been cancelled
Lint & Format / sh-lint (push) Has been cancelled
Lint & Format / go-lint (push) Has been cancelled
Monorepo Lint / lint-shell (push) Has been cancelled
Monorepo Lint / lint-js (push) Has been cancelled
sync: 2026-03-16 18:00 — 21 files from Alexandria
RoadChain-SHA2048: c316572452cf6246
RoadChain-Identity: alexa@sovereign
RoadChain-Full: c316572452cf6246294a9bf089ca177403d4187333252430e919cc2f55176f80722ae82807051dbc5772fa50a393bfc6ba5f1dc464d9ae0a435082d60da68a8f61c7521bc0fbc0509bf9e043035456b80763949d1e1e30cca278c3b264e78a417b62be8636feeda64580bd6eac8adede7fcb7c095eadc922a1057710484f98d08f347104bf31434f66ced3da243d480c58a4dfa0cc068ddfda54acadde593e3964f2a6ee69283eb62557638056c9e69be5cf70d8d9698b4c07f76c3f2d91e713cff5568eef41c51991648e8e28e2e0523cf04269cb5be393cae9250b285c968b97e330f5ddf91836d3f67555a147108c594274f1878fe4dc8f47ec4b2291b82f
2026-03-16 18:00:02 -05:00

309 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# BlackRoad Collaboration Watchdog — health monitoring, cleanup, digests
# Usage: memory-watchdog.sh <command> [args]
set -e
COLLAB_DB="$HOME/.blackroad/collaboration.db"
HANDOFF_DIR="$HOME/.blackroad/memory/handoffs"
JOURNAL="$HOME/.blackroad/memory/journals/master-journal.jsonl"
SLACK_API="https://blackroad-slack.amundsonalexa.workers.dev"
PINK='\033[38;5;205m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
RED='\033[0;31m'
BOLD='\033[1m'
NC='\033[0m'
sql() { sqlite3 "$COLLAB_DB" "$@" 2>/dev/null; }
post_to_slack() {
local text="$1"
curl -s --max-time 3 --connect-timeout 2 \
-X POST "$SLACK_API/post" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg t "$text" '{text:$t}')" >/dev/null 2>&1 || true
}
check_db() {
if [[ ! -f "$COLLAB_DB" ]]; then
echo -e "${RED}Collaboration DB not found. Run: memory-collaboration.sh init${NC}"
exit 1
fi
}
# ── SWEEP — Clean stale data ──
cmd_sweep() {
check_db
echo -e "${PINK}[WATCHDOG]${NC} ${BOLD}Sweeping stale data...${NC}"
# Mark stale sessions as abandoned (>2h, still active)
local stale
stale=$(sql "SELECT count(*) FROM sessions WHERE status='active' AND last_seen < datetime('now', '-2 hours');")
if [[ "$stale" -gt 0 ]]; then
sql "UPDATE sessions SET status='abandoned' WHERE status='active' AND last_seen < datetime('now', '-2 hours');"
echo -e " ${YELLOW}Abandoned $stale stale session(s)${NC}"
else
echo -e " ${GREEN}No stale sessions${NC}"
fi
# Archive old messages (>7 days)
local old_msgs
old_msgs=$(sql "SELECT count(*) FROM messages WHERE created_at < datetime('now', '-7 days');")
if [[ "$old_msgs" -gt 0 ]]; then
sql "DELETE FROM messages WHERE created_at < datetime('now', '-7 days');"
echo -e " ${YELLOW}Archived $old_msgs old message(s)${NC}"
else
echo -e " ${GREEN}No old messages to archive${NC}"
fi
# Clean picked-up handoff files (>3 days)
local cleaned=0
for f in "$HANDOFF_DIR"/handoff-*.json; do
[[ ! -f "$f" ]] && continue
local pbu
pbu=$(jq -r '.picked_up_by // ""' "$f" 2>/dev/null)
if [[ -n "$pbu" && "$pbu" != "" && "$pbu" != "null" ]]; then
local created
created=$(jq -r '.created_at' "$f" 2>/dev/null)
local file_age
file_age=$(( $(date +%s) - $(date -jf "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null || echo $(date +%s)) ))
if [[ "$file_age" -gt 259200 ]]; then # 3 days
rm -f "$f"
((cleaned++))
fi
fi
done
[[ "$cleaned" -gt 0 ]] && echo -e " ${YELLOW}Cleaned $cleaned old handoff file(s)${NC}"
# Report orphaned handoffs
local orphaned
orphaned=$(sql "SELECT count(*) FROM handoffs WHERE picked_up_by='' AND created_at < datetime('now', '-24 hours');")
if [[ "$orphaned" -gt 0 ]]; then
echo -e " ${RED}$orphaned orphaned handoff(s) — never picked up (>24h)${NC}"
fi
# DB size
local db_size
db_size=$(du -h "$COLLAB_DB" | cut -f1)
echo -e " ${BLUE}DB size: $db_size${NC}"
# Total counts
local sessions msgs handoffs
sessions=$(sql "SELECT count(*) FROM sessions;")
msgs=$(sql "SELECT count(*) FROM messages;")
handoffs=$(sql "SELECT count(*) FROM handoffs;")
echo -e " ${BLUE}Totals: $sessions sessions, $msgs messages, $handoffs handoffs${NC}"
}
# ── HEARTBEAT — Quick health pulse ──
cmd_heartbeat() {
local slack_flag="${1:-}"
local issues=()
# Check DB
if [[ -f "$COLLAB_DB" ]]; then
local integrity
integrity=$(sql "PRAGMA integrity_check;" 2>/dev/null)
if [[ "$integrity" != "ok" ]]; then
issues+=("DB integrity: $integrity")
fi
else
issues+=("Collaboration DB missing")
fi
# Check Slack
local slack_status
slack_status=$(curl -s --max-time 2 --connect-timeout 1 "$SLACK_API/health" 2>/dev/null | jq -r '.status' 2>/dev/null || echo "unreachable")
if [[ "$slack_status" != "alive" ]]; then
issues+=("Slack Worker: $slack_status")
fi
# Check journal
if [[ -f "$JOURNAL" ]]; then
local last_entry
last_entry=$(tail -1 "$JOURNAL" 2>/dev/null)
echo "$last_entry" | jq . >/dev/null 2>&1 || issues+=("Journal: last entry not valid JSON")
else
issues+=("Journal file missing")
fi
# Check disk space
local disk_used
disk_used=$(du -sm "$HOME/.blackroad/" 2>/dev/null | cut -f1)
if [[ "${disk_used:-0}" -gt 500 ]]; then
issues+=("Disk: ~/.blackroad/ is ${disk_used}MB")
fi
# Active sessions
local active=0
if [[ -f "$COLLAB_DB" ]]; then
active=$(sql "SELECT count(*) FROM sessions WHERE status='active';" 2>/dev/null || echo 0)
fi
if [[ ${#issues[@]} -eq 0 ]]; then
echo -e "${GREEN}✅ All systems nominal${NC}$active active session(s), Slack connected, DB healthy"
if [[ "$slack_flag" == "--slack" ]]; then
post_to_slack "💚 Watchdog heartbeat: all systems nominal — $active active session(s)"
fi
else
echo -e "${RED}⚠️ Issues detected:${NC}"
for issue in "${issues[@]}"; do
echo -e " ${RED}$issue${NC}"
done
if [[ "$slack_flag" == "--slack" ]]; then
local msg="⚠️ Watchdog heartbeat: ${#issues[@]} issue(s)"
for issue in "${issues[@]}"; do
msg="$msg\n • $issue"
done
post_to_slack "$msg"
fi
fi
}
# ── DIGEST — Collaboration digest ──
cmd_digest() {
check_db
local slack_flag="${1:-}"
local sessions_24h msgs_24h handoffs_24h handoffs_picked
sessions_24h=$(sql "SELECT count(*) FROM sessions WHERE started_at > datetime('now', '-24 hours');")
msgs_24h=$(sql "SELECT count(*) FROM messages WHERE created_at > datetime('now', '-24 hours');")
handoffs_24h=$(sql "SELECT count(*) FROM handoffs WHERE created_at > datetime('now', '-24 hours');")
handoffs_picked=$(sql "SELECT count(*) FROM handoffs WHERE picked_up_at > datetime('now', '-24 hours') AND picked_up_by != '';")
echo -e "${PINK}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${PINK}${NC} ${BOLD}Collaboration Digest (24h)${NC} ${PINK}${NC}"
echo -e "${PINK}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e " Sessions: ${GREEN}${sessions_24h}${NC}"
echo -e " Messages: ${BLUE}${msgs_24h}${NC}"
echo -e " Handoffs: ${CYAN}${handoffs_24h} created${NC}, ${GREEN}${handoffs_picked} picked up${NC}"
echo ""
# Recent sessions
echo -e " ${BOLD}Sessions:${NC}"
sql "SELECT session_id, status, focus FROM sessions WHERE started_at > datetime('now', '-24 hours') ORDER BY started_at DESC LIMIT 10;" | while IFS='|' read -r sid status focus; do
local icon="●"
case "$status" in
active) icon="${GREEN}${NC}" ;;
completed) icon="${BLUE}${NC}" ;;
abandoned) icon="${YELLOW}${NC}" ;;
esac
echo -e " $icon ${CYAN}${sid}${NC}${focus:+ — $focus}"
done
echo ""
# Top message types
echo -e " ${BOLD}Message Types:${NC}"
sql "SELECT type, count(*) as cnt FROM messages WHERE created_at > datetime('now', '-24 hours') GROUP BY type ORDER BY cnt DESC;" | while IFS='|' read -r type cnt; do
echo -e " ${type}: ${GREEN}${cnt}${NC}"
done
if [[ "$slack_flag" == "--slack" ]]; then
local msg="📊 *Collaboration Digest (24h)*\n"
msg="${msg}Sessions: ${sessions_24h} | Messages: ${msgs_24h} | Handoffs: ${handoffs_24h} created, ${handoffs_picked} picked up"
post_to_slack "$msg"
echo ""
echo -e " ${GREEN}Posted to Slack${NC}"
fi
}
# ── AUDIT — Full system audit ──
cmd_audit() {
check_db
echo -e "${PINK}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${PINK}${NC} ${BOLD}Collaboration System Audit${NC} ${PINK}${NC}"
echo -e "${PINK}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
# DB integrity
local integrity
integrity=$(sql "PRAGMA integrity_check;")
echo -e " ${BOLD}DB Integrity:${NC} ${integrity}"
# All sessions
echo ""
echo -e " ${BOLD}All Sessions:${NC}"
sql "SELECT session_id, status, started_at, last_seen, focus FROM sessions ORDER BY started_at DESC;" | while IFS='|' read -r sid status started last focus; do
local icon
case "$status" in
active) icon="${GREEN}${NC}" ;;
completed) icon="${BLUE}${NC}" ;;
abandoned) icon="${YELLOW}${NC}" ;;
*) icon="?" ;;
esac
echo -e " $icon ${CYAN}${sid}${NC} [${status}] ${started:0:19}${focus:+ — $focus}"
done
# Orphaned handoffs
echo ""
local orphaned
orphaned=$(sql "SELECT count(*) FROM handoffs WHERE picked_up_by='' AND created_at < datetime('now', '-24 hours');")
echo -e " ${BOLD}Orphaned Handoffs (>24h, never picked up):${NC} ${orphaned}"
if [[ "$orphaned" -gt 0 ]]; then
sql "SELECT handoff_id, from_session, message FROM handoffs WHERE picked_up_by='' AND created_at < datetime('now', '-24 hours') LIMIT 5;" | while IFS='|' read -r hid from msg; do
echo -e " ${RED}${NC} ${hid} from ${CYAN}${from}${NC}: ${msg:0:60}"
done
fi
# Duplicate session check
echo ""
local dupes
dupes=$(sql "SELECT session_id, count(*) as cnt FROM sessions GROUP BY session_id HAVING cnt > 1;" 2>/dev/null)
if [[ -n "$dupes" ]]; then
echo -e " ${RED}Duplicate sessions found:${NC}"
echo "$dupes" | while IFS='|' read -r sid cnt; do
echo -e " ${RED}$sid: $cnt entries${NC}"
done
else
echo -e " ${GREEN}No duplicate sessions${NC}"
fi
# DB stats
echo ""
local db_size pages
db_size=$(du -h "$COLLAB_DB" | cut -f1)
pages=$(sql "PRAGMA page_count;")
local page_size
page_size=$(sql "PRAGMA page_size;")
echo -e " ${BOLD}DB Stats:${NC} $db_size, $pages pages × ${page_size}B"
echo -e " ${BOLD}Journal mode:${NC} $(sql 'PRAGMA journal_mode;')"
}
# ── HELP ──
cmd_help() {
cat <<EOF
${PINK}╔════════════════════════════════════════════════════════════╗${NC}
${PINK}║${NC} ${BOLD}BlackRoad Collaboration Watchdog${NC} ${PINK}║${NC}
${PINK}╚════════════════════════════════════════════════════════════╝${NC}
${BOLD}Commands:${NC}
${CYAN}heartbeat [--slack]${NC} Quick health check (post to Slack with --slack)
${CYAN}sweep${NC} Clean stale sessions, old messages, orphaned handoffs
${CYAN}digest [--slack]${NC} 24h collaboration digest (post to Slack with --slack)
${CYAN}audit${NC} Full system audit: integrity, sessions, orphans
${BOLD}Cron schedule:${NC}
*/30 heartbeat
3am sweep
9am digest --slack
EOF
}
case "${1:-help}" in
heartbeat|hb) shift 2>/dev/null || true; cmd_heartbeat "$@" ;;
sweep|clean) cmd_sweep ;;
digest|report) shift 2>/dev/null || true; cmd_digest "$@" ;;
audit) cmd_audit ;;
help|--help|-h) cmd_help ;;
*)
echo -e "${RED}Unknown: $1${NC}"
cmd_help
exit 1
;;
esac