#!/usr/bin/env bash
# generate-fixtures.sh — Run all scenarios and write golden fixtures to tests/golden/
# Run from any directory. Uses absolute paths throughout.
# SAFETY: Verifies real memsearch HEAD is unchanged after session-end scenarios.

set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
# Set HOOKS_DIR to override, e.g. for Python port replay:
#   HOOKS_DIR=/path/to/python-hooks bash generate-fixtures.sh --replay-dir /tmp/out
HOOKS_DIR="${HOOKS_DIR:-$(cd "$SCRIPT_DIR/../hooks" && pwd -P)}"
GOLDEN_DIR="$SCRIPT_DIR/golden"
INPUTS_DIR="$SCRIPT_DIR/inputs"
REPLAY_DIR=""

EXPECTED_HEAD="15804ed63ebf6400f22d719ee102857985f9a9d9"

# ---------------------------------------------------------------------------
# Parse arguments
# ---------------------------------------------------------------------------
while [[ $# -gt 0 ]]; do
  case "$1" in
    --replay-dir) REPLAY_DIR="$2"; shift 2 ;;
    *) echo "Unknown arg: $1" >&2; exit 1 ;;
  esac
done

# If --replay-dir is set, write there instead of golden/
if [[ -n "$REPLAY_DIR" ]]; then
  mkdir -p "$REPLAY_DIR"
  GOLDEN_DIR="$REPLAY_DIR"
fi

# ---------------------------------------------------------------------------
# Safety: record real memsearch HEAD
# ---------------------------------------------------------------------------
REAL_HEAD_BEFORE=$(git -C /home/jared/.memsearch rev-parse HEAD 2>/dev/null || echo "NO_REAL_REPO")
echo "=== SAFETY CHECK: real HEAD before = $REAL_HEAD_BEFORE ==="
# In replay mode, skip the strict hash check (HEAD may have advanced since fixtures were made).
# The safety invariant is before==after, not before==EXPECTED_HEAD.
if [[ -z "$REPLAY_DIR" && "$REAL_HEAD_BEFORE" != "$EXPECTED_HEAD" ]]; then
  echo "ERROR: HEAD mismatch before run. Expected $EXPECTED_HEAD, got $REAL_HEAD_BEFORE" >&2
  exit 1
fi

# ---------------------------------------------------------------------------
# Pre-run global cleanup of /tmp stale flags
# ---------------------------------------------------------------------------
cleanup_tmp_flags() {
  rm -f /tmp/memory-context-injected-golden-sess-{003,004,005,006,006b,006c,007,008}
  rm -f /tmp/claude-vault-touched-golden-sess-{006,006b,006c,007,008}
}
cleanup_tmp_flags

mkdir -p "$GOLDEN_DIR"

# ---------------------------------------------------------------------------
# Normalization helper — takes sandbox path as $1, reads stdin
# ---------------------------------------------------------------------------
normalize() {
  local sandbox_path="$1"
  sed \
    -e "s|[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}T[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}Z|__TIMESTAMP__|g" \
    -e "s|[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}|__DATE__|g" \
    -e "s|${sandbox_path}|__SANDBOX__|g" \
    -e "s|cwd=[^ ]*|cwd=__CWD__|g" \
    -e "s|/home/jared|__REAL_HOME__|g"
}

# ---------------------------------------------------------------------------
# Build a fresh sandbox for a scenario
# Returns sandbox path in SANDBOX (exported)
# ---------------------------------------------------------------------------
make_sandbox() {
  local scenario="$1"
  local sb
  sb=$(mktemp -d)
  sb=$(cd "$sb" && pwd -P)

  mkdir -p "$sb/.claude/plugins/os-vault"
  cat > "$sb/.claude/plugins/os-vault/config.yaml" <<YAML
vault_path: ${sb}/vault
graphify_output_dir: ${sb}/vault/graphify-out
ollama_model: qwen25-coder-7b-16k
stale_threshold_days: 7
memsearch_dir: /home/jared/.memsearch
YAML

  mkdir -p "$sb/vault/journal"
  mkdir -p "$sb/.cache/graphify"
  mkdir -p "$sb/bin"

  # Graphify shim — log path baked in
  cat > "$sb/bin/graphify" <<BASH
#!/usr/bin/env bash
echo "[fake-graphify] args: \$@" >> "${sb}/graphify-shim.log"
exit 0
BASH
  chmod +x "$sb/bin/graphify"

  # Git shim — log path BAKED IN (absolute, not env-var-only) for safety in subshells
  # Emits NOTHING to stdout/stderr
  cat > "$sb/bin/git" <<BASH
#!/usr/bin/env bash
for arg in "\$@"; do
  if [ "\$arg" = "/home/jared/.memsearch" ]; then
    echo "[git-shim] BLOCKED: git \$@" >> "${sb}/git-shim.log"
    exit 0
  fi
done
exec /usr/bin/git "\$@"
BASH
  chmod +x "$sb/bin/git"

  echo "$sb"
}

# ---------------------------------------------------------------------------
# Helper: write all fixture files for a scenario
# ---------------------------------------------------------------------------
write_fixture() {
  local scenario="$1"
  local sb="$2"
  local stdout_content="$3"
  local exit_code="$4"
  local orig_input="$5"

  local sdir="$GOLDEN_DIR/$scenario"
  mkdir -p "$sdir/sideeffects"

  echo "$stdout_content" | normalize "$sb" > "$sdir/stdout.txt"
  echo "$exit_code" > "$sdir/exit_code.txt"
  cp "$orig_input" "$sdir/input.json"
}

# ---------------------------------------------------------------------------
# SCENARIO: session-start-fresh
# ---------------------------------------------------------------------------
run_session_start_fresh() {
  echo ""
  echo "--- session-start-fresh ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "session-start-fresh")
  local sdir="$GOLDEN_DIR/session-start-fresh"
  mkdir -p "$sdir/sideeffects"

  # Setup: stamp file exists (fresh mtime) → rebuild NOT needed
  mkdir -p "$sb/.cache/graphify"
  touch "$sb/.cache/graphify/vault-rebuild.stamp"

  # Record log offset
  local log_offset=0
  [[ -f /tmp/memory-hook.log ]] && log_offset=$(wc -c < /tmp/memory-hook.log)

  # Expand __SANDBOX__ in input
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/session-start-fresh.json" > "$input_expanded"

  # Run hook
  local stdout exit_code=0
  stdout=$(HOME="$sb" PATH="$sb/bin:$PATH" GRAPHIFY_SHIM_LOG="$sb/graphify-shim.log" \
    bash "$HOOKS_DIR/session-start.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  # Write main fixtures
  write_fixture "session-start-fresh" "$sb" "$stdout" "$exit_code" "$INPUTS_DIR/session-start-fresh.json"

  # Side effect: memory-hook.log delta
  if [[ -f /tmp/memory-hook.log ]]; then
    dd if=/tmp/memory-hook.log bs=1 skip="$log_offset" 2>/dev/null \
      | normalize "$sb" > "$sdir/sideeffects/memory-hook-log-delta.txt"
  else
    echo "(no log entries)" > "$sdir/sideeffects/memory-hook-log-delta.txt"
  fi

  echo "stdout: $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  echo "log delta: $(cat "$sdir/sideeffects/memory-hook-log-delta.txt")"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# SCENARIO: session-start-stale
# ---------------------------------------------------------------------------
run_session_start_stale() {
  echo ""
  echo "--- session-start-stale ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "session-start-stale")
  local sdir="$GOLDEN_DIR/session-start-stale"
  mkdir -p "$sdir/sideeffects"

  # Setup: NO stamp file → rebuild needed
  # (we do NOT create vault-rebuild.stamp)

  # Record log offset
  local log_offset=0
  [[ -f /tmp/memory-hook.log ]] && log_offset=$(wc -c < /tmp/memory-hook.log)

  # Expand input
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/session-start-stale.json" > "$input_expanded"

  # Run hook
  local stdout exit_code=0
  stdout=$(HOME="$sb" PATH="$sb/bin:$PATH" GRAPHIFY_SHIM_LOG="$sb/graphify-shim.log" \
    bash "$HOOKS_DIR/session-start.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  # Poll for graphify shim log (background nohup'd call)
  for i in $(seq 1 10); do
    [[ -f "$sb/graphify-shim.log" ]] && break
    sleep 0.5
  done

  # Write main fixtures
  write_fixture "session-start-stale" "$sb" "$stdout" "$exit_code" "$INPUTS_DIR/session-start-stale.json"

  # Side effect: memory-hook.log delta
  if [[ -f /tmp/memory-hook.log ]]; then
    dd if=/tmp/memory-hook.log bs=1 skip="$log_offset" 2>/dev/null \
      | normalize "$sb" > "$sdir/sideeffects/memory-hook-log-delta.txt"
  else
    echo "(no log entries)" > "$sdir/sideeffects/memory-hook-log-delta.txt"
  fi

  # Side effect: graphify shim args
  if [[ -f "$sb/graphify-shim.log" ]]; then
    normalize "$sb" < "$sb/graphify-shim.log" > "$sdir/sideeffects/graphify-shim-args.txt"
  else
    echo "(graphify not invoked within 5s)" > "$sdir/sideeffects/graphify-shim-args.txt"
  fi

  echo "stdout: $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  echo "log delta: $(cat "$sdir/sideeffects/memory-hook-log-delta.txt")"
  echo "graphify shim: $(cat "$sdir/sideeffects/graphify-shim-args.txt")"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# SCENARIO: session-context-first-with-graph
# ---------------------------------------------------------------------------
run_session_context_first_with_graph() {
  echo ""
  echo "--- session-context-first-with-graph ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "session-context-first-with-graph")
  local sdir="$GOLDEN_DIR/session-context-first-with-graph"
  mkdir -p "$sdir/sideeffects"

  # Setup: create a real git repo with graph.json
  mkdir -p "$sb/vault-project/graphify-out"
  echo '{"nodes":[],"edges":[]}' > "$sb/vault-project/graphify-out/graph.json"
  /usr/bin/git init "$sb/vault-project" >/dev/null 2>&1
  /usr/bin/git -C "$sb/vault-project" config user.email "test@test.com"
  /usr/bin/git -C "$sb/vault-project" config user.name "Test"
  /usr/bin/git -C "$sb/vault-project" commit --allow-empty -m "init" >/dev/null 2>&1

  # Expand input
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/session-context-first-with-graph.json" > "$input_expanded"

  # Run hook from vault-project cwd
  local stdout exit_code=0
  stdout=$(cd "$sb/vault-project" && HOME="$sb" PATH="$sb/bin:$PATH" \
    bash "$HOOKS_DIR/session-context.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  # Write main fixtures
  write_fixture "session-context-first-with-graph" "$sb" "$stdout" "$exit_code" \
    "$INPUTS_DIR/session-context-first-with-graph.json"

  # Side effect: flag file created?
  if [[ -f /tmp/memory-context-injected-golden-sess-003 ]]; then
    echo "yes" > "$sdir/sideeffects/flag-file-created.txt"
  else
    echo "no" > "$sdir/sideeffects/flag-file-created.txt"
  fi

  echo "stdout (normalized): $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  echo "flag created: $(cat "$sdir/sideeffects/flag-file-created.txt")"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# SCENARIO: session-context-reentry
# ---------------------------------------------------------------------------
run_session_context_reentry() {
  echo ""
  echo "--- session-context-reentry ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "session-context-reentry")
  local sdir="$GOLDEN_DIR/session-context-reentry"
  mkdir -p "$sdir/sideeffects"

  # Setup: flag file already exists (reentry)
  touch /tmp/memory-context-injected-golden-sess-004

  # Expand input
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/session-context-reentry.json" > "$input_expanded"

  # Run hook from /tmp
  local stdout exit_code=0
  stdout=$(cd /tmp && HOME="$sb" PATH="$sb/bin:$PATH" \
    bash "$HOOKS_DIR/session-context.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  # Write main fixtures
  write_fixture "session-context-reentry" "$sb" "$stdout" "$exit_code" \
    "$INPUTS_DIR/session-context-reentry.json"

  echo "stdout: $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# SCENARIO: session-context-no-graph
# ---------------------------------------------------------------------------
run_session_context_no_graph() {
  echo ""
  echo "--- session-context-no-graph ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "session-context-no-graph")
  local sdir="$GOLDEN_DIR/session-context-no-graph"
  mkdir -p "$sdir/sideeffects"

  # Setup: git repo but NO graphify-out/graph.json
  mkdir -p "$sb/noproject"
  /usr/bin/git init "$sb/noproject" >/dev/null 2>&1
  /usr/bin/git -C "$sb/noproject" config user.email "test@test.com"
  /usr/bin/git -C "$sb/noproject" config user.name "Test"
  /usr/bin/git -C "$sb/noproject" commit --allow-empty -m "init" >/dev/null 2>&1

  # Expand input
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/session-context-no-graph.json" > "$input_expanded"

  # Run hook from noproject cwd
  local stdout exit_code=0
  stdout=$(cd "$sb/noproject" && HOME="$sb" PATH="$sb/bin:$PATH" \
    bash "$HOOKS_DIR/session-context.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  # Write main fixtures
  write_fixture "session-context-no-graph" "$sb" "$stdout" "$exit_code" \
    "$INPUTS_DIR/session-context-no-graph.json"

  echo "stdout (normalized): $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# SCENARIO: post-tool-use-vault-md
# ---------------------------------------------------------------------------
run_post_tool_use_vault_md() {
  echo ""
  echo "--- post-tool-use-vault-md ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "post-tool-use-vault-md")
  local sdir="$GOLDEN_DIR/post-tool-use-vault-md"
  mkdir -p "$sdir/sideeffects"

  # Setup: fresh stamp + vault notes dir
  touch "$sb/.cache/graphify/vault-rebuild.stamp"
  mkdir -p "$sb/vault/notes"

  # Expand input
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/post-tool-use-vault-md.json" > "$input_expanded"

  # Run hook
  local stdout exit_code=0
  stdout=$(HOME="$sb" PATH="$sb/bin:$PATH" \
    bash "$HOOKS_DIR/post-tool-use-write.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  # Write main fixtures
  write_fixture "post-tool-use-vault-md" "$sb" "$stdout" "$exit_code" \
    "$INPUTS_DIR/post-tool-use-vault-md.json"

  # Side effect: touch file content
  if [[ -f /tmp/claude-vault-touched-golden-sess-006 ]]; then
    normalize "$sb" < /tmp/claude-vault-touched-golden-sess-006 \
      > "$sdir/sideeffects/touch-file.txt"
  else
    echo "(not created)" > "$sdir/sideeffects/touch-file.txt"
  fi

  # Side effect: post-tool-use.log
  if [[ -f "$sb/.cache/graphify/post-tool-use.log" ]]; then
    normalize "$sb" < "$sb/.cache/graphify/post-tool-use.log" \
      > "$sdir/sideeffects/post-tool-use-log.txt"
  else
    echo "(not created)" > "$sdir/sideeffects/post-tool-use-log.txt"
  fi

  # Side effect: stamp deleted?
  if [[ ! -f "$sb/.cache/graphify/vault-rebuild.stamp" ]]; then
    echo "yes" > "$sdir/sideeffects/stamp-deleted.txt"
  else
    echo "no" > "$sdir/sideeffects/stamp-deleted.txt"
  fi

  echo "stdout: $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  echo "touch-file: $(cat "$sdir/sideeffects/touch-file.txt")"
  echo "log: $(cat "$sdir/sideeffects/post-tool-use-log.txt")"
  echo "stamp-deleted: $(cat "$sdir/sideeffects/stamp-deleted.txt")"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# SCENARIO: post-tool-use-non-md
# ---------------------------------------------------------------------------
run_post_tool_use_non_md() {
  echo ""
  echo "--- post-tool-use-non-md ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "post-tool-use-non-md")
  local sdir="$GOLDEN_DIR/post-tool-use-non-md"
  mkdir -p "$sdir/sideeffects"

  mkdir -p "$sb/vault/notes"

  # Expand input
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/post-tool-use-non-md.json" > "$input_expanded"

  # Run hook
  local stdout exit_code=0
  stdout=$(HOME="$sb" PATH="$sb/bin:$PATH" \
    bash "$HOOKS_DIR/post-tool-use-write.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  write_fixture "post-tool-use-non-md" "$sb" "$stdout" "$exit_code" \
    "$INPUTS_DIR/post-tool-use-non-md.json"

  # Confirm touch file and log were NOT created (no-op)
  if [[ -f /tmp/claude-vault-touched-golden-sess-006b ]]; then
    echo "UNEXPECTED: touch file was created" > "$sdir/sideeffects/noops.txt"
  else
    echo "ok: no touch file (non-md guard fired)" > "$sdir/sideeffects/noops.txt"
  fi

  echo "stdout: $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  echo "noops: $(cat "$sdir/sideeffects/noops.txt")"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# SCENARIO: post-tool-use-outside-vault
# ---------------------------------------------------------------------------
run_post_tool_use_outside_vault() {
  echo ""
  echo "--- post-tool-use-outside-vault ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "post-tool-use-outside-vault")
  local sdir="$GOLDEN_DIR/post-tool-use-outside-vault"
  mkdir -p "$sdir/sideeffects"

  # Expand input (no __SANDBOX__ in this one but run through sed anyway)
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/post-tool-use-outside-vault.json" > "$input_expanded"

  # Run hook
  local stdout exit_code=0
  stdout=$(HOME="$sb" PATH="$sb/bin:$PATH" \
    bash "$HOOKS_DIR/post-tool-use-write.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  write_fixture "post-tool-use-outside-vault" "$sb" "$stdout" "$exit_code" \
    "$INPUTS_DIR/post-tool-use-outside-vault.json"

  if [[ -f /tmp/claude-vault-touched-golden-sess-006c ]]; then
    echo "UNEXPECTED: touch file was created" > "$sdir/sideeffects/noops.txt"
  else
    echo "ok: no touch file (outside-vault guard fired)" > "$sdir/sideeffects/noops.txt"
  fi

  echo "stdout: $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  echo "noops: $(cat "$sdir/sideeffects/noops.txt")"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# SCENARIO: session-end-with-touches
# SAFETY: real HEAD must be verified after this run.
# ---------------------------------------------------------------------------
run_session_end_with_touches() {
  echo ""
  echo "--- session-end-with-touches ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "session-end-with-touches")
  local sdir="$GOLDEN_DIR/session-end-with-touches"
  mkdir -p "$sdir/sideeffects"

  # Git shim preflight
  echo "  [preflight] Testing git shim..."
  "$sb/bin/git" -C /home/jared/.memsearch status >/dev/null 2>/dev/null
  if [[ -f "$sb/git-shim.log" ]]; then
    echo "  [preflight] PASS — shim intercepted /home/jared/.memsearch call"
    cat "$sb/git-shim.log"
    # Clear the preflight entry so fixture only captures hook-time calls
    rm -f "$sb/git-shim.log"
  else
    echo "  [preflight] WARNING — shim log not found; proceeding anyway"
  fi

  # Setup: touch file with two paths
  printf '%s\n%s\n' "${sb}/vault/notes/test.md" "${sb}/vault/notes/other.md" \
    > /tmp/claude-vault-touched-golden-sess-007

  # Ensure journal dir exists
  mkdir -p "$sb/vault/journal"

  # Expand input
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/session-end-with-touches.json" > "$input_expanded"

  # Run hook from /tmp (avoids git rev-parse returning cc-os repo as project)
  local stdout exit_code=0
  stdout=$(cd /tmp && HOME="$sb" PATH="$sb/bin:$PATH" \
    bash "$HOOKS_DIR/session-end.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  # IMMEDIATE HEAD CHECK after first session-end run
  local head_after
  head_after=$(git -C /home/jared/.memsearch rev-parse HEAD 2>/dev/null || echo "NO_REAL_REPO")
  echo "  [safety] HEAD after session-end-with-touches: $head_after"
  if [[ "$head_after" != "$REAL_HEAD_BEFORE" ]]; then
    echo "SAFETY BREACH: memsearch HEAD changed! $head_after != $REAL_HEAD_BEFORE" >&2
    rm -rf "$sb"
    exit 99
  fi
  echo "  [safety] PASS"

  write_fixture "session-end-with-touches" "$sb" "$stdout" "$exit_code" \
    "$INPUTS_DIR/session-end-with-touches.json"

  # Side effect: journal content (find by glob, not recomputing date)
  local journal_file
  journal_file=$(ls "$sb/vault/journal/"*.md 2>/dev/null | head -1)
  if [[ -n "$journal_file" && -f "$journal_file" ]]; then
    normalize "$sb" < "$journal_file" > "$sdir/sideeffects/journal.txt"
  else
    echo "(journal file not created)" > "$sdir/sideeffects/journal.txt"
  fi

  # Side effect: git shim log
  if [[ -f "$sb/git-shim.log" ]]; then
    normalize "$sb" < "$sb/git-shim.log" > "$sdir/sideeffects/git-shim-log.txt"
  else
    echo "(no git shim log — memsearch block not triggered?)" > "$sdir/sideeffects/git-shim-log.txt"
  fi

  # Side effect: touch file deleted?
  if [[ ! -f /tmp/claude-vault-touched-golden-sess-007 ]]; then
    echo "deleted" > "$sdir/sideeffects/touch-file-deleted.txt"
  else
    echo "NOT deleted (unexpected)" > "$sdir/sideeffects/touch-file-deleted.txt"
  fi

  echo "stdout: $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  echo "journal (first 10 lines):"
  head -10 "$sdir/sideeffects/journal.txt" || true
  echo "git-shim-log: $(cat "$sdir/sideeffects/git-shim-log.txt")"
  echo "touch-file-deleted: $(cat "$sdir/sideeffects/touch-file-deleted.txt")"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# SCENARIO: session-end-no-touches
# ---------------------------------------------------------------------------
run_session_end_no_touches() {
  echo ""
  echo "--- session-end-no-touches ---"
  cleanup_tmp_flags
  local sb
  sb=$(make_sandbox "session-end-no-touches")
  local sdir="$GOLDEN_DIR/session-end-no-touches"
  mkdir -p "$sdir/sideeffects"

  # Setup: NO touch file for golden-sess-008
  mkdir -p "$sb/vault/journal"

  # Expand input
  local input_expanded="$sb/input-expanded.json"
  sed "s|__SANDBOX__|${sb}|g" "$INPUTS_DIR/session-end-no-touches.json" > "$input_expanded"

  # Run hook from /tmp
  local stdout exit_code=0
  stdout=$(cd /tmp && HOME="$sb" PATH="$sb/bin:$PATH" \
    bash "$HOOKS_DIR/session-end.sh" < "$input_expanded" 2>/dev/null) || exit_code=$?

  # Safety check
  local head_after
  head_after=$(git -C /home/jared/.memsearch rev-parse HEAD 2>/dev/null || echo "NO_REAL_REPO")
  echo "  [safety] HEAD after session-end-no-touches: $head_after"
  if [[ "$head_after" != "$REAL_HEAD_BEFORE" ]]; then
    echo "SAFETY BREACH: memsearch HEAD changed!" >&2
    rm -rf "$sb"
    exit 99
  fi
  echo "  [safety] PASS"

  write_fixture "session-end-no-touches" "$sb" "$stdout" "$exit_code" \
    "$INPUTS_DIR/session-end-no-touches.json"

  # Side effect: journal content
  local journal_file
  journal_file=$(ls "$sb/vault/journal/"*.md 2>/dev/null | head -1)
  if [[ -n "$journal_file" && -f "$journal_file" ]]; then
    normalize "$sb" < "$journal_file" > "$sdir/sideeffects/journal.txt"
  else
    echo "(journal file not created)" > "$sdir/sideeffects/journal.txt"
  fi

  # Side effect: git shim log
  if [[ -f "$sb/git-shim.log" ]]; then
    normalize "$sb" < "$sb/git-shim.log" > "$sdir/sideeffects/git-shim-log.txt"
  else
    echo "(no git shim log — memsearch block not triggered?)" > "$sdir/sideeffects/git-shim-log.txt"
  fi

  echo "stdout: $(cat "$sdir/stdout.txt")"
  echo "exit_code: $exit_code"
  echo "journal (first 10 lines):"
  head -10 "$sdir/sideeffects/journal.txt" || true
  echo "git-shim-log: $(cat "$sdir/sideeffects/git-shim-log.txt")"
  rm -rf "$sb"
}

# ---------------------------------------------------------------------------
# Run all scenarios
# ---------------------------------------------------------------------------
echo "=== Generating golden fixtures ==="
run_session_start_fresh
run_session_start_stale
run_session_context_first_with_graph
run_session_context_reentry
run_session_context_no_graph
run_post_tool_use_vault_md
run_post_tool_use_non_md
run_post_tool_use_outside_vault
run_session_end_with_touches   # safety-critical — HEAD checked immediately after
run_session_end_no_touches

# ---------------------------------------------------------------------------
# Final safety check
# ---------------------------------------------------------------------------
echo ""
echo "=== FINAL SAFETY CHECK ==="
REAL_HEAD_AFTER=$(git -C /home/jared/.memsearch rev-parse HEAD 2>/dev/null || echo "NO_REAL_REPO")
echo "BEFORE: $REAL_HEAD_BEFORE"
echo "AFTER:  $REAL_HEAD_AFTER"
if [[ "$REAL_HEAD_AFTER" == "$REAL_HEAD_BEFORE" ]]; then
  echo "SAFETY CHECK PASSED — real memsearch HEAD unchanged"
else
  echo "SAFETY CHECK FAILED — HEAD CHANGED from $REAL_HEAD_BEFORE to $REAL_HEAD_AFTER"
  exit 1
fi

echo ""
echo "=== All fixtures written to $GOLDEN_DIR ==="
find "$GOLDEN_DIR" -type f | sort
