2026-06-23 12:43:39 -04:00
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
# deploy-r720-ws-rollout.sh — attended UPDATE of the live R720 agent-team
|
|
|
|
|
|
# coordinator to the WS0–WS5 rollout (PRs #43–#46 + the activation wiring).
|
|
|
|
|
|
#
|
|
|
|
|
|
# This is an UPDATE, not a first-time provision: P1 is already deployed per
|
|
|
|
|
|
# agent-team/DEPLOY-R720.md (repo rsynced to ~/orchestrator, venv at
|
|
|
|
|
|
# agent-team/.venv, systemd unit agent-team-coordinator.service running).
|
|
|
|
|
|
# Run this from the MAC, after the WS branches have merged to main. It rsyncs
|
|
|
|
|
|
# the new code, installs the new deps, appends the new secrets if absent, syncs
|
|
|
|
|
|
# the engineering handbook, restarts the coordinator, and smoke-tests.
|
|
|
|
|
|
#
|
|
|
|
|
|
# It is idempotent and FAILS LOUDLY. It changes a live box, so:
|
|
|
|
|
|
# 1) SNAPSHOT FIRST (Hyper-V checkpoint of sh-secrev on the R720 host).
|
|
|
|
|
|
# 2) It prompts before the restart.
|
|
|
|
|
|
#
|
|
|
|
|
|
# What goes LIVE after this (the safe, ungated seams):
|
|
|
|
|
|
# * WS1 in-process multi-model invokers (bind_multi_invoker, already wired)
|
|
|
|
|
|
# * WS5 handbook context_provider injected into the planner prompt
|
|
|
|
|
|
# * WS2 Slack /new-task -> start a task (AUTHZ-01 owner allowlist gated)
|
|
|
|
|
|
# The HTTP API (WS1) + the /delegate hook are OPTIONAL and started separately
|
|
|
|
|
|
# (see step 6). The P3 dispatch/build-verify path stays INERT (gated).
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
|
|
# ── Config (override via env) ────────────────────────────────────────────────
|
|
|
|
|
|
BOX="${BOX:-adam@10.10.60.120}"
|
|
|
|
|
|
SSH_KEY="${SSH_KEY:-$HOME/.ssh/r720_seahaven}"
|
|
|
|
|
|
REPO_LOCAL="${REPO_LOCAL:-$HOME/Documents/repositories/orchestrator}"
|
|
|
|
|
|
HANDBOOK_LOCAL="${HANDBOOK_LOCAL:-$HOME/Documents/repositories/engineering-handbook}"
|
|
|
|
|
|
# Where the handbook lands on the box; must match SEA_HAVEN_HANDBOOK_DIR below.
|
|
|
|
|
|
HANDBOOK_REMOTE="${HANDBOOK_REMOTE:-/home/adam/.sea-haven/engineering-handbook}"
|
|
|
|
|
|
SSH="ssh -i ${SSH_KEY} ${BOX}"
|
|
|
|
|
|
|
|
|
|
|
|
say() { printf '\n\033[1;36m== %s\033[0m\n' "$*"; }
|
|
|
|
|
|
confirm() { read -r -p "$1 [y/N] " a; [ "$a" = "y" ] || [ "$a" = "Y" ]; }
|
|
|
|
|
|
|
|
|
|
|
|
say "Preflight"
|
|
|
|
|
|
[ -f "${SSH_KEY}" ] || { echo "missing SSH key ${SSH_KEY}"; exit 1; }
|
|
|
|
|
|
$SSH true || { echo "cannot reach ${BOX}"; exit 1; }
|
|
|
|
|
|
echo "SNAPSHOT REMINDER: take a Hyper-V checkpoint of sh-secrev on the R720 host now."
|
|
|
|
|
|
confirm "Snapshot taken and ready to update the LIVE coordinator?" || { echo "aborted"; exit 1; }
|
|
|
|
|
|
|
|
|
|
|
|
say "1. rsync repo (Mac -> box; same excludes as the P1 runbook)"
|
|
|
|
|
|
rsync -av --exclude .env --exclude .venv --exclude .git --exclude '__pycache__' \
|
|
|
|
|
|
"${REPO_LOCAL}/" "${BOX}:orchestrator/"
|
|
|
|
|
|
|
|
|
|
|
|
say "2. rsync engineering handbook -> ${HANDBOOK_REMOTE} (WS5 context_provider source)"
|
|
|
|
|
|
if [ -d "${HANDBOOK_LOCAL}" ]; then
|
|
|
|
|
|
$SSH "mkdir -p ${HANDBOOK_REMOTE}"
|
|
|
|
|
|
rsync -av --delete --exclude .git "${HANDBOOK_LOCAL}/" "${BOX}:${HANDBOOK_REMOTE}/"
|
|
|
|
|
|
else
|
|
|
|
|
|
echo "WARN: ${HANDBOOK_LOCAL} not found; context_provider will return '' (fail-safe). Skipping."
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-06-23 13:37:11 -04:00
|
|
|
|
say "3. Install venv deps from the pinned requirements.txt"
|
|
|
|
|
|
# Install the FULL pinned set into the agent-team venv. This includes the
|
|
|
|
|
|
# non-Claude model stack (langchain-anthropic/-openai/-google-genai/-community)
|
|
|
|
|
|
# that the in-process invokers (WS1: GPT-4.1 review, Gemini scan, DeepSeek build)
|
|
|
|
|
|
# import via models.py — WITHOUT these, models.py fails to import and the review
|
|
|
|
|
|
# loop silently fail-closes to REQUEST_CHANGES (the non-Claude models never run).
|
|
|
|
|
|
# Also brings fastapi/uvicorn (WS1 HTTP API). Leaves the venv-only deps that are
|
|
|
|
|
|
# NOT in requirements.txt (claude-agent-sdk, slack_sdk, slack_bolt) untouched.
|
|
|
|
|
|
$SSH 'cd ~/orchestrator/agent-team && . .venv/bin/activate && pip install --upgrade -r ~/orchestrator/requirements.txt'
|
2026-06-23 12:43:39 -04:00
|
|
|
|
|
|
|
|
|
|
say "4. Append new secrets to ~/secrev.env if absent (mode 600, never committed)"
|
|
|
|
|
|
# AGENT_TEAM_API_TOKEN: required only if you run the HTTP API / /delegate hook.
|
|
|
|
|
|
# SEA_HAVEN_HANDBOOK_DIR: where load_handbook_conventions() reads from.
|
|
|
|
|
|
$SSH "bash -s" <<REMOTE
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
touch ~/secrev.env && chmod 600 ~/secrev.env
|
|
|
|
|
|
grep -q '^SEA_HAVEN_HANDBOOK_DIR=' ~/secrev.env || \
|
|
|
|
|
|
echo 'SEA_HAVEN_HANDBOOK_DIR=${HANDBOOK_REMOTE}' >> ~/secrev.env
|
|
|
|
|
|
if grep -q '^AGENT_TEAM_API_TOKEN=' ~/secrev.env; then
|
|
|
|
|
|
echo 'AGENT_TEAM_API_TOKEN already set; leaving as-is.'
|
|
|
|
|
|
else
|
|
|
|
|
|
echo 'AGENT_TEAM_API_TOKEN NOT set. Add it now (generated on the Mac):'
|
|
|
|
|
|
echo ' echo "AGENT_TEAM_API_TOKEN=<token>" >> ~/secrev.env && chmod 600 ~/secrev.env'
|
|
|
|
|
|
echo '(only needed for the HTTP API / auto-delegate hook; the coordinator runs without it.)'
|
|
|
|
|
|
fi
|
|
|
|
|
|
REMOTE
|
|
|
|
|
|
|
|
|
|
|
|
say "5. Restart the coordinator daemon"
|
|
|
|
|
|
confirm "Restart agent-team-coordinator.service now?" || { echo "skipped restart"; exit 0; }
|
|
|
|
|
|
$SSH 'sudo systemctl restart agent-team-coordinator.service && sleep 2 && systemctl is-active agent-team-coordinator.service'
|
|
|
|
|
|
$SSH 'journalctl -u agent-team-coordinator.service -n 30 --no-pager'
|
|
|
|
|
|
|
|
|
|
|
|
say "6. (OPTIONAL) HTTP API + /delegate hook — start only if you want them"
|
|
|
|
|
|
cat <<'NOTE'
|
|
|
|
|
|
The coordinator now serves WS5 context + WS2 /new-task. The WS1 HTTP API is a
|
|
|
|
|
|
SEPARATE process (api.serve(), 127.0.0.1:8765, bearer auth). To run it:
|
|
|
|
|
|
- ensure AGENT_TEAM_API_TOKEN is set in ~/secrev.env
|
|
|
|
|
|
- run: cd ~/orchestrator/agent-team && . .venv/bin/activate && \
|
|
|
|
|
|
python3 -c "from agent_team.api import serve; serve()"
|
|
|
|
|
|
- (for persistence, add a second systemd unit; not auto-installed here.)
|
|
|
|
|
|
Then set AGENT_TEAM_API_TOKEN + AGENT_TEAM_API_URL in the Mac Claude Code env
|
|
|
|
|
|
to enable the /delegate hook (sea-haven-claude-plugin).
|
|
|
|
|
|
NOTE
|
|
|
|
|
|
|
|
|
|
|
|
say "7. SMOKE TESTS (manual)"
|
|
|
|
|
|
cat <<'SMOKE'
|
|
|
|
|
|
a) Coordinator up: systemctl is-active agent-team-coordinator.service -> active
|
|
|
|
|
|
b) Handbook visible: cd ~/orchestrator/agent-team && . .venv/bin/activate && \
|
|
|
|
|
|
python3 -c "from agent_team.nodes.handbook import load_handbook_conventions as h; print(bool(h()))" -> True
|
|
|
|
|
|
c) Slack /new-task: post "/new-task add a smoke-test file" in #agent-team as an
|
|
|
|
|
|
allowlisted owner -> the bot replies with a clarifying question.
|
|
|
|
|
|
d) (if API running) auth: curl -s -o /dev/null -w '%{http_code}' \
|
|
|
|
|
|
-H "Authorization: Bearer $AGENT_TEAM_API_TOKEN" http://127.0.0.1:8765/tasks -> 405 (GET not allowed = API up + authed)
|
|
|
|
|
|
ROLLBACK: restore the pre-update Hyper-V checkpoint (one-command revert).
|
|
|
|
|
|
SMOKE
|
|
|
|
|
|
say "Done."
|