#!/usr/bin/env bash
# fork_agent.sh — fork a Claude Code agent by cloning its session file.
#
# Usage:
#   ./fork_agent.sh <source_agent> <new_agent>
#   ./fork_agent.sh yams yam_sim
#
# What it does (mechanical only):
#   1. Locates the source agent's cwd + session UUID from resume_fleet.sh WINDOW_PLAN
#      (auto-discovers via bootstrap signature if UUID isn't pinned in ROSTER).
#   2. Copies the source JSONL to a new UUID in the same project dir.
#   3. Creates a new tmux window named <new_agent> with matching cwd.
#   4. Launches `claude --resume <new_uuid>` in that window.
#   5. Prints the new UUID + a checklist for the judgment work you still owe.
#
# What you still have to do:
#   * agents/<new>/{ROLE.md, inbox.md, outbox.md, status.md}
#   * vault/fleet/agents/<new>/{overview,tasks,memory}.md
#   * Shared vault domain slice if the split has joint knowledge
#   * Add entries to config.yaml, para/.agents/config.yaml, GUIDELINES.md roster
#   * Add entry to resume_fleet.sh WINDOW_PLAN with the printed UUID
#   * Send divergence message to the new agent
#   * Send heads-up message to the source agent
#
# Full write-up: see feedback_fork_agent_pattern.md in manager memory.

set -euo pipefail

if [[ $# -ne 2 ]]; then
  echo "Usage: $0 <source_agent> <new_agent>"
  echo "Example: $0 yams yam_sim"
  exit 1
fi

SRC="$1"
NEW="$2"
SESSION="agents"
FLEET_DIR="/data/cameron/agents_stuff"
RESUME_FLEET="$FLEET_DIR/resume_fleet.sh"

# --- Preconditions ---
command -v tmux >/dev/null || { echo "ERROR: tmux not on PATH"; exit 1; }
tmux has-session -t "$SESSION" 2>/dev/null || { echo "ERROR: no tmux session '$SESSION'"; exit 1; }
REUSE_WINDOW=0
if tmux list-windows -t "$SESSION" -F '#{window_name}' | grep -qxF "$NEW"; then
  # Allow reuse ONLY if the window is empty (bash prompt, no claude child).
  win_pane_pid=$(tmux display-message -t "$SESSION:$NEW" -p '#{pane_pid}')
  if pgrep -P "$win_pane_pid" -f 'claude' >/dev/null 2>&1; then
    echo "ERROR: tmux window '$NEW' exists AND has claude running under PID $win_pane_pid. Kill it first."
    exit 1
  fi
  echo "==> Window '$NEW' exists but is empty (no claude child) — reusing."
  REUSE_WINDOW=1
fi
[[ -f "$RESUME_FLEET" ]] || { echo "ERROR: not found: $RESUME_FLEET"; exit 1; }

# --- Locate source cwd + optional pinned UUID from WINDOW_PLAN ---
# WINDOW_PLAN format: "name|type|cwd|uuid_override"
plan_line="$(grep -oE "\"$SRC\|agent\|[^\"]*\"" "$RESUME_FLEET" | head -1 | tr -d '"')"
if [[ -z "$plan_line" ]]; then
  echo "ERROR: no '$SRC' entry in $RESUME_FLEET WINDOW_PLAN"
  exit 1
fi
IFS='|' read -r _ _ SRC_CWD SRC_UUID_OVERRIDE <<<"$plan_line"

# Project-dir encoding: cwd's / and _ become -
proj_encoded=$(echo "$SRC_CWD" | sed 's/[/_]/-/g')
proj_dir="$HOME/.claude/projects/$proj_encoded"
[[ -d "$proj_dir" ]] || { echo "ERROR: project dir missing: $proj_dir"; exit 1; }

# --- Resolve source UUID: live tmux > ROSTER pin > signature autodiscover ---
#
# Live tmux is preferred because it asks the source agent's actual running
# `claude --resume <uuid>` process for its UUID. This is the ONLY reliable path
# once forks exist: mtime-based signature discovery misfires because clones
# share the source's bootstrap signature and have newer mtimes than the
# original (learned yam_calib fork 2026-07-02).
SRC_UUID=""
if tmux list-windows -t "$SESSION" -F '#{window_name}' | grep -qxF "$SRC"; then
  src_pane_pid=$(tmux display-message -t "$SESSION:$SRC" -p '#{pane_pid}' 2>/dev/null)
  if [[ -n "$src_pane_pid" ]]; then
    live_cmd=$(pgrep -P "$src_pane_pid" -a 2>/dev/null | head -1)
    live_uuid=$(echo "$live_cmd" | grep -oE 'resume [a-f0-9-]{36}' | awk '{print $2}')
    if [[ -n "$live_uuid" && -f "$proj_dir/$live_uuid.jsonl" ]]; then
      SRC_UUID="$live_uuid"
      echo "==> Resolved '$SRC' UUID from LIVE tmux process: $SRC_UUID"
    fi
  fi
fi
if [[ -z "$SRC_UUID" && -n "$SRC_UUID_OVERRIDE" ]]; then
  SRC_UUID="$SRC_UUID_OVERRIDE"
  echo "==> Resolved '$SRC' UUID from resume_fleet.sh ROSTER: $SRC_UUID"
fi
if [[ -z "$SRC_UUID" ]]; then
  echo "==> No live process + no ROSTER pin — falling back to mtime autodiscover (UNRELIABLE if forks exist)"
  SRC_UUID=$(python3 - "$SRC" "$proj_dir" <<'PY'
import json, glob, os, sys
name, proj_dir = sys.argv[1], sys.argv[2]
sig = "you are the '%s' agent" % name.lower()
best, bm = '', -1
for f in glob.glob(proj_dir + '/*.jsonl'):
    try:
        with open(f) as fh:
            for line in fh:
                try: o = json.loads(line)
                except: continue
                if o.get('type') != 'user': continue
                c = o.get('message', {}).get('content', '')
                if isinstance(c, list):
                    c = ' '.join(x.get('text', '') for x in c if isinstance(x, dict))
                if c and sig in c.lower():
                    m = os.path.getmtime(f)
                    if m > bm: bm, best = m, os.path.basename(f)[:-6]
                break
    except Exception: pass
print(best)
PY
  )
fi

[[ -n "$SRC_UUID" ]] || { echo "ERROR: no session UUID for '$SRC' (add to ROSTER or check signature)"; exit 1; }

SRC_JSONL="$proj_dir/$SRC_UUID.jsonl"
[[ -f "$SRC_JSONL" ]] || { echo "ERROR: source JSONL missing: $SRC_JSONL"; exit 1; }
SRC_SIZE=$(du -h "$SRC_JSONL" | cut -f1)

# --- Clone ---
NEW_UUID=$(python3 -c 'import uuid; print(uuid.uuid4())')
NEW_JSONL="$proj_dir/$NEW_UUID.jsonl"
[[ ! -f "$NEW_JSONL" ]] || { echo "ERROR: new JSONL already exists (UUID collision — retry): $NEW_JSONL"; exit 1; }

echo "==> Cloning source session ($SRC_SIZE)"
echo "    src: $SRC_JSONL"
echo "    dst: $NEW_JSONL"
cp "$SRC_JSONL" "$NEW_JSONL"

# --- New tmux window (or reuse the existing empty one) ---
if [[ $REUSE_WINDOW -eq 1 ]]; then
  echo "==> Reusing existing window '$NEW' — cd + launch"
  tmux send-keys -t "$SESSION:$NEW" "cd $SRC_CWD" Enter
  sleep 1
else
  echo "==> Creating tmux window '$NEW' at cwd $SRC_CWD"
  tmux new-window -t "$SESSION" -n "$NEW" -c "$SRC_CWD"
  sleep 1
fi
tmux send-keys -t "$SESSION:$NEW" "claude --resume $NEW_UUID --dangerously-skip-permissions" Enter

# --- Post-checklist ---
cat <<POST

════════════════════════════════════════════════════════════════════════
FORK COMPLETE (mechanical). Session may take 30–90s to load if large.
════════════════════════════════════════════════════════════════════════

Source:   $SRC       ($SRC_UUID)
New:      $NEW       ($NEW_UUID)
Cloned:   $SRC_SIZE
Cwd:      $SRC_CWD
Window:   $SESSION:$NEW

STILL TO DO (judgment work):

  1. Write agents/$NEW/{ROLE.md, inbox.md, outbox.md, status.md}
     ROLE.md must state:
       * this is a fork of $SRC (byte-for-byte session clone as of $(date -Iseconds))
       * new scope (what $NEW owns going forward)
       * files rule: never edit agents/$SRC/* or vault/fleet/agents/$SRC/*
       * shared vault domain slice for joint knowledge, if applicable

  2. Write vault/fleet/agents/$NEW/{overview,tasks,memory}.md

  3. Add roster entries to:
       * $FLEET_DIR/config.yaml
       * /data/cameron/para/.agents/config.yaml
       * $FLEET_DIR/shared/GUIDELINES.md (roster table row)

  4. Add reboot-survival entry to $RESUME_FLEET WINDOW_PLAN:
       "$NEW|agent|$SRC_CWD|$NEW_UUID"
       (Use the EXPLICIT UUID above — autodiscover would find the $SRC
       bootstrap signature and misresume the wrong session.)

  5. Send DIVERGENCE message to $NEW via tmux send-keys after it loads:
       * "you are a fork of $SRC as of <ts>"
       * scope: what you own now
       * files rule: never edit agents/$SRC/*, use your own
       * bootstrap task: mine inherited memory to shared vault domain doc
       * persistent shells belong to $SRC — spawn your own if needed
       * ANTI-IDENTITY-CONFUSION CLAUSE (mandatory): "If you find yourself
         acknowledging $SRC's scope or saying you'll drop $NEW's work — STOP.
         You ARE $NEW. The drop-off already happened via the fork. Re-read
         your ROLE.md and start doing $NEW's bootstrap task NOW."
         (Fresh forks routinely re-enact $SRC acknowledging the fork rather
         than acting as $NEW adopting the new scope. Seen on financials
         2026-07-06. Include this clause and expect to send a corrective
         follow-up if the first response is still $SRC-identified.)

  6. Send HEADS-UP to $SRC via tmux send-keys:
       * "$NEW forked off with your context"
       * scope: what $SRC no longer owns
       * cross-ping convention when updating shared vault

════════════════════════════════════════════════════════════════════════
POST
