sherlock.sh6.0 KBView on GitHub

6 error spans on 0 routes in the last 7 days.

#!/usr/bin/env bash
# Sherlock — invoke the Cedar debugging agent from the command line.
#
# Usage:
#   ./scripts/sherlock.sh "<email> is getting wrong signatures on drafts"
#   ./scripts/sherlock.sh --execution exec_abc123 "why did this fail?"
#   ./scripts/sherlock.sh --user <email> --notify "#cedar-bugs" "no draft sent"
#   ./scripts/sherlock.sh --outstanding        (process all pending_investigation monitors)
#   ./scripts/sherlock.sh --self-improve       (headless gap-detection loop)
#
# Requires: claude CLI (Claude Code), psql (for --outstanding)

set -euo pipefail

REPO_ROOT="$(git rev-parse --show-toplevel)"
SKILL_DIR="$REPO_ROOT/apps/server/.claude/skills/sherlock"

# --- parse flags ---
USER_EMAIL=""
EXECUTION_ID=""
NOTIFY_CHANNEL=""
SELF_IMPROVE=false
OUTSTANDING=false
BUG_REPORT=""

while [[ $# -gt 0 ]]; do
  case "$1" in
    --user)          USER_EMAIL="$2";      shift 2 ;;
    --execution)     EXECUTION_ID="$2";   shift 2 ;;
    --notify)        NOTIFY_CHANNEL="$2"; shift 2 ;;
    --self-improve)  SELF_IMPROVE=true;   shift ;;
    --outstanding)   OUTSTANDING=true;    shift ;;
    *)               BUG_REPORT="${BUG_REPORT:+$BUG_REPORT }$1"; shift ;;
  esac
done

# --- check for claude CLI ---
if ! command -v claude &>/dev/null; then
  echo "Error: 'claude' CLI not found. Install Claude Code to use Sherlock."
  exit 1
fi

SYSTEM_PROMPT="$(cat "$SKILL_DIR/SKILL.md" "$SKILL_DIR/SHERLOCK_SYSTEM_KNOWLEDGE.md" "$SKILL_DIR/report-template.md")"

# ============================================================
# --outstanding: process all pending_investigation monitors
# ============================================================
if [[ "$OUTSTANDING" == "true" ]]; then
  if [[ -z "${DATABASE_URL:-}" ]]; then
    # Try to load from .env
    if [[ -f "$REPO_ROOT/.env" ]]; then
      DATABASE_URL="$(grep '^DATABASE_URL=' "$REPO_ROOT/.env" | cut -d= -f2-)"
    fi
  fi

  if [[ -z "${DATABASE_URL:-}" ]]; then
    echo "Error: DATABASE_URL not set. Set it in your environment or .env file."
    exit 1
  fi

  echo ""
  echo "🔍 Sherlock — processing outstanding monitors..."
  echo ""

  # Fetch all pending_investigation monitors
  MONITORS=$(psql "$DATABASE_URL" -t -A -F'|' -c "
    SELECT
      sm.id,
      sm.user_id,
      u.email,
      sm.trigger_config->>'verify_condition' AS verify_condition,
      ae.run_id AS latest_execution_id
    FROM sherlock_monitors sm
    LEFT JOIN users u ON sm.user_id = u.id
    LEFT JOIN LATERAL (
      SELECT run_id FROM agent_executions
      WHERE user_id = sm.user_id AND status = 'completed'
      ORDER BY created_at DESC LIMIT 1
    ) ae ON true
    WHERE sm.status = 'pending_investigation'
    ORDER BY sm.id;
  " 2>/dev/null || true)

  if [[ -z "$MONITORS" ]]; then
    echo "No pending monitors to investigate."
    exit 0
  fi

  TOTAL=0
  INVESTIGATED=0
  FAILED=0

  while IFS='|' read -r MONITOR_ID USER_ID USER_EMAIL_ROW VERIFY_CONDITION LATEST_EXEC_ID; do
    [[ -z "$MONITOR_ID" ]] && continue
    TOTAL=$((TOTAL + 1))

    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo "Monitor: $MONITOR_ID"
    echo "User:    ${USER_EMAIL_ROW:-$USER_ID}"
    echo "Exec:    ${LATEST_EXEC_ID:-(none found)}"
    echo ""

    PROMPT="Investigating outstanding monitor $MONITOR_ID.
User: ${USER_EMAIL_ROW:-$USER_ID}
${LATEST_EXEC_ID:+Latest execution ID: $LATEST_EXEC_ID}

Condition to verify:
${VERIFY_CONDITION:-(no condition stored)}

This monitor was flagged by the Haiku evaluator as a failure or uncertain. Run a full investigation using the standard Sherlock workflow. After completing:
- If the problem is confirmed: apply the fix (config change directly, code fix via git worktree + PR) and set monitor status to 'failed'
- If the problem is resolved / was a false positive: set monitor status back to 'active'

Update sherlock_monitors SET status = 'active' or 'failed' accordingly via psql when done."

    cd "$REPO_ROOT"
    if claude --print --system-prompt "$SYSTEM_PROMPT" "$PROMPT"; then
      INVESTIGATED=$((INVESTIGATED + 1))
    else
      FAILED=$((FAILED + 1))
      echo "⚠️  Investigation errored for monitor $MONITOR_ID"
    fi

    echo ""
  done <<< "$MONITORS"

  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Summary: $TOTAL monitors, $INVESTIGATED investigated, $FAILED errored"
  exit 0
fi

# ============================================================
# --self-improve mode
# ============================================================
if [[ "$SELF_IMPROVE" == "true" ]]; then
  BUG_REPORT="Self-improve mode: scan for observability gaps in the codebase. Do not investigate a specific bug. Instead:
1. Check .stale-sections and refresh any stale knowledge sections
2. Scan recent sherlock_investigations where evidence_confidence = 'gap'
3. For each gap, verify whether the observability patch was committed
4. If not, add the missing createStructuredLog call and open a PR
5. Scan for symptom classes with investigation_count = 0 and propose new query_plan entries in sherlock_symptom_patterns"
fi

# ============================================================
# Standard one-off investigation
# ============================================================
if [[ -z "$BUG_REPORT" ]]; then
  echo "Usage: ./scripts/sherlock.sh [--user email] [--execution id] [--notify channel] \"bug description\""
  echo "       ./scripts/sherlock.sh --outstanding"
  echo "       ./scripts/sherlock.sh --self-improve"
  exit 1
fi

# --- build context lines ---
CONTEXT=""
[[ -n "$USER_EMAIL" ]]     && CONTEXT="${CONTEXT}User: $USER_EMAIL\n"
[[ -n "$EXECUTION_ID" ]]   && CONTEXT="${CONTEXT}Execution ID: $EXECUTION_ID\n"
[[ -n "$NOTIFY_CHANNEL" ]] && CONTEXT="${CONTEXT}Notify: $NOTIFY_CHANNEL\n"

PROMPT="$BUG_REPORT"
[[ -n "$CONTEXT" ]] && PROMPT="$PROMPT\n\n$(echo -e "$CONTEXT")"

echo ""
echo "🔍 Sherlock — investigating..."
echo ""

cd "$REPO_ROOT"
claude --print --system-prompt "$SYSTEM_PROMPT" "$PROMPT"