← back to Ticket System

nightly-cleanup.sh

72 lines

#!/usr/bin/env bash
# Nightly 4am board hygiene (Steve 2026-07-28) — the scheduled companion to the
# on-demand self-completion we built this session. Two conservative, reversible
# passes; neither ever EXECUTES a gated action or falsely claims work shipped:
#
#   1. CNCP approvals auto-complete sweep — flips approvals to done when a ticket
#      they reference is done, the memo is marked complete, or an approve has
#      settled (> APPROVAL_AUTODONE_DAYS). POST /api/approvals/sweep.
#   2. Ticket auto-done — closes doing/blocked tickets whose LATEST line declares
#      the whole ticket complete (idle ≥6h, no live worker, not neg/gated).
#   3. CNCP panel retire — hard-deletes items older than CNCP_RETIRE_DAYS (default 7)
#      across the accumulating tabs (parking-lot, wins, lessons, session-recaps) and
#      drops stopped/archived loops. Snapshots every file first; restarts CNCP so
#      caches reload. project TODO.md files are NEVER touched.
#
# Installed as launchd com.steve.nightly-approvals-tickets (04:00 daily). Skip a
# pass with CNCP_APPROVAL_AUTODONE=0 / TICKET_AUTODONE=0 / CNCP_RETIRE=0.
set -u
ROOT="$HOME/Projects/ticket-system"
LOG="$ROOT/nightly-cleanup.log"
NODE="$(command -v node || echo /opt/homebrew/bin/node)"
CNCP="http://localhost:3333"
TS="$(date '+%F %T')"

{
  echo "=== nightly-cleanup $TS ==="

  # 1) CNCP approvals self-completion sweep
  if [ "${CNCP_APPROVAL_AUTODONE:-1}" != "0" ]; then
    echo "[1/2] approvals sweep → POST $CNCP/api/approvals/sweep"
    curl -s -m 30 -X POST "$CNCP/api/approvals/sweep" -H 'Content-Type: application/json' -d '{}' \
      || echo "  (approvals sweep unreachable — CNCP down?)"
    echo
  else
    echo "[1/2] approvals sweep SKIPPED (CNCP_APPROVAL_AUTODONE=0)"
  fi

  # 2) Ticket auto-done (conservative; reversible)
  if [ "${TICKET_AUTODONE:-1}" != "0" ]; then
    echo "[2/3] ticket auto-done → node ticket-autodone.js --apply"
    ( cd "$ROOT" && "$NODE" ticket-autodone.js --apply )
  else
    echo "[2/3] ticket auto-done SKIPPED (TICKET_AUTODONE=0)"
  fi
  echo

  # 3) CNCP panel retire (>N days across accumulating tabs; snapshots first)
  if [ "${CNCP_RETIRE:-1}" != "0" ]; then
    CNCP_DIR="$HOME/cncp-starter"
    if [ -f "$CNCP_DIR/cncp-retire.py" ]; then
      echo "[3/3] cncp panel retire → python3 cncp-retire.py --days ${CNCP_RETIRE_DAYS:-7}"
      OUT="$( cd "$CNCP_DIR" && python3 cncp-retire.py --days "${CNCP_RETIRE_DAYS:-7}" )"
      echo "$OUT"
      # restart CNCP only if something was actually retired (so caches reload)
      if ! echo "$OUT" | grep -q 'retired 0$'; then
        echo "  retire changed files — restarting CNCP (com.user.cncp) to reload caches"
        launchctl kickstart -k "gui/$(id -u)/com.user.cncp" >/dev/null 2>&1 || echo "  (CNCP restart failed — check com.user.cncp)"
      fi
    else
      echo "[3/3] cncp panel retire SKIPPED (cncp-retire.py not found at $CNCP_DIR)"
    fi
  else
    echo "[3/3] cncp panel retire SKIPPED (CNCP_RETIRE=0)"
  fi

  echo "=== done $TS ==="
} >> "$LOG" 2>&1

# keep the log bounded
tail -1200 "$LOG" > "$LOG.tmp" 2>/dev/null && mv "$LOG.tmp" "$LOG"
exit 0