docs(integration): document WS0-WS5 components + WS-rollout deploy

This commit is contained in:
Adam Moussa 2026-06-23 12:56:08 -04:00
parent 0689d1696c
commit 2e5972c476
4 changed files with 128 additions and 1 deletions

View file

@ -123,6 +123,10 @@ The `security-review/` subsystem is a high-recall, anti-complacency security gat
See `security-review/README.md` for full detail and `security-review/DEPLOY-R720.md` for the VM runbook. See `security-review/README.md` for full detail and `security-review/DEPLOY-R720.md` for the VM runbook.
## agent-team (R720 durable SDLC pipeline)
The `agent-team/` subsystem is a separate, durable, human-gated SDLC pipeline (LangGraph + SQLite ledger) that runs as an always-on coordinator daemon on the same `sh-secrev` R720 VM. It is distinct from this stateless router: it persists tasks across restarts and runs INTAKE → CLARIFY → PLAN → REVIEW (build/verify is deploy-gated and inert). It reuses this orchestrator's `models.py` for its non-Claude invokers, and exposes an opt-in FastAPI HTTP API (loopback, bearer auth) plus a `/delegate` Claude Code plugin hook (`sea-haven-claude-plugin/`). See `agent-team/README.md` and `agent-team/DEPLOY-R720.md`.
## Setup ## Setup
1. Install dependencies: `pip install -r requirements.txt` 1. Install dependencies: `pip install -r requirements.txt`

View file

@ -118,6 +118,51 @@ The unit runs `python3 run-team.py serve` from
secrets from `EnvironmentFile=/home/adam/secrev.env`. `Restart=on-failure` keeps secrets from `EnvironmentFile=/home/adam/secrev.env`. `Restart=on-failure` keeps
it up across transient faults; `journalctl -u` is the live log. it up across transient faults; `journalctl -u` is the live log.
## 4b. WS0–WS5 rollout — UPDATE an already-deployed box
The steps above (§1–4) are the **first-time** P1 provision. To bring an
already-deployed coordinator up to the WS0–WS5 rollout, use the attended update
script rather than re-running the manual steps:
```
# From the Mac, after the WS branches have merged to main, snapshot first:
agent-team/scripts/deploy-r720-ws-rollout.sh
```
It is an UPDATE (not a provision): it snapshots-reminds, rsyncs the new code,
rsyncs the engineering handbook to the box, installs the new deps, appends the
new secrets if absent, restarts the coordinator, and smoke-tests. It is
idempotent and fails loudly. What goes **live** after it: WS1 in-process
multi-model invokers (`bind_multi_invoker`, already wired), the WS5 handbook
`context_provider` injected into the planner prompt, and the WS2 Slack
`/new-task` command (AUTHZ-01 owner-allowlist gated). The P3 dispatch/build-verify
path stays **inert** (gated behind the `agent-apply` GitHub Environment approval).
**New venv deps** (the coordinator does not need them; only the optional HTTP
API does) — now pinned in the root `requirements.txt`:
| pip dep | Why |
|---|---|
| `fastapi==0.136.1` | the WS1 HTTP API app (`agent_team/api.py`) |
| `uvicorn==0.46.0` | ASGI server for `api.serve()` |
**New env vars** — append to `~/secrev.env` (mode 600, never committed):
- `SEA_HAVEN_HANDBOOK_DIR` — where `load_handbook_conventions()` reads the
engineering handbook (the script syncs it to `/home/adam/.sea-haven/engineering-handbook`
by default; this var must match). Fail-safe: if the dir is missing the
`context_provider` returns `""` and the planner runs without handbook context.
- `AGENT_TEAM_API_TOKEN` — bearer token for the HTTP API / `/delegate` hook
**only**. Not needed by the coordinator daemon itself. The HTTP API refuses to
start if this is unset/empty.
**The HTTP API is a separate, opt-in process** — it is **not** started by the
coordinator daemon. Run it explicitly (`api.serve()`, binds `127.0.0.1:8765`,
bearer auth) only if you want the `/delegate` Claude Code hook or the
`POST /tasks` / `GET /tasks/{thread_id}` / `POST /orchestrator/invoke` endpoints.
The `/docs` + `/openapi` routes are disabled and it binds loopback by design (do
not change to `0.0.0.0`). See the deploy script's step 6 for how to start it.
## 5. P1 live exit-criteria demo (§3.3.1) ## 5. P1 live exit-criteria demo (§3.3.1)
Demonstrate all four once the service is live. Map each to the operator commands Demonstrate all four once the service is live. Map each to the operator commands

View file

@ -39,6 +39,10 @@ agent-team/
graph.py # LangGraph wiring: P1 (intake→clarify→plan) + opt-in P2 graph.py # LangGraph wiring: P1 (intake→clarify→plan) + opt-in P2
# review loop + opt-in P3 build/verify subgraph # review loop + opt-in P3 build/verify subgraph
invoker.py # §3.1 real Claude path (subscription-OAuth / API / Bedrock) invoker.py # §3.1 real Claude path (subscription-OAuth / API / Bedrock)
invoker_multi.py # WS1 in-process non-Claude invokers (GPT-4.1 / DeepSeek /
# Gemini via the orchestrator's models.py); bind_multi_invoker()
api.py # WS1 FastAPI HTTP API (bearer auth, 127.0.0.1:8765) — SEPARATE
# opt-in process (api.serve()), NOT started by the coordinator
billing.py # §3.1 claude_invoke billing-mode seam billing.py # §3.1 claude_invoke billing-mode seam
ci_gate.py # §3.3.2 pure-code authenticated-Checks PASS/FAIL gate ci_gate.py # §3.3.2 pure-code authenticated-Checks PASS/FAIL gate
task_model.py / state_store.py task_model.py / state_store.py
@ -52,17 +56,26 @@ agent-team/
builders.py + builders_llm.py # candidate diff (DeepSeek) — INERT, proposes only builders.py + builders_llm.py # candidate diff (DeepSeek) — INERT, proposes only
verifier.py + verifier_llm.py # ci_gate sole PASS authority; LLM = fix-proposer verifier.py + verifier_llm.py # ci_gate sole PASS authority; LLM = fix-proposer
build_verify_subgraph.py # P3 BUILD→VERIFY topology (opt-in) build_verify_subgraph.py # P3 BUILD→VERIFY topology (opt-in)
handbook.py # WS5 load_handbook_conventions (handbook seam,
# fail-safe → "" if dir missing); planner context
dispatch_invoker.py # WS3 auto-dispatch node — INERT (NOT wired live)
transport/ # one adapter contract + a live impl per channel transport/ # one adapter contract + a live impl per channel
base.py # Transport ABC + QuestionSet / NormalizedAnswer base.py # Transport ABC + QuestionSet / NormalizedAnswer
slack_adapter.py + slack_live.py + slack_listener.py # Block Kit + Socket Mode slack_adapter.py + slack_live.py + slack_listener.py # Block Kit + Socket Mode + /new-task
github_adapter.py + github_live.py + github_intake.py # issue-comment + issue intake github_adapter.py + github_live.py + github_intake.py # issue-comment + issue intake
claude_code_adapter.py + claude_code_live.py # file-drop responder claude_code_adapter.py + claude_code_live.py # file-drop responder
scripts/ # deploy-r720-ws-rollout.sh — attended WS0–WS5 UPDATE of the box
ci/ # §3.3.2 split-job CI apply/verify workflow (DEPLOY-GATED) ci/ # §3.3.2 split-job CI apply/verify workflow (DEPLOY-GATED)
systemd/ # agent-team-coordinator.service (not installed) systemd/ # agent-team-coordinator.service (not installed)
DEPLOY-R720.md # provisioning runbook (snapshot-first, rsync, tokens, demo) DEPLOY-R720.md # provisioning runbook (snapshot-first, rsync, tokens, demo)
tests/ # pytest, one module per source module + sim harness tests/ # pytest, one module per source module + sim harness
``` ```
The Claude Code plugin lives in a sibling top-level dir, `../sea-haven-claude-plugin/`
(CLAUDE.md, settings.template.json, `hooks/user_prompt_submit.py`): a
`UserPromptSubmit` hook that forwards `/delegate <task>` prompts from Claude Code
to the HTTP API's `POST /tasks` (env `AGENT_TEAM_API_URL` / `AGENT_TEAM_API_TOKEN`).
The top directory is kebab-case (`agent-team/`); the importable package is The top directory is kebab-case (`agent-team/`); the importable package is
snake_case (`agent_team/`), per the engineering handbook. snake_case (`agent_team/`), per the engineering handbook.
@ -85,6 +98,22 @@ snake_case (`agent_team/`), per the engineering handbook.
GPT-4.1 (review) and DeepSeek (builders) route through the local orchestrator GPT-4.1 (review) and DeepSeek (builders) route through the local orchestrator
`run.py`. Switching Claude billing is a config flip. `run.py`. Switching Claude billing is a config flip.
## WS0–WS5 rollout glossary
The "WS-rollout" (workstreams 0–5) layered HTTP/integration surfaces onto the
P1–P4 pipeline. What is **live** vs **inert** after the rollout:
| WS | What it adds | Live? |
|---|---|---|
| WS1 | `invoker_multi.py` (in-process GPT-4.1 / DeepSeek / Gemini via the orchestrator's `models.py`) + `api.py` (FastAPI HTTP API, bearer auth via `AGENT_TEAM_API_TOKEN`, binds `127.0.0.1:8765`, `/docs`+`/openapi` disabled, concurrency-capped) | `bind_multi_invoker()` wired in `run-team.py` `_cmd_serve` (LIVE); the **HTTP API is a separate opt-in process** (`api.serve()`), NOT started by the coordinator |
| WS5 | `nodes/handbook.py` `load_handbook_conventions` (reads `SEA_HAVEN_HANDBOOK_DIR` or `~/.sea-haven/engineering-handbook`, fail-safe → `""`); `retriever.py` `save_memory` writes to a `_box-drafts/` review queue | LIVE — the planner prompt receives the handbook via the `context_provider` seam in `run-team.py` `_build_coordinator` |
| WS2/WS0/WS4 | Slack `/new-task` slash command (AUTHZ-01 owner-allowlist gated) → `Coordinator.set_new_task_callback`; the `sea-haven-claude-plugin/` (CLAUDE.md, settings, `/delegate` `UserPromptSubmit` hook) | LIVE (`/new-task` wired in `serve`); the plugin/HTTP-API path is opt-in |
| WS3 | `nodes/dispatch_invoker.py` (auto-dispatch LangGraph node) + graph/coordinator wiring | **INERT — NOT wired live.** The `agent-apply` GitHub Environment human-approval gate is KEPT; the P3 dispatch/build-verify path stays inert pending per-task `run_id` plumbing + a CI-boundary security re-review |
The HTTP API endpoints: `POST /tasks` (start a task), `GET /tasks/{thread_id}`
(status), `POST /orchestrator/invoke` (one-shot model invoke). See
`DEPLOY-R720.md` for the WS-rollout deploy (`scripts/deploy-r720-ws-rollout.sh`).
## Running the tests ## Running the tests
``` ```

View file

@ -237,6 +237,55 @@ journalctl -u agent-team-coordinator.service -e | grep -i "inbound Slack listene
--- ---
## Incident 5b — WS0–WS5 surfaces (HTTP API, /new-task, handbook context)
These were added by the WS-rollout (see `agent-team/DEPLOY-R720.md` §4b). They
layer onto the coordinator; none of them should take down the maintenance loop.
- **HTTP API down / unreachable** — the WS1 FastAPI app (`agent_team/api.py`) is
a **separate, opt-in process** (`api.serve()`, `127.0.0.1:8765`, bearer auth),
**not** started by the coordinator daemon. If `/delegate` from Claude Code or
`POST /tasks` over HTTP stops working, the coordinator itself is unaffected —
check the API process separately:
```bash
curl -sS -o /dev/null -w '%{http_code}\n' \
-H "Authorization: Bearer $AGENT_TEAM_API_TOKEN" http://127.0.0.1:8765/tasks
# 405 = API up + authed (GET not allowed on /tasks); 000 = process down;
# 401 = AGENT_TEAM_API_TOKEN mismatch (client vs ~/secrev.env).
```
The API refuses to start if `AGENT_TEAM_API_TOKEN` is unset/empty (logs a
`RuntimeError`). Fix the token, restart the API process. Tasks already in the
ledger are unaffected — the API is only an *intake/invoke* front door; answer
via Slack or the CLI as usual.
- **`/new-task` Slack command not responding** — the WS2 slash command is
AUTHZ-01 owner-allowlist gated and routes through the same Socket Mode listener
as answers. If it silently does nothing, it is almost always the owner
allowlist (same failure mode as Incident 3's live-Slack path):
```bash
journalctl -u agent-team-coordinator.service -e | grep -iE "new-task|owner|unauthorized"
# unauthorized sender / unconfigured allowlist -> fix AGENT_TEAM_SLACK_OWNER_IDS
```
Fallback: start the task from the CLI (`run-team.py start --task "..."`) or the
HTTP API. If the listener itself is down, see Incident 5 (inbound listener).
- **Handbook dir missing → planner runs without handbook context** — the WS5
`context_provider` (`load_handbook_conventions`) is **fail-safe**: if
`SEA_HAVEN_HANDBOOK_DIR` (or `~/.sea-haven/engineering-handbook`) is missing or
unreadable it returns `""` and the planner runs normally, just without handbook
conventions injected. This is **degraded, not broken** — no park, no alarm.
Confirm and restore:
```bash
grep '^SEA_HAVEN_HANDBOOK_DIR=' ~/secrev.env
ls "$(grep '^SEA_HAVEN_HANDBOOK_DIR=' ~/secrev.env | cut -d= -f2)" # dir present + populated?
```
Re-sync the handbook (the deploy script does this) and restart the daemon so
the planner picks it back up. The WS3 dispatch node is **inert** (gated) and
should never appear in pipeline activity; if it does, treat as an unexpected
state and escalate.
---
## Incident 6 — COMPLACENCY / COVERAGE alarms (Plane-1 checkers) ## Incident 6 — COMPLACENCY / COVERAGE alarms (Plane-1 checkers)
These come from the nightly checker run, not the coordinator daemon (design §6.4, These come from the nightly checker run, not the coordinator daemon (design §6.4,