From 2e5972c476a1295715b916d4bc27a68b147c8c0f Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Tue, 23 Jun 2026 12:56:08 -0400 Subject: [PATCH] docs(integration): document WS0-WS5 components + WS-rollout deploy --- README.md | 4 +++ agent-team/DEPLOY-R720.md | 45 ++++++++++++++++++++++++ agent-team/README.md | 31 ++++++++++++++++- docs/provisioning/OPERATOR-RUNBOOK.md | 49 +++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f282c3..39d9b23 100644 --- a/README.md +++ b/README.md @@ -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. +## 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 1. Install dependencies: `pip install -r requirements.txt` diff --git a/agent-team/DEPLOY-R720.md b/agent-team/DEPLOY-R720.md index 18cc9a7..71f9a8f 100644 --- a/agent-team/DEPLOY-R720.md +++ b/agent-team/DEPLOY-R720.md @@ -118,6 +118,51 @@ The unit runs `python3 run-team.py serve` from secrets from `EnvironmentFile=/home/adam/secrev.env`. `Restart=on-failure` keeps 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) Demonstrate all four once the service is live. Map each to the operator commands diff --git a/agent-team/README.md b/agent-team/README.md index bf43434..8219fe1 100644 --- a/agent-team/README.md +++ b/agent-team/README.md @@ -39,6 +39,10 @@ agent-team/ graph.py # LangGraph wiring: P1 (intake→clarify→plan) + opt-in P2 # review loop + opt-in P3 build/verify subgraph 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 ci_gate.py # §3.3.2 pure-code authenticated-Checks PASS/FAIL gate task_model.py / state_store.py @@ -52,17 +56,26 @@ agent-team/ builders.py + builders_llm.py # candidate diff (DeepSeek) — INERT, proposes only verifier.py + verifier_llm.py # ci_gate sole PASS authority; LLM = fix-proposer 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 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 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) systemd/ # agent-team-coordinator.service (not installed) DEPLOY-R720.md # provisioning runbook (snapshot-first, rsync, tokens, demo) 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 ` 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 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 `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 ``` diff --git a/docs/provisioning/OPERATOR-RUNBOOK.md b/docs/provisioning/OPERATOR-RUNBOOK.md index 806b583..98f1f38 100644 --- a/docs/provisioning/OPERATOR-RUNBOOK.md +++ b/docs/provisioning/OPERATOR-RUNBOOK.md @@ -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) These come from the nightly checker run, not the coordinator daemon (design §6.4,