#!/bin/bash
# auto_pull.sh — keep this machine's ~/CLAUDE_PROJECTS main in sync with origin.
#
# CANONICAL, machine-agnostic (REC-28 + checkout-health-hardening). Both the MacBook and
# the Studio symlink ~/.local/bin/{macbook,studio}_auto_pull.sh -> this file, so a fix
# lands ONCE in the repo and propagates to both machines via autopull itself. The launchd
# plists (com.recoil.{macbook,studio}-autopull) still point at the ~/.local/bin names;
# the symlink redirects them here.
#
# SAFE by design:
#   - only ever fast-forwards `main`; never touches a feature branch
#   - uses `git merge --ff-only` (NOT a clean-tree gate) so chronically-dirty runtime files
#     (e.g. the tracked cortex launchd log) don't block sync, BUT ff-only still REFUSES
#     (never clobbers) if a real local change would be lost.
#   - on refuse it surfaces how-far-behind via a ~/.recoil-autopull-STUCK marker so a blocked
#     autopull can't silently compound (the 2026-06-21 "54 commits behind" incident, where a
#     forgotten uncommitted file froze sync while the peer machine kept merging).
set -uo pipefail
REPO="$HOME/CLAUDE_PROJECTS"
LOG="$HOME/.recoil-autopull.log"
STUCK="$HOME/.recoil-autopull-STUCK"
TS="$(date '+%Y-%m-%dT%H:%M:%S')"
cd "$REPO" 2>/dev/null || { echo "$TS no-repo at $REPO" >> "$LOG"; exit 0; }

BR="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '?')"
if [ "$BR" != "main" ]; then echo "$TS skip: on branch '$BR'" >> "$LOG"; exit 0; fi

if ! git fetch origin --quiet 2>>"$LOG"; then echo "$TS fetch-failed" >> "$LOG"; exit 0; fi

LOCAL="$(git rev-parse @ 2>/dev/null || echo x)"
REMOTE="$(git rev-parse origin/main 2>/dev/null || echo y)"
if [ "$LOCAL" = "$REMOTE" ]; then
  echo "$TS ok: up-to-date @ ${LOCAL:0:8}" >> "$LOG"; rm -f "$STUCK"
elif git merge --ff-only origin/main --quiet 2>>"$LOG"; then
  echo "$TS pulled ${LOCAL:0:8} -> $(git rev-parse --short HEAD)" >> "$LOG"; rm -f "$STUCK"
else
  # ff-only refused (uncommitted/untracked conflict or local divergence). SAFE-skipped to
  # preserve work — but surface how-far-behind so it can't silently compound.
  BEHIND="$(git rev-list --count HEAD..origin/main 2>/dev/null || echo '?')"
  NDIRTY="$(git status --porcelain --untracked-files=all 2>/dev/null | grep -c . | tr -d ' ' || echo '?')"
  echo "$TS skip: ff-only refused — BEHIND $BEHIND, $NDIRTY dirty file(s); commit/stash to resume" >> "$LOG"
  printf '%s\n' \
    "AUTOPULL BLOCKED since $TS — $BEHIND commit(s) behind origin/main, $NDIRTY uncommitted/untracked file(s)." \
    "Local code/self-gates ground STALE until you commit or stash. History: ~/.recoil-autopull.log" > "$STUCK"
fi

# Cap log growth (heartbeat runs every 5 min).
tail -n 500 "$LOG" > "$LOG.tmp" 2>/dev/null && mv "$LOG.tmp" "$LOG"
