#!/usr/bin/env bash
# local-dispatch-guard — refuse to start an agent seat on the workstation.
#
# usage: local-dispatch-guard <dispatcher-name>
#   exit 0  — dispatch may proceed
#   exit 97 — headless local dispatch denied; stderr names the remote path
#
# Contract for callers (docs/specs/2026-08-06-execution-plane-insulation.md, P4): every
# dispatcher runs this before spawning a seat and propagates 97 unchanged. Same shape as
# ~/.claude/bin/install-headless-guard: the naive local form fails, so an agent that
# "forgets" cannot comply wrongly.
#
# A terminal on any of fd 0/1/2 means a person is attached, and a human session is never
# blocked for any reason. An agent's Bash subprocess has pipes on all three, so the deny
# lands exactly on the headless dispatches that burn the laptop.
#
# HARNESS_SEAT_CONTAINER=1 (harness seats) and OD_LOCAL_DISPATCH_OK=1 (everything else) are
# set by the launcher that already placed this process on a buildbox: there the machine IS
# the execution plane and nothing is being pushed onto the workstation.
set -uo pipefail

name="${1:-dispatch}"

[[ "${HARNESS_SEAT_CONTAINER:-}" == 1 ]] && exit 0
[[ "${OD_LOCAL_DISPATCH_OK:-}" == 1 ]] && exit 0
[[ -t 0 || -t 1 || -t 2 ]] && exit 0

cat >&2 <<EOF
$name: headless agent dispatch on this workstation is denied (exit 97).
No agent compute runs on the laptop — dispatch the seat on a buildbox instead:
  seat-remote launch --host auto --account <slug> --project <path>
Inventory and the kill switch: deckctl agents list | deckctl agents stop --all
EOF
exit 97
