#!/usr/bin/env bash
# check-read-size.sh  -  PreToolUse read gate for the multi-agent pipeline.
#
# The third deterministic hook, and the first on the READ side. The other two
# (pre-commit-check, agent-guard) inspect what the run writes; this one inspects
# what it pays to read.
#
# The bill it exists for: a phase that reads six 900-line files pays for 5,400
# lines at that phase's own rung, and the part it needed was a handful of
# symbols. `offload-ref.sh` already took the other half of that bill - the build
# log, the diff, the test output - by parking the payload and printing a pointer.
# Nothing looked at file reads, because nothing could: only a hook sees a tool
# call before it runs.
#
# Contract (Claude Code PreToolUse hook):
#   - Reads the tool-call JSON on stdin.
#   - Exit 2  -> BLOCK (reason on stderr, shown to the model).
#   - Exit 0  -> allow.
#
# Modes  -  `prefs.global.bulkRead.mode`:
#   off       (default) nothing is inspected; the hook is a no-op.
#   observe   decide and LOG, never block. This is the measurement: run the
#             pipeline normally and metrics.jsonl carries the read-size
#             distribution, so the saving can be estimated before anything is
#             built on top of it.
#   enforce   block a whole-file read over `minLines` and tell the model to
#             delegate it to bulk-read.sh instead.
#
# Why observe is a real mode and not a debug flag: the honest order is measure,
# then route. A gate switched straight to enforce has no baseline to be
# compared against, and "we cut tokens" becomes a claim nothing can check.
#
# Why the development phase is exempt by default: in Claude Code, `Edit` requires
# the same file to have been read first. A gate that blocks reads in Phase 3
# blocks editing. The gate is for the phases that read to UNDERSTAND.
#
# Safety design (fail-OPEN): any internal error, missing python3, missing helper,
# empty payload -> exit 0. It never executes the inspected command, never writes
# to the inspected file, makes no network call, and prints no file content.

set -u

HERE="$(cd "$(dirname "$0")" 2>/dev/null && pwd || true)"
HELPER="$HERE/check-read-size.py"
[ -f "$HELPER" ] || exit 0
command -v python3 >/dev/null 2>&1 || exit 0

PAYLOAD="$(cat 2>/dev/null || true)"
[ -z "$PAYLOAD" ] && exit 0

DECISION="$(printf '%s' "$PAYLOAD" | python3 "$HELPER" 2>/dev/null || true)"
[ -z "$DECISION" ] && exit 0

VERDICT="$(printf '%s' "$DECISION" | cut -f1)"
FILE="$(printf '%s' "$DECISION" | cut -f2)"
LINES="$(printf '%s' "$DECISION" | cut -f3)"
WHY="$(printf '%s' "$DECISION" | cut -f4)"

# Telemetry is best-effort and never gates the decision. `log-metric.sh` already
# tolerates an unwritable directory, so the only guard needed here is the
# script's own presence.
note() {
  [ -x "$HERE/log-metric.sh" ] || return 0
  "$HERE/log-metric.sh" "${MULTI_AGENT_TASK_ID:-unknown}" "${MULTI_AGENT_PHASE:-0}" \
    "read.$1" lines="${LINES:-0}" reason="${WHY:-}" >/dev/null 2>&1 || true
}

case "$VERDICT" in
  OBSERVE)
    note observed
    exit 0 ;;
  BLOCK)
    note blocked
    echo "BLOCKED by check-read-size: ${FILE} is ${LINES} lines." >&2
    echo "Reading it whole costs this phase's rung for a file whose useful part is a few symbols." >&2
    echo "" >&2
    echo "Delegate it, then work from what comes back:" >&2
    echo "  bash \$HOME/.claude/scripts/bulk-read.sh --file '${FILE}' --question '<what you need from it>'" >&2
    echo "" >&2
    echo "The summary carries LINE NUMBERS, so a follow-up Read with an offset/limit around the" >&2
    echo "region you actually need is the intended next step and passes this gate." >&2
    echo "Full text stays on disk under .multi-agent/refs/ if the summary is not enough." >&2
    exit 2 ;;
  *)
    exit 0 ;;
esac
