#!/usr/bin/env bash
# tags: [safety, session-start, governance]
# description: SessionStart hook. Teaches Claude to verify checkable references in memory (PRs, branches, files, tasks) before acting on them, and surfaces sibling git worktrees on the cwd repo so the session knows concurrent work is in flight.
#
# .claude/hooks/session-state-awareness.sh
#
# SessionStart hook — installs two pieces of context:
#
# 1. A read-side rule: "verify before you act on a memory entry that
#    names a checkable artifact." Memory is a snapshot; the world is the
#    oracle. Without this, the session reads a stale entry ("PR #X is
#    open") and cascades wrong decisions. See
#    docs/methodology/session-safety.md for the full discipline.
#
# 2. A live snapshot of any sibling git worktrees on the cwd repo. If
#    `git worktree list` shows >1 worktree, a concurrent session is
#    likely active. The hook reports the list so Claude can factor that
#    into its work — for example, by routing mutations through a fresh
#    worktree rather than the primary worktree.
#
# Distribution: registered in .claude-plugin/hooks/hooks.json so every
# session in every repo with the bewith plugin installed gets this
# awareness.
#
# Concurrency: read-only. The hook only emits text. It never mutates
# state, never opens files for write, never touches branches.
#
# Exit codes: 0 always (it's awareness, not enforcement).

set -euo pipefail

# Build the guidance text. Kept short to stay a nudge rather than a wall.
read -r -d '' GUIDANCE <<'GUIDANCE_EOF' || true
[bewith session-state awareness]

Memory is a snapshot, not the world. When you read a memory entry that
names a checkable artifact, VERIFY current state before acting on it.

Checkable references and their cheap verifications:

  - "PR #<n>"            -> gh pr view <n> --json state,mergedAt,headRefName
  - "branch <name>"      -> git ls-remote --exit-code origin <name>
  - "file at <path>"     -> test -e <path>
  - "task CU-<id>"       -> clickup_get_task (MCP) or gh issue view if linked
  - "status: <state>" on a named artifact -> inspect the artifact directly
  - "next session task is X" -> X may already be done, or the current
                                session may already be X — verify

If the current state differs from what memory implies, STATE THE DRIFT
to the user in one line before relying on the entry. Concrete phrasing:

  Memory says PR #23 is open with a doc pending. Actual state: PR #23
  is merged; PRs #24, #25 also landed since. Working against current
  master.

Verify lazily — only when you are about to act on the entry. Bulk-
verifying every memory entry at session start is wasteful and the
results go stale.

Memory entries about user preferences, project goals, or non-checkable
opinions do NOT need verification. The rule applies only to entries
that name concrete platform state (PRs, branches, files, tasks).

See docs/methodology/session-safety.md for the full discipline,
including the worktree pattern for write-side safety.

------------------------------------------------------------
Memory-write discipline — inline, not batched:

After substantive moments (PR opened/merged, scope shift, decision
recorded, methodology fact discovered), UPDATE the relevant memory
entry IMMEDIATELY — do not batch all writes to session end.

Why: inline writes capture the why-was-this-true accurately. Batched
writes lose detail by the time the wrap happens and risk being skipped
entirely if the session is interrupted. The PostToolUse hook
post-pr-create-memory-nudge.sh nudges you right after a PR action;
respond to it then.

For end-of-session housekeeping (roadmap heartbeat, stale-entry sweep,
team-rule proposals from generic insights), invoke /end-session.
GUIDANCE_EOF

# Detect sibling worktrees on the cwd repo. If we're inside a git repo
# and `git worktree list` returns more than one entry, flag them.
WORKTREE_NOTE=""
if REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"; then
  WORKTREE_COUNT=$(git -C "$REPO_ROOT" worktree list --porcelain 2>/dev/null | grep -c '^worktree ' || true)
  if (( WORKTREE_COUNT > 1 )); then
    WORKTREE_LIST=$(git -C "$REPO_ROOT" worktree list 2>/dev/null || true)
    WORKTREE_NOTE=$'\n\n[bewith concurrent-session signal]\n\nThis repo has '"${WORKTREE_COUNT}"$' active git worktrees. Another session\nmay be running against the same .git/. Treat the working tree as\npotentially busy:\n\n'"${WORKTREE_LIST}"$'\n\nBefore mutating git state, prefer the worktree pattern (see\ndocs/methodology/session-safety.md). Do not run checkout, reset,\nbranch, or merge on the primary worktree without confirming the developer\nis not mid-flow in another session.'
  fi
fi

# Emit the combined context. Claude Code SessionStart hooks read
# hookSpecificOutput.additionalContext from stdout.
COMBINED="${GUIDANCE}${WORKTREE_NOTE}"

jq -n --arg ctx "$COMBINED" '{
  hookSpecificOutput: {
    hookEventName: "SessionStart",
    additionalContext: $ctx
  }
}'

exit 0
