docs(integration): document WS0-WS5 components + WS-rollout deploy
This commit is contained in:
parent
0689d1696c
commit
2e5972c476
4 changed files with 128 additions and 1 deletions
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <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
|
||||
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
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Reference in a new issue